让我们以开发套件中的大按键模块(Push Button Module)为例,介绍如何编写一个外设驱动。
前提条件
- 如果还没有了解如何使用 Ruff 编写应用,可以阅读这里。
- 如果你希望在
rap layout --visual
能够正确进行图形化展示,请在软件仓库上传并配置设备信息。参考这里了解如何在软件仓库添加设备。 - 查看设备对应的硬件文档,确定接口类型。本文中的大按键模块,需要连接的是一个 GPIO 接口作为输入。
第一步:创建项目
在待创建项目的目录下,使用 rap
创建项目。注意,这里使用的初始化参数是 driver
,它会生成一些驱动相关的配置文件。rap init driver
根据提示,填写相应内容,一切顺利的话,一个新的目录就创建出来了,驱动项目就在其中。该命令还会生成 package.json
和 driver.json
:
package.json
是软件包相关的配置信息driver.json
是驱动相关的配置信息
第二步:声明接口信息
在这个例子里,大按键模块需要一个 GPIO 接口作为其输入,我们需要将这个信息声明在 driver.json
里。{
"inputs": {
"gpio": {
"type": "gpio",
"args": {
"direction": "in",
"edge": "both"
}
}
}
}
在 inputs
下,我们声明了一个作为输入的对象。这个对象的名字叫做 gpio
。在程序里,我们会用 gpio
引用这个变量,其类型( type
)是 gpio
。同时,我们还设置了这个 GPIO 的一些参数,比如,它是用作输入的,触发方式是上升沿和下降沿都可以触发。关于 GPIO 的参数,可以参考其 API 文档。
第三步:编写代码
对于这个驱动,我们要提供几个行为:
- 当按键按下时,产生一个
push
事件 - 当按键松开时,产生一个
release
事件 - 提供方法
isPushed
,查询按键是否被按下
根据硬件的特性,当按键按下的时候,GPIO
接口的值为0,当按键松开时,GPIO
接口的值为1,我们编写如下代码:var driver = require('ruff-driver');
var ButtonState = {
pushed: 0,
released: 1
};
module.exports = driver({
attach: function(inputs) {
var that = this;
this._gpio = inputs['gpio'];
this._currentState = ButtonState.released;
this._gpio.on('interrupt', function(state) {
if (that._currentState === state) {
return;
}
that._currentState = state;
if (state === ButtonState.pushed) {
that.emit('push');
} else {
that.emit('release');
}
});
},
exports: {
isPushed: function() {
return this._currentState === ButtonState.pushed;
}
}
});
更多驱动编程细节可以了解驱动编程模型。
编外步骤:测试驱动
Ruff 提供了一个辅助程序库,我们只要提供一个模拟的接口(在这里是 GPIO ),就可以让测试在开发机上进行,保证了代码逻辑上的正确性。var runner = require('ruff-driver-runner');
var assert = require('assert');
var path = require('path');
var driverPath = path.join(__dirname, '..');
var when = require('ruff-mock').when;
exports['test should emit push event'] = function(done) {
var run = false;
runner.run(driverPath, function(error, context) {
if (error) {
done(error);
return;
}
var button = context.device;
var gpio = context.inputs['gpio'];
button.on('push', function() {
done();
});
gpio.emit('interrupt', 0);
});
};
exports['test should return true when button is pushed'] = function(done) {
runner.run(driverPath, function(error, context) {
if (error) {
done(error);
return;
}
var button = context.device;
var gpio = context.inputs['gpio'];
gpio.emit('interrupt', 0);
assert(button.isPushed());
done()
});
};
require('test').run(exports);
(test/button-gpio-test.js)
这里用到的是几个框架:
- 一个简单的测试框架(
test
),符合 CommonJS 的 Unit Testing 规范。 - ruff-mock,是 Ruff 提供的一个通用 mock 框架。
- ruff-driver-runner,是 Ruff 提供的驱动测试框架,所有的设备都已经由模拟对象替代,保证可以在开发机上执行。
运行如下命令执行测试:ruff test/driver-test.js
关于驱动测试,更多细节参见驱动测试。
第四步:硬件测试
驱动编写好之后,还必须把驱动部署到硬件上进行实际的测试。请参考起步走和应用开发步骤,编写一个测试应用。
不同的是,我们需要安装本地驱动。
建议使用符号链接直接将驱动链接至项目
ruff_modules
文件夹内,避免反复添加。
rap device add -i <device-id> -l </path/to/driver> |
最后,就是把我们的应用部署到硬件上进行测试。
扩展步骤:发布驱动
经过开发和硬件测试,驱动的开发已经完成。如果想将这个驱动分享给别人,可以把驱动发布到软件包仓库。如果还没有申请软件包仓库的账号,请这里申请账号。
注意 如果你希望在rap layout --visual
能够正确进行图形化展示,请在软件仓库上传并配置设备信息。
在命令行里运行如下命令,输入软件包仓库的账号信息:rap add-user
在命令行里运行如下命令,发布驱动:rap publish
一切顺利的话,你已经完成了一个驱动的开发,并将其发布出去。恭喜你,你已经成为了一个合格的硬件驱动开发工程师。