利用Node.js控制嵌入式系统硬件。

工作原理

利用Node.js的Addon模块作为调用硬件控制C库的中间件,Node.js异步的特性,非常适合处理各种硬件的IO。本次实践使用的是cubieboard的开发板,Node.js将会控制板子的GPIO的PD0,让它1000ms产生一个高低电平,使LED灯能够一闪一闪的样子。


执行代码

vi run.js

var GPIO = require('./build/Release/gpio');

var LED = GPIO.PD0;
var status = 0;
GPIO.init();

GPIO.setcfg(LED, GPIO.OUT);

// 让led 一闪一闪
var blink = function(){
    if(status){
        GPIO.output(LED, GPIO.LOW);
        status = 0;
    } else {
        GPIO.output(LED, GPIO.HIGH);
        status = 1;
    }
}

setInterval(blink, 1000);

vi gpio.cpp

#include <node.h>
// 调用cubieboard的硬件控制库
extern "C"{
#include "gpio_lib.h"
}
// 定义一些工具宏
#define WIRING_DEFINE_CONSTANT(NAME, VALUE) (target)->Set( \
        v8::String::NewSymbol(NAME), \
        v8::Integer::New(VALUE), \
        static_cast<v8::PropertyAttribute>(v8::ReadOnly|v8::DontDelete) \
);
#define PD0    SUNXI_GPD(0)
using namespace v8;
// 初始化gpio的寄存器
Handle<Value> GPIO_init(const Arguments& args) {
    HandleScope scope;
    int32_t result;
    result = sunxi_gpio_init();
    if(result == SETUP_DEVMEM_FAIL){
    return ThrowException(
            Exception::TypeError(String::New("SETUP_DEVMEM_FAIL Error"))
        );
    }
    if(result == SETUP_MALLOC_FAIL){
        return ThrowException(
            Exception::TypeError(String::New("SETUP_MALLOC_FAIL Error"))
        );
    }
    if(result == SETUP_MMAP_FAIL){
        return ThrowException(
            Exception::TypeError(String::New("SETUP_MMAP_FAIL Error"))
        ); 
    }
    return scope.Close(Integer::New(SETUP_OK));
}
Handle<Value> GPIO_cleanup(const Arguments& args) {
    HandleScope scope;
    sunxi_gpio_cleanup();
    return scope.Close(Undefined());
}
// 获取引脚当状态 IN,OUT,PRE?
Handle<Value> GPIO_getcfg(const Arguments& args) {
    HandleScope scope;

    if (args.Length() < 1){
        return ThrowException(
            Exception::TypeError(String::New("Wrong number of arguments"))
        );
    }
    if (!args[0]->IsNumber()){
        return ThrowException(
             Exception::TypeError(String::New("Wrong arguments"))
        );
    }
    int32_t result;
    int32_t gpio = args[0]->ToInteger()->Value();
    result = sunxi_gpio_get_cfgpin(gpio);
    return scope.Close(Integer::New(result));
}
// 读取的电平状态 HIGH?LOW?
Handle<Value> GPIO_input(const Arguments& args) {
    HandleScope scope;
    if (args.Length() < 1){
        return ThrowException(
            Exception::TypeError(String::New("Wrong number of arguments"))
        );
    }
    if (!args[0]->IsNumber()){
        return ThrowException(
             Exception::TypeError(String::New("Wrong arguments"))
        );
    }
    int32_t result;
    int32_t gpio = args[0]->ToInteger()->Value();
    result = sunxi_gpio_input(gpio);
    if(result == -1){
        return ThrowException(
             Exception::TypeError(String::New("Reading pin failed"))
        );
    }
    return scope.Close(Integer::New(result));
}

// 输出电平
Handle<Value> GPIO_output(const Arguments& args) {
    HandleScope scope;
    if (args.Length() < 2){
        return ThrowException(
            Exception::TypeError(String::New("Wrong number of arguments"))
        );    
    }
    if (!args[0]->IsNumber() || !args[1]->IsNumber()){
        return ThrowException(           
         Exception::TypeError(String::New("Wrong arguments"))
        );
    }
    int32_t gpio = args[0]->ToInteger()->Value();
    int32_t value = args[1]->ToInteger()->Value();
    if( value != 0 && value != 1) {
        return ThrowException(                        
         Exception::TypeError(String::New("Invalid output state"))
        );
    }
    if(sunxi_gpio_get_cfgpin(gpio) != SUNXI_GPIO_OUTPUT) {
    return ThrowException( 
             Exception::TypeError(String::New("Invalid output state"))
        );
    }
    sunxi_gpio_output(gpio, value);
    return scope.Close(Undefined());
}
// 设置GPIO功能 IN?OUT?PRE?
Handle<Value> GPIO_setcfg(const Arguments& args) {
    HandleScope scope;
    if (args.Length() < 2){
        return ThrowException(
            Exception::TypeError(String::New("Wrong number of arguments"))
        );
    }
    if (!args[0]->IsNumber() || !args[1]->IsNumber()){
        return ThrowException(
             Exception::TypeError(String::New("Wrong arguments"))
        );
    }
    int32_t gpio = args[0]->ToInteger()->Value();
    int32_t direction = args[1]->ToInteger()->Value();
    if(direction != 0 && direction != 1 && direction != 2) {
        return ThrowException(
             Exception::TypeError(String::New("Invalid direction"))
        );
    }
    sunxi_gpio_set_cfgpin(gpio, direction);
    return scope.Close(Undefined());
}
void RegisterModule(Handle<Object> target) {
    NODE_SET_METHOD(target, "init", GPIO_init);
    NODE_SET_METHOD(target, "cleanup", GPIO_cleanup);
    NODE_SET_METHOD(target, "output", GPIO_output);
    NODE_SET_METHOD(target, "setcfg", GPIO_setcfg);
    NODE_SET_METHOD(target, "input", GPIO_input);
    NODE_SET_METHOD(target, "getcfg", GPIO_getcfg);
    WIRING_DEFINE_CONSTANT("HIGH", HIGH)
    WIRING_DEFINE_CONSTANT("LOW", LOW)
    WIRING_DEFINE_CONSTANT("PD0", PD0)
    WIRING_DEFINE_CONSTANT("IN", INPUT)
    WIRING_DEFINE_CONSTANT("OUT", OUTPUT)
    WIRING_DEFINE_CONSTANT("PER", PER)
}
NODE_MODULE(gpio, RegisterModule);

vi binding.gyp

{
  "targets": [
    {
      "target_name": "gpio",
      "include_dirs": ["lib"],
      "sources": [ 
    "gpio.cpp",
    "lib/gpio_lib.c",
    "lib/gpio_lib.h"
      ]
    }
  ]
}

测试

利用万用表对准 GND 和 PD0,可以发现程序运行后,电压3V - 0V 每隔1000ms产生一次变化,如果接上LED,LED就会一闪一闪的样子。打算去买一块面包板,演示一下。