diff --git a/node/tessel-export.js b/node/tessel-export.js index 1e828a6..45a8d5d 100644 --- a/node/tessel-export.js +++ b/node/tessel-export.js @@ -917,6 +917,8 @@ Tessel.SPI = function(params, port) { this.chipSelectActive = params.chipSelectActive === 'high' || params.chipSelectActive === 1 ? 1 : 0; + this.chipSelectDelay = (params.chipSelectDelayUs/1e3) || 0; + if (this.chipSelectActive) { // active high, pull low for now this.chipSelect.low(); @@ -969,11 +971,21 @@ Tessel.SPI = function(params, port) { }; Tessel.SPI.prototype.send = function(data, callback) { - this._port.cork(); + this.chipSelect.low(); - this._port._tx(data, callback); - this.chipSelect.high(); - this._port.uncork(); + + setTimeout(() => { + // Only cork if we have specified a target delay, otherwise delay increases + if (this.chipSelectDelay > 0) { + this._port.cork(); + } + this._port._tx(data, callback); + this.chipSelect.high(); + if (this.chipSelectDelay > 0) { + this._port.uncork(); + } + }, this.chipSelectDelay); + }; Tessel.SPI.prototype.disable = function() { @@ -984,19 +996,36 @@ Tessel.SPI.prototype.disable = function() { }; Tessel.SPI.prototype.receive = function(data_len, callback) { - this._port.cork(); + this.chipSelect.low(); - this._port._rx(data_len, callback); - this.chipSelect.high(); - this._port.uncork(); + + setTimeout(() => { + // Only cork if we have specified a target delay, otherwise delay increases + if (this.chipSelectDelay > 0) { + this._port.cork(); + } + this._port._rx(data_len, callback); + if (this.chipSelectDelay > 0) { + this._port.uncork(); + } + }, this.chipSelectDelay); }; Tessel.SPI.prototype.transfer = function(data, callback) { - this._port.cork(); + this.chipSelect.low(); - this._port._txrx(data, callback); - this.chipSelect.high(); - this._port.uncork(); + + setTimeout(() => { + // Only cork if we have specified a target delay, otherwise delay increases + if (this.chipSelectDelay > 0) { + this._port.cork(); + } + this._port._txrx(data, callback); + this.chipSelect.high(); + if (this.chipSelectDelay > 0) { + this._port.uncork(); + } + }, this.chipSelectDelay); }; Tessel.UART = function(port, options) { @@ -1509,7 +1538,7 @@ function getWifiInfo() { if (bcastMatches === null) { recursiveWifi(network); } else { - // Successful matches will have a result that looks like: + // Successful matches will have a result that looks like: // ["Bcast:0.0.0.0", "Bcast", "0.0.0.0"] if (bcastMatches.length === 3) { network.ip = bcastMatches[2]; diff --git a/node/test/unit/tessel.js b/node/test/unit/tessel.js index e3a980a..acfa3d9 100644 --- a/node/test/unit/tessel.js +++ b/node/test/unit/tessel.js @@ -2354,7 +2354,10 @@ exports['Tessel.SPI'] = { return this.socket; }.bind(this)); - this.tessel = new Tessel(); + // We want to disable other ports so that we can pass around mock data. + // Otherwise, port A and B get 'readable' events without having any + // queued callbacks (because they are queued on the port we create below). + this.tessel = new Tessel({ ports: {A: false, B: false} }); this.cork = sandbox.stub(Tessel.Port.prototype, 'cork'); this.uncork = sandbox.stub(Tessel.Port.prototype, 'uncork'); @@ -2432,6 +2435,59 @@ exports['Tessel.SPI'] = { test.ok(this.spiDisable.calledOnce, true); test.done(); + }, + chipSelectDelayOptions: function(test) { + test.expect(2); + + var delayUs = 10000; + var usToMs = 1000; + + var s1 = new this.port.SPI(); + + var s2 = new this.port.SPI({chipSelectDelayUs: delayUs}); + + test.equal(s1.chipSelectDelay, 0); + + test.equal(s2.chipSelectDelay, delayUs/usToMs); + + test.done(); + }, + chipSelectDelayFunctionality: function(test) { + test.expect(2); + + var delayUs = 10000; + var usToMs = 1000; + + var s1 = new this.port.SPI({chipSelectDelayUs: delayUs}); + + var timeoutSpy = sandbox.spy(global, 'setTimeout'); + + s1.transfer(new Buffer(1), () => { + // One timeout called within transfer and one in the test below + test.equal(timeoutSpy.callCount, 2); + // The first call (chip select delay) waited an appropriate amount of time) + test.equal(timeoutSpy.getCall(0).args[1], delayUs/usToMs); + // Restore setTimeout + timeoutSpy.restore(); + test.done(); + }); + + // Wait until after the chip select delay has passed + // and the callback was enqueued. Only return data on the first request + setTimeout(() => { + var called = false; + this.socket.read = () => { + if (called) { + return new Buffer([]); + } + called = true; + + return new Buffer([0x84, 0x1]); + }; + + // Prod the socket to read our buffer + s1._port.sock.emit('readable'); + }, (delayUs / 10) * 2 ); } };