From 36f024c5caef08daf82d77424fdb5494dcc9cd02 Mon Sep 17 00:00:00 2001 From: Vicary Archangel Date: Mon, 22 Oct 2012 21:15:46 +0800 Subject: [PATCH 01/16] Tinkerings. 1. Correctly expose constants as documented. 2. Adding "delay" as a property in the C wrapper. 3. Changed internal function full_duplex_transfer to actually accept speed, delay and size as arguments. 4. Passing aforementioned values into spi_ioc_transfer, which is ignored currently. --- spi.js | 57 ++++++++++--------- src/spi_binding.cc | 139 ++++++++++++++++++++++++++++++++++++--------- src/spi_binding.h | 17 ++++-- 3 files changed, 153 insertions(+), 60 deletions(-) diff --git a/spi.js b/spi.js index 75923de..f31a7e0 100644 --- a/spi.js +++ b/spi.js @@ -18,21 +18,22 @@ var _spi = require('bindings')('_spi.node'); -var MODE = [ - _spi.SPI_MODE_0, - _spi.SPI_MODE_1, - _spi.SPI_MODE_2, - _spi.SPI_MODE_3 -]; +// Consistance with docs +var MODE = { + MODE_0: _spi.MODE_0 +, MODE_1: _spi.MODE_1 +, MODE_2: _spi.MODE_2 +, MODE_3: _spi.MODE_3 +}; var CS = { - 'high': _spi.SPI_CS_HIGH, - 'low': 0, - 'none': _spi.SPI_NO_CS + none: _spi.NO_CS +, high: _spi.CS_HIGH +, low: _spi.CS_LOW }; function isFunction(object) { - return object && getClass.call(object) == '[object Function]'; + return object && typeof object == 'function'; } var Spi = function(device, options, callback) { @@ -43,44 +44,48 @@ var Spi = function(device, options, callback) { options = {}; } - options = options || { }; // Default to an empty object + options = options || {}; // Default to an empty object for(var attrname in options) { var value = options[attrname]; if (attrname in this._spi) { - console.trace("Setting " + attrname + "=" + value); + // console.trace("Setting " + attrname + "=" + value); this._spi[attrname](value); } } - this._spi.open(device); + this.device = device; + + // this._spi.open(device); - callback(this); // TODO: Update once open is async; + isFunction(callback) && callback(this); // TODO: Update once open is async; +} + +Spi.prototype.open = function() { + return this._spi.open(this.device); +} + +Spi.prototype.close = function() { + return this._spi.close(); } Spi.prototype.write = function(buf, callback) { this._spi.transfer(buf, null); - if (callback !== undefined) { - callback(this, buf); // TODO: Update once transfer is async; - } + isFunction(callback) && callback(this, buf); // TODO: Update once open is async; } Spi.prototype.read = function(buf, callback) { this._spi.transfer(null, buf); - if (callback !== undefined) { - callback(this, buf); // TODO: Update once transfer is async; - } + isFunction(callback) && callback(this, buf); // TODO: Update once open is async; } -Spi.prototype.transfer = function(wrbuf, rdbuf, callback) { - this._spi.transfer(wrbuf, rdbuf); +Spi.prototype.transfer = function(txbuf, rxbuf, callback) { + this._spi.transfer(txbuf, rxbuf); - if (callback !== undefined) { - callback(this, rdbuf); // TODO: Update once transfer is async; - } + isFunction(callback) && callback(this, rxbuf); // TODO: Update once open is async; } module.exports.MODE = MODE; module.exports.CS = CS; -module.exports.Spi = Spi; +module.exports.Spi = Spi; \ No newline at end of file diff --git a/src/spi_binding.cc b/src/spi_binding.cc index d4757fc..b473897 100644 --- a/src/spi_binding.cc +++ b/src/spi_binding.cc @@ -43,54 +43,114 @@ Persistent Spi::constructor; void Spi::Initialize(Handle target) { HandleScope scope; + // var t = function() {}; Local t = FunctionTemplate::New(New); + // t = function _spi() {}; t->SetClassName(String::NewSymbol("_spi")); t->InstanceTemplate()->SetInternalFieldCount(1); + // t.prototype.open = open; t->PrototypeTemplate()->Set(String::NewSymbol("open"), FunctionTemplate::New(Open)->GetFunction()); + // t.prototype.close = close; t->PrototypeTemplate()->Set(String::NewSymbol("close"), FunctionTemplate::New(Close)->GetFunction()); + + // t.prototype.transfer = transfer; t->PrototypeTemplate()->Set(String::NewSymbol("transfer"), FunctionTemplate::New(Transfer)->GetFunction()); + + // t.prototype.mode = GetSetMode; t->PrototypeTemplate()->Set(String::NewSymbol("mode"), FunctionTemplate::New(GetSetMode)->GetFunction()); + + // t.prototype.chipSelect = GetSetChipSelect; t->PrototypeTemplate()->Set(String::NewSymbol("chipSelect"), FunctionTemplate::New(GetSetChipSelect)->GetFunction()); - t->PrototypeTemplate()->Set(String::NewSymbol("bitsPerWord"), + + // t.prototype.bitsPerWord = GetSetBitsPerWord; + t->PrototypeTemplate()->Set(String::NewSymbol("size"), FunctionTemplate::New(GetSetBitsPerWord)->GetFunction()); + + // t.prototype.bitOrder = GetSetBitOrder; t->PrototypeTemplate()->Set(String::NewSymbol("bitOrder"), FunctionTemplate::New(GetSetBitOrder)->GetFunction()); + + // t.prototype.maxSpeed = GetSetMaxSpeed; t->PrototypeTemplate()->Set(String::NewSymbol("maxSpeed"), FunctionTemplate::New(GetSetMaxSpeed)->GetFunction()); + + // t.prototype.halfDuplex = GetSet3Wire; t->PrototypeTemplate()->Set(String::NewSymbol("halfDuplex"), FunctionTemplate::New(GetSet3Wire)->GetFunction()); + + // t.prototype.delay = GetSetDelay; + t->PrototypeTemplate()->Set(String::NewSymbol("delay"), + FunctionTemplate::New(GetSetDelay)->GetFunction()); + + // t.prototype.loopback = GetSetLoop; t->PrototypeTemplate()->Set(String::NewSymbol("loopback"), FunctionTemplate::New(GetSetLoop)->GetFunction()); + // var constructor = t; // in context of new. constructor = Persistent::New(t->GetFunction()); - target->Set(String::NewSymbol("_spi"), constructor); - - // SPI modes - NODE_DEFINE_CONSTANT(target, SPI_MODE_0); - NODE_DEFINE_CONSTANT(target, SPI_MODE_1); - NODE_DEFINE_CONSTANT(target, SPI_MODE_2); - NODE_DEFINE_CONSTANT(target, SPI_MODE_3); - - // Logic Level High for Chip Select - NODE_DEFINE_CONSTANT(target, SPI_CS_HIGH); - // No Chip Select - NODE_DEFINE_CONSTANT(target, SPI_NO_CS); + // exports._spi = constructor; + target->Set(String::NewSymbol("_spi"), constructor); + /* Should be change here. + + // SPI modes + NODE_DEFINE_CONSTANT(target, SPI_MODE_0); + NODE_DEFINE_CONSTANT(target, SPI_MODE_1); + NODE_DEFINE_CONSTANT(target, SPI_MODE_2); + NODE_DEFINE_CONSTANT(target, SPI_MODE_3); + + // Logic Level High for Chip Select + NODE_DEFINE_CONSTANT(target, SPI_CS_HIGH); + + // No Chip Select + NODE_DEFINE_CONSTANT(target, SPI_NO_CS); + */ + /* From: + _spi = { + SPI_MODE_0: 0 + , SPI_MODE_1: 1 + , SPI_MODE_2: 2 + , SPI_MODE_3: 3 + + , SPI_NO_CS: 64 + , SPI_CS_HIGH: 4 + }; + */ + /* To: + var spi = require('spi'); + + spi.MODE_0 == 0; + spi.MODE_1 == 1; + spi.MODE_2 == 2; + spi.MODE_3 == 3; + + spi.CS_LOW == 0; + spi.CS_HIGH == 4; + spi.NO_CS == 64; + */ + + // Expose constants + target->Set(v8::String::NewSymbol("MODE_0"), v8::Integer::New(SPI_MODE_0)); + target->Set(v8::String::NewSymbol("MODE_1"), v8::Integer::New(SPI_MODE_1)); + target->Set(v8::String::NewSymbol("MODE_2"), v8::Integer::New(SPI_MODE_2)); + target->Set(v8::String::NewSymbol("MODE_3"), v8::Integer::New(SPI_MODE_3)); + + target->Set(v8::String::NewSymbol("NO_CS"), v8::Integer::New(SPI_NO_CS)); + target->Set(v8::String::NewSymbol("CS_LOW"), v8::Integer::New(0)); + target->Set(v8::String::NewSymbol("CS_HIGH"), v8::Integer::New(SPI_CS_HIGH)); } // new Spi(string device) //Handle Spi::New(const Arguments& args) { SPI_FUNC_IMPL(New) { - - Spi* spi = new Spi(); spi->Wrap(args.This()); @@ -165,7 +225,9 @@ Handle Spi::Transfer(const Arguments &args) { } Handle retval = self->full_duplex_transfer(write_buffer, read_buffer, - MAX(write_length, read_length)); + MAX(write_length, read_length), + self->m_max_speed, self->m_delay, self->m_bits_per_word); + if (!retval->IsUndefined()) { return retval; } @@ -173,23 +235,31 @@ Handle Spi::Transfer(const Arguments &args) { FUNCTION_CHAIN; } -Handle Spi::full_duplex_transfer(char *write, char *read, size_t length) { - struct spi_ioc_transfer data = { 0 }; - - // .tx_buf = (unsigned long)write, - // .rx_buf = (unsigned long)read, - // .len = length, - // .delay_usecs = 0, // Unsure exactly what delay is for.. - // .speed_hz = 0, // Use the max speed set when opened - // .bits_per_word = 0, // Use the bits per word set when opened - //}; +Handle Spi::full_duplex_transfer(char *write, + char *read, + size_t length, + uint32_t speed, + uint16_t delay, + uint8_t bits) { + // struct spi_ioc_transfer data = { 0 }; + + struct spi_ioc_transfer data = { + (unsigned long)write, + (unsigned long)read, + length, + speed, + delay, // Still unsure ... just expose to options. + bits, + }; int ret = ioctl(this->m_fd, SPI_IOC_MESSAGE(1), &data); + if (ret == 1) { return ERROR("Unable to send SPI message"); } - return Undefined(); + return v8::Integer::New(ret); + // return ret; } // This overrides any of the OTHER set functions since modes are predefined @@ -256,6 +326,7 @@ SPI_FUNC_IMPL(GetSetMaxSpeed) { FUNCTION_CHAIN; } + SPI_FUNC_IMPL(GetSet3Wire) { FUNCTION_PREAMBLE; GETTER(1, Boolean::New((self->m_mode&SPI_3WIRE) > 0)); @@ -269,5 +340,17 @@ SPI_FUNC_IMPL(GetSet3Wire) { FUNCTION_CHAIN; } +// Expose m_delay as "delay" +SPI_FUNC_IMPL(GetSetDelay) { + FUNCTION_PREAMBLE; + GETTER(1, Number::New(self->m_delay)); + REQ_INT_ARG_GT(0, Delay, in_value, 0); + ASSERT_NOT_OPEN; + + self->m_delay = in_value; + + FUNCTION_CHAIN; +} + SPI_FUNC_BOOLEAN_TOGGLE_IMPL(GetSetLoop, SPI_LOOP); -SPI_FUNC_BOOLEAN_TOGGLE_IMPL(GetSetBitOrder, SPI_LSB_FIRST); +SPI_FUNC_BOOLEAN_TOGGLE_IMPL(GetSetBitOrder, SPI_LSB_FIRST); \ No newline at end of file diff --git a/src/spi_binding.h b/src/spi_binding.h index 8c55790..a9de602 100644 --- a/src/spi_binding.h +++ b/src/spi_binding.h @@ -35,8 +35,11 @@ class Spi : ObjectWrap { static void Initialize(Handle target); private: - Spi() : m_fd(-1), m_mode(0), m_bits_per_word(8), - m_max_speed(1000000) { } + Spi() : m_fd(-1), + m_mode(0), + m_max_speed(1000000), // default speed in Hz () 1MHz + m_delay(0), // default bits per word + m_bits_per_word(8) { } // expose delay to options ~Spi() { } // Probably close fd if it's open SPI_FUNC(New); @@ -47,16 +50,18 @@ class Spi : ObjectWrap { SPI_FUNC(GetSetChipSelect); SPI_FUNC(GetSetMaxSpeed); SPI_FUNC(GetSet3Wire); + SPI_FUNC(GetSetDelay); SPI_FUNC(GetSetLoop); SPI_FUNC(GetSetBitOrder); SPI_FUNC(GetSetBitsPerWord); - Handle full_duplex_transfer(char *write, char *read, size_t length); + Handle full_duplex_transfer(char *write, char *read, size_t length, uint32_t speed, uint16_t delay, uint8_t bits); int m_fd; int m_mode; - int m_bits_per_word; - int m_max_speed; + uint32_t m_max_speed; + uint16_t m_delay; + uint8_t m_bits_per_word; }; #define ERROR(STR) ThrowException(Exception::Error(String::New(STR))) @@ -123,4 +128,4 @@ SPI_FUNC_IMPL(NAME) { \ if (retval == -1) return ThrowException(Exception::Error(String::New( \ "Unable to set " #CTRL))); -#define MAX(a,b) (a>b ? a:b) +#define MAX(a,b) (a>b ? a:b) \ No newline at end of file From a88615125f54fc68537dbe7bed4c5be203b91d57 Mon Sep 17 00:00:00 2001 From: Vicary Archangel Date: Tue, 23 Oct 2012 03:25:26 +0800 Subject: [PATCH 02/16] Document fix on options. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 313504a..5d84ae9 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,8 @@ var spi = require("spi"); var MyDevice = new spi.Spi("/dev/spidev1.1", { "mode": 0, // Always do mode first if you need something other than Mode 0 - "chip_select": spi.NO_CS - "max_speed": 1000000, // In Hz + "chipSelect": spi.NO_CS + "maxSpeed": 1000000, // In Hz "size": 8, // How many bits per word }); From 735ab79449c36f71eeb70cbe65bb3a8f07256b4d Mon Sep 17 00:00:00 2001 From: Anders Brownworth Date: Thu, 20 Dec 2012 15:17:17 -0500 Subject: [PATCH 03/16] formatting --- src/spi_binding.cc | 14 +++----------- src/spi_binding.h | 12 ++++++------ 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/spi_binding.cc b/src/spi_binding.cc index b473897..02a4aae 100644 --- a/src/spi_binding.cc +++ b/src/spi_binding.cc @@ -87,7 +87,7 @@ void Spi::Initialize(Handle target) { // t.prototype.delay = GetSetDelay; t->PrototypeTemplate()->Set(String::NewSymbol("delay"), - FunctionTemplate::New(GetSetDelay)->GetFunction()); + FunctionTemplate::New(GetSetDelay)->GetFunction()); // t.prototype.loopback = GetSetLoop; t->PrototypeTemplate()->Set(String::NewSymbol("loopback"), @@ -235,14 +235,7 @@ Handle Spi::Transfer(const Arguments &args) { FUNCTION_CHAIN; } -Handle Spi::full_duplex_transfer(char *write, - char *read, - size_t length, - uint32_t speed, - uint16_t delay, - uint8_t bits) { - // struct spi_ioc_transfer data = { 0 }; - +Handle Spi::full_duplex_transfer(char *write, char *read, size_t length, uint32_t speed, uint16_t delay, uint8_t bits) { struct spi_ioc_transfer data = { (unsigned long)write, (unsigned long)read, @@ -259,7 +252,6 @@ Handle Spi::full_duplex_transfer(char *write, } return v8::Integer::New(ret); - // return ret; } // This overrides any of the OTHER set functions since modes are predefined @@ -353,4 +345,4 @@ SPI_FUNC_IMPL(GetSetDelay) { } SPI_FUNC_BOOLEAN_TOGGLE_IMPL(GetSetLoop, SPI_LOOP); -SPI_FUNC_BOOLEAN_TOGGLE_IMPL(GetSetBitOrder, SPI_LSB_FIRST); \ No newline at end of file +SPI_FUNC_BOOLEAN_TOGGLE_IMPL(GetSetBitOrder, SPI_LSB_FIRST); diff --git a/src/spi_binding.h b/src/spi_binding.h index a9de602..558184e 100644 --- a/src/spi_binding.h +++ b/src/spi_binding.h @@ -36,11 +36,11 @@ class Spi : ObjectWrap { private: Spi() : m_fd(-1), - m_mode(0), - m_max_speed(1000000), // default speed in Hz () 1MHz - m_delay(0), // default bits per word - m_bits_per_word(8) { } // expose delay to options - ~Spi() { } // Probably close fd if it's open + m_mode(0), + m_max_speed(1000000), // default speed in Hz () 1MHz + m_delay(0), // default bits per word + m_bits_per_word(8) { } // expose delay to options + ~Spi() { } // Probably close fd if it's open SPI_FUNC(New); SPI_FUNC(Open); @@ -128,4 +128,4 @@ SPI_FUNC_IMPL(NAME) { \ if (retval == -1) return ThrowException(Exception::Error(String::New( \ "Unable to set " #CTRL))); -#define MAX(a,b) (a>b ? a:b) \ No newline at end of file +#define MAX(a,b) (a>b ? a:b) From 775ac67b2e70c9b5df70ab98d4edf68bd2869cb6 Mon Sep 17 00:00:00 2001 From: Anders Brownworth Date: Thu, 20 Dec 2012 15:18:52 -0500 Subject: [PATCH 04/16] formatting / uncommented device open / prep rx buffer so it is the same size as the tx buffer --- spi.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/spi.js b/spi.js index f31a7e0..f26141a 100644 --- a/spi.js +++ b/spi.js @@ -20,16 +20,16 @@ var _spi = require('bindings')('_spi.node'); // Consistance with docs var MODE = { - MODE_0: _spi.MODE_0 -, MODE_1: _spi.MODE_1 -, MODE_2: _spi.MODE_2 -, MODE_3: _spi.MODE_3 + MODE_0: _spi.MODE_0, + MODE_1: _spi.MODE_1, + MODE_2: _spi.MODE_2, + MODE_3: _spi.MODE_3 }; var CS = { - none: _spi.NO_CS -, high: _spi.CS_HIGH -, low: _spi.CS_LOW + none: _spi.NO_CS, + high: _spi.CS_HIGH, + low: _spi.CS_LOW }; function isFunction(object) { @@ -56,7 +56,7 @@ var Spi = function(device, options, callback) { this.device = device; - // this._spi.open(device); + this._spi.open(device); isFunction(callback) && callback(this); // TODO: Update once open is async; } @@ -81,6 +81,7 @@ Spi.prototype.read = function(buf, callback) { isFunction(callback) && callback(this, buf); // TODO: Update once open is async; } Spi.prototype.transfer = function(txbuf, rxbuf, callback) { + rxbuf = new Buffer(txbuf.length); // tx and rx buffers need to be the same size this._spi.transfer(txbuf, rxbuf); isFunction(callback) && callback(this, rxbuf); // TODO: Update once open is async; @@ -88,4 +89,4 @@ Spi.prototype.transfer = function(txbuf, rxbuf, callback) { module.exports.MODE = MODE; module.exports.CS = CS; -module.exports.Spi = Spi; \ No newline at end of file +module.exports.Spi = Spi; From 206e31f813fa332eea3875f85150369f05c6fda8 Mon Sep 17 00:00:00 2001 From: Anders Brownworth Date: Thu, 20 Dec 2012 19:47:51 -0500 Subject: [PATCH 05/16] minor re-wording --- README.md | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 5d84ae9..da94a5c 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,14 @@ node-spi ======== -A NodeJS interface to the SPI bus on embedded linux machines. +A NodeJS interface to the SPI bus typically found on embedded linux machines. There is a native interface and a wrapped JS interface with a slightly better API. -**THIS CODE IS NOT FINISHED YET, NOTHING IS FUNCTIONING YET!** - -*Note: The first version will be blocking. I know this is antithetics to -the nodejs philosophy, but I think it's important, when dealing with blocking -interfaces, to get the code working in a blocking manner, and then introduce -the async calls using eio.* +*Note: The first version will be blocking. I know this is antithetical to +the node.js philosophy, but I think its important to get the code working in a +blocking manner first, and then introduce the async calls using eio.* Basic Usage =========== @@ -19,18 +16,18 @@ Basic Usage ```javascript var spi = require("spi"); -var MyDevice = new spi.Spi("/dev/spidev1.1", { - "mode": 0, // Always do mode first if you need something other than Mode 0 +var MyDevice = new spi.Spi("/dev/spidev0.1", { + "mode": 0, // do mode first if you need something other than Mode 0 "chipSelect": spi.NO_CS - "maxSpeed": 1000000, // In Hz - "size": 8, // How many bits per word + "maxSpeed": 1000000, // In Hz + "size": 8, // How many bits per word }); var out_buffer = new Buffer([ 0x23, 0x48, 0xAF, 0x19, 0x19, 0x19 ]); MyDevice.transfer(out_buffer, outbuffer.Length(), function(device, recv_buffer) { - // Do Something with the data in the recv buffer, if anything exists + // Do something with the data in the recv buffer, if anything exists }); ``` @@ -42,12 +39,10 @@ Ideally, for each SPI device that is being controlled should have it's own object that implements the protocol necessary to talk to your device so that the device protocol is defined in one place. -An example project is -[node-adafruit-pixel](https://github.com/RussTheAerialist/node-adafruit-pixel) -which is a node module to control the -[AdaFruit RGB Pixels](http://www.adafruit.com/products/738). The interface is -defined in terms of color and pixels, and not in messages being sent via the -SPI bus, but it uses node-spi to do it's work. +An example project is [node-adafruit-pixel](https://github.com/RussTheAerialist/node-adafruit-pixel) +which is a node module to control the [AdaFruit RGB Pixels](http://www.adafruit.com/products/738). +The interface is defined in terms of color and pixels, and not in messages +being sent via the SPI bus, but it uses node-spi to do it's work. Native Api Reference ==================== From bf1f72d835a1fe846791bde5696b6117c846fe5e Mon Sep 17 00:00:00 2001 From: Anders Brownworth Date: Fri, 1 Feb 2013 14:58:01 -0500 Subject: [PATCH 06/16] getters and setters for options / more examples in README added: mode() chipSelect() bitsPerWord() bitOrder() ORDER_MSB ORDER_LSB maxSpeed() halfDuplex() loopback() Not all options work on all platforms. --- README.md | 117 ++++++++++++++++++++++++++++---------- spi.js | 136 ++++++++++++++++++++++++++++++++++++--------- src/spi_binding.cc | 3 + src/spi_binding.h | 4 +- 4 files changed, 203 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index da94a5c..5561ce7 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A NodeJS interface to the SPI bus typically found on embedded linux machines. There is a native interface and a wrapped JS interface with a slightly better API. -*Note: The first version will be blocking. I know this is antithetical to +*Note: The first version will be blocking. I know this is antithetical to the node.js philosophy, but I think its important to get the code working in a blocking manner first, and then introduce the async calls using eio.* @@ -14,21 +14,23 @@ Basic Usage =========== ```javascript -var spi = require("spi"); - -var MyDevice = new spi.Spi("/dev/spidev0.1", { - "mode": 0, // do mode first if you need something other than Mode 0 - "chipSelect": spi.NO_CS - "maxSpeed": 1000000, // In Hz - "size": 8, // How many bits per word -}); - -var out_buffer = new Buffer([ 0x23, 0x48, 0xAF, 0x19, 0x19, 0x19 ]); - -MyDevice.transfer(out_buffer, outbuffer.Length(), - function(device, recv_buffer) { - // Do something with the data in the recv buffer, if anything exists -}); +var SPI = require('spi'); + +var spi = new SPI.Spi('/dev/spidev0.0', { + 'mode': SPI.MODE['MODE_0'], // always set mode as the first option + 'chipSelect': SPI.CS['none'] // 'none', 'high', 'low' + }, function(s){s.open();}); + +var txbuf = new Buffer([ 0x23, 0x48, 0xAF, 0x19, 0x19, 0x19 ]); +var rxbuf = new Buffer([ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]); + +spi.transfer(txbuf, rxbuf, function(device, buf) { + // rxbuf and buf should be the same here + var s = ""; + for (var i=0; i < buf.length; i++) + s = s + buf[i] + " "; + console.log(s + "- " + new Date().getTime()); + }); ``` How you should **really** use the library @@ -47,7 +49,7 @@ being sent via the SPI bus, but it uses node-spi to do it's work. Native Api Reference ==================== -This section documents the native api which is defined in module \_spi.node. +This section documents the native api which is defined in module _spi.node. This is the interface that the normal Spi interface uses, but having a good understanding of this part is important, as some people may want to use the native interface directly. @@ -55,13 +57,50 @@ native interface directly. Creating, Opening, and Closing the device ----------------------------------------- -**\_spi.Spi constructor** - The constructor takes a single argument, the path -to the spi dev file in /dev. We do not check that the file exists until you -call open. +**\_spi.Spi constructor** - The constructor only requires the path to the spi +dev file in /dev. Options and a callback are not required but can be specified. + +Example: +```javascript +var spi = new SPI.Spi('/dev/spidev0.1'); +``` + +Options can include: +* **mode**: Can be one of 'MODE_0', 'MODE_1', 'MODE_2' or 'MODE_3'. It should always be the first option. +* **chipSelect**: Can be one of 'none', 'high' or 'low'. +* **maxSpeed**: The maximum bitrate in Hz. +* **size**: (AKA: bits per word) Anything other than 8 is not yet supported on the Raspberry Pi. +* **bitOrder**: true = MSB first, false = LSB first. +* **delay**: +* **3Wire**: + +Example: +```javascript +var spi = new SPI.Spi('/dev/spidev0.0', {'mode': SPI.MODE['MODE_0']}); +``` + +The callback returns a handle to the newly created SPI object. It might be +handy to .open() it if you don't need to change options. + +Example: +```javascript +var spi = new SPI.Spi('/dev/spidev0.0', {}, function(s){s.open;}); +``` **open()** - This function takes no arguments and will open the device, setting all of the options that were previously set. Once the device is open, we do not -allow you to change the settings to the device. +allow you to change the settings on the device. + +Example: +```javascript +var spi = new SPI.Spi('/dev/spidev0.0', {'mode': SPI.MODE['MODE_0']}); + +// get/set aditional options +spi.maxSpeed(20000); // in Hz +console.log('max speed: ' + spi.maxSpeed()); + +spi.open(); // once opened, you can't change the options +``` **close()** - This function should always be called before ending. Right now the destructor for the underlying C++ class does not call close(), but that @@ -110,21 +149,43 @@ if you'd like. Getting and Sending Data ------------------------ +**transfer()** - This takes two buffers, a write and a read buffer. SPI +only reads when a byte is written so communicaton is usually full duplex. -**transfer()** - This takes two buffers, a write buffer and a read buffer. -If you only want to do one way transfer, then pass null to that argument. For -example, writes would look like this: - +Exmple: ```javascript var buff = new Buffer([0x12, 0x12, 0x12]); spi.transfer(buff, null); ``` -Reads would look like this: +As a convenience feature, read and write functions pad zeros in the opposite +direction to make simple read and writes work. + +**read(buffer, callback)** - Reads as much data as the given buffer is big. +The results of the read are available in the callback. + +Example: +```javascript +var buf1 = new Buffer(8); +spi.read(buf1, function(device, buf2) { + var s = ""; + for (var i=0; i < buf.length; i++) + s = s + buf[i] + " "; + console.log(s); + }); +``` + +**write(buffer, callback)** - Writes out the given buffer. +Example: ```javascript -var buff = new Buffer(8); -spi.transfer(null, buff); +var buf = new Buffer([0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0]); +spi.write(buf, function(device, buf2) { + var s = ""; + for (var i=0; i < buf.length; i++) + s = s + buf[i] + " "; + console.log(s); + }); ``` Remember that these native apis are currently blocking. I will update, once I diff --git a/spi.js b/spi.js index f26141a..f29d8ce 100644 --- a/spi.js +++ b/spi.js @@ -32,61 +32,143 @@ var CS = { low: _spi.CS_LOW }; +var ORDER = { + msb: _spi.ORDER_MSB, + lsb: _spi.ORDER_LSB +}; + function isFunction(object) { - return object && typeof object == 'function'; + return object && typeof object == 'function'; } var Spi = function(device, options, callback) { - this._spi = new _spi._spi(); - - if (callback == undefined) { - callback = options; - options = {}; - } + this._spi = new _spi._spi(); - options = options || {}; // Default to an empty object + options = options || {}; // Default to an empty object - for(var attrname in options) { - var value = options[attrname]; - if (attrname in this._spi) { - // console.trace("Setting " + attrname + "=" + value); - this._spi[attrname](value); + for(var attrname in options) { + var value = options[attrname]; + if (attrname in this._spi) { + this._spi[attrname](value); + } + else + console.log("Unknown option: " + attrname + "=" + value); } - } - this.device = device; + this.device = device; - this._spi.open(device); - - isFunction(callback) && callback(this); // TODO: Update once open is async; + isFunction(callback) && callback(this); // TODO: Update once open is async; } Spi.prototype.open = function() { - return this._spi.open(this.device); + return this._spi.open(this.device); } Spi.prototype.close = function() { - return this._spi.close(); + return this._spi.close(); } Spi.prototype.write = function(buf, callback) { - this._spi.transfer(buf, null); + this._spi.transfer(buf, new Buffer(buf.length)); - isFunction(callback) && callback(this, buf); // TODO: Update once open is async; + isFunction(callback) && callback(this, buf); // TODO: Update once open is async; } Spi.prototype.read = function(buf, callback) { - this._spi.transfer(null, buf); + this._spi.transfer(new Buffer(buf.length), buf); - isFunction(callback) && callback(this, buf); // TODO: Update once open is async; + isFunction(callback) && callback(this, buf); // TODO: Update once open is async; } + Spi.prototype.transfer = function(txbuf, rxbuf, callback) { - rxbuf = new Buffer(txbuf.length); // tx and rx buffers need to be the same size - this._spi.transfer(txbuf, rxbuf); + // tx and rx buffers need to be the same size + this._spi.transfer(txbuf, rxbuf); + + isFunction(callback) && callback(this, rxbuf); // TODO: Update once open is async; +} + +Spi.prototype.mode = function(mode) { + if (typeof(mode) != 'undefined') + if (mode == MODE['MODE_0'] || mode == MODE['MODE_1'] || + mode == MODE['MODE_2'] || mode == MODE['MODE_3']) + return this._spi['mode'](mode); + else { + console.log('Illegal mode'); + return -1; + } + else + return this._spi['mode'](); +} + +Spi.prototype.chipSelect = function(cs) { + if (typeof(cs) != 'undefined') + if (cs == CS['none'] || cs == CS['high'] || cs == MODE['low']) + return this._spi['chipSelect'](cs); + else { + console.log('Illegal chip selection'); + return -1; + } + else + return this._spi['chipSelect'](); +} + +Spi.prototype.bitsPerWord = function(bpw) { + if (typeof(bpw) != 'undefined') + if (bpw > 1) + return this._spi['bitsPerWord'](bpw); + else { + console.log('Illegal bits per word'); + return -1; + } + else + return this._spi['bitsPerWord'](); +} + +Spi.prototype.bitOrder = function(bo) { + if (typeof(bo) != 'undefined') + if (bo == ORDER['msb'] || bo == ORDER['lsb']) + return this._spi['bitOrder'](bo); + else { + console.log('Illegal bit order'); + return -1; + } + else + return this._spi['bitOrder'](); +} + +Spi.prototype.maxSpeed = function(speed) { + if (typeof(speed) != 'undefined') + if (speed > 0) + return this._spi['maxSpeed'](speed); + else { + console.log('Speed must be positive'); + return -1; + } + else + return this._spi['maxSpeed'](); +} + +Spi.prototype.halfDuplex = function(duplex) { + if (typeof(duplex) != 'undefined') + if (duplex) + return this._spi['halfDuplex'](true); + else + return this._spi['halfDuplex'](false); + else + return this._spi['halfDuplex'](); +} - isFunction(callback) && callback(this, rxbuf); // TODO: Update once open is async; +Spi.prototype.loopback = function(loop) { + if (typeof(loop) != 'undefined') + if (loop) + return this._spi['loopback'](true); + else + return this._spi['loopback'](false); + else + return this._spi['loopback'](); } module.exports.MODE = MODE; module.exports.CS = CS; +module.exports.ORDER = ORDER; module.exports.Spi = Spi; diff --git a/src/spi_binding.cc b/src/spi_binding.cc index 02a4aae..d49cc16 100644 --- a/src/spi_binding.cc +++ b/src/spi_binding.cc @@ -146,6 +146,9 @@ void Spi::Initialize(Handle target) { target->Set(v8::String::NewSymbol("NO_CS"), v8::Integer::New(SPI_NO_CS)); target->Set(v8::String::NewSymbol("CS_LOW"), v8::Integer::New(0)); target->Set(v8::String::NewSymbol("CS_HIGH"), v8::Integer::New(SPI_CS_HIGH)); + + target->Set(v8::String::NewSymbol("ORDER_MSB"), v8::Boolean::New(false)); + target->Set(v8::String::NewSymbol("ORDER_LSB"), v8::Boolean::New(true)); } // new Spi(string device) diff --git a/src/spi_binding.h b/src/spi_binding.h index 558184e..f8d289d 100644 --- a/src/spi_binding.h +++ b/src/spi_binding.h @@ -38,8 +38,8 @@ class Spi : ObjectWrap { Spi() : m_fd(-1), m_mode(0), m_max_speed(1000000), // default speed in Hz () 1MHz - m_delay(0), // default bits per word - m_bits_per_word(8) { } // expose delay to options + m_delay(0), // expose delay to options + m_bits_per_word(8) { } // default bits per word ~Spi() { } // Probably close fd if it's open SPI_FUNC(New); From 9d3f2de122ab2b8982d8ea973a8fee02eaa542f7 Mon Sep 17 00:00:00 2001 From: Anders Brownworth Date: Fri, 1 Feb 2013 15:06:54 -0500 Subject: [PATCH 07/16] wording fixes --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5561ce7..f8e0976 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ node-spi ======== -A NodeJS interface to the SPI bus typically found on embedded linux machines. +A NodeJS interface to the SPI bus typically found on embedded linux machines +such as the Raspberry Pi. There is a native interface and a wrapped JS interface with a slightly better API. @@ -66,13 +67,12 @@ var spi = new SPI.Spi('/dev/spidev0.1'); ``` Options can include: -* **mode**: Can be one of 'MODE_0', 'MODE_1', 'MODE_2' or 'MODE_3'. It should always be the first option. -* **chipSelect**: Can be one of 'none', 'high' or 'low'. -* **maxSpeed**: The maximum bitrate in Hz. -* **size**: (AKA: bits per word) Anything other than 8 is not yet supported on the Raspberry Pi. -* **bitOrder**: true = MSB first, false = LSB first. -* **delay**: -* **3Wire**: +* mode +* chipSelect +* maxSpeed +* bitsPerWord +* bitOrder +* delay Example: ```javascript From b73b79023874dccb43881759464dcd1f4931140c Mon Sep 17 00:00:00 2001 From: Anders Brownworth Date: Fri, 1 Feb 2013 15:07:57 -0500 Subject: [PATCH 08/16] wording fixes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f8e0976..1c8bda0 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ var spi = new SPI.Spi('/dev/spidev0.0', {'mode': SPI.MODE['MODE_0']}); ``` The callback returns a handle to the newly created SPI object. It might be -handy to .open() it if you don't need to change options. +handy to .open() it if you set all of your options in one shot. Example: ```javascript From 7d3a70b6e7655052cffd440d94a76f7a33a51246 Mon Sep 17 00:00:00 2001 From: Anders Brownworth Date: Fri, 1 Feb 2013 15:09:03 -0500 Subject: [PATCH 09/16] wording fixes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1c8bda0..d25b8db 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ handy to .open() it if you set all of your options in one shot. Example: ```javascript -var spi = new SPI.Spi('/dev/spidev0.0', {}, function(s){s.open;}); +var spi = new SPI.Spi('/dev/spidev0.0', {}, function(s){s.open();}); ``` **open()** - This function takes no arguments and will open the device, setting From a933594721b3bc28216b7c6d1db1e2ceacfed72e Mon Sep 17 00:00:00 2001 From: Anders Brownworth Date: Fri, 1 Feb 2013 15:16:18 -0500 Subject: [PATCH 10/16] return this._spi to enable option chaining --- spi.js | 56 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/spi.js b/spi.js index f29d8ce..6f2c9d3 100644 --- a/spi.js +++ b/spi.js @@ -90,8 +90,10 @@ Spi.prototype.transfer = function(txbuf, rxbuf, callback) { Spi.prototype.mode = function(mode) { if (typeof(mode) != 'undefined') if (mode == MODE['MODE_0'] || mode == MODE['MODE_1'] || - mode == MODE['MODE_2'] || mode == MODE['MODE_3']) - return this._spi['mode'](mode); + mode == MODE['MODE_2'] || mode == MODE['MODE_3']) { + this._spi['mode'](mode); + return this._spi; + } else { console.log('Illegal mode'); return -1; @@ -102,8 +104,10 @@ Spi.prototype.mode = function(mode) { Spi.prototype.chipSelect = function(cs) { if (typeof(cs) != 'undefined') - if (cs == CS['none'] || cs == CS['high'] || cs == MODE['low']) - return this._spi['chipSelect'](cs); + if (cs == CS['none'] || cs == CS['high'] || cs == MODE['low']) { + this._spi['chipSelect'](cs); + return this._spi; + } else { console.log('Illegal chip selection'); return -1; @@ -114,8 +118,10 @@ Spi.prototype.chipSelect = function(cs) { Spi.prototype.bitsPerWord = function(bpw) { if (typeof(bpw) != 'undefined') - if (bpw > 1) - return this._spi['bitsPerWord'](bpw); + if (bpw > 1) { + this._spi['bitsPerWord'](bpw); + return this._spi; + } else { console.log('Illegal bits per word'); return -1; @@ -126,8 +132,10 @@ Spi.prototype.bitsPerWord = function(bpw) { Spi.prototype.bitOrder = function(bo) { if (typeof(bo) != 'undefined') - if (bo == ORDER['msb'] || bo == ORDER['lsb']) - return this._spi['bitOrder'](bo); + if (bo == ORDER['msb'] || bo == ORDER['lsb']) { + this._spi['bitOrder'](bo); + return this._spi; + } else { console.log('Illegal bit order'); return -1; @@ -138,32 +146,42 @@ Spi.prototype.bitOrder = function(bo) { Spi.prototype.maxSpeed = function(speed) { if (typeof(speed) != 'undefined') - if (speed > 0) - return this._spi['maxSpeed'](speed); + if (speed > 0) { + this._spi['maxSpeed'](speed); + return this._spi; + } else { console.log('Speed must be positive'); return -1; - } + } else return this._spi['maxSpeed'](); } Spi.prototype.halfDuplex = function(duplex) { if (typeof(duplex) != 'undefined') - if (duplex) - return this._spi['halfDuplex'](true); - else - return this._spi['halfDuplex'](false); + if (duplex) { + this._spi['halfDuplex'](true); + return this._spi; + } + else { + this._spi['halfDuplex'](false); + return this._spi; + } else return this._spi['halfDuplex'](); } Spi.prototype.loopback = function(loop) { if (typeof(loop) != 'undefined') - if (loop) - return this._spi['loopback'](true); - else - return this._spi['loopback'](false); + if (loop) { + this._spi['loopback'](true); + return this._spi; + } + else { + this._spi['loopback'](false); + return this._spi; + } else return this._spi['loopback'](); } From 923b6bc6fdc7c0f04cf5ae7f483522c443255de6 Mon Sep 17 00:00:00 2001 From: Anders Brownworth Date: Fri, 1 Feb 2013 15:16:40 -0500 Subject: [PATCH 11/16] wording fixes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d25b8db..3f202a4 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Example: var spi = new SPI.Spi('/dev/spidev0.0', {}, function(s){s.open();}); ``` -**open()** - This function takes no arguments and will open the device, setting +**open()** - This function takes no arguments and will open the device using all of the options that were previously set. Once the device is open, we do not allow you to change the settings on the device. From 925efa9d66eca34cbc045e3afa891f2c2b0530a5 Mon Sep 17 00:00:00 2001 From: Anders Brownworth Date: Fri, 1 Feb 2013 15:21:27 -0500 Subject: [PATCH 12/16] wording fixes --- README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3f202a4..f64950a 100644 --- a/README.md +++ b/README.md @@ -154,8 +154,15 @@ only reads when a byte is written so communicaton is usually full duplex. Exmple: ```javascript -var buff = new Buffer([0x12, 0x12, 0x12]); -spi.transfer(buff, null); +var txbuf = new Buffer([0x80, 0x00]); +var rxbuf = new Buffer([0x00, 0x00]); + +spi.transfer(txbuf, rxbuf, function(device, buf) { + var s = ""; + for (var i=0; i < buf.length; i++) + s = s + buf[i] + " "; + console.log(s); + }); ``` As a convenience feature, read and write functions pad zeros in the opposite From 84b198b9882db3fcdf8f79a09c0d6a7adb921d80 Mon Sep 17 00:00:00 2001 From: Anders Brownworth Date: Fri, 1 Feb 2013 15:22:28 -0500 Subject: [PATCH 13/16] wording fixes --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f64950a..0f70bb1 100644 --- a/README.md +++ b/README.md @@ -154,8 +154,8 @@ only reads when a byte is written so communicaton is usually full duplex. Exmple: ```javascript -var txbuf = new Buffer([0x80, 0x00]); -var rxbuf = new Buffer([0x00, 0x00]); +var txbuf = new Buffer([ 0x23, 0x48, 0xAF, 0x19, 0x19, 0x19 ]); +var rxbuf = new Buffer([ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]); spi.transfer(txbuf, rxbuf, function(device, buf) { var s = ""; From 6d06c98dcbbd6e9ebcc907bc31ca61e95c314037 Mon Sep 17 00:00:00 2001 From: Anders Brownworth Date: Fri, 1 Feb 2013 15:23:56 -0500 Subject: [PATCH 14/16] wording fixes --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0f70bb1..b6da7a8 100644 --- a/README.md +++ b/README.md @@ -149,8 +149,9 @@ if you'd like. Getting and Sending Data ------------------------ -**transfer()** - This takes two buffers, a write and a read buffer. SPI -only reads when a byte is written so communicaton is usually full duplex. +**transfer(txbuf, rxbuf, callback)** - This takes two buffers, a write and a +read buffer, and optionally a callback. SPI only reads when a byte is written +so communicaton is usually full duplex. Exmple: ```javascript From 0ab0955b971f9b6cc5a974c604325dfe206318bf Mon Sep 17 00:00:00 2001 From: Anders Brownworth Date: Fri, 1 Feb 2013 15:28:44 -0500 Subject: [PATCH 15/16] wording fixes --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b6da7a8..c32a0ea 100644 --- a/README.md +++ b/README.md @@ -69,10 +69,11 @@ var spi = new SPI.Spi('/dev/spidev0.1'); Options can include: * mode * chipSelect -* maxSpeed * bitsPerWord * bitOrder -* delay +* maxSpeed +* halfDuplex +* loopback Example: ```javascript From 6f745bee468c07a769af0446d48862641bf4503e Mon Sep 17 00:00:00 2001 From: Anders Brownworth Date: Fri, 1 Feb 2013 15:30:15 -0500 Subject: [PATCH 16/16] wording fixes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c32a0ea..0d79f2b 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ var SPI = require('spi'); var spi = new SPI.Spi('/dev/spidev0.0', { 'mode': SPI.MODE['MODE_0'], // always set mode as the first option - 'chipSelect': SPI.CS['none'] // 'none', 'high', 'low' + 'chipSelect': SPI.CS['none'] // 'none', 'high' - defaults to low }, function(s){s.open();}); var txbuf = new Buffer([ 0x23, 0x48, 0xAF, 0x19, 0x19, 0x19 ]);