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/README.md b/README.md index fef920d..2744ef2 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, M1 PLUS +* 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; +} + +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; + + case 'nanopi-neo': + chipType = "SUNXI"; + pinmap = "PINMAP_NEO"; + 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 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 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 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 new Error("Unknown PWM channel for pin " + pin); + } +} + +function set_pin_pwm(pin) { + var gpiopin = pin_to_gpio(pin); + var channel, func; + + if (rpio_options.gpiomem) + throw new Error("PWM not available in gpiomem mode"); + + 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); + + bindcall2(binding.gpio_function, gpiopin, func); + + /* + * For now we assume mark-space mode, balanced is unsupported. + */ + bindcall3(binding.pwm_set_mode, channel, 1, 1); } /* @@ -409,378 +561,348 @@ 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 || {}; - - 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; - - /* - * 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(); - } - } - - /* - * Open the bcm2835 driver. - */ - bindcall(binding.rpio_init, Number(rpio_options.gpiomem)); - rpio_inited = true; -} +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 ' + + rpio_options.defmock + ' mock mode'); + rpio_options.mock = rpio_options.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; +}; -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); - - 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; - } -} +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); + + 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 new Error("Unsupported mode " + mode); + } +}; -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; - } -} +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 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(); - } - - if (rpio_options.gpiomem) - throw "i²c not available in gpiomem mode"; - - bindcall(binding.i2c_begin); -} +rpio.prototype.i2cBegin = function() { + if (!rpio_inited) { + /* i²c requires full /dev/mem */ + rpio_options.gpiomem = false; + rpio.prototype.init(); + } -rpio.prototype.i2cSetSlaveAddress = function(addr) -{ - return bindcall(binding.i2c_set_slave_address, addr); -} + if (rpio_options.gpiomem) + throw new Error("i²c not available in gpiomem mode"); -rpio.prototype.i2cSetClockDivider = function(divider) -{ - if ((divider % 2) !== 0) - throw "Clock divider must be an even number"; + bindcall(binding.i2c_begin); +}; - return bindcall(binding.i2c_set_clock_divider, divider); -} +rpio.prototype.i2cSetSlaveAddress = function(addr) { + return bindcall(binding.i2c_set_slave_address, addr); +}; -rpio.prototype.i2cSetBaudRate = function(baud) -{ - return bindcall(binding.i2c_set_baudrate, baud); -} +rpio.prototype.i2cSetClockDivider = function(divider) { + if ((divider % 2) !== 0) + throw new Error("Clock divider must be an even number"); -rpio.prototype.i2cRead = function(buf, len) -{ - if (len === undefined) - len = buf.length; + return bindcall(binding.i2c_set_clock_divider, divider); +}; - if (len > buf.length) - throw "Buffer not large enough to accommodate request"; +rpio.prototype.i2cSetBaudRate = function(baud) { + return bindcall(binding.i2c_set_baudrate, baud); +}; - return bindcall2(binding.i2c_read, buf, len); -} +rpio.prototype.i2cRead = 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 new Error("Buffer not large enough to accommodate request"); - if (len > buf.length) - throw "Buffer not large enough to accommodate request"; + return bindcall2(binding.i2c_read, buf, len); +}; - return bindcall2(binding.i2c_write, buf, len); -} +rpio.prototype.i2cWrite = function(buf, len) { + if (len === undefined) + len = buf.length; -rpio.prototype.i2cEnd = function() -{ - bindcall(binding.i2c_end); -} + if (len > buf.length) + throw new Error("Buffer not large enough to accommodate request"); + + return bindcall2(binding.i2c_write, buf, len); +}; + +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(); - } - - if (rpio_options.gpiomem) - throw "SPI not available in gpiomem mode"; - - bindcall(binding.spi_begin); -} +rpio.prototype.spiBegin = function() { + if (!rpio_inited) { + /* SPI requires full /dev/mem */ + rpio_options.gpiomem = false; + rpio.prototype.init(); + } -rpio.prototype.spiChipSelect = function(cs) -{ - return bindcall(binding.spi_chip_select, cs); -} + if (rpio_options.gpiomem) + throw new Error("SPI not available in gpiomem mode"); -rpio.prototype.spiSetCSPolarity = function(cs, active) -{ - return bindcall2(binding.spi_set_cs_polarity, cs, active); -} + bindcall(binding.spi_begin); +}; -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.spiChipSelect = function(cs) { + return bindcall(binding.spi_chip_select, cs); +}; - return bindcall(binding.spi_set_clock_divider, divider); -} +rpio.prototype.spiSetCSPolarity = function(cs, active) { + return bindcall2(binding.spi_set_cs_polarity, cs, active); +}; -rpio.prototype.spiSetDataMode = function(mode) -{ - return bindcall(binding.spi_set_data_mode, mode); -} +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"); -rpio.prototype.spiTransfer = function(txbuf, rxbuf, len) -{ - return bindcall3(binding.spi_transfer, txbuf, rxbuf, len); -} + return bindcall(binding.spi_set_clock_divider, divider); +}; -rpio.prototype.spiWrite = function(buf, len) -{ - return bindcall2(binding.spi_write, buf, len); -} +rpio.prototype.spiSetDataMode = function(mode) { + return bindcall(binding.spi_set_data_mode, mode); +}; -rpio.prototype.spiEnd = function() -{ - bindcall(binding.spi_end); -} +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. */ -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); }); 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==" + } + } +} 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..551b1b8 --- /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) + return 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 1; +} + +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