From ea8f007ed8675abd9b636013e89b00279e973631 Mon Sep 17 00:00:00 2001 From: Lubos Strapko Date: Mon, 13 Nov 2017 16:51:06 +0100 Subject: [PATCH 1/5] Support for SUNXI chipset (NanoPI & OrangePI) --- README.md | 4 +- binding.gyp | 1 + lib/rpio.js | 180 ++++++++--- package.json | 4 +- src/bcm2835.c | 41 +++ src/bcm2835.h | 12 +- src/rpio.cc | 326 +++++++++++++++----- src/sunxi.c | 824 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/sunxi.h | 41 +++ 9 files changed, 1305 insertions(+), 128 deletions(-) mode change 100644 => 100755 binding.gyp mode change 100644 => 100755 lib/rpio.js create mode 100755 src/sunxi.c create mode 100755 src/sunxi.h diff --git a/README.md b/README.md index fef920d..cd70688 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ node-rpio ========= This is a high performance node.js addon which provides access to the Raspberry -Pi GPIO interface, supporting regular GPIO as well as i²c, PWM, and SPI. +Pi, Orange Pi and NanoPi NEO Plus2 GPIO interface, supporting regular GPIO as well as i²c, PWM, and SPI. [![Node.js version](https://img.shields.io/node/v/rpio.svg)](http://nodejs.org/download/) [![NPM version](https://badge.fury.io/js/rpio.svg)](http://badge.fury.io/js/rpio) @@ -11,6 +11,8 @@ Pi GPIO interface, supporting regular GPIO as well as i²c, PWM, and SPI. ## Compatibility * Raspberry Pi Models: A, B (revisions 1.0 and 2.0), A+, B+, 2, 3, Zero. +* NanoPi Models: NEO, NEO2 +* Orange Pi * Node.js Versions: 0.8, 0.10, 0.12, 4, 5, 6, 7, 8 Newer versions of node.js require you to install the GCC 4.8 packages for C++11 diff --git a/binding.gyp b/binding.gyp old mode 100644 new mode 100755 index 2eeb9b0..ab33e8a --- a/binding.gyp +++ b/binding.gyp @@ -5,6 +5,7 @@ "include_dirs": [ "1000) { + pinmap = "PINMAP_ORANGEPI"; + } else { + pinmap = "PINMAP_NEO"; + } + break; + default: + return false; + } } return true; @@ -295,10 +381,12 @@ function set_mock_pinmap() switch (rpio_options.mock) { case 'raspi-b-r1': pinmap = "PINMAP_26_R1"; + chipType = "BCM2835"; break; case 'raspi-a': case 'raspi-b': pinmap = "PINMAP_26"; + chipType = "BCM2835"; break; case 'raspi-a+': case 'raspi-b+': @@ -307,6 +395,7 @@ function set_mock_pinmap() case 'raspi-zero': case 'raspi-zero-w': pinmap = "PINMAP_40"; + chipType = "BCM2835"; break; default: return false; @@ -340,7 +429,7 @@ function pin_to_gpio(pin) function check_sys_gpio(pin) { - if (fs.existsSync('/sys/class/gpio/gpio' + pin)) + if (chipType === "BCM2835" && fs.existsSync('/sys/class/gpio/gpio' + pin)) throw "GPIO" + pin + " is currently in use by /sys/class/gpio"; } @@ -435,6 +524,7 @@ rpio.prototype.init = function(opts) */ pincache = {}; pinmap = null; + chipType = null; /* * Allow the user to specify a mock board to emulate, otherwise try @@ -455,11 +545,13 @@ rpio.prototype.init = function(opts) } /* - * Open the bcm2835 driver. + * init rpio library */ - bindcall(binding.rpio_init, Number(rpio_options.gpiomem)); + var gpiomem = Number(rpio_options.gpiomem); + if (chipType === "SUNXI") gpiomem += 2; + bindcall(binding.rpio_init, gpiomem); rpio_inited = true; -} +}; rpio.prototype.open = function(pin, mode, init) { @@ -488,7 +580,7 @@ rpio.prototype.open = function(pin, mode, init) default: throw "Unsupported mode " + mode; } -} +}; rpio.prototype.mode = function(pin, mode) { @@ -504,12 +596,12 @@ rpio.prototype.mode = function(pin, mode) default: throw "Unsupported mode " + mode; } -} +}; rpio.prototype.read = function(pin) { return bindcall(binding.gpio_read, pin_to_gpio(pin)); -} +}; rpio.prototype.readbuf = function(pin, buf, len) { @@ -520,12 +612,12 @@ rpio.prototype.readbuf = function(pin, buf, len) throw "Buffer not large enough to accommodate request"; return bindcall3(binding.gpio_readbuf, pin_to_gpio(pin), buf, len); -} +}; rpio.prototype.write = function(pin, value) { return bindcall2(binding.gpio_write, pin_to_gpio(pin), value); -} +}; rpio.prototype.writebuf = function(pin, buf, len) { @@ -536,7 +628,7 @@ rpio.prototype.writebuf = function(pin, buf, len) throw "Buffer not large enough to accommodate request"; return bindcall3(binding.gpio_writebuf, pin_to_gpio(pin), buf, len); -} +}; rpio.prototype.readpad = function(group) { @@ -544,7 +636,7 @@ rpio.prototype.readpad = function(group) throw "Pad control not available in gpiomem mode"; return bindcall(binding.gpio_pad_read, group); -} +}; rpio.prototype.writepad = function(group, control) { @@ -552,12 +644,12 @@ rpio.prototype.writepad = function(group, control) throw "Pad control not available in gpiomem mode"; bindcall2(binding.gpio_pad_write, group, control); -} +}; rpio.prototype.pud = function(pin, state) { bindcall2(binding.gpio_pud, pin_to_gpio(pin), state); -} +}; rpio.prototype.poll = function(pin, cb, direction) { @@ -602,7 +694,7 @@ rpio.prototype.poll = function(pin, cb, direction) event_running = false; } } -} +}; rpio.prototype.close = function(pin, reset) { @@ -619,7 +711,7 @@ rpio.prototype.close = function(pin, reset) rpio.prototype.pud(pin, rpio.prototype.PULL_OFF); rpio.prototype.mode(pin, rpio.prototype.INPUT); } -} +}; /* * PWM @@ -630,21 +722,21 @@ rpio.prototype.pwmSetClockDivider = function(divider) throw "Clock divider must be zero or power of two"; return bindcall(binding.pwm_set_clock, divider); -} +}; rpio.prototype.pwmSetRange = function(pin, range) { var channel = get_pwm_channel(pin); return bindcall2(binding.pwm_set_range, channel, range); -} +}; rpio.prototype.pwmSetData = function(pin, data) { var channel = get_pwm_channel(pin); return bindcall2(binding.pwm_set_data, channel, data); -} +}; /* * i²c @@ -661,12 +753,12 @@ rpio.prototype.i2cBegin = function() throw "i²c not available in gpiomem mode"; bindcall(binding.i2c_begin); -} +}; rpio.prototype.i2cSetSlaveAddress = function(addr) { return bindcall(binding.i2c_set_slave_address, addr); -} +}; rpio.prototype.i2cSetClockDivider = function(divider) { @@ -674,12 +766,12 @@ rpio.prototype.i2cSetClockDivider = function(divider) throw "Clock divider must be an even number"; return bindcall(binding.i2c_set_clock_divider, divider); -} +}; rpio.prototype.i2cSetBaudRate = function(baud) { return bindcall(binding.i2c_set_baudrate, baud); -} +}; rpio.prototype.i2cRead = function(buf, len) { @@ -690,7 +782,7 @@ rpio.prototype.i2cRead = function(buf, len) throw "Buffer not large enough to accommodate request"; return bindcall2(binding.i2c_read, buf, len); -} +}; rpio.prototype.i2cWrite = function(buf, len) { @@ -701,12 +793,12 @@ rpio.prototype.i2cWrite = function(buf, len) throw "Buffer not large enough to accommodate request"; return bindcall2(binding.i2c_write, buf, len); -} +}; rpio.prototype.i2cEnd = function() { bindcall(binding.i2c_end); -} +}; /* * SPI @@ -723,17 +815,17 @@ rpio.prototype.spiBegin = function() throw "SPI not available in gpiomem mode"; bindcall(binding.spi_begin); -} +}; rpio.prototype.spiChipSelect = function(cs) { return bindcall(binding.spi_chip_select, cs); -} +}; rpio.prototype.spiSetCSPolarity = function(cs, active) { return bindcall2(binding.spi_set_cs_polarity, cs, active); -} +}; rpio.prototype.spiSetClockDivider = function(divider) { @@ -741,27 +833,27 @@ rpio.prototype.spiSetClockDivider = function(divider) throw "Clock divider must be an even number between 0 and 65536"; return bindcall(binding.spi_set_clock_divider, divider); -} +}; rpio.prototype.spiSetDataMode = function(mode) { return bindcall(binding.spi_set_data_mode, mode); -} +}; rpio.prototype.spiTransfer = function(txbuf, rxbuf, len) { return bindcall3(binding.spi_transfer, txbuf, rxbuf, len); -} +}; rpio.prototype.spiWrite = function(buf, len) { return bindcall2(binding.spi_write, buf, len); -} +}; rpio.prototype.spiEnd = function() { bindcall(binding.spi_end); -} +}; /* * Misc functions. @@ -769,17 +861,17 @@ rpio.prototype.spiEnd = function() rpio.prototype.sleep = function(secs) { bindcall(binding.rpio_usleep, secs * 1000000); -} +}; rpio.prototype.msleep = function(msecs) { bindcall(binding.rpio_usleep, msecs * 1000); -} +}; rpio.prototype.usleep = function(usecs) { bindcall(binding.rpio_usleep, usecs); -} +}; process.on('exit', function(code) { bindcall(binding.rpio_close); diff --git a/package.json b/package.json index cc50031..6f8270c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "rpio", "version": "0.9.19", - "description": "High performance GPIO/i2c/PWM/SPI module for Raspberry Pi", + "description": "High performance GPIO/i2c/PWM/SPI module for Raspberry Pi, NanoPi and Orange Pi", "main": "./lib/rpio.js", "scripts": { "test": "true", @@ -29,6 +29,8 @@ "raspberry", "raspberrypi", "raspberry pi", + "nanopi", + "orangepi", "rpi", "spi" ], diff --git a/src/bcm2835.c b/src/bcm2835.c index e679265..1ad2ea3 100644 --- a/src/bcm2835.c +++ b/src/bcm2835.c @@ -21,6 +21,9 @@ #define BCK2835_LIBRARY_BUILD #include "bcm2835.h" +#define RPIO_EVENT_LOW 0x1 +#define RPIO_EVENT_HIGH 0x2 + /* This define enables a little test program (by default a blinking output on pin RPI_GPIO_PIN_11) // You can do some safe, non-destructive testing on any platform with: // gcc bcm2835.c -D BCM2835_TEST @@ -288,6 +291,26 @@ void bcm2835_gpio_set_eds_multi(uint32_t mask) volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4; bcm2835_peri_write(paddr, mask); } +void bcm2835_gpio_event_set(uint8_t pin, uint32_t direction) +{ + /* Clear all possible trigger events. */ + bcm2835_gpio_clr_ren(pin); + bcm2835_gpio_clr_fen(pin); + bcm2835_gpio_clr_hen(pin); + bcm2835_gpio_clr_len(pin); + bcm2835_gpio_clr_aren(pin); + bcm2835_gpio_clr_afen(pin); + + /* + * Add the requested events, using the synchronous rising and + * falling edge detection bits. + */ + if (direction & RPIO_EVENT_HIGH) + bcm2835_gpio_ren(pin); + + if (direction & RPIO_EVENT_LOW) + bcm2835_gpio_fen(pin); +} /* Rising edge detect enable */ void bcm2835_gpio_ren(uint8_t pin) @@ -402,6 +425,24 @@ void bcm2835_gpio_pudclk(uint8_t pin, uint8_t on) bcm2835_peri_write(paddr, (on ? 1 : 0) << shift); } + +void bcm2835_pullUpDnControl(uint8_t pin, uint8_t pud) +{ + /* + * We use our own version of bcm2835_gpio_set_pud as that uses + * delayMicroseconds() which requires access to the timers and + * therefore /dev/mem and root. Our version is identical, except for + * using usleep() instead. + */ + bcm2835_gpio_pud(pud); + usleep(10); + bcm2835_gpio_pudclk(pin, 1); + usleep(10); + bcm2835_gpio_pud(BCM2835_GPIO_PUD_OFF); + bcm2835_gpio_pudclk(pin, 0); +} + + /* Read GPIO pad behaviour for groups of GPIOs */ uint32_t bcm2835_gpio_pad(uint8_t group) { diff --git a/src/bcm2835.h b/src/bcm2835.h index ea0d011..459a135 100644 --- a/src/bcm2835.h +++ b/src/bcm2835.h @@ -1189,6 +1189,8 @@ extern "C" { */ extern void bcm2835_gpio_set_eds_multi(uint32_t mask); + extern void bcm2835_gpio_event_set(uint8_t pin, uint32_t direction); + /*! Enable Rising Edge Detect Enable for the specified pin. When a rising edge is detected, sets the appropriate pin in Event Detect Status. The GPRENn registers use @@ -1273,15 +1275,7 @@ extern "C" { \param[in] pud The desired Pull-up/down mode. One of BCM2835_GPIO_PUD_* from bcm2835PUDControl \sa bcm2835_gpio_set_pud() */ - extern void bcm2835_gpio_pud(uint8_t pud); - - /*! Clocks the Pull-up/down value set earlier by bcm2835_gpio_pud() into the pin. - \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. - \param[in] on HIGH to clock the value from bcm2835_gpio_pud() into the pin. - LOW to remove the clock. - \sa bcm2835_gpio_set_pud() - */ - extern void bcm2835_gpio_pudclk(uint8_t pin, uint8_t on); + extern void bcm2835_pullUpDnControl(uint8_t pin, uint8_t pud); /*! Reads and returns the Pad Control for the given GPIO group. \param[in] group The GPIO pad group number, one of BCM2835_PAD_GROUP_GPIO_* diff --git a/src/rpio.cc b/src/rpio.cc index 1c0a210..8a177f0 100644 --- a/src/rpio.cc +++ b/src/rpio.cc @@ -17,14 +17,17 @@ #include #include /* usleep() */ #include "bcm2835.h" +#include "sunxi.h" -#define RPIO_EVENT_LOW 0x1 -#define RPIO_EVENT_HIGH 0x2 +#define BCM2835 0x1 +#define SUNXI 0x2 using namespace Nan; +uint8_t type = 0; + /* - * GPIO function select. Pass through all values supported by bcm2835. + * GPIO function select. Pass through all values supported by wiringPi. */ NAN_METHOD(gpio_function) { @@ -34,7 +37,14 @@ NAN_METHOD(gpio_function) (info[1]->NumberValue() > 7)) return ThrowTypeError("Incorrect arguments"); - bcm2835_gpio_fsel(info[0]->NumberValue(), info[1]->NumberValue()); + uint8_t pin = info[0]->NumberValue(); + uint8_t mode = info[1]->NumberValue(); + + if (type == BCM2835) { + bcm2835_gpio_fsel(pin, mode); + } else if (type == SUNXI) { + sunxi_gpio_fsel(pin, mode); + } } /* @@ -45,7 +55,16 @@ NAN_METHOD(gpio_read) if ((info.Length() != 1) || (!info[0]->IsNumber())) return ThrowTypeError("Incorrect arguments"); - info.GetReturnValue().Set(bcm2835_gpio_lev(info[0]->NumberValue())); + uint8_t pin = info[0]->NumberValue(); + uint8_t value = 0; + + if (type == BCM2835) { + value = bcm2835_gpio_lev(pin); + } else if (type == SUNXI) { + value = sunxi_gpio_lev(pin); + } + + info.GetReturnValue().Set(value); } NAN_METHOD(gpio_readbuf) @@ -61,8 +80,14 @@ NAN_METHOD(gpio_readbuf) buf = node::Buffer::Data(info[1]->ToObject()); + uint8_t pin = info[0]->NumberValue(); + for (i = 0; i < info[2]->NumberValue(); i++) - buf[i] = bcm2835_gpio_lev(info[0]->NumberValue()); + if (type == BCM2835) { + buf[i] = bcm2835_gpio_lev(pin); + } else if (type == SUNXI) { + buf[i] = sunxi_gpio_lev(pin); + } } NAN_METHOD(gpio_write) @@ -72,7 +97,15 @@ NAN_METHOD(gpio_write) !info[1]->IsNumber()) return ThrowTypeError("Incorrect arguments"); - bcm2835_gpio_write(info[0]->NumberValue(), info[1]->NumberValue()); + uint8_t pin = info[0]->NumberValue(); + uint8_t on = info[1]->NumberValue(); + + if (type == BCM2835) { + bcm2835_gpio_write(pin, on); + } else if (type == SUNXI) { + sunxi_gpio_write(pin, on); + } + } NAN_METHOD(gpio_writebuf) @@ -88,8 +121,14 @@ NAN_METHOD(gpio_writebuf) buf = node::Buffer::Data(info[1]->ToObject()); + uint8_t pin = info[0]->NumberValue(); + for (i = 0; i < info[2]->NumberValue(); i++) - bcm2835_gpio_write(info[0]->NumberValue(), buf[i]); + if (type == BCM2835) { + bcm2835_gpio_write(pin, buf[i]); + } else if (type == SUNXI) { + sunxi_gpio_write(pin, buf[i]); + } } NAN_METHOD(gpio_pad_read) @@ -97,7 +136,16 @@ NAN_METHOD(gpio_pad_read) if ((info.Length() != 1) || !info[0]->IsNumber()) return ThrowTypeError("Incorrect arguments"); - info.GetReturnValue().Set(bcm2835_gpio_pad(info[0]->NumberValue())); + uint8_t group = info[0]->NumberValue(); + uint32_t state = 0; + + if (type == BCM2835) { + state = bcm2835_gpio_pad(group); + } else if (type == SUNXI) { + return ThrowTypeError("SUNXI doesn't support PAD control"); + } + + info.GetReturnValue().Set(state); } NAN_METHOD(gpio_pad_write) @@ -107,7 +155,14 @@ NAN_METHOD(gpio_pad_write) !info[1]->IsNumber()) return ThrowTypeError("Incorrect arguments"); - bcm2835_gpio_set_pad(info[0]->NumberValue(), info[1]->NumberValue()); + uint8_t group = info[0]->NumberValue(); + uint32_t control = info[1]->NumberValue(); + + if (type == BCM2835) { + bcm2835_gpio_set_pad(group, control); + } else if (type == SUNXI) { + return ThrowTypeError("SUNXI doesn't support PAD control"); + } } NAN_METHOD(gpio_pud) @@ -117,18 +172,14 @@ NAN_METHOD(gpio_pud) !info[1]->IsNumber()) return ThrowTypeError("Incorrect arguments"); - /* - * We use our own version of bcm2835_gpio_set_pud as that uses - * delayMicroseconds() which requires access to the timers and - * therefore /dev/mem and root. Our version is identical, except for - * using usleep() instead. - */ - bcm2835_gpio_pud(info[1]->NumberValue()); - usleep(10); - bcm2835_gpio_pudclk(info[0]->NumberValue(), 1); - usleep(10); - bcm2835_gpio_pud(BCM2835_GPIO_PUD_OFF); - bcm2835_gpio_pudclk(info[0]->NumberValue(), 0); + uint8_t pin = info[0]->NumberValue(); + uint8_t pud = info[1]->NumberValue(); + + if (type == BCM2835) { + bcm2835_pullUpDnControl(pin, pud); + } else if (type == SUNXI) { + sunxi_pullUpDnControl(pin, pud); + } } NAN_METHOD(gpio_event_set) @@ -138,39 +189,42 @@ NAN_METHOD(gpio_event_set) !info[1]->IsNumber()) return ThrowTypeError("Incorrect arguments"); - /* Clear all possible trigger events. */ - bcm2835_gpio_clr_ren(info[0]->NumberValue()); - bcm2835_gpio_clr_fen(info[0]->NumberValue()); - bcm2835_gpio_clr_hen(info[0]->NumberValue()); - bcm2835_gpio_clr_len(info[0]->NumberValue()); - bcm2835_gpio_clr_aren(info[0]->NumberValue()); - bcm2835_gpio_clr_afen(info[0]->NumberValue()); + uint8_t pin = info[0]->NumberValue(); + uint32_t direction = info[1]->NumberValue(); - /* - * Add the requested events, using the synchronous rising and - * falling edge detection bits. - */ - if ((uint32_t)info[1]->NumberValue() & RPIO_EVENT_HIGH) - bcm2835_gpio_ren(info[0]->NumberValue()); + if (type == BCM2835) { + bcm2835_gpio_event_set(pin, direction); + } else if (type == SUNXI) { + return ThrowTypeError("SUNXI doesn't support poll events"); + } - if ((uint32_t)info[1]->NumberValue() & RPIO_EVENT_LOW) - bcm2835_gpio_fen(info[0]->NumberValue()); } NAN_METHOD(gpio_event_poll) { - uint32_t rval = 0; - if ((info.Length() != 1) || !info[0]->IsNumber()) return ThrowTypeError("Incorrect arguments"); + if (type == SUNXI) { + return ThrowTypeError("SUNXI doesn't support poll events"); + } + + uint32_t rval = 0; + + uint32_t mask = info[0]->NumberValue(); + /* * Interrupts are not supported, so this merely reports that an event * happened in the time period since the last poll. There is no way to * know which trigger caused the event. */ - if ((rval = bcm2835_gpio_eds_multi(info[0]->NumberValue()))) - bcm2835_gpio_set_eds_multi(rval); + + if (type == BCM2835) { + if ((rval = bcm2835_gpio_eds_multi(mask))) + bcm2835_gpio_set_eds_multi(rval); + } else if (type == SUNXI) { + return ThrowTypeError("SUNXI doesn't support poll events"); + } info.GetReturnValue().Set(rval); } @@ -180,8 +234,14 @@ NAN_METHOD(gpio_event_clear) if ((info.Length() != 1) || !info[0]->IsNumber()) return ThrowTypeError("Incorrect arguments"); - bcm2835_gpio_clr_fen(info[0]->NumberValue()); - bcm2835_gpio_clr_ren(info[0]->NumberValue()); + uint8_t pin = info[0]->NumberValue(); + + if (type == BCM2835) { + bcm2835_gpio_clr_fen(pin); + bcm2835_gpio_clr_ren(pin); + } else if (type == SUNXI) { + return ThrowTypeError("SUNXI doesn't support poll events"); + } } /* @@ -189,7 +249,11 @@ NAN_METHOD(gpio_event_clear) */ NAN_METHOD(i2c_begin) { - bcm2835_i2c_begin(); + if (type == BCM2835) { + bcm2835_i2c_begin(); + } else if (type == SUNXI) { + sunxi_i2c_begin(1); + } } NAN_METHOD(i2c_set_clock_divider) @@ -197,7 +261,13 @@ NAN_METHOD(i2c_set_clock_divider) if ((info.Length() != 1) || (!info[0]->IsNumber())) return ThrowTypeError("Incorrect arguments"); - bcm2835_i2c_setClockDivider(info[0]->NumberValue()); + uint16_t divider = info[0]->NumberValue(); + + if (type == BCM2835) { + bcm2835_i2c_setClockDivider(divider); + } else if (type == SUNXI) { + return ThrowTypeError("SUNXI doesn't support i2c clock divider"); + } } NAN_METHOD(i2c_set_baudrate) @@ -205,7 +275,13 @@ NAN_METHOD(i2c_set_baudrate) if ((info.Length() != 1) || (!info[0]->IsNumber())) return ThrowTypeError("Incorrect arguments"); - bcm2835_i2c_set_baudrate(info[0]->NumberValue()); + uint32_t baudrate = info[0]->NumberValue(); + + if (type == BCM2835) { + bcm2835_i2c_set_baudrate(baudrate); + } else if (type == SUNXI) { + return ThrowTypeError("SUNXI doesn't support i2c baudrate"); + } } NAN_METHOD(i2c_set_slave_address) @@ -213,12 +289,22 @@ NAN_METHOD(i2c_set_slave_address) if ((info.Length() != 1) || (!info[0]->IsNumber())) return ThrowTypeError("Incorrect arguments"); - bcm2835_i2c_setSlaveAddress(info[0]->NumberValue()); + uint8_t addr = info[0]->NumberValue(); + + if (type == BCM2835) { + bcm2835_i2c_setSlaveAddress(addr); + } else if (type == SUNXI) { + sunxi_i2c_setSlaveAddress(addr); + } } NAN_METHOD(i2c_end) { - bcm2835_i2c_end(); + if (type == BCM2835) { + bcm2835_i2c_end(); + } else if (type == SUNXI) { + // no implementation for sunxi + } } @@ -230,15 +316,21 @@ NAN_METHOD(i2c_end) */ NAN_METHOD(i2c_read) { - uint8_t rval; + uint8_t rval = 0; if ((info.Length() != 2) || (!info[0]->IsObject()) || (!info[1]->IsNumber())) return ThrowTypeError("Incorrect arguments"); - rval = bcm2835_i2c_read(node::Buffer::Data(info[0]->ToObject()), - info[1]->NumberValue()); + char* buf = node::Buffer::Data(info[0]->ToObject()); + uint32_t len = info[1]->NumberValue(); + + if (type == BCM2835) { + rval = bcm2835_i2c_read(buf, len); + } else if (type == SUNXI) { + rval = sunxi_i2c_read(buf, len); + } info.GetReturnValue().Set(rval); } @@ -252,8 +344,14 @@ NAN_METHOD(i2c_write) (!info[1]->IsNumber())) return ThrowTypeError("Incorrect arguments"); - rval = bcm2835_i2c_write(node::Buffer::Data(info[0]->ToObject()), - info[1]->NumberValue()); + char* buf = node::Buffer::Data(info[0]->ToObject()); + uint32_t len = info[1]->NumberValue(); + + if (type == BCM2835) { + rval = bcm2835_i2c_write(buf, len); + } else if (type == SUNXI) { + rval = sunxi_i2c_write(buf, len); + } info.GetReturnValue().Set(rval); } @@ -266,7 +364,12 @@ NAN_METHOD(pwm_set_clock) if ((info.Length() != 1) || (!info[0]->IsNumber())) return ThrowTypeError("Incorrect arguments"); - bcm2835_pwm_set_clock(info[0]->NumberValue()); + uint32_t divisor = info[0]->NumberValue(); + if (type == BCM2835) { + bcm2835_pwm_set_clock(divisor); + } else if (type == SUNXI) { + sunxi_pwm_set_clock(divisor); + } } NAN_METHOD(pwm_set_mode) @@ -277,8 +380,15 @@ NAN_METHOD(pwm_set_mode) (!info[2]->IsNumber())) return ThrowTypeError("Incorrect arguments"); - bcm2835_pwm_set_mode(info[0]->NumberValue(), info[1]->NumberValue(), - info[2]->NumberValue()); + uint8_t channel = info[0]->NumberValue(); + uint8_t markspace = info[1]->NumberValue(); + uint8_t enabled = info[2]->NumberValue(); + + if (type == BCM2835) { + bcm2835_pwm_set_mode(channel, markspace, enabled); + } else if (type == SUNXI) { + sunxi_pwm_set_mode(channel, markspace, enabled); + } } NAN_METHOD(pwm_set_range) @@ -288,7 +398,14 @@ NAN_METHOD(pwm_set_range) (!info[1]->IsNumber())) return ThrowTypeError("Incorrect arguments"); - bcm2835_pwm_set_range(info[0]->NumberValue(), info[1]->NumberValue()); + uint8_t channel = info[0]->NumberValue(); + uint32_t range = info[1]->NumberValue(); + + if (type == BCM2835) { + bcm2835_pwm_set_range(channel, range); + } else if (type == SUNXI) { + sunxi_pwm_set_range(range); + } } NAN_METHOD(pwm_set_data) @@ -298,7 +415,14 @@ NAN_METHOD(pwm_set_data) (!info[1]->IsNumber())) return ThrowTypeError("Incorrect arguments"); - bcm2835_pwm_set_data(info[0]->NumberValue(), info[1]->NumberValue()); + uint8_t channel = info[0]->NumberValue(); + uint32_t data = info[1]->NumberValue(); + + if (type == BCM2835) { + bcm2835_pwm_set_data(channel, data); + } else if (type == SUNXI) { + sunxi_pwm_set_data(data); + } } /* @@ -306,7 +430,11 @@ NAN_METHOD(pwm_set_data) */ NAN_METHOD(spi_begin) { - bcm2835_spi_begin(); + if (type == BCM2835) { + bcm2835_spi_begin(); + } else if (type == SUNXI) { + sunxi_spi_begin(); + } } NAN_METHOD(spi_chip_select) @@ -314,7 +442,13 @@ NAN_METHOD(spi_chip_select) if ((info.Length() != 1) || (!info[0]->IsNumber())) return ThrowTypeError("Incorrect arguments"); - bcm2835_spi_chipSelect(info[0]->NumberValue()); + uint8_t cs = info[0]->NumberValue(); + + if (type == BCM2835) { + bcm2835_spi_chipSelect(cs); + } else if (type == SUNXI) { + return ThrowTypeError("SUNXI doesn't support selecting chips"); + } } NAN_METHOD(spi_set_cs_polarity) @@ -324,8 +458,14 @@ NAN_METHOD(spi_set_cs_polarity) (!info[1]->IsNumber())) return ThrowTypeError("Incorrect arguments"); - bcm2835_spi_setChipSelectPolarity(info[0]->NumberValue(), - info[1]->NumberValue()); + uint8_t cs = info[0]->NumberValue(); + uint8_t active = info[1]->NumberValue(); + + if (type == BCM2835) { + bcm2835_spi_setChipSelectPolarity(cs, active); + } else if (type == SUNXI) { + return ThrowTypeError("SUNXI doesn't support polarity selections"); + } } NAN_METHOD(spi_set_clock_divider) @@ -333,7 +473,13 @@ NAN_METHOD(spi_set_clock_divider) if ((info.Length() != 1) || (!info[0]->IsNumber())) return ThrowTypeError("Incorrect arguments"); - bcm2835_spi_setClockDivider(info[0]->NumberValue()); + uint16_t divider = info[0]->NumberValue(); + + if (type == BCM2835) { + bcm2835_spi_setClockDivider(divider); + } else if (type == SUNXI) { + return ThrowTypeError("SUNXI doesn't support clock divider"); + } } NAN_METHOD(spi_set_data_mode) @@ -341,7 +487,13 @@ NAN_METHOD(spi_set_data_mode) if ((info.Length() != 1) || (!info[0]->IsNumber())) return ThrowTypeError("Incorrect arguments"); - bcm2835_spi_setDataMode(info[0]->NumberValue()); + uint8_t mode = info[0]->NumberValue(); + + if (type == BCM2835) { + bcm2835_spi_setDataMode(mode); + } else if (type == SUNXI) { + return ThrowTypeError("SUNXI doesn't support changing mode"); + } } NAN_METHOD(spi_transfer) @@ -352,9 +504,15 @@ NAN_METHOD(spi_transfer) (!info[2]->IsNumber())) return ThrowTypeError("Incorrect arguments"); - bcm2835_spi_transfernb(node::Buffer::Data(info[0]->ToObject()), - node::Buffer::Data(info[1]->ToObject()), - info[2]->NumberValue()); + char* tbuf = node::Buffer::Data(info[0]->ToObject()); + char* rbuf = node::Buffer::Data(info[1]->ToObject()); + uint32_t len = info[2]->NumberValue(); + + if (type == BCM2835) { + bcm2835_spi_transfernb(tbuf, rbuf, len); + } else if (type == SUNXI) { + sunxi_spi_transfernb(tbuf, rbuf, len); + } } NAN_METHOD(spi_write) @@ -364,13 +522,23 @@ NAN_METHOD(spi_write) (!info[1]->IsNumber())) return ThrowTypeError("Incorrect arguments"); - bcm2835_spi_writenb(node::Buffer::Data(info[0]->ToObject()), - info[1]->NumberValue()); + char* tbuf = node::Buffer::Data(info[0]->ToObject()); + uint32_t len = info[1]->NumberValue(); + + if (type == BCM2835) { + bcm2835_spi_writenb(tbuf, len); + } else if (type == SUNXI) { + return ThrowTypeError("SUNXI doesn't support writing only mode"); + } } NAN_METHOD(spi_end) { - bcm2835_spi_end(); + if (type == BCM2835) { + bcm2835_spi_end(); + } else if (type == SUNXI) { + //not implemented yet + } } /* @@ -381,13 +549,25 @@ NAN_METHOD(rpio_init) if ((info.Length() != 1) || (!info[0]->IsNumber())) return ThrowTypeError("Incorrect arguments"); - if (!bcm2835_init(info[0]->NumberValue())) - return ThrowError("Could not initialize bcm2835 GPIO library"); + uint8_t gpiomem = info[0]->NumberValue(); + if (gpiomem<2) { + type = BCM2835; + if (!bcm2835_init(gpiomem)) + return ThrowError("Could not initialize bcm2835 GPIO library"); + } else { + type = SUNXI; + if (!sunxi_init(gpiomem-2)) + return ThrowError("Could not initialize sunxi GPIO library"); + } } NAN_METHOD(rpio_close) { - bcm2835_close(); + if (type == BCM2835) { + bcm2835_close(); + } else if (type == SUNXI) { + //sunxi doesn't implement this + } } /* diff --git a/src/sunxi.c b/src/sunxi.c new file mode 100755 index 0000000..11abde4 --- /dev/null +++ b/src/sunxi.c @@ -0,0 +1,824 @@ +/* + * This implementation is based on WiringNP project + * https://github.com/friendlyarm/WiringNP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#include "sunxi.h" + +#ifndef TRUE +#define TRUE (1==1) +#define FALSE (1==2) +#endif + +// Environment Variables + +#define ENV_DEBUG "SUNXI_DEBUG" +#define ENV_CODES "SUNXI_CODES" + + +// Access from ARM Running Linux +// Taken from Gert/Doms code. Some of this is not in the manual +// that I can find )-: + +#define BLOCK_SIZE (4*1024) + +// Locals to hold pointers to the hardware + +static volatile uint32_t *gpio; +static volatile uint32_t *pwm; +static volatile uint32_t *clk; + +#ifdef USE_TIMER +static volatile uint32_t *timer; +static volatile uint32_t *timerIrqRaw; +#endif + +#define SUNXI_GPIO_BASE (0x01C20800) +#define MAP_SIZE (4096*2) +#define MAP_MASK (MAP_SIZE - 1) + +#define CLOCK_BASE (0x00101000) +#define GPIO_BASE (0x01C20000) +#define GPIO_PWM (0x01C21000) + +#define SUNXI_PWM_CTRL_REG (0x01C21400) +#define SUNXI_PWM_CH0_PERIOD (0x01C21404) + +#define SUNXI_PWM_CH0_EN (1 << 4) +#define SUNXI_PWM_CH0_ACT_STA (1 << 5) +#define SUNXI_PWM_SCLK_CH0_GATING (1 << 6) +#define SUNXI_PWM_CH0_MS_MODE (1 << 7) //pulse mode +#define SUNXI_PWM_CH0_PUL_START (1 << 8) + +#define PWM_CLK_DIV_120 0 + +#define I2C_SLAVE 0x0703 +#define I2C_SMBUS 0x0720 /* SMBus-level access */ + +// SMBus transaction types +#define I2C_SMBUS_BYTE_DATA 2 + +// PWM +#define PWM_MODE_MS 0 + + +// Failure modes +#define WPI_FATAL (1==1) +#define WPI_ALMOST (1==2) + +// The SPI bus parameters +// Variables as they need to be passed as pointers later on + +const char *spiDev0 = "/dev/spidev0.0" ; +//const char *spiDev1 = "/dev/spidev0.1" ; +const uint8_t spiMode = 0 ; +const uint8_t spiBPW = 8 ; +const uint16_t spiDelay = 0 ; + + +static uint64_t epochMilli, epochMicro; + +// Misc + +static volatile int pinPass = -1; + +// Debugging & Return codes + +int wiringPiDebug = FALSE; // guenter FALSE ; +int wiringPiReturnCodes = FALSE; + +// ISR Data + +//static void (*isrFunctions [64])(void); + + +static int upDnConvert[3] = {3, 3, 1}; + +static int pwmmode = 0; + +int i2c_fd = -1; +uint8_t i2c_addr = 0; +int spi_fd = -1; + +// SMBus messages + +#define I2C_SMBUS_BLOCK_MAX 32 /* As specified in SMBus standard */ + +#define I2C_SMBUS_READ 1 +#define I2C_SMBUS_WRITE 0 +/* + * Functions + ********************************************************************************* + */ + +/* + * delay: + * Wait for some number of milliseconds + ********************************************************************************* + */ + +void delay(unsigned int howLong) { + struct timespec sleeper, dummy; + + sleeper.tv_sec = (time_t) (howLong / 1000); + sleeper.tv_nsec = (long) (howLong % 1000) * 1000000; + + nanosleep(&sleeper, &dummy); +} + +/* + * sunxi_delayMicroseconds: + * This is somewhat intersting. It seems that on the Pi, a single call + * to nanosleep takes some 80 to 130 microseconds anyway, so while + * obeying the standards (may take longer), it's not always what we + * want! + * + * So what I'll do now is if the delay is less than 100uS we'll do it + * in a hard loop, watching a built-in counter on the ARM chip. This is + * somewhat sub-optimal in that it uses 100% CPU, something not an issue + * in a microcontroller, but under a multi-tasking, multi-user OS, it's + * wastefull, however we've no real choice )-: + * + * Plan B: It seems all might not be well with that plan, so changing it + * to use gettimeofday () and poll on that instead... + ********************************************************************************* + */ + +void delayMicrosecondsHard(unsigned int howLong) { + struct timeval tNow, tLong, tEnd; + + gettimeofday(&tNow, NULL); + tLong.tv_sec = howLong / 1000000; + tLong.tv_usec = howLong % 1000000; + timeradd(&tNow, &tLong, &tEnd); + + while (timercmp(&tNow, &tEnd, <)) + gettimeofday(&tNow, NULL); +} + +void sunxi_delayMicroseconds(unsigned int howLong) { + struct timespec sleeper; + unsigned int uSecs = howLong % 1000000; + unsigned int wSecs = howLong / 1000000; + + /**/ if (howLong == 0) + return; + else if (howLong < 100) + delayMicrosecondsHard(howLong); + else { + sleeper.tv_sec = wSecs; + sleeper.tv_nsec = (long) (uSecs * 1000L); + nanosleep(&sleeper, NULL); + } +} + + + + +uint32_t readl(uint32_t addr) { + uint32_t val = 0; + uint32_t mmap_base = (addr & ~MAP_MASK); + uint32_t mmap_seek = ((addr - mmap_base) >> 2); + val = *(gpio + mmap_seek); + return val; + +} + +void writel(uint32_t val, uint32_t addr) { + uint32_t mmap_base = (addr & ~MAP_MASK); + uint32_t mmap_seek = ((addr - mmap_base) >> 2); + *(gpio + mmap_seek) = val; +} + +const char * int2bin(uint32_t param) { + int bits = sizeof(uint32_t)*CHAR_BIT; + static char buffer[sizeof(uint32_t)*CHAR_BIT + 1]; + char chars[2] = {'0', '1'}; + int i,j,offset; + for (i = 0; i < bits; i++) { + j = bits - i - 1; + offset = (param & (1 << j)) >> j; + buffer[i] = chars[offset]; + } + buffer[bits] = '\0'; + return buffer; +} + + +void print_pwm_reg() { + uint32_t val = readl(SUNXI_PWM_CTRL_REG); + uint32_t val2 = readl(SUNXI_PWM_CH0_PERIOD); + if (wiringPiDebug) { + printf("SUNXI_PWM_CTRL_REG: %s\n", int2bin(val)); + printf("SUNXI_PWM_CH0_PERIOD: %s\n", int2bin(val2)); + } +} + +void sunxi_pwm_set_enable(int en) { + int val = 0; + val = readl(SUNXI_PWM_CTRL_REG); + if (en) { + val |= (SUNXI_PWM_CH0_EN | SUNXI_PWM_SCLK_CH0_GATING); + } + else { + val &= ~(SUNXI_PWM_CH0_EN | SUNXI_PWM_SCLK_CH0_GATING); + } + if (wiringPiDebug) + printf(">>function%s,no:%d,enable? :0x%x\n", __func__, __LINE__, val); + writel(val, SUNXI_PWM_CTRL_REG); + delay(1); + print_pwm_reg(); +} + +void sunxi_pwm_mode(int mode) { + int val = 0; + val = readl(SUNXI_PWM_CTRL_REG); + mode &= 1; //cover the mode to 0 or 1 + if (mode) { //pulse mode + val |= (SUNXI_PWM_CH0_MS_MODE | SUNXI_PWM_CH0_PUL_START); + pwmmode = 1; + } else { //cycle mode + val &= ~(SUNXI_PWM_CH0_MS_MODE); + pwmmode = 0; + } + val |= (SUNXI_PWM_CH0_ACT_STA); + if (wiringPiDebug) + printf(">>function%s,no:%d,mode? :0x%x\n", __func__, __LINE__, val); + writel(val, SUNXI_PWM_CTRL_REG); + delay(1); + print_pwm_reg(); +} + +void sunxi_pwm_set_clk(int clk) { + int val = 0; + if (wiringPiDebug) + printf(">>function%s,no:%d\n", __func__, __LINE__); + // sunxi_pwm_set_enable(0); + val = readl(SUNXI_PWM_CTRL_REG); + if (wiringPiDebug) + printf("read reg val: 0x%x\n", val); + //clear clk to 0 + val &= 0xf801f0; + val |= ((clk & 0xf) << 15); //todo check wether clk is invalid or not + writel(val, SUNXI_PWM_CTRL_REG); + sunxi_pwm_set_enable(1); + if (wiringPiDebug) + printf(">>function%s,no:%d,clk? :0x%x\n", __func__, __LINE__, val); + delay(1); + print_pwm_reg(); +} + +/** + * ch0 and ch1 set the same,16 bit period and 16 bit act + */ +uint32_t sunxi_pwm_get_period(void) { + uint32_t period_cys = 0; + period_cys = readl(SUNXI_PWM_CH0_PERIOD); //get ch1 period_cys + if (wiringPiDebug) { + printf("periodcys: %d\n", period_cys); + } + period_cys &= 0xffff0000; //get period_cys + period_cys = period_cys >> 16; + if (wiringPiDebug) + printf(">>func:%s,no:%d,period/range:%d", __func__, __LINE__, period_cys); + delay(1); + return period_cys; +} + +uint32_t sunxi_pwm_get_act(void) { + uint32_t period_act = 0; + period_act = readl(SUNXI_PWM_CH0_PERIOD); //get ch1 period_cys + period_act &= 0xffff; //get period_act + if (wiringPiDebug) + printf(">>func:%s,no:%d,period/range:%d", __func__, __LINE__, period_act); + delay(1); + return period_act; +} + +void sunxi_pwm_set_act(int act_cys) { + uint32_t per0 = 0; + //keep period the same, clear act_cys to 0 first + if (wiringPiDebug) + printf(">>func:%s no:%d\n", __func__, __LINE__); + per0 = readl(SUNXI_PWM_CH0_PERIOD); + if (wiringPiDebug) + printf("read reg val: 0x%x\n", per0); + per0 &= 0xffff0000; + act_cys &= 0xffff; + act_cys |= per0; + if (wiringPiDebug) + printf("write reg val: 0x%x\n", act_cys); + writel(act_cys, SUNXI_PWM_CH0_PERIOD); + delay(1); + print_pwm_reg(); +} + +int sunxi_get_gpio_mode(int pin) { + uint32_t regval = 0; + int bank = pin >> 5; + int index = pin - (bank << 5); + int offset = ((index - ((index >> 3) << 3)) << 2); + uint32_t reval = 0; + uint32_t phyaddr = SUNXI_GPIO_BASE + (bank * 36) + ((index >> 3) << 2); + if (wiringPiDebug) + printf("func:%s pin:%d, bank:%d index:%d phyaddr:0x%x\n", __func__, pin, bank, index, phyaddr); + regval = readl(phyaddr); + if (wiringPiDebug) + printf("read reg val: 0x%x offset:%d return: %d\n", regval, offset, reval); + // reval=regval &(reval+(7 << offset)); + reval = (regval >> offset)&7; + if (wiringPiDebug) + printf("read reg val: 0x%x offset:%d return: %d\n", regval, offset, reval); + return reval; +} + +void sunxi_gpio_fsel(uint8_t pin, uint8_t mode) { + uint32_t regval = 0; + int bank = pin >> 5; + int index = pin - (bank << 5); + int offset = ((index - ((index >> 3) << 3)) << 2); + uint32_t phyaddr = SUNXI_GPIO_BASE + (bank * 36) + ((index >> 3) << 2); + if (wiringPiDebug) + printf("func:%s pin:%d, MODE:%d bank:%d index:%d phyaddr:0x%x\n", __func__, pin, mode, bank, index, phyaddr); + regval = readl(phyaddr); + if (wiringPiDebug) + printf("read reg val: 0x%x offset:%d\n", regval, offset); + if (0 == mode) { + regval &= ~(7 << offset); + writel(regval, phyaddr); + regval = readl(phyaddr); + if (wiringPiDebug) + printf("Input mode set over reg val: 0x%x\n", regval); + } else if (1 == mode) { + regval &= ~(7 << offset); + regval |= (1 << offset); + if (wiringPiDebug) + printf("Out mode ready set val: 0x%x\n", regval); + writel(regval, phyaddr); + regval = readl(phyaddr); + if (wiringPiDebug) + printf("Out mode set over reg val: 0x%x\n", regval); + } + else if (2 == mode) { + // set pin PWMx to pwm mode + regval &= ~(7 << offset); + regval |= (0x3 << offset); + if (wiringPiDebug) + printf(">>>>>line:%d PWM mode ready to set val: 0x%x\n", __LINE__, regval); + writel(regval, phyaddr); + sunxi_delayMicroseconds(200); + regval = readl(phyaddr); + if (wiringPiDebug) + printf("<<<<>func:%s no:%d\n", __func__, __LINE__); + range &= 0xffff; //set max period to 2^16 + range = range << 16; + val = readl(SUNXI_PWM_CH0_PERIOD); + if (wiringPiDebug) + printf("read reg val: 0x%x\n", val); + val &= 0x0000ffff; + range |= val; + if (wiringPiDebug) + printf("write reg val: 0x%x\n", range); + writel(range, SUNXI_PWM_CH0_PERIOD); + delay(1); + val = readl(SUNXI_PWM_CH0_PERIOD); + if (wiringPiDebug) + printf("readback reg val: 0x%x\n", val); + print_pwm_reg(); +} + +/* + * pwmSetClock: + * Set/Change the PWM clock. Originally my code, but changed + * (for the better!) by Chris Hall, + * after further study of the manual and testing with a 'scope + ********************************************************************************* + */ + +void sunxi_pwm_set_clock(uint32_t divisor) { + sunxi_pwm_set_clk(divisor); + sunxi_pwm_set_enable(1); + return; +} + +/* + ********************************************************************************* + * Core Functions + ********************************************************************************* + */ + +/* + * pullUpDownCtrl: + * Control the internal pull-up/down resistors on a GPIO pin + * The Arduino only has pull-ups and these are enabled by writing 1 + * to a port when in input mode - this paradigm doesn't quite apply + * here though. + ********************************************************************************* + */ + +void sunxi_pullUpDnControl(int pin, int pud) { + pud = upDnConvert[pud]; + uint32_t regval = 0; + int bank = pin >> 5; + int index = pin - (bank << 5); + int sub = index >> 4; + int sub_index = index - 16 * sub; + uint32_t phyaddr = SUNXI_GPIO_BASE + (bank * 36) + 0x1c + sub * 4; // +0x10 -> pullUpDn reg + if (wiringPiDebug) + printf("func:%s pin:%d,bank:%d index:%d sub:%d phyaddr:0x%x\n", __func__, pin, bank, index, sub, phyaddr); + regval = readl(phyaddr); + if (wiringPiDebug) + printf("pullUpDn reg:0x%x, pud:0x%x sub_index:%d\n", regval, pud, sub_index); + regval &= ~(3 << (sub_index << 1)); + regval |= (pud << (sub_index << 1)); + if (wiringPiDebug) + printf("pullUpDn val ready to set:0x%x\n", regval); + writel(regval, phyaddr); + regval = readl(phyaddr); + if (wiringPiDebug) + printf("pullUpDn reg after set:0x%x addr:0x%x\n", regval, phyaddr); + delay(1); + return; +} +/* + * sunxi_gpio_lev: + * Read the value of a given Pin, returning HIGH or LOW + ********************************************************************************* + */ + +uint8_t sunxi_gpio_lev(uint8_t pin) { + uint32_t regval = 0; + int bank = pin >> 5; + int index = pin - (bank << 5); + uint32_t phyaddr = SUNXI_GPIO_BASE + (bank * 36) + 0x10; // +0x10 -> data reg + if (wiringPiDebug) + printf("func:%s pin:%d,bank:%d index:%d phyaddr:0x%x\n", __func__, pin, bank, index, phyaddr); + regval = readl(phyaddr); + regval = regval >> index; + regval &= 1; + if (wiringPiDebug) + printf("***** read reg val: 0x%x,bank:%d,index:%d,line:%d\n", regval, bank, index, __LINE__); + return regval; +} + +/* + * sunxi_gpio_write: + * Set an output bit + ********************************************************************************* + */ + +void sunxi_gpio_write(uint8_t pin, uint8_t on) { + uint32_t regval = 0; + int bank = pin >> 5; + int index = pin - (bank << 5); + uint32_t phyaddr = SUNXI_GPIO_BASE + (bank * 36) + 0x10; // +0x10 -> data reg + if (wiringPiDebug) + printf("func:%s pin:%d, value:%d bank:%d index:%d phyaddr:0x%x\n", __func__, pin, on, bank, index, phyaddr); + regval = readl(phyaddr); + if (wiringPiDebug) + printf("befor write reg val: 0x%x,index:%d\n", regval, index); + if (0 == on) { + regval &= ~(1 << index); + writel(regval, phyaddr); + regval = readl(phyaddr); + if (wiringPiDebug) + printf("LOW val set over reg val: 0x%x\n", regval); + } else { + regval |= (1 << index); + writel(regval, phyaddr); + regval = readl(phyaddr); + if (wiringPiDebug) + printf("HIGH val set over reg val: 0x%x\n", regval); + } +} + +/* + * pwmWrite: + * Set an output PWM value + ********************************************************************************* + */ + +void sunxi_pwm_set_data(uint32_t data) { + + + uint32_t a_val = 0; + if (pwmmode == 1)//sycle + { + sunxi_pwm_mode(1); + } else { + //sunxi_pwm_mode(0); + } + a_val = sunxi_pwm_get_period(); + if (wiringPiDebug) + printf("==> no:%d period now is :%d,act_val to be set:%d\n", __LINE__, a_val, data); + if (data - a_val > 0) { + printf("val pwmWrite 0 <= X <= 1024\n"); + printf("Or you can set new range by yourself by pwmSetRange(range\n"); + return; + } + //if value changed chang it + sunxi_pwm_set_enable(0); + sunxi_pwm_set_act(data); + sunxi_pwm_set_enable(1); + if (wiringPiDebug) + printf("this fun is ok now %s,%d\n", __func__, __LINE__); + + return; +} + +/* + * initialiseEpoch: + * Initialise our start-of-time variable to be the current unix + * time in milliseconds and microseconds. + ********************************************************************************* + */ + +static void initialiseEpoch(void) { + struct timeval tv; + + gettimeofday(&tv, NULL); + epochMilli = (uint64_t) tv.tv_sec * (uint64_t) 1000 + (uint64_t) (tv.tv_usec / 1000); + epochMicro = (uint64_t) tv.tv_sec * (uint64_t) 1000000 + (uint64_t) (tv.tv_usec); +} + +/* + * wiringPiSetup: + * Must be called once at the start of your program execution. + * + * Default setup: Initialises the system into wiringPi Pin mode and uses the + * memory mapped hardware directly. + * + * Changed now to revert to "gpio" mode if we're running on a Compute Module. + ********************************************************************************* + */ + +int sunxi_init(int gpiomem) { + int fd; + // int boardRev; + if (getenv(ENV_DEBUG) != NULL) + wiringPiDebug = TRUE; + + if (getenv(ENV_CODES) != NULL) + wiringPiReturnCodes = TRUE; + + if (geteuid() != 0) + (void)wiringPiFailure(WPI_FATAL, "sunxi_init: Must be root. (Did you forget sudo?)\n"); + + if (wiringPiDebug) + printf("wiringPi: sunxi_init called\n"); + + // Open the master /dev/memory device + + if ((fd = open("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC)) < 0) + return wiringPiFailure(WPI_ALMOST, "sunxi_init: Unable to open /dev/mem: %s\n", strerror(errno)); + + // GPIO: + // BLOCK SIZE * 2 increases range to include pwm addresses + gpio = (uint32_t *) mmap(0, BLOCK_SIZE*10, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO_BASE); + if ((int32_t) gpio == -1) + return wiringPiFailure(WPI_ALMOST, "sunxi_init: mmap (GPIO) failed: %s\n", strerror(errno)); + + // PWM + + pwm = (uint32_t *) mmap(0, BLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO_PWM); + if ((int32_t) pwm == -1) + return wiringPiFailure(WPI_ALMOST, "sunxi_init: mmap (PWM) failed: %s\n", strerror(errno)); + + // Clock control (needed for PWM) + + clk = (uint32_t *) mmap(0, BLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, CLOCK_BASE); + if ((int32_t) clk == -1) + return wiringPiFailure(WPI_ALMOST, "sunxi_init: mmap (CLOCK) failed: %s\n", strerror(errno)); + + initialiseEpoch(); + + return 0; +} + +int wiringPiI2CSetupInterface (const char *device, int devId) +{ + + if ((i2c_fd = open (device, O_RDWR)) < 0) + return wiringPiFailure (WPI_ALMOST, "Unable to open I2C device: %s\n", strerror (errno)) ; + + if (ioctl (i2c_fd, I2C_SLAVE, devId) < 0) + return wiringPiFailure (WPI_ALMOST, "Unable to select I2C device: %s\n", strerror (errno)) ; + return 1; +} + +int sunxi_i2c_begin (const int devId) +{ + int rev ; + const char *device ; + + rev = 1; + if (rev == 1) + device = "/dev/i2c-0" ; + else if (rev == 2) + device = "/dev/i2c-1" ; + else if (rev == 3) + device = "/dev/i2c-0"; // guenter fuer orange pi device = "/dev/i2c-2"; + else + device = "/dev/i2c-3" ; + + return wiringPiI2CSetupInterface (device, devId) ; +} + +void sunxi_i2c_setSlaveAddress(uint8_t addr) +{ + i2c_addr = addr; +} + +union i2c_smbus_data +{ + uint8_t byte ; + uint16_t word ; + uint8_t block [I2C_SMBUS_BLOCK_MAX + 2] ; // block [0] is used for length + one more for PEC +} ; + +struct i2c_smbus_ioctl_data +{ + char read_write ; + uint8_t command ; + int size ; + union i2c_smbus_data *data ; +} ; + +static inline int i2c_smbus_access (int fd, char rw, uint8_t command, int size, union i2c_smbus_data *data) +{ + struct i2c_smbus_ioctl_data args ; + + args.read_write = rw ; + args.command = command ; + args.size = size ; + args.data = data ; + return ioctl (fd, I2C_SMBUS, &args) ; +} + +uint8_t sunxi_i2c_read(char* buf, uint32_t len) +{ + + union i2c_smbus_data data ; + uint8_t result = 0; + uint32_t i = 0; + + while ((result >=0) & (i=0) & (i Date: Wed, 6 Dec 2017 17:24:21 +0100 Subject: [PATCH 2/5] Fixing Error: Could not initialize sunxi GPIO library --- src/sunxi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sunxi.c b/src/sunxi.c index 11abde4..551b1b8 100755 --- a/src/sunxi.c +++ b/src/sunxi.c @@ -649,7 +649,7 @@ int sunxi_init(int gpiomem) { wiringPiReturnCodes = TRUE; if (geteuid() != 0) - (void)wiringPiFailure(WPI_FATAL, "sunxi_init: Must be root. (Did you forget sudo?)\n"); + return wiringPiFailure(WPI_FATAL, "sunxi_init: Must be root. (Did you forget sudo?)\n"); if (wiringPiDebug) printf("wiringPi: sunxi_init called\n"); @@ -679,7 +679,7 @@ int sunxi_init(int gpiomem) { initialiseEpoch(); - return 0; + return 1; } int wiringPiI2CSetupInterface (const char *device, int devId) From fbe8000e558cf18b7314a6cb69817459afd134cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miloslav=20Mrvi=CC=81k?= Date: Thu, 26 Jul 2018 21:26:28 +0200 Subject: [PATCH 3/5] Added support for NanoPI M1 --- README.md | 2 +- lib/rpio.js | 1053 ++++++++++++++++++++++++++------------------------- 2 files changed, 530 insertions(+), 525 deletions(-) diff --git a/README.md b/README.md index cd70688..2744ef2 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Pi, Orange Pi and NanoPi NEO Plus2 GPIO interface, supporting regular GPIO as we ## Compatibility * Raspberry Pi Models: A, B (revisions 1.0 and 2.0), A+, B+, 2, 3, Zero. -* NanoPi Models: NEO, NEO2 +* NanoPi Models: NEO, NEO2, M1 PLUS * Orange Pi * Node.js Versions: 0.8, 0.10, 0.12, 4, 5, 6, 7, 8 diff --git a/lib/rpio.js b/lib/rpio.js index 7428515..b414cc0 100755 --- a/lib/rpio.js +++ b/lib/rpio.js @@ -22,9 +22,8 @@ var EventEmitter = require('events').EventEmitter; /* * Event Emitter gloop. */ -function rpio() -{ - EventEmitter.call(this); +function rpio() { + EventEmitter.call(this); } util.inherits(rpio, EventEmitter); module.exports = new rpio; @@ -85,9 +84,9 @@ rpio.prototype.PAD_SLEW_UNLIMITED = 0x10; */ var rpio_inited = false; var rpio_options = { - gpiomem: true, - mapping: 'physical', - mock: false, + gpiomem: true, + mapping: 'physical', + mock: false, }; /* Default mock mode if hardware is unsupported. */ @@ -96,42 +95,38 @@ var defmock = "raspi-3"; /* * Wrapper functions. */ -function bindcall(bindfunc, optarg) -{ - if (rpio_options.mock) - return; +function bindcall(bindfunc, optarg) { + if (rpio_options.mock) + return; - return bindfunc(optarg); + return bindfunc(optarg); } -function bindcall2(bindfunc, arg1, arg2) -{ - if (rpio_options.mock) - return; +function bindcall2(bindfunc, arg1, arg2) { + if (rpio_options.mock) + return; - return bindfunc(arg1, arg2); + return bindfunc(arg1, arg2); } -function bindcall3(bindfunc, arg1, arg2, arg3) -{ - if (rpio_options.mock) - return; +function bindcall3(bindfunc, arg1, arg2, arg3) { + if (rpio_options.mock) + return; - return bindfunc(arg1, arg2, arg3); + return bindfunc(arg1, arg2, arg3); } -function warn(msg) -{ - console.error("WARNING: " + msg); +function warn(msg) { + console.error("WARNING: " + msg); } /* * Map physical pin to BCM GPIOxx numbering. There are currently three * layouts: * - * PINMAP_26_R1: 26-pin early original models A and B (PCB rev 1.0) - * PINMAP_26: 26-pin standard models A and B (PCB rev 2.0) - * PINMAP_40: 40-pin models + * PINMAP_26_R1: 26-pin early original models A and B (PCB rev 1.0) + * PINMAP_26: 26-pin standard models A and B (PCB rev 2.0) + * PINMAP_40: 40-pin models * * A -1 indicates an unusable pin. Each table starts with a -1 so that we * can index into the array by pin number. @@ -210,68 +205,90 @@ var pinmaps = { 26, 20, /* P37 P38 */ -1, 21 /* P39 P40 */ ], - - PINMAP_ORANGEPI: [ - -1, - -1, -1, - 12, -1, - 11, -1, - 6, 13, - -1, 14, - 1, 110, - 0, -1, - 3, 68, - -1, 71, - 64, -1, - 65, 2, - 66, 67, - -1, 21, - 19, 18, - 7, -1, - 8, 200, - 9, -1, - 10, 201, - 20, 198, - -1, 199 - ], - PINMAP_NEO: [ - -1, - -1, -1, - 12, -1, - 11, -1, - 203, 198, - -1, 199, - 0, 6, - 2, -1, - 3, 200, - -1, 201, - 64, -1, - 65, 1, - 66, 67, - -1, 17, - 19, 18, - 20, -1, - 21, 7, - 8, -1, - 16, 13, - 9,15, - -1, 14 - ], - PINMAP_NEO2: [ - -1, - -1, -1, - 12, -1, - 11, -1, - 203, 198, - -1, 199, - 0, 6, - 2, -1, - 3, 200, - -1, 201, - 64, -1, - 65, 1, - 66, 77 - ] + PINMAP_ORANGEPI: [ + -1, + -1, -1, + 12, -1, + 11, -1, + 6, 13, + -1, 14, + 1, 110, + 0, -1, + 3, 68, + -1, 71, + 64, -1, + 65, 2, + 66, 67, + -1, 21, + 19, 18, + 7, -1, + 8, 200, + 9, -1, + 10, 201, + 20, 198, + -1, 199 + ], + PINMAP_NANOPI_M1_PLUS: [ + -1, + -1, -1, + 12, -1, + 11, -1, + 203, 198, + -1, 199, + 0, 6, + 2, -1, + 3, 200, + -1, 201, + 64, -1, + 65, 1, + 66, 67, + -1, 17, + 19, 18, + 20, -1, + 21, -1, + -1, -1, + -1, -1, + 9, -1, + -1, -1 + ], + PINMAP_NEO: [ + -1, + -1, -1, + 12, -1, + 11, -1, + 203, 198, + -1, 199, + 0, 6, + 2, -1, + 3, 200, + -1, 201, + 64, -1, + 65, 1, + 66, 67, + -1, 17, + 19, 18, + 20, -1, + 21, 7, + 8, -1, + 16, 13, + 9,15, + -1, 14 + ], + PINMAP_NEO2: [ + -1, + -1, -1, + 12, -1, + 11, -1, + 203, 198, + -1, 199, + 0, 6, + 2, -1, + 3, 200, + -1, 201, + 64, -1, + 65, 1, + 66, 77 + ] }; /* @@ -284,210 +301,232 @@ var event_pins = {}; var event_mask = 0x0; var event_running = false; -function event_poll() -{ - var active = bindcall(binding.gpio_event_poll, event_mask); +function event_poll() { + var active = bindcall(binding.gpio_event_poll, event_mask); + + for (var gpiopin in event_pins) { + if (active & (1 << gpiopin)) + module.exports.emit('pin' + gpiopin); + } +} - for (gpiopin in event_pins) { - if (active & (1 << gpiopin)) - module.exports.emit('pin' + gpiopin); - } +function read(path) { + try { + var contents = fs.readFileSync(path, 'ascii'); + if (contents) { + return contents.toString(); + } + } catch (err) { + /* It is the purpose of this function to silently catch this error */ + } + return false; } /* * Set up GPIO mapping based on board revision. */ -function detect_pinmap() -{ - var cpuinfo, boardrev, match, cpu, mips; - - try { - cpuinfo = fs.readFileSync('/proc/cpuinfo', 'ascii'); - } catch (err) { - return false; - } - - if (!cpuinfo) - return false; - - cpuinfo.toString().split(/\n/).forEach(function(line) { - if (match = line.match(/^Revision.*(.{4})/)) { - boardrev = parseInt(match[1], 16); - } else if (match = line.match(/^Hardware.*:\s([^\s]+)/)) { - cpu = match[1]; - } else if (match = line.match(/^BogoMIPS.*:\s([^\s]+)/)) { - mips = +match[1]; - } - }); - - switch (boardrev) { - case 0x2: - case 0x3: - pinmap = "PINMAP_26_R1"; - chipType = "BCM2835"; - break; - case 0x4: - case 0x5: - case 0x6: - case 0x7: - case 0x8: - case 0x9: - case 0xd: - case 0xe: - case 0xf: - pinmap = "PINMAP_26"; - chipType = "BCM2835"; - break; - case 0x10: - case 0x12: - case 0x13: - case 0x15: - case 0x92: - case 0x93: - case 0xc1: - case 0x1041: - case 0x2042: - case 0x2082: - pinmap = "PINMAP_40"; - chipType = "BCM2835"; - break; - default: - switch (cpu) { - case 'Allwinnersun50iw2Family': - //Allwinner H5 CPU - pinmap = "PINMAP_NEO2"; - chipType = "SUNXI"; - break; - case 'sun50i': - case 'sun8i': - //Allwinner H3 CPU +function detect_pinmap() { + + var cpuinfo, boardrev, match, cpu, mips; + + cpuinfo = read('/proc/cpuinfo'); + + if (!cpuinfo) return false; + + cpuinfo.split(/\n/).forEach(function(line) { + if (match = line.match(/^Revision.*(.{4})/)) { + boardrev = parseInt(match[1], 16); + } else if (match = line.match(/^Hardware.*:\s([^\s]+)/)) { + cpu = match[1]; + } else if (match = line.match(/^BogoMIPS.*:\s([^\s]+)/)) { + mips = +match[1]; + } + }); + + switch (boardrev) { + case 0x2: + case 0x3: + pinmap = "PINMAP_26_R1"; + chipType = "BCM2835"; + break; + case 0x4: + case 0x5: + case 0x6: + case 0x7: + case 0x8: + case 0x9: + case 0xd: + case 0xe: + case 0xf: + pinmap = "PINMAP_26"; + chipType = "BCM2835"; + break; + case 0x10: + case 0x12: + case 0x13: + case 0x15: + case 0x92: + case 0x93: + case 0xc1: + case 0x1041: + case 0x2042: + case 0x2082: + pinmap = "PINMAP_40"; + chipType = "BCM2835"; + break; + + default: + switch (cpu) { + case 'Allwinner': + { + var model = read('/proc/device-tree/model'); + switch (model) { + // Not well tested. Working on my device running Armbian + case 'FriendlyElec NanoPi M1 Plus\u0000': chipType = "SUNXI"; - if (mips>1000) { - pinmap = "PINMAP_ORANGEPI"; - } else { - pinmap = "PINMAP_NEO"; - } + pinmap = "PINMAP_NANOPI_M1_PLUS"; break; - default: - return false; - } - } - return true; + default: + return false; + } + } + break; + + case 'Allwinnersun50iw2Family': + //Allwinner H5 CPU + pinmap = "PINMAP_NEO2"; + chipType = "SUNXI"; + break; + + case 'sun50i': + case 'sun8i': + //Allwinner H3 CPU + chipType = "SUNXI"; + if (mips > 1000) { + pinmap = "PINMAP_ORANGEPI"; + } else { + pinmap = "PINMAP_NEO"; + } + break; + + default: + return false; + } + } + + return true; } -function set_mock_pinmap() -{ - switch (rpio_options.mock) { - case 'raspi-b-r1': - pinmap = "PINMAP_26_R1"; - chipType = "BCM2835"; - break; - case 'raspi-a': - case 'raspi-b': - pinmap = "PINMAP_26"; - chipType = "BCM2835"; - break; - case 'raspi-a+': - case 'raspi-b+': - case 'raspi-2': - case 'raspi-3': - case 'raspi-zero': - case 'raspi-zero-w': - pinmap = "PINMAP_40"; - chipType = "BCM2835"; - break; - default: - return false; - } - - return true; +function set_mock_pinmap() { + switch (rpio_options.mock) { + case 'raspi-b-r1': + pinmap = "PINMAP_26_R1"; + chipType = "BCM2835"; + break; + + case 'raspi-a': + case 'raspi-b': + pinmap = "PINMAP_26"; + chipType = "BCM2835"; + break; + + case 'raspi-a+': + case 'raspi-b+': + case 'raspi-2': + case 'raspi-3': + case 'raspi-zero': + case 'raspi-zero-w': + pinmap = "PINMAP_40"; + chipType = "BCM2835"; + break; + + default: + return false; + } + + return true; } -function pin_to_gpio(pin) -{ - if (pincache[pin]) - return pincache[pin]; - - switch (rpio_options.mapping) { - case 'physical': - if (pinmaps[pinmap][pin] == -1 || pinmaps[pinmap][pin] == null) - throw "Invalid pin"; - pincache[pin] = pinmaps[pinmap][pin]; - break; - case 'gpio': - if (pinmaps[pinmap].indexOf(pin) === -1) - throw "Invalid pin"; - pincache[pin] = pin; - break; - default: - throw "Unsupported GPIO mode"; - } - - return pincache[pin]; +function pin_to_gpio(pin) { + if (pincache[pin]) + return pincache[pin]; + + switch (rpio_options.mapping) { + case 'physical': + if (pinmaps[pinmap][pin] == -1 || pinmaps[pinmap][pin] == null) + throw new Error("Invalid pin: physical=" + pin); + pincache[pin] = pinmaps[pinmap][pin]; + break; + case 'gpio': + if (pinmaps[pinmap].indexOf(pin) === -1) + throw new Error("Invalid pin: gpio=" + pin); + pincache[pin] = pin; + break; + default: + throw new Error("Unsupported GPIO mode"); + } + + return pincache[pin]; } -function check_sys_gpio(pin) -{ - if (chipType === "BCM2835" && fs.existsSync('/sys/class/gpio/gpio' + pin)) - throw "GPIO" + pin + " is currently in use by /sys/class/gpio"; +function check_sys_gpio(pin) { + if (chipType === "BCM2835" && fs.existsSync('/sys/class/gpio/gpio' + pin)) + throw new Error("GPIO" + pin + " is currently in use by /sys/class/gpio"); } -function get_pwm_function(pin) -{ - var gpiopin = pin_to_gpio(pin); - - switch (gpiopin) { - case 12: - case 13: - return 4; /* BCM2835_GPIO_FSEL_ALT0 */ - case 18: - case 19: - return 2; /* BCM2835_GPIO_FSEL_ALT5 */ - default: - throw "Pin " + pin + " does not support hardware PWM"; - } +function get_pwm_function(pin) { + var gpiopin = pin_to_gpio(pin); + + switch (gpiopin) { + case 12: + case 13: + return 4; /* BCM2835_GPIO_FSEL_ALT0 */ + case 18: + case 19: + return 2; /* BCM2835_GPIO_FSEL_ALT5 */ + default: + throw new Error("Pin " + pin + " does not support hardware PWM"); + } } -function get_pwm_channel(pin) -{ - var gpiopin = pin_to_gpio(pin); - - switch (gpiopin) { - case 12: - case 18: - return 0; - case 13: - case 19: - return 1; - default: - throw "Unknown PWM channel for pin " + pin; - } +function get_pwm_channel(pin) { + var gpiopin = pin_to_gpio(pin); + + switch (gpiopin) { + case 12: + case 18: + return 0; + case 13: + case 19: + return 1; + default: + throw new Error("Unknown PWM channel for pin " + pin); + } } -function set_pin_pwm(pin) -{ - var gpiopin = pin_to_gpio(pin); - var channel, func; +function set_pin_pwm(pin) { + var gpiopin = pin_to_gpio(pin); + var channel, func; - if (rpio_options.gpiomem) - throw "PWM not available in gpiomem mode"; + if (rpio_options.gpiomem) + throw new Error("PWM not available in gpiomem mode"); - check_sys_gpio(gpiopin); + check_sys_gpio(gpiopin); - /* - * PWM channels and alternate functions differ from pin to pin, set - * them up appropriately based on selected pin. - */ - channel = get_pwm_channel(pin); - func = get_pwm_function(pin); + /* + * PWM channels and alternate functions differ from pin to pin, set + * them up appropriately based on selected pin. + */ + channel = get_pwm_channel(pin); + func = get_pwm_function(pin); - bindcall2(binding.gpio_function, gpiopin, func); + bindcall2(binding.gpio_function, gpiopin, func); - /* - * For now we assume mark-space mode, balanced is unsupported. - */ - bindcall3(binding.pwm_set_mode, channel, 1, 1); + /* + * For now we assume mark-space mode, balanced is unsupported. + */ + bindcall3(binding.pwm_set_mode, channel, 1, 1); } /* @@ -498,381 +537,347 @@ function set_pin_pwm(pin) * Default warning handler, if the user registers their own then this one * is cancelled. */ -function default_warn_handler(msg) -{ - if (module.exports.listenerCount('warn') > 1) { - module.exports.removeListener('warn', default_warn_handler); - return; - } - warn(msg); +function default_warn_handler(msg) { + if (module.exports.listenerCount('warn') > 1) { + module.exports.removeListener('warn', default_warn_handler); + return; + } + warn(msg); } module.exports.on('warn', default_warn_handler); -rpio.prototype.init = function(opts) -{ - opts = opts || {}; +rpio.prototype.init = function(opts) { + opts = opts || {}; + + for (var k in rpio_options) { + if (k in opts) + rpio_options[k] = opts[k]; + } + + /* + * Invalidate the pin cache and mapping as we may be in the process + * of changing them. + */ + pincache = {}; + pinmap = null; + chipType = null; + + /* + * Allow the user to specify a mock board to emulate, otherwise try + * to autodetect the board, and fall back to mock mode if running on + * an unsupported platform. + */ + if (rpio_options.mock) { + if (!set_mock_pinmap()) + throw new Error("Unsupported mock mode " + rpio_options.mock); + } else { + if (!detect_pinmap()) { + module.exports.emit('warn', + 'Hardware auto-detect failed, running in ' + + defmock + ' mock mode'); + rpio_options.mock = defmock; + set_mock_pinmap(); + } + } - for (var k in rpio_options) { - if (k in opts) - rpio_options[k] = opts[k]; - } - - /* - * Invalidate the pin cache and mapping as we may be in the process - * of changing them. - */ - pincache = {}; - pinmap = null; - chipType = null; - - /* - * Allow the user to specify a mock board to emulate, otherwise try - * to autodetect the board, and fall back to mock mode if running on - * an unsupported platform. - */ - if (rpio_options.mock) { - if (!set_mock_pinmap()) - throw "Unsupported mock mode " + rpio_options.mock; - } else { - if (!detect_pinmap()) { - module.exports.emit('warn', - 'Hardware auto-detect failed, running in ' + - defmock + ' mock mode'); - rpio_options.mock = defmock; - set_mock_pinmap(); - } - } - - /* - * init rpio library - */ - var gpiomem = Number(rpio_options.gpiomem); - if (chipType === "SUNXI") gpiomem += 2; - bindcall(binding.rpio_init, gpiomem); - rpio_inited = true; + /* + * init rpio library + */ + var gpiomem = Number(rpio_options.gpiomem); + if (chipType === "SUNXI") gpiomem += 2; + bindcall(binding.rpio_init, gpiomem); + rpio_inited = true; }; -rpio.prototype.open = function(pin, mode, init) -{ - if (!rpio_inited) { - /* PWM requires full /dev/mem */ - if (mode === rpio.prototype.PWM) - rpio_options.gpiomem = false; - rpio.prototype.init(); - } +rpio.prototype.open = function(pin, mode, init) { + if (!rpio_inited) { + /* PWM requires full /dev/mem */ + if (mode === rpio.prototype.PWM) + rpio_options.gpiomem = false; + rpio.prototype.init(); + } - var gpiopin = pin_to_gpio(pin); + var gpiopin = pin_to_gpio(pin); - check_sys_gpio(gpiopin); + check_sys_gpio(gpiopin); - switch (mode) { - case rpio.prototype.INPUT: - if (init !== undefined) - bindcall2(binding.gpio_pud, gpiopin, init); - return bindcall2(binding.gpio_function, gpiopin, rpio.prototype.INPUT); - case rpio.prototype.OUTPUT: - if (init !== undefined) - bindcall2(binding.gpio_write, gpiopin, init); - return bindcall2(binding.gpio_function, gpiopin, rpio.prototype.OUTPUT); - case rpio.prototype.PWM: - return set_pin_pwm(pin); - default: - throw "Unsupported mode " + mode; - } + switch (mode) { + case rpio.prototype.INPUT: + if (init !== undefined) + bindcall2(binding.gpio_pud, gpiopin, init); + return bindcall2(binding.gpio_function, gpiopin, rpio.prototype.INPUT); + case rpio.prototype.OUTPUT: + if (init !== undefined) + bindcall2(binding.gpio_write, gpiopin, init); + return bindcall2(binding.gpio_function, gpiopin, rpio.prototype.OUTPUT); + case rpio.prototype.PWM: + return set_pin_pwm(pin); + default: + throw new Error("Unsupported mode " + mode); + } }; -rpio.prototype.mode = function(pin, mode) -{ - var gpiopin = pin_to_gpio(pin); +rpio.prototype.mode = function(pin, mode) { + var gpiopin = pin_to_gpio(pin); - switch (mode) { - case rpio.prototype.INPUT: - return bindcall2(binding.gpio_function, gpiopin, rpio.prototype.INPUT); - case rpio.prototype.OUTPUT: - return bindcall2(binding.gpio_function, gpiopin, rpio.prototype.OUTPUT); - case rpio.prototype.PWM: - return set_pin_pwm(pin); - default: - throw "Unsupported mode " + mode; - } + switch (mode) { + case rpio.prototype.INPUT: + return bindcall2(binding.gpio_function, gpiopin, rpio.prototype.INPUT); + case rpio.prototype.OUTPUT: + return bindcall2(binding.gpio_function, gpiopin, rpio.prototype.OUTPUT); + case rpio.prototype.PWM: + return set_pin_pwm(pin); + default: + throw new Error("Unsupported mode " + mode); + } }; -rpio.prototype.read = function(pin) -{ - return bindcall(binding.gpio_read, pin_to_gpio(pin)); +rpio.prototype.read = function(pin) { + return bindcall(binding.gpio_read, pin_to_gpio(pin)); }; -rpio.prototype.readbuf = function(pin, buf, len) -{ - if (len === undefined) - len = buf.length; +rpio.prototype.readbuf = function(pin, buf, len) { + if (len === undefined) + len = buf.length; - if (len > buf.length) - throw "Buffer not large enough to accommodate request"; + if (len > buf.length) + throw new Error("Buffer not large enough to accommodate request"); - return bindcall3(binding.gpio_readbuf, pin_to_gpio(pin), buf, len); + return bindcall3(binding.gpio_readbuf, pin_to_gpio(pin), buf, len); }; -rpio.prototype.write = function(pin, value) -{ - return bindcall2(binding.gpio_write, pin_to_gpio(pin), value); +rpio.prototype.write = function(pin, value) { + return bindcall2(binding.gpio_write, pin_to_gpio(pin), value); }; -rpio.prototype.writebuf = function(pin, buf, len) -{ - if (len === undefined) - len = buf.length; +rpio.prototype.writebuf = function(pin, buf, len) { + if (len === undefined) + len = buf.length; - if (len > buf.length) - throw "Buffer not large enough to accommodate request"; + if (len > buf.length) + throw new Error("Buffer not large enough to accommodate request"); - return bindcall3(binding.gpio_writebuf, pin_to_gpio(pin), buf, len); + return bindcall3(binding.gpio_writebuf, pin_to_gpio(pin), buf, len); }; -rpio.prototype.readpad = function(group) -{ - if (rpio_options.gpiomem) - throw "Pad control not available in gpiomem mode"; +rpio.prototype.readpad = function(group) { + if (rpio_options.gpiomem) + throw new Error("Pad control not available in gpiomem mode"); - return bindcall(binding.gpio_pad_read, group); + return bindcall(binding.gpio_pad_read, group); }; -rpio.prototype.writepad = function(group, control) -{ - if (rpio_options.gpiomem) - throw "Pad control not available in gpiomem mode"; +rpio.prototype.writepad = function(group, control) { + if (rpio_options.gpiomem) + throw new Error("Pad control not available in gpiomem mode"); - bindcall2(binding.gpio_pad_write, group, control); + bindcall2(binding.gpio_pad_write, group, control); }; -rpio.prototype.pud = function(pin, state) -{ - bindcall2(binding.gpio_pud, pin_to_gpio(pin), state); +rpio.prototype.pud = function(pin, state) { + bindcall2(binding.gpio_pud, pin_to_gpio(pin), state); }; -rpio.prototype.poll = function(pin, cb, direction) -{ - var gpiopin = pin_to_gpio(pin); +rpio.prototype.poll = function(pin, cb, direction) { + var gpiopin = pin_to_gpio(pin); - if (direction === undefined) - direction = rpio.prototype.POLL_BOTH; + if (direction === undefined) + direction = rpio.prototype.POLL_BOTH; - /* - * If callback is a function, set up pin for polling, otherwise - * clear it. - */ - if (typeof(cb) === 'function') { - if (gpiopin in event_pins) - throw "Pin " + pin + " is already listening for events." + /* + * If callback is a function, set up pin for polling, otherwise + * clear it. + */ + if (typeof(cb) === 'function') { + if (gpiopin in event_pins) + throw new Error("Pin " + pin + " is already listening for events."); - bindcall2(binding.gpio_event_set, gpiopin, direction); + bindcall2(binding.gpio_event_set, gpiopin, direction); - var pincb = function() { - cb(pin); - }; - module.exports.on('pin' + gpiopin, pincb); + var pincb = function() { + cb(pin); + }; + module.exports.on('pin' + gpiopin, pincb); - event_pins[gpiopin] = pincb; - event_mask |= (1 << gpiopin); + event_pins[gpiopin] = pincb; + event_mask |= (1 << gpiopin); - if (!(event_running)) - event_running = setInterval(event_poll, 1); - } else { - if (!(gpiopin in event_pins)) - throw "Pin " + pin + " is not listening for events." + if (!(event_running)) + event_running = setInterval(event_poll, 1); + } else { + if (!(gpiopin in event_pins)) + throw new Error("Pin " + pin + " is not listening for events."); - bindcall(binding.gpio_event_clear, gpiopin); + bindcall(binding.gpio_event_clear, gpiopin); - rpio.prototype.removeListener('pin' + gpiopin, event_pins[gpiopin]); + rpio.prototype.removeListener('pin' + gpiopin, event_pins[gpiopin]); - delete event_pins[gpiopin]; - event_mask &= ~(1 << gpiopin); + delete event_pins[gpiopin]; + event_mask &= ~(1 << gpiopin); - if (Object.keys(event_pins).length === 0) { - clearInterval(event_running); - event_running = false; - } - } + if (Object.keys(event_pins).length === 0) { + clearInterval(event_running); + event_running = false; + } + } }; -rpio.prototype.close = function(pin, reset) -{ - var gpiopin = pin_to_gpio(pin); +rpio.prototype.close = function(pin, reset) { + var gpiopin = pin_to_gpio(pin); - if (reset === undefined) - reset = rpio.prototype.PIN_RESET; + if (reset === undefined) + reset = rpio.prototype.PIN_RESET; - if (gpiopin in event_pins) - rpio.prototype.poll(pin, null); + if (gpiopin in event_pins) + rpio.prototype.poll(pin, null); - if (reset) { - if (!rpio_options.gpiomem) - rpio.prototype.pud(pin, rpio.prototype.PULL_OFF); - rpio.prototype.mode(pin, rpio.prototype.INPUT); - } + if (reset) { + if (!rpio_options.gpiomem) + rpio.prototype.pud(pin, rpio.prototype.PULL_OFF); + rpio.prototype.mode(pin, rpio.prototype.INPUT); + } }; /* * PWM */ -rpio.prototype.pwmSetClockDivider = function(divider) -{ - if (divider !== 0 && (divider & (divider - 1)) !== 0) - throw "Clock divider must be zero or power of two"; +rpio.prototype.pwmSetClockDivider = function(divider) { + if (divider !== 0 && (divider & (divider - 1)) !== 0) + throw new Error("Clock divider must be zero or power of two"); - return bindcall(binding.pwm_set_clock, divider); + return bindcall(binding.pwm_set_clock, divider); }; -rpio.prototype.pwmSetRange = function(pin, range) -{ - var channel = get_pwm_channel(pin); +rpio.prototype.pwmSetRange = function(pin, range) { + var channel = get_pwm_channel(pin); - return bindcall2(binding.pwm_set_range, channel, range); + return bindcall2(binding.pwm_set_range, channel, range); }; -rpio.prototype.pwmSetData = function(pin, data) -{ - var channel = get_pwm_channel(pin); +rpio.prototype.pwmSetData = function(pin, data) { + var channel = get_pwm_channel(pin); - return bindcall2(binding.pwm_set_data, channel, data); + return bindcall2(binding.pwm_set_data, channel, data); }; /* * i²c */ -rpio.prototype.i2cBegin = function() -{ - if (!rpio_inited) { - /* i²c requires full /dev/mem */ - rpio_options.gpiomem = false; - rpio.prototype.init(); - } +rpio.prototype.i2cBegin = function() { + if (!rpio_inited) { + /* i²c requires full /dev/mem */ + rpio_options.gpiomem = false; + rpio.prototype.init(); + } - if (rpio_options.gpiomem) - throw "i²c not available in gpiomem mode"; + if (rpio_options.gpiomem) + throw new Error("i²c not available in gpiomem mode"); - bindcall(binding.i2c_begin); + bindcall(binding.i2c_begin); }; -rpio.prototype.i2cSetSlaveAddress = function(addr) -{ - return bindcall(binding.i2c_set_slave_address, addr); +rpio.prototype.i2cSetSlaveAddress = function(addr) { + return bindcall(binding.i2c_set_slave_address, addr); }; -rpio.prototype.i2cSetClockDivider = function(divider) -{ - if ((divider % 2) !== 0) - throw "Clock divider must be an even number"; +rpio.prototype.i2cSetClockDivider = function(divider) { + if ((divider % 2) !== 0) + throw new Error("Clock divider must be an even number"); - return bindcall(binding.i2c_set_clock_divider, divider); + return bindcall(binding.i2c_set_clock_divider, divider); }; -rpio.prototype.i2cSetBaudRate = function(baud) -{ - return bindcall(binding.i2c_set_baudrate, baud); +rpio.prototype.i2cSetBaudRate = function(baud) { + return bindcall(binding.i2c_set_baudrate, baud); }; -rpio.prototype.i2cRead = function(buf, len) -{ - if (len === undefined) - len = buf.length; +rpio.prototype.i2cRead = function(buf, len) { + if (len === undefined) + len = buf.length; - if (len > buf.length) - throw "Buffer not large enough to accommodate request"; + if (len > buf.length) + throw new Error("Buffer not large enough to accommodate request"); - return bindcall2(binding.i2c_read, buf, len); + return bindcall2(binding.i2c_read, buf, len); }; -rpio.prototype.i2cWrite = function(buf, len) -{ - if (len === undefined) - len = buf.length; +rpio.prototype.i2cWrite = function(buf, len) { + if (len === undefined) + len = buf.length; - if (len > buf.length) - throw "Buffer not large enough to accommodate request"; + if (len > buf.length) + throw new Error("Buffer not large enough to accommodate request"); - return bindcall2(binding.i2c_write, buf, len); + return bindcall2(binding.i2c_write, buf, len); }; -rpio.prototype.i2cEnd = function() -{ - bindcall(binding.i2c_end); +rpio.prototype.i2cEnd = function() { + bindcall(binding.i2c_end); }; /* * SPI */ -rpio.prototype.spiBegin = function() -{ - if (!rpio_inited) { - /* SPI requires full /dev/mem */ - rpio_options.gpiomem = false; - rpio.prototype.init(); - } +rpio.prototype.spiBegin = function() { + if (!rpio_inited) { + /* SPI requires full /dev/mem */ + rpio_options.gpiomem = false; + rpio.prototype.init(); + } - if (rpio_options.gpiomem) - throw "SPI not available in gpiomem mode"; + if (rpio_options.gpiomem) + throw new Error("SPI not available in gpiomem mode"); - bindcall(binding.spi_begin); + bindcall(binding.spi_begin); }; -rpio.prototype.spiChipSelect = function(cs) -{ - return bindcall(binding.spi_chip_select, cs); +rpio.prototype.spiChipSelect = function(cs) { + return bindcall(binding.spi_chip_select, cs); }; -rpio.prototype.spiSetCSPolarity = function(cs, active) -{ - return bindcall2(binding.spi_set_cs_polarity, cs, active); +rpio.prototype.spiSetCSPolarity = function(cs, active) { + return bindcall2(binding.spi_set_cs_polarity, cs, active); }; -rpio.prototype.spiSetClockDivider = function(divider) -{ - if ((divider % 2) !== 0 || divider < 0 || divider > 65536) - throw "Clock divider must be an even number between 0 and 65536"; +rpio.prototype.spiSetClockDivider = function(divider) { + if ((divider % 2) !== 0 || divider < 0 || divider > 65536) + throw new Error("Clock divider must be an even number between 0 and 65536"); - return bindcall(binding.spi_set_clock_divider, divider); + return bindcall(binding.spi_set_clock_divider, divider); }; -rpio.prototype.spiSetDataMode = function(mode) -{ - return bindcall(binding.spi_set_data_mode, mode); +rpio.prototype.spiSetDataMode = function(mode) { + return bindcall(binding.spi_set_data_mode, mode); }; -rpio.prototype.spiTransfer = function(txbuf, rxbuf, len) -{ - return bindcall3(binding.spi_transfer, txbuf, rxbuf, len); +rpio.prototype.spiTransfer = function(txbuf, rxbuf, len) { + return bindcall3(binding.spi_transfer, txbuf, rxbuf, len); }; -rpio.prototype.spiWrite = function(buf, len) -{ - return bindcall2(binding.spi_write, buf, len); +rpio.prototype.spiWrite = function(buf, len) { + return bindcall2(binding.spi_write, buf, len); }; -rpio.prototype.spiEnd = function() -{ - bindcall(binding.spi_end); +rpio.prototype.spiEnd = function() { + bindcall(binding.spi_end); }; /* * Misc functions. */ -rpio.prototype.sleep = function(secs) -{ - bindcall(binding.rpio_usleep, secs * 1000000); +rpio.prototype.sleep = function(secs) { + bindcall(binding.rpio_usleep, secs * 1000000); }; -rpio.prototype.msleep = function(msecs) -{ - bindcall(binding.rpio_usleep, msecs * 1000); +rpio.prototype.msleep = function(msecs) { + bindcall(binding.rpio_usleep, msecs * 1000); }; -rpio.prototype.usleep = function(usecs) -{ - bindcall(binding.rpio_usleep, usecs); +rpio.prototype.usleep = function(usecs) { + bindcall(binding.rpio_usleep, usecs); }; process.on('exit', function(code) { - bindcall(binding.rpio_close); + bindcall(binding.rpio_close); }); From d74cfc7ec24784705e255fae2f7da116f32d0c06 Mon Sep 17 00:00:00 2001 From: Mike Jones Date: Sat, 13 Feb 2021 03:00:08 -0500 Subject: [PATCH 4/5] 1) Default defmock to nanopi neo. Sorry, I'm selfish. 2) Properly detect nanopi neo. 3) Fix incorrect pinmap values. --- .gitignore | 2 ++ lib/rpio.js | 68 ++++++++++++++++++++++++++++++++--------------- package-lock.json | 26 ++++++++++++++++++ 3 files changed, 75 insertions(+), 21 deletions(-) create mode 100644 package-lock.json diff --git a/.gitignore b/.gitignore index e3fbd98..2c1d00a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ build node_modules + +*.tgz diff --git a/lib/rpio.js b/lib/rpio.js index b414cc0..0e0ddb6 100755 --- a/lib/rpio.js +++ b/lib/rpio.js @@ -90,7 +90,7 @@ var rpio_options = { }; /* Default mock mode if hardware is unsupported. */ -var defmock = "raspi-3"; +var defmock = "nanopi-neo"; /* * Wrapper functions. @@ -253,25 +253,43 @@ var pinmaps = { ], PINMAP_NEO: [ -1, - -1, -1, - 12, -1, - 11, -1, - 203, 198, - -1, 199, - 0, 6, - 2, -1, - 3, 200, - -1, 201, - 64, -1, - 65, 1, - 66, 67, - -1, 17, - 19, 18, - 20, -1, - 21, 7, - 8, -1, - 16, 13, - 9,15, + /* Header block 1 - Serial */ + -1, -1, /* P1 P2 */ /* Mini-shield: P1 P2 */ + 12, -1, /* P3 P4 */ /* Mini-shield: P3 P4 */ + 11, -1, /* P5 P6 */ /* Mini-shield: P5 P6 */ + 203, 198, /* P7 P8 */ /* Mini-shield: P7 P8 */ + -1, 199, /* P9 P10 */ /* Mini-shield: P9 P10 */ + 0, 6, /* P11 P12 */ /* Mini-shield: P11 P12 */ + 2, -1, /* P13 P14 */ /* Mini-shield: P13 P14 */ + 3, 200, /* P15 P16 */ /* Mini-shield: P15 P16 */ + -1, 201, /* P17 P18 */ /* Mini-shield: P17 P18 */ + 64, -1, /* P19 P20 */ + 65, 1, /* P21 P22 */ /* Mini-shield: -1 P22 */ + 66, 67, /* P23 P24 */ + /* Header block 2 - USB/Audio */ + -1, 15, /* P25 P26 */ /* Mini-shield: -1 P19 */ + -1, 16, /* P27 P28 */ /* Mini-shield: -1 P21 */ + -1, 14, /* P29 P30 */ /* Mini-shield: -1 P23 */ + -1, 13, /* P31 P32 */ /* Mini-shield: -1 P24 */ + -1, -1, /* P33 P34 */ + -1, -1, /* P35 P36 */ + 17, -1, /* P37 P38 */ /* Mini-shield: P26 -1 */ + -1, -1, /* P39 P40 */ + -1, 5, /* P41 P42 */ + -1, 4, /* P43 P44 */ + -1, -1, /* P45 P46 */ + -1, -1, /* P47 P48 */ /* Mini-shield: P20 P25 (both GND) */ + /* Header block 3 - Network */ + -1, -1, /* P49 P50 */ + -1, -1, /* P51 P52 */ + -1, -1, /* P53 P54 */ + -1, -1, /* P55 P56 */ + -1, -1, /* P57 P58 */ + -1, -1, /* P59 P60 */ + -1, 7, /* P61 P62 */ + -1, -1, /* P63 P64 */ + -1, -1, /* P65 P66 */ + -1, -1, /* P67 P68 */ -1, 14 ], PINMAP_NEO2: [ @@ -386,7 +404,10 @@ function detect_pinmap() { chipType = "SUNXI"; pinmap = "PINMAP_NANOPI_M1_PLUS"; break; - + case 'FriendlyARM NanoPi NEO\u0000': + chipType = "SUNXI"; + pinmap = "PINMAP_NEO"; + break; default: return false; } @@ -441,6 +462,11 @@ function set_mock_pinmap() { chipType = "BCM2835"; break; + case 'nanopi-neo': + chipType = "SUNXI"; + pinmap = "PINMAP_NEO"; + break; + default: return false; } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..6036360 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,26 @@ +{ + "name": "rpio", + "version": "0.9.19", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" + } + } +} From 7f640f4bfbe9c349d79022f8134fc1298c23750c Mon Sep 17 00:00:00 2001 From: Mike Jones Date: Sat, 13 Feb 2021 12:51:10 -0500 Subject: [PATCH 5/5] Allow defmock to be provided on init() so the user can specify which hardware they want mocked if no hardware detected. --- lib/rpio.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/rpio.js b/lib/rpio.js index 0e0ddb6..232ad50 100755 --- a/lib/rpio.js +++ b/lib/rpio.js @@ -87,11 +87,9 @@ var rpio_options = { gpiomem: true, mapping: 'physical', mock: false, + defmock: 'raspi-3' // default mock mode if hardware is unsupported }; -/* Default mock mode if hardware is unsupported. */ -var defmock = "nanopi-neo"; - /* * Wrapper functions. */ @@ -601,12 +599,13 @@ rpio.prototype.init = function(opts) { if (!detect_pinmap()) { module.exports.emit('warn', 'Hardware auto-detect failed, running in ' + - defmock + ' mock mode'); - rpio_options.mock = defmock; + rpio_options.defmock + ' mock mode'); + rpio_options.mock = rpio_options.defmock; set_mock_pinmap(); } } + /* * init rpio library */