rpio
Version:
High performance GPIO/i2c/PWM/SPI module for Raspberry Pi, Orange Pi, Banana Pi
1,201 lines (1,059 loc) • 27.4 kB
JavaScript
/*
* Copyright (c) 2020 Jonathan Perkin <jonathan@perkin.org.uk>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
var binding = require('bindings')('rpio');
var fs = require('fs');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
/*
* Event Emitter gloop.
*/
function rpio()
{
EventEmitter.call(this);
}
util.inherits(rpio, EventEmitter);
module.exports = new rpio;
/*
* Supported SoC types. Must be kept in sync with rpio.cc.
*/
rpio.prototype.SOC_BCM2835 = 0x0;
rpio.prototype.SOC_SUNXI = 0x1;
/*
* Constants.
*/
rpio.prototype.LOW = 0x0;
rpio.prototype.HIGH = 0x1;
/*
* Supported function select modes. INPUT and OUTPUT match the bcm2835
* function select integers. PWM is handled specially.
*/
rpio.prototype.INPUT = 0x0;
rpio.prototype.OUTPUT = 0x1;
rpio.prototype.PWM = 0x2;
/*
* Configure builtin pullup/pulldown resistors.
*/
rpio.prototype.PULL_OFF = 0x0;
rpio.prototype.PULL_DOWN = 0x1;
rpio.prototype.PULL_UP = 0x2;
/*
* Pin edge detect events. Default to both.
*/
rpio.prototype.POLL_LOW = 0x1; /* Falling edge detect */
rpio.prototype.POLL_HIGH = 0x2; /* Rising edge detect */
rpio.prototype.POLL_BOTH = 0x3; /* POLL_LOW | POLL_HIGH */
/*
* Reset pin status on close (default), or preserve current status.
*/
rpio.prototype.PIN_PRESERVE = 0x0;
rpio.prototype.PIN_RESET = 0x1;
/*
* GPIO Pad Control
*/
rpio.prototype.PAD_GROUP_0_27 = 0x0;
rpio.prototype.PAD_GROUP_28_45 = 0x1;
rpio.prototype.PAD_GROUP_46_53 = 0x2;
rpio.prototype.PAD_DRIVE_2mA = 0x00;
rpio.prototype.PAD_DRIVE_4mA = 0x01;
rpio.prototype.PAD_DRIVE_6mA = 0x02;
rpio.prototype.PAD_DRIVE_8mA = 0x03;
rpio.prototype.PAD_DRIVE_10mA = 0x04;
rpio.prototype.PAD_DRIVE_12mA = 0x05;
rpio.prototype.PAD_DRIVE_14mA = 0x06;
rpio.prototype.PAD_DRIVE_16mA = 0x07;
rpio.prototype.PAD_HYSTERESIS = 0x08;
rpio.prototype.PAD_SLEW_UNLIMITED = 0x10;
/*
* Default pin mode is 'physical'. Other option is 'gpio'
*/
var rpio_inited = false;
var rpio_options = {
gpiomem: true,
mapping: 'physical',
mock: false,
close_on_exit: true
};
/* Default mock mode if hardware is unsupported. */
var defmock = 'raspi-3';
/*
* Wrapper functions.
*/
function bindcall(bindfunc, optarg)
{
if (rpio_options.mock)
return;
return bindfunc(optarg);
}
function bindcall2(bindfunc, arg1, arg2)
{
if (rpio_options.mock)
return;
return bindfunc(arg1, arg2);
}
function bindcall3(bindfunc, arg1, arg2, arg3)
{
if (rpio_options.mock)
return;
return bindfunc(arg1, arg2, arg3);
}
function bindcall4(bindfunc, arg1, arg2, arg3, arg4)
{
if (rpio_options.mock)
return;
return bindfunc(arg1, arg2, arg3, arg4);
}
function warn(msg)
{
console.error('WARNING: ' + msg);
}
/*
* Map physical pin to BCM GPIOxx numbering. There are currently three
* layouts:
*
* PINMAP_26_R1: 26-pin original model B (PCB rev 1.0)
* PINMAP_26: 26-pin 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.
*/
var pincache = {};
var soctype = null;
var pinmap = null;
var mockmap = {};
var pinmaps = {
/*
* Original Raspberry Pi, PCB revision 1.0
*/
PINMAP_26_R1: [
-1,
-1, -1, /* P1 P2 */
0, -1, /* P3 P4 */
1, -1, /* P5 P6 */
4, 14, /* P7 P8 */
-1, 15, /* P9 P10 */
17, 18, /* P11 P12 */
21, -1, /* P13 P14 */
22, 23, /* P15 P16 */
-1, 24, /* P17 P18 */
10, -1, /* P19 P20 */
9, 25, /* P21 P22 */
11, 8, /* P23 P24 */
-1, 7 /* P25 P26 */
],
/*
* Original Raspberry Pi, PCB revision 2.0.
*
* Differs to R1 on pins 3, 5, and 13.
*
* XXX: no support yet for the P5 header pins.
*/
PINMAP_26: [
-1,
-1, -1, /* P1 P2 */
2, -1, /* P3 P4 */
3, -1, /* P5 P6 */
4, 14, /* P7 P8 */
-1, 15, /* P9 P10 */
17, 18, /* P11 P12 */
27, -1, /* P13 P14 */
22, 23, /* P15 P16 */
-1, 24, /* P17 P18 */
10, -1, /* P19 P20 */
9, 25, /* P21 P22 */
11, 8, /* P23 P24 */
-1, 7 /* P25 P26 */
],
/*
* Raspberry Pi 40-pin models.
*
* First 26 pins are the same as PINMAP_26.
*/
PINMAP_40: [
-1,
-1, -1, /* P1 P2 */
2, -1, /* P3 P4 */
3, -1, /* P5 P6 */
4, 14, /* P7 P8 */
-1, 15, /* P9 P10 */
17, 18, /* P11 P12 */
27, -1, /* P13 P14 */
22, 23, /* P15 P16 */
-1, 24, /* P17 P18 */
10, -1, /* P19 P20 */
9, 25, /* P21 P22 */
11, 8, /* P23 P24 */
-1, 7, /* P25 P26 */
0, 1, /* P27 P28 */
5, -1, /* P29 P30 */
6, 12, /* P31 P32 */
13, -1, /* P33 P34 */
19, 16, /* P35 P36 */
26, 20, /* P37 P38 */
-1, 21 /* P39 P40 */
],
/*
* Compute Modules.
*
* https://www.raspberrypi.org/documentation/hardware/computemodule/datasheets/rpi_DATA_CM_1p0.pdf
* https://datasheets.raspberrypi.org/cm4/cm4-datasheet.pdf
*/
PINMAP_CM1: [
-1,
-1, -1, /* P1 P2 */
0, -1, /* P3 P4 */
1, -1, /* P5 P6 */
-1, -1, /* P7 P8 */
2, -1, /* P9 P10 */
3, -1, /* P11 P12 */
-1, -1, /* P13 P14 */
4, -1, /* P15 P16 */
5, -1, /* P17 P18 */
-1, -1, /* P19 P20 */
6, -1, /* P21 P22 */
7, -1, /* P23 P24 */
-1, -1, /* P25 P26 */
8, 28, /* P27 P28 */
9, 29, /* P29 P30 */
-1, -1, /* P31 P32 */
10, 30, /* P33 P34 */
11, 31, /* P35 P36 */
-1, -1, /* P37 P38 */
-1, -1, /* P39 P40 */
-1, -1, /* P41 P42 */
-1, -1, /* P43 P44 */
12, 32, /* P45 P46 */
13, 33, /* P47 P48 */
-1, -1, /* P49 P50 */
14, 34, /* P51 P52 */
15, 35, /* P53 P54 */
-1, -1, /* P55 P56 */
16, 36, /* P57 P58 */
17, 37, /* P59 P60 */
-1, -1, /* P61 P62 */
18, 38, /* P63 P64 */
19, 39, /* P65 P66 */
-1, -1, /* P67 P68 */
20, 40, /* P69 P70 */
21, 41, /* P71 P72 */
-1, -1, /* P73 P74 */
22, 42, /* P75 P76 */
23, 43, /* P77 P78 */
-1, -1, /* P79 P80 */
24, 44, /* P81 P82 */
25, 45, /* P83 P84 */
-1, -1, /* P85 P86 */
26, 46, /* P87 P88 */
27, 47, /* P89 P90 */
-1, -1, /* P91 P92 */
-1, -1, /* P93 P94 */
-1, -1, /* P95 P96 */
-1, -1, /* P97 P98 */
-1, -1, /* P99 P100 */
-1, -1, /* P101 P102 */
-1, -1, /* P103 P104 */
-1, -1, /* P105 P106 */
-1, -1, /* P107 P108 */
-1, -1, /* P109 P110 */
-1, -1, /* P111 P112 */
-1, -1, /* P113 P114 */
-1, -1, /* P115 P116 */
-1, -1, /* P117 P118 */
-1, -1, /* P119 P120 */
-1, -1, /* P121 P122 */
-1, -1, /* P123 P124 */
-1, -1, /* P125 P126 */
-1, -1, /* P127 P128 */
-1, -1, /* P129 P130 */
-1, -1, /* P131 P132 */
-1, -1, /* P133 P134 */
-1, -1, /* P135 P136 */
-1, -1, /* P137 P138 */
-1, -1, /* P139 P140 */
-1, -1, /* P141 P142 */
-1, -1, /* P143 P144 */
-1, -1, /* P145 P146 */
-1, -1, /* P147 P148 */
-1, -1, /* P149 P150 */
-1, -1, /* P151 P152 */
-1, -1, /* P153 P154 */
-1, -1, /* P155 P156 */
-1, -1, /* P157 P158 */
-1, -1, /* P159 P160 */
-1, -1, /* P161 P162 */
-1, -1, /* P163 P164 */
-1, -1, /* P165 P166 */
-1, -1, /* P167 P168 */
-1, -1, /* P169 P170 */
-1, -1, /* P171 P172 */
-1, -1, /* P173 P174 */
-1, -1, /* P175 P176 */
-1, -1, /* P177 P178 */
-1, -1, /* P179 P180 */
-1, -1, /* P181 P182 */
-1, -1, /* P183 P184 */
-1, -1, /* P185 P186 */
-1, -1, /* P187 P188 */
-1, -1, /* P189 P190 */
-1, -1, /* P191 P192 */
-1, -1, /* P193 P194 */
-1, -1, /* P195 P196 */
-1, -1, /* P197 P198 */
-1, -1 /* P199 P200 */
],
PINMAP_CM4: [
-1,
-1, -1, /* P1 P2 */
-1, -1, /* P3 P4 */
-1, -1, /* P5 P6 */
-1, -1, /* P7 P8 */
-1, -1, /* P9 P10 */
-1, -1, /* P11 P12 */
-1, -1, /* P13 P14 */
-1, -1, /* P15 P16 */
-1, -1, /* P17 P18 */
-1, -1, /* P19 P20 */
-1, -1, /* P21 P22 */
-1, 26, /* P23 P24 */
21, 19, /* P25 P26 */
20, 13, /* P27 P28 */
16, 6, /* P29 P30 */
12, -1, /* P31 P32 */
-1, 5, /* P33 P34 */
1, 0, /* P35 P36 */
7, 11, /* P37 P38 */
8, 9, /* P39 P40 */
25, -1, /* P41 P42 */
-4, 10, /* P43 P44 */
24, 22, /* P45 P46 */
23, 27, /* P47 P48 */
18, 17, /* P49 P50 */
15, -1, /* P51 P52 */
-1, 4, /* P53 P54 */
14, 3, /* P55 P56 */
-1, 2, /* P57 P58 */
-1, -1, /* P59 P60 */
-1, -1, /* P61 P62 */
-1, -1, /* P63 P64 */
-1, -1, /* P65 P66 */
-1, -1, /* P67 P68 */
-1, -1, /* P69 P70 */
-1, -1, /* P71 P72 */
-1, -1, /* P73 P74 */
-1, -1, /* P75 P76 */
-1, -1, /* P77 P78 */
-1, 45, /* P79 P80 */
-1, 44, /* P81 P82 */
-1, -1, /* P83 P84 */
-1, -1, /* P85 P86 */
-1, -1, /* P87 P88 */
-1, -1, /* P89 P90 */
-1, -1, /* P91 P92 */
-1, -1, /* P93 P94 */
-1, -1, /* P95 P96 */
-1, -1, /* P97 P98 */
-1, -1, /* P99 P100 */
-1, -1, /* P101 P102 */
-1, -1, /* P103 P104 */
-1, -1, /* P105 P106 */
-1, -1, /* P107 P108 */
-1, -1, /* P109 P110 */
-1, -1, /* P111 P112 */
-1, -1, /* P113 P114 */
-1, -1, /* P115 P116 */
-1, -1, /* P117 P118 */
-1, -1, /* P119 P120 */
-1, -1, /* P121 P122 */
-1, -1, /* P123 P124 */
-1, -1, /* P125 P126 */
-1, -1, /* P127 P128 */
-1, -1, /* P129 P130 */
-1, -1, /* P131 P132 */
-1, -1, /* P133 P134 */
-1, -1, /* P135 P136 */
-1, -1, /* P137 P138 */
-1, -1, /* P139 P140 */
-1, -1, /* P141 P142 */
-1, -1, /* P143 P144 */
-1, -1, /* P145 P146 */
-1, -1, /* P147 P148 */
-1, -1, /* P149 P150 */
-1, -1, /* P151 P152 */
-1, -1, /* P153 P154 */
-1, -1, /* P155 P156 */
-1, -1, /* P157 P158 */
-1, -1, /* P159 P160 */
-1, -1, /* P161 P162 */
-1, -1, /* P163 P164 */
-1, -1, /* P165 P166 */
-1, -1, /* P167 P168 */
-1, -1, /* P169 P170 */
-1, -1, /* P171 P172 */
-1, -1, /* P173 P174 */
-1, -1, /* P175 P176 */
-1, -1, /* P177 P178 */
-1, -1, /* P179 P180 */
-1, -1, /* P181 P182 */
-1, -1, /* P183 P184 */
-1, -1, /* P185 P186 */
-1, -1, /* P187 P188 */
-1, -1, /* P189 P190 */
-1, -1, /* P191 P192 */
-1, -1, /* P193 P194 */
-1, -1, /* P195 P196 */
-1, -1, /* P197 P198 */
-1, -1 /* P199 P200 */
],
/*
* Orange Pi Zero (26 pin)
*/
PINMAP_OPI_26: [
-1,
-1, -1, /* P1 P2 */
12, -1, /* P3 P4 */
11, -1, /* P5 P6 */
6, 198, /* P7 P8 */
-1, 199, /* P9 P10 */
1, 7, /* P11 P12 */
0, -1, /* P13 P14 */
3, 19, /* P15 P16 */
-1, 18, /* P17 P18 */
15, -1, /* P19 P20 */
16, 2, /* P21 P22 */
14, 13, /* P23 P24 */
-1, 10, /* P25 P26 */
],
/*
* Banana Pi M2 Zero (40 pin)
*/
PINMAP_BPI_M2Z: [
-1,
-1, -1, /* P1 P2 */
12, -1, /* P3 P4 */
11, -1, /* P5 P6 */
6, 13, /* P7 P8 */
-1, 14, /* P9 P10 */
1, 16, /* P11 P12 */
0, -1, /* P13 P14 */
3, 15, /* P15 P16 */
-1, 68, /* P17 P18 */
64, -1, /* P19 P20 */
65, 2, /* P21 P22 */
66, 67, /* P23 P24 */
-1, 71, /* P25 P26 */
19, 18, /* P27 P28 */
7, -1, /* P29 P30 */
8, 354, /* P31 P32 */
9, -1, /* P33 P34 */
10, 356, /* P35 P36 */
17, 21, /* P37 P38 */
-1, 20 /* P39 P40 */
],
/*
* Banana Pi M2 Berry / Ultra (40 pin)
*/
PINMAP_BPI_M2U: [
-1,
-1, -1, /* P1 P2 */
53, -1, /* P3 P4 */
52, -1, /* P5 P6 */
35, 274, /* P7 P8 */
-1, 275, /* P9 P10 */
276, 273, /* P11 P12 */
277, -1, /* P13 P14 */
249, 272, /* P15 P16 */
-1, 250, /* P17 P18 */
64, -1, /* P19 P20 */
65, 251, /* P21 P22 */
66, 87, /* P23 P24 */
-1, 248, /* P25 P26 */
257, 256, /* P27 P28 */
224, -1, /* P29 P30 */
225, 116, /* P31 P32 */
226, -1, /* P33 P34 */
227, 231, /* P35 P36 */
228, 230, /* P37 P38 */
-1, 229 /* P39 P40 */
]
}
/*
* Pin event polling. We track which pins are being monitored, and create a
* bitmask for efficient checks. The event_poll function is executed in a
* setInterval() context whenever any pins are being monitored, and emits
* events when their EDS bit is set.
*/
var event_pins = {};
var event_mask = 0x0;
var event_running = false;
function event_poll()
{
var active = bindcall(binding.gpio_event_poll, event_mask);
for (gpiopin in event_pins) {
if (active & (1 << gpiopin))
module.exports.emit('pin' + gpiopin);
}
}
/*
* Detect Raspberry Pi model and the pinmap in use using device tree.
*/
function detect_pinmap()
{
var model;
try {
model = fs.readFileSync('/proc/device-tree/model', 'ascii');
model = model.replace(/\0+$/, '');
} catch (err) {
return false;
}
if (m = model.match(/^Raspberry Pi (.*)$/)) {
soctype = rpio.prototype.SOC_BCM2835;
/*
* Original 26-pin PCB 1.0. Note that older kernels do not
* expose the "Rev 1" string here, affecting e.g. Debian 7,
* but that release is way past EOL at this point.
*
* An open question is what DT returns for the newer revisions,
* as we may be able to reverse the logic here. Answers on a
* postcard to my "raspberry pi cpuinfo vs device-tree" gist.
*/
if (m[1] == 'Model B Rev 1') {
pinmap = 'PINMAP_26_R1';
return true;
}
/*
* Regular 26-pin variants with the P5 header.
*/
if (m[1].indexOf('Model') === 0 && m[1].indexOf('Plus') === -1) {
pinmap = 'PINMAP_26';
return true;
}
/*
* 200-pin Compute Modules.
*/
if (m[1].indexOf('Compute Module 4') === 0) {
pinmap = 'PINMAP_CM4';
return true;
}
if (m[1].indexOf('Compute Module') === 0) {
pinmap = 'PINMAP_CM1';
return true;
}
/*
* All other models are 40-pin variants with the same pinmap.
*/
pinmap = 'PINMAP_40';
return true;
}
/*
* Orange Pi Zero (H2+)
*
* XXX: According to linux-sunxi.org sun8iw7p may match 40-pin models?
*/
if ((m = model.match(/^sun8iw7p/)) ||
(m = model.match(/Orange Pi Zero/))) {
soctype = rpio.prototype.SOC_SUNXI;
pinmap = 'PINMAP_OPI_26';
return true;
}
/*
* Banana Pi M2 Zero (H2+)
*/
if (m = model.match(/Banana Pi BPI-M2-Zero/)) {
soctype = rpio.prototype.SOC_SUNXI;
pinmap = 'PINMAP_BPI_M2Z';
return true;
}
/*
* Banana Pi M2 Ultra (R40, V40)
*/
if (m = model.match(/^sun8iw11p/)) {
soctype = rpio.prototype.SOC_SUNXI;
pinmap = 'PINMAP_BPI_M2U';
return true;
}
return false;
}
function set_mock_pinmap()
{
switch (rpio_options.mock) {
case 'raspi-b-r1':
soctype = rpio.prototype.SOC_BCM2835;
pinmap = 'PINMAP_26_R1';
break;
case 'raspi-a':
case 'raspi-b':
soctype = rpio.prototype.SOC_BCM2835;
pinmap = 'PINMAP_26';
break;
case 'raspi-a+':
case 'raspi-b+':
case 'raspi-2':
case 'raspi-3':
case 'raspi-4':
case 'raspi-400':
case 'raspi-zero':
case 'raspi-zero-w':
soctype = rpio.prototype.SOC_BCM2835;
pinmap = 'PINMAP_40';
break;
case 'cm':
case 'cm1':
case 'cm3':
case 'cm3-lite':
case 'compute-module':
case 'compute-module-3':
case 'compute-module-3-lite':
pinmap = 'PINMAP_CM1';
break;
case 'cm4':
case 'compute-module-4':
pinmap = 'PINMAP_CM4';
break;
case 'orangepi-zero':
soctype = rpio.prototype.SOC_SUNXI;
pinmap = 'PINMAP_OPI_26';
break;
case 'bananapi-m2-berry':
case 'bananapi-m2-ultra':
soctype = rpio.prototype.SOC_SUNXI;
pinmap = 'PINMAP_BPI_M2U';
break;
case 'bananapi-m2-zero':
soctype = rpio.prototype.SOC_SUNXI;
pinmap = 'PINMAP_BPI_M2Z';
break;
default:
return false;
}
return true;
}
function pin_to_gpio(pin)
{
if (pincache[pin])
return pincache[pin];
errstr = util.format('Pin %d is not valid when using %s mapping',
pin, rpio_options.mapping);
switch (rpio_options.mapping) {
case 'physical':
if (pinmaps[pinmap][pin] == -1 ||
pinmaps[pinmap][pin] == null) {
throw new Error(errstr);
}
pincache[pin] = pinmaps[pinmap][pin];
break;
case 'gpio':
if (pinmaps[pinmap].indexOf(pin) === -1)
throw new Error(errstr);
pincache[pin] = pin;
break;
default:
throw new Error('Unsupported GPIO mode');
}
return pincache[pin];
}
function check_sys_gpio(pin)
{
if (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);
}
/*
* 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);
}
module.exports.on('warn', default_warn_handler);
/*
* GPIO
*/
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;
soctype = 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();
}
}
/*
* Allow the user to suppress automatic close of rpio on exit.
*/
if (rpio_options.close_on_exit) {
process.on('exit', function(code) {
rpio.prototype.exit();
});
}
/*
* Open the SoC driver.
*/
bindcall2(binding.rpio_init, soctype, rpio_options.gpiomem ? 1 : 0);
rpio_inited = true;
}
rpio.prototype.mode = function(pin, mode, init)
{
var gpiopin = pin_to_gpio(pin);
switch (mode) {
case rpio.prototype.INPUT:
bindcall2(binding.gpio_function, gpiopin, rpio.prototype.INPUT);
if (init !== undefined)
bindcall2(binding.gpio_pud, gpiopin, init);
return;
case rpio.prototype.OUTPUT:
if (init !== undefined) {
if (rpio_options.mock)
mockmap[gpiopin] = init;
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.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);
return rpio.prototype.mode(pin, mode, init);
}
rpio.prototype.read = function(pin, mode)
{
if (rpio_options.mock) {
return mockmap[pin_to_gpio(pin)]
? mockmap[pin_to_gpio(pin)]
: mockmap[pin_to_gpio(pin)] = 0
}
return bindcall2(binding.gpio_read, pin_to_gpio(pin), mode ? 1 : 0);
}
rpio.prototype.readbuf = function(pin, buf, len, mode)
{
if (len === undefined)
len = buf.length;
if (len > buf.length)
throw new Error('Buffer not large enough to accommodate request');
return bindcall4(binding.gpio_readbuf, pin_to_gpio(pin), buf, len, mode ? 1 : 0);
}
rpio.prototype.write = function(pin, value)
{
if (rpio_options.mock)
return mockmap[pin_to_gpio(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;
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);
}
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);
}
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);
}
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);
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 new Error('Pin ' + pin + ' is already listening for events.');
bindcall2(binding.gpio_event_set, gpiopin, direction);
var pincb = function() {
cb(pin);
};
module.exports.on('pin' + gpiopin, pincb);
event_pins[gpiopin] = pincb;
event_mask |= (1 << gpiopin);
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);
module.exports.removeListener('pin' + gpiopin, event_pins[gpiopin]);
delete event_pins[gpiopin];
event_mask &= ~(1 << gpiopin);
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);
if (rpio_options.mock)
mockmap[pin_to_gpio(pin)] = undefined
if (reset === undefined)
reset = rpio.prototype.PIN_RESET;
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);
}
}
rpio.prototype.exit = function()
{
bindcall(binding.rpio_close);
}
/*
* PWM
*/
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);
}
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);
}
/*
* i2c
*/
rpio.prototype.i2cBegin = function()
{
if (!rpio_inited) {
/* i2c requires full /dev/mem */
rpio_options.gpiomem = false;
rpio.prototype.init();
}
if (rpio_options.gpiomem)
throw new Error('i2c 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)
{
if ((divider % 2) !== 0)
throw new Error('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)
{
if (len === undefined)
len = buf.length;
if (len > buf.length)
throw new Error('Buffer not large enough to accommodate request');
return bindcall2(binding.i2c_read, buf, len);
}
rpio.prototype.i2cReadRegisterRestart = function(reg, buf, len)
{
if (len === undefined)
len = buf.length;
if (len > buf.length)
throw new Error('Buffer not large enough to accommodate request');
return bindcall3(binding.i2c_read_register_rs, reg, buf, len);
}
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');
return bindcall2(binding.i2c_write, buf, len);
}
rpio.prototype.i2cWriteReadRestart = function(cmdbuf, cmdlen, rbuf, rlen)
{
if (cmdlen === undefined)
cmdlen = cmdbuf.length;
if (cmdlen > cmdbuf.length)
throw new Error('Write buffer not large enough to accommodate request');
if (rlen === undefined)
rlen = rbuf.length;
if (rlen > rbuf.length)
throw new Error('Read buffer not large enough to accommodate request');
return bindcall4(binding.i2c_write_read_rs, cmdbuf, cmdlen, rbuf, rlen);
}
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 new Error('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)
{
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);
}
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.
*/
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);
}