一撑瞧、前言
近期因工作需求學(xué)習(xí)了一下 IOT.js 和 AWorks 平臺通用外設(shè)接口(包括:ADC棵譬、GPIO、I2C预伺、PWM订咸、SPI 和 UART),并將它們逐一適配到 IOT.js 中酬诀,為后續(xù) AWTK-MVMM 的 JS項目支持平臺外設(shè)調(diào)用奠定基礎(chǔ)脏嚷,此處做筆記記錄一下。
- 【工作筆記】IOT.js適配AWorks平臺通用外設(shè)接口(1):ADC瞒御;
- 【工作筆記】IOT.js適配AWorks平臺通用外設(shè)接口(2):GPIO父叙;
- 【工作筆記】IOT.js適配AWorks平臺通用外設(shè)接口(3):I2C;
- 【工作筆記】IOT.js適配AWorks平臺通用外設(shè)接口(4):PWM肴裙;
- 【工作筆記】IOT.js適配AWorks平臺通用外設(shè)接口(5):SPI趾唱;
- 【工作筆記】IOT.js適配AWorks平臺通用外設(shè)接口(6):UART;
備注:IOT.js 和 AWorks 的相關(guān)介紹請看第一篇 ADC 適配筆記蜻懦。
二甜癞、GPIO
在使用一個I/O口前,必須正確的配置I/O的功能和模式宛乃。同時悠咱,由于一個I/O口往往可以用于多種功能,但同一時刻只能用于某一確定的功能烤惊,為了避免沖突乔煞,在將一個引腳用作某一功能前,應(yīng)該向 AWorks 系統(tǒng)申請柒室,使用完畢后再釋放渡贾。
當(dāng)將I/O口用作GPIO(General Purpose Input Output)功能時,GPIO作為一種通用輸入/輸出接口雄右,有兩種常用的使用方式:
- 一種是用作普通的輸入/輸出接口空骚;
- 一種是用作中斷輸入接口纺讲,即當(dāng)指定的輸入狀態(tài)事件發(fā)生(比如:下降沿)時,觸發(fā)用戶自定義回調(diào)函數(shù)囤屹。
我們這里主要是適配第一種普通的輸入/輸出接口熬甚,中斷GPIO不過多介紹,具體可以自行查看 AWorks 的官方API文檔肋坚。
2.1 I/O配置
通常情況下乡括,一個引腳往往可以用作多種功能,例如诲泌,在 i.MX280 中诚卸,PIO3_0可以用作下面4種功能:
- 應(yīng)用串口0的接收引腳葵第;
- I2C的時鐘引腳合溺;
- 調(diào)試串口的CTS引腳卒密;
- 通用GPIO。
同時辫愉,GPIO往往還具有多種模式痰腮,比如上拉误辑、下拉、開漏潦匈、推挽等凰锡。為了正確使用一個IO口魂角,必須先將其配置為正確的功能和模式。AWorks提供了I/O配置接口挣惰,其原型為:
aw_err_t aw_gpio_pin_cfg(int pin, uint32_t flags);
其中竖幔,pin為引腳編號馋评,flags 為配置的功能和模式標(biāo)志市咆,返回值為標(biāo)準(zhǔn)的錯誤號汉操,返回AW_OK時表示配置成功,否則表示配置失敗蒙兰,可能是由于部分功能和模式不支持造成的磷瘤。
2.2 I/O的申請和釋放
如上文所述 PIO3_0 的功能眾多,但是在同一時刻搜变,其只能被用作某一確定的功能采缚,并不能同時使用多種功能。
隨著系統(tǒng)復(fù)雜度的上升挠他,用戶往往很難保證某一引腳只被用作一種功能扳抽,稍有不慎,就可能將某一引腳通用是配置為多種功能殖侵,此時贸呢,部分使用某種功能的應(yīng)用程序?qū)⒉荒苷9ぷ鳌T谶@種情況下拢军,用戶往往很難發(fā)現(xiàn)工作異常的原因楞陷。
為了保證引腳功能的互斥使用,AWorks提供了一種申請機制茉唉,在將引腳用作某一功能前固蛾,必須向系統(tǒng)申請,若該引腳處于空閑狀態(tài)度陆,還未被申請過艾凯,則本次申請成功,同時標(biāo)記該引腳已被使用懂傀。若在申請前览芳,該引腳已被申請,則本次申請失敗鸿竖。當(dāng)與個引腳使用完畢時,應(yīng)該釋放該引腳铸敏,以便系統(tǒng)將該引腳分配給下一個申請者缚忧。
(1)申請引腳的函數(shù)原型:
aw_err_t aw_gpio_pin_request(const char *p_name, const int *p_pins, int num);
其中,p_name為申請者的名字杈笔,p_pins指向引腳列表闪水,num為本次申請的引腳個數(shù)。返回值為標(biāo)準(zhǔn)的錯誤號蒙具,返回AW_OK時表示申請成功球榆,可以正常使用相關(guān)的引腳朽肥,否則表示申請失敗,相關(guān)引腳已被占用持钉,無法使用衡招。
(2)釋放引腳的函數(shù)原型:
aw_err_t aw_gpio_pin_release(const int *p_pins, int num);
其中,p_pins指向引腳列表每强,num為本次申請的引腳個數(shù)始腾。返回值為標(biāo)準(zhǔn)的錯誤號,返回AW_OK時表示釋放成功空执,否則表示釋放失敗浪箭,可能是由于參數(shù)錯誤導(dǎo)致的。
2.3 普通I/O接口
當(dāng)一個引腳配置為通用I/O接口時(輸入或輸出)辨绊,則可以通過相關(guān)的I/O接口控制其輸出狀態(tài)獲取其輸入狀態(tài)奶栖。相關(guān)接口如下:
- aw_gpio_get:讀取GPIO引腳的輸入值/輸出值;
- aw_gpio_set:讀取GPIO引腳的輸出值门坷;
- aw_gpio_toggle:翻轉(zhuǎn)GPIO引腳的輸出值宣鄙。
三、適配過程
3.1 AWorks演示代碼
先來看看這些GPIO接口的基本用法拜鹤,我們在底板上跑一下簡單的例程框冀。
步驟一:外設(shè)禁能,在AWorks工程配置文件 aw_prj_params.h
中關(guān)閉以下宏定義禁能對應(yīng)的LED燈設(shè)備敏簿,避免引腳復(fù)用:
//#define AW_DEV_GPIO_LED /**< \brief LED */
步驟二:到外設(shè)文件中查看設(shè)備對應(yīng)的引腳明也,比如這里查看 awbl_hwconf_gpio_led.h
文件,找到 LED 燈設(shè)備所對應(yīng)的引腳為 GPIO1_19惯裕,即底板上的 run 引腳温数。
步驟三:編寫例程,申請GPIO1_19引腳并將其初始化為輸出功能蜻势、上拉模式撑刺,通過修改引腳的高低電平讓LED燈出現(xiàn)閃爍的現(xiàn)象。示例代碼如下:
#include "aw_gpio.h"
#include "aw_led.h"
#define GPIO_LED DM_GPIO_LED /* LED燈的引腳編號 */
int main()
{
int i = 0;
aw_kprintf("\nGPIO demo testing...\r\n");
int gpio_led_test[] = {GPIO_LED};
if (aw_gpio_pin_request("gpio_led_test",
gpio_led_test,
AW_NELEMENTS(gpio_led_test)) == AW_OK) {
/* GPIO 引腳配置:輸出功能握玛、上拉模式 */
aw_gpio_pin_cfg(GPIO_LED, AW_GPIO_OUTPUT | AW_GPIO_PULL_UP);
}
/* LED以1s的周期閃爍5次 */
for (i = 0; i < 5; i++) {
aw_gpio_set(GPIO_LED, 0);
aw_mdelay(500);
aw_gpio_set(GPIO_LED, 1);
aw_mdelay(500);
}
/* LED以0.2s的周期持續(xù)閃爍 */
for (i = 0; i < 40; i++) {
aw_gpio_toggle(GPIO_LED);
aw_mdelay(100);
}
aw_gpio_pin_release(gpio_led_test, AW_NELEMENTS(gpio_led_test));
aw_kprintf("\nGPIO demo exit...\r\n");
return 0;
}
輸出結(jié)果:
GPIO demo testing...
GPIO demo exit...
測試結(jié)果主要是觀察M1052底板上的LED燈閃爍够傍。
[圖片上傳失敗...(image-a9e2ed-1651557698126)]
3.2 C語言適配層
在 IOT.js 中,適配某個平臺的外設(shè)通常需要實現(xiàn) src/modules/iotjs_module_xxx.h
文件中的接口挠铲,比如這里我們需要實現(xiàn) iotjs_module_gpio.h
中的相關(guān)接口:
#ifndef IOTJS_MODULE_GPIO_H
#define IOTJS_MODULE_GPIO_H
#include "iotjs_def.h"
#include "iotjs_module_periph_common.h"
typedef enum {
kGpioDirectionIn = 0,
kGpioDirectionOut,
__kGpioDirectionMax
} GpioDirection;
typedef enum {
kGpioModeNone = 0,
kGpioModePullup,
kGpioModePulldown,
kGpioModeFloat,
kGpioModePushpull,
kGpioModeOpendrain,
__kGpioModeMax
} GpioMode;
typedef enum {
kGpioEdgeNone = 0,
kGpioEdgeRising,
kGpioEdgeFalling,
kGpioEdgeBoth,
__kGpioEdgeMax
} GpioEdge;
typedef struct iotjs_gpio_platform_data_s iotjs_gpio_platform_data_t;
// This Gpio class provides interfaces for GPIO operation.
typedef struct {
jerry_value_t jobject;
iotjs_gpio_platform_data_t* platform_data;
bool value;
uint32_t pin;
GpioDirection direction;
GpioMode mode;
GpioEdge edge;
} iotjs_gpio_t;
bool iotjs_gpio_open(iotjs_gpio_t* gpio);
bool iotjs_gpio_write(iotjs_gpio_t* gpio);
bool iotjs_gpio_read(iotjs_gpio_t* gpio);
bool iotjs_gpio_close(iotjs_gpio_t* gpio);
bool iotjs_gpio_set_direction(iotjs_gpio_t* gpio);
// Platform-related functions; they are implemented
// by platform code (i.e.: linux, nuttx, tizen).
void iotjs_gpio_create_platform_data(iotjs_gpio_t* gpio);
void iotjs_gpio_destroy_platform_data(
iotjs_gpio_platform_data_t* platform_data);
#endif /* IOTJS_MODULE_GPIO_H */
適配層(src/modules/aworks/iotjs_module_gpio-aworks.c
)代碼如下:
#if !defined(WITH_AWORKS)
#error "Module __FILE__ is for AWorks only"
#endif
#include "iotjs_def.h"
#include "aw_gpio.h"
#include "modules/iotjs_module_gpio.h"
struct iotjs_gpio_platform_data_s {
bool is_request;
};
#define AWORKS_GPIO_REQ_NAME "iotjs_gpio"
static int direction_to_constant(GpioDirection direction) {
switch (direction) {
case kGpioDirectionIn:
return AW_GPIO_INPUT;
case kGpioDirectionOut:
return AW_GPIO_OUTPUT;
}
return -1;
}
static int mode_to_constant(GpioMode mode) {
switch (mode) {
/* 不設(shè)置模式時冕屯,默認(rèn)為浮空模式 */
case kGpioModeNone:
return AW_GPIO_FLOAT;
case kGpioModePullup:
return AW_GPIO_PULL_UP;
case kGpioModePulldown:
return AW_GPIO_PULL_DOWN;
case kGpioModeFloat:
return AW_GPIO_FLOAT;
case kGpioModePushpull:
return AW_GPIO_PUSH_PULL;
case kGpioModeOpendrain:
return AW_GPIO_OPEN_DRAIN;
}
return -1;
}
void iotjs_gpio_create_platform_data(iotjs_gpio_t* gpio) {
gpio->platform_data = IOTJS_ALLOC(iotjs_gpio_platform_data_t);
}
void iotjs_gpio_destroy_platform_data(
iotjs_gpio_platform_data_t* platform_data) {
IOTJS_ASSERT(platform_data);
IOTJS_RELEASE(platform_data);
}
bool iotjs_gpio_open(iotjs_gpio_t* gpio) {
DDDLOG("%s - pin: %d, direction: %d, mode: %d", __func__, gpio->pin,
gpio->direction, gpio->mode);
aw_err_t ret;
int mode;
int direction;
int p_pins[] = { gpio->pin };
ret = aw_gpio_pin_request(AWORKS_GPIO_REQ_NAME, p_pins, AW_NELEMENTS(p_pins));
if (ret != AW_OK) {
gpio->platform_data->is_request = false;
DLOG("%s: gpio pin number error or occupied(%d)", __func__, ret);
return false;
} else {
gpio->platform_data->is_request = true;
}
direction = direction_to_constant(gpio->direction);
if (direction < 0) {
DLOG("%s: gpio pin direction error(%d)", __func__, gpio->direction);
return false;
}
mode = mode_to_constant(gpio->mode);
if (mode < 0) {
DLOG("%s: gpio pin mode error(%d)", __func__, gpio->mode);
return false;
}
ret = aw_gpio_pin_cfg(gpio->pin, direction | mode);
if (ret != AW_OK) {
DLOG("%s: gpio pin config error(%d)", __func__, ret);
return false;
}
return true;
}
bool iotjs_gpio_write(iotjs_gpio_t* gpio) {
aw_err_t ret = aw_gpio_set(gpio->pin, (int)gpio->value);
if (ret != AW_OK) {
DLOG("%s, Cannot write value(%d).", __func__, ret);
return false;
}
return true;
}
bool iotjs_gpio_read(iotjs_gpio_t* gpio) {
int ret = aw_gpio_get(gpio->pin);
if (ret < 0) {
DLOG("%s, Cannot read value(%d).", __func__, ret);
return false;
}
gpio->value = (bool)ret;
return true;
}
bool iotjs_gpio_close(iotjs_gpio_t* gpio) {
if (gpio->platform_data->is_request) {
aw_err_t ret;
int p_pins[] = { gpio->pin };
ret = aw_gpio_pin_release(p_pins, AW_NELEMENTS(p_pins));
if (ret != AW_OK) {
DLOG("%s: gpio pin number error (%d)", __func__, ret);
return false;
} else {
gpio->platform_data->is_request = false;
}
}
return true;
}
bool iotjs_gpio_set_direction(iotjs_gpio_t* gpio) {
aw_err_t ret;
int direction = direction_to_constant(gpio->direction);
ret = aw_gpio_pin_cfg(gpio->pin, direction);
if (ret != AW_OK) {
DLOG("%s, Cannot set direction(%d).", __func__, ret);
return false;
}
return true;
}
3.2 JS測試代碼
適配好后,我們編寫 JS 代碼測試一下拂苹,同樣利用 LED燈進(jìn)行測試:
var gpio = require('gpio'); /* 導(dǎo)入gpio模塊 */
var value = 0;
var loopCnt = 10;
var configuration = {
pin: 19, /* LED燈的引腳編號 */
direction: gpio.DIRECTION.OUT, /* 輸出功能 */
mode: gpio.MODE.PULLUP /* 上拉模式 */
};
console.log('test start');
ledGpio = gpio.openSync(configuration);
var loop = setInterval(function() {
if (--loopCnt < 0) {
console.log('test complete');
clearInterval(loop);
gpio.closeSync();
} else {
value = value ^ 1;
gpio20.writeSync(value);
}
}, 500)
輸出結(jié)果:
test start
test complete
LED燈一秒鐘閃爍一次安聘,共5次。