【工作筆記】IOT.js適配AWorks平臺通用外設(shè)接口(2):GPIO

一撑瞧、前言

近期因工作需求學(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 的相關(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次。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市浴韭,隨后出現(xiàn)的幾起案子丘喻,更是在濱河造成了極大的恐慌,老刑警劉巖念颈,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泉粉,死亡現(xiàn)場離奇詭異,居然都是意外死亡舍肠,警方通過查閱死者的電腦和手機搀继,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來翠语,“玉大人叽躯,你說我怎么就攤上這事〖±ǎ” “怎么了点骑?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長谍夭。 經(jīng)常有香客問我黑滴,道長,這世上最難降的妖魔是什么紧索? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任袁辈,我火速辦了婚禮,結(jié)果婚禮上珠漂,老公的妹妹穿的比我還像新娘晚缩。我一直安慰自己,他們只是感情好媳危,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布荞彼。 她就那樣靜靜地躺著,像睡著了一般待笑。 火紅的嫁衣襯著肌膚如雪鸣皂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天暮蹂,我揣著相機與錄音寞缝,去河邊找鬼。 笑死仰泻,一個胖子當(dāng)著我的面吹牛第租,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播我纪,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了浅悉?” 一聲冷哼從身側(cè)響起趟据,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎术健,沒想到半個月后汹碱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡荞估,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年咳促,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片勘伺。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡跪腹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出飞醉,到底是詐尸還是另有隱情冲茸,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布缅帘,位于F島的核電站轴术,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏钦无。R本人自食惡果不足惜逗栽,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望失暂。 院中可真熱鬧彼宠,春花似錦、人聲如沸趣席。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宣肚。三九已至想罕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間霉涨,已是汗流浹背按价。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留笙瑟,地道東北人楼镐。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像往枷,于是被迫代替她去往敵國和親框产。 傳聞我的和親對象是個殘疾皇子凄杯,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容