【工作筆記】IOT.js適配AWorks平臺(tái)通用外設(shè)接口(4):PWM

一、前言

近期因工作需求學(xué)習(xí)了一下 IOT.js 和 AWorks 平臺(tái)通用外設(shè)接口(包括:ADC统舀、GPIO、I2C劳景、PWM绑咱、SPI 和 UART),并將它們逐一適配到 IOT.js 中枢泰,為后續(xù) AWTK-MVMM 的 JS項(xiàng)目支持平臺(tái)外設(shè)調(diào)用奠定基礎(chǔ)描融,此處做筆記記錄一下。

備注:IOT.js 和 AWorks 的相關(guān)介紹請(qǐng)看第一篇 ADC 適配筆記。

二只损、PWM

2.1 PWM 簡(jiǎn)介

大小和方向隨時(shí)間發(fā)生周期性變化的電流稱為交流電一姿,交流中最基本的波形稱為正弦波,除此之外的波形稱為非正弦波跃惫。計(jì)算機(jī)叮叹、電視機(jī)、雷達(dá)燈裝置中使用的信號(hào)稱為脈沖波爆存,鋸齒波等蛉顽,其電壓和電流波形都是非正弦交流的一種。

PWM(Pulse Width Modulation)就是脈沖寬度調(diào)制的意思先较,一種脈沖編碼技術(shù)携冤,即可以按照信號(hào)電平改變脈沖寬度。而脈沖寬度調(diào)制波的周期也是固定的闲勺,用占空比(高電平/周期曾棕,有效電平在整個(gè)信號(hào)周期中的時(shí)間比率,范圍0~100%)來表示編碼數(shù)值菜循。PWM可以用于模擬信號(hào)電平進(jìn)行數(shù)字編碼睁蕾,也可以通過高電平(或低電平)在整個(gè)周期中的時(shí)間來控制輸出的能量,從而控制電機(jī)轉(zhuǎn)速或LED亮度债朵、蜂鳴器響度等子眶。

簡(jiǎn)單理解,PWM 是按一定的規(guī)則對(duì)各脈沖的寬度進(jìn)行調(diào)制序芦,既可以改變逆變電路輸出電壓的大小臭杰,也可以改變輸出頻率。在下本的例程中體現(xiàn)在控制無源蜂鳴器的響度和頻率谚中。

2.2 PWM 接口

AWorks 提供了用于控制 PWM 的接口函數(shù):

  • aw_pwm_config:配置PWM(周期時(shí)間渴杆、脈寬時(shí)間等);
  • aw_pwm_enable:使能PWM輸出宪塔;
  • aw_pwm_disable:禁能PWM輸出磁奖。

三、適配過程

3.1 AWorks演示代碼

先來看看這些PWM相關(guān)接口的基本用法某筐,我們?cè)诘装迳吓芤幌潞?jiǎn)單的例程比搭。

步驟一:外設(shè)使能,在AWorks工程配置文件 aw_prj_params.h 中開啟以下宏定義使能蜂鳴器設(shè)備以及相關(guān)的計(jì)時(shí)器:

#define AW_DEV_PWM_BUZZER          /**< \brief PWM Buzzer(蜂鳴器南誊,需要配套 PWM) */
#define AW_DEV_IMX1050_QTIMER3_PWM /**< \brief iMX1050 QTimer3 PWM (與 PWM Buzzer 配套的 PWM身诺,需要開啟) */

步驟二:到外設(shè)文件中查看設(shè)備對(duì)應(yīng)的引腳蜜托,比如這里查看 awbl_hwconf_imx1050_qtimer3_pwm.h 文件,可以看到該設(shè)備使用的引腳為 GPIO1_18 霉赡,我們需確定這個(gè)引腳能正常使用橄务。

步驟三:編寫例程,設(shè)置蜂鳴器的周期與脈寬占比穴亏。示例代碼如下:

#include "aw_pwm.h"

#define AW_PWM_ID   DE_BUZZER_PWMID /* 蜂鳴器PWM ID */

int main()
{
    uint32_t    period1 = 2000000;      /* (ns) */
    uint32_t    period2 = 1000000;      /* (ns) */

    aw_kprintf("\nPWM demo testing...\n");

    while(1) {

        /* 配置 PWM 的有效時(shí)間(高電平時(shí)間)50% ,周期 period1*/
        aw_pwm_config(AW_PWM_ID, period1 / 2, period1);
        aw_pwm_enable(AW_PWM_ID);      /* 使能通道 */
        aw_mdelay(250);
        aw_pwm_disable(AW_PWM_ID);     /* 禁能通道 */
        aw_mdelay(250);

        /* 配置 PWM 的有效時(shí)間(高電平時(shí)間)2% ,周期 period1*/
        aw_pwm_config(AW_PWM_ID, period1 / 50, period1);
        aw_pwm_enable(AW_PWM_ID);      /* 使能通道 */
        aw_mdelay(250);
        aw_pwm_disable(AW_PWM_ID);     /* 禁能通道 */
        aw_mdelay(250);

        /* 配置 PWM 的有效時(shí)間(高電平時(shí)間)50% ,周期 period2*/
        aw_pwm_config(AW_PWM_ID, period2 / 2, period2);
        aw_pwm_enable(AW_PWM_ID);      /* 使能通道 */
        aw_mdelay(250);
        aw_pwm_disable(AW_PWM_ID);     /* 禁能通道 */
        aw_mdelay(250);

        /* 配置 PWM 的有效時(shí)間(高電平時(shí)間)2% ,周期 period2*/
        aw_pwm_config(AW_PWM_ID, period2 / 50, period2);
        aw_pwm_enable(AW_PWM_ID);      /* 使能通道 */
        aw_mdelay(250);
        aw_pwm_disable(AW_PWM_ID);     /* 禁能通道 */
        aw_mdelay(250);
    }
    return 0;
}

這里的測(cè)試效果可以用示波器抓取波形來觀察蜂挪。

3.2 C語言適配層

在 IOT.js 中,適配某個(gè)平臺(tái)的外設(shè)通常需要實(shí)現(xiàn) src/modules/iotjs_module_xxx.h 文件中的接口嗓化,比如這里我們需要實(shí)現(xiàn) iotjs_module_pwm.h 中的相關(guān)接口:

#ifndef IOTJS_MODULE_PWM_H
#define IOTJS_MODULE_PWM_H

#include "iotjs_def.h"
#include "iotjs_module_periph_common.h"

#if defined(__TIZENRT__)
#include <iotbus_pwm.h>
#include <stdint.h>
#endif

// Forward declaration of platform data. These are only used by platform code.
// Generic PWM module never dereferences platform data pointer.
typedef struct iotjs_pwm_platform_data_s iotjs_pwm_platform_data_t;

typedef struct {
  jerry_value_t jobject;
  iotjs_pwm_platform_data_t* platform_data;

  uint32_t pin;
  double duty_cycle;
  double period;
  bool enable;
} iotjs_pwm_t;

jerry_value_t iotjs_pwm_set_platform_config(iotjs_pwm_t* pwm,
                                            const jerry_value_t jconfig);
bool iotjs_pwm_open(iotjs_pwm_t* pwm);
bool iotjs_pwm_set_period(iotjs_pwm_t* pwm);
bool iotjs_pwm_set_dutycycle(iotjs_pwm_t* pwm);
bool iotjs_pwm_set_enable(iotjs_pwm_t* pwm);
bool iotjs_pwm_close(iotjs_pwm_t* pwm);

// Platform-related functions; they are implemented
// by platform code (i.e.: linux, nuttx, tizen).
void iotjs_pwm_create_platform_data(iotjs_pwm_t* pwm);
void iotjs_pwm_destroy_platform_data(iotjs_pwm_platform_data_t* platform_data);

#endif /* IOTJS_MODULE_PWM_H */

適配層(src/modules/aworks/iotjs_module_pwm-aworks.c)代碼如下:

#if !defined(WITH_AWORKS)
#error "Module __FILE__ is for AWorks only"
#endif

#include "iotjs_def.h"
#include "aw_gpio.h"
#include "aw_pwm.h"
#include "modules/iotjs_module_pwm.h"

struct iotjs_pwm_platform_data_s {
  int pid;
};

/* PWM通道的id號(hào) */
#define AWORKS_PWM_STRING_PID "pid"

#define AWORKS_GPIO_REQ_NAME "iotjs_pwm"

void iotjs_pwm_create_platform_data(iotjs_pwm_t* pwm) {
  pwm->platform_data = IOTJS_ALLOC(iotjs_pwm_platform_data_t);
  pwm->platform_data->pid = -1;
}

void iotjs_pwm_destroy_platform_data(iotjs_pwm_platform_data_t* platform_data) {
  IOTJS_RELEASE(platform_data);
}

jerry_value_t iotjs_pwm_set_platform_config(iotjs_pwm_t* pwm,
                                            const jerry_value_t jconfig) {
  JS_GET_REQUIRED_CONF_VALUE(jconfig, pwm->platform_data->pid,
                             AWORKS_PWM_STRING_PID, number);

  return jerry_create_undefined();
}

static bool iotjs_pwm_set_period_and_dutycycle(iotjs_pwm_t* pwm) {
  aw_err_t ret;
  unsigned long duty_ns;
  unsigned long period_ns;
  iotjs_pwm_platform_data_t* platform_data = pwm->platform_data;

  period_ns = (unsigned long)(1000 * 1000 * 1000 * pwm->period);
  duty_ns = (unsigned long)period_ns - (period_ns * pwm->duty_cycle);

  ret = aw_pwm_config(platform_data->pid, duty_ns, period_ns);

  return ret == AW_OK ? true : false;
}

bool iotjs_pwm_open(iotjs_pwm_t* pwm) {
  aw_err_t ret;
  int p_pins[] = { pwm->pin };

  ret = aw_gpio_pin_request(AWORKS_GPIO_REQ_NAME, p_pins, AW_NELEMENTS(p_pins));

  if (ret == AW_ERROR) {
    return iotjs_pwm_set_period_and_dutycycle(pwm);
  } else if (ret == -AW_ENXIO) {
    DLOG("%s: pwm pin number error(%d)", __func__, ret);
  } else if (ret == AW_OK) {
    aw_gpio_pin_release(p_pins, AW_NELEMENTS(p_pins));
    DLOG("%s: pwm pin is free, please check(%d)", __func__, ret);
  } else {
    DLOG("%s: pwm pin request unknow return(%d)", __func__, ret);
  }

  return false;
}

bool iotjs_pwm_set_period(iotjs_pwm_t* pwm) {
  return iotjs_pwm_set_period_and_dutycycle(pwm);
}

bool iotjs_pwm_set_dutycycle(iotjs_pwm_t* pwm) {
  return iotjs_pwm_set_period_and_dutycycle(pwm);
}

bool iotjs_pwm_set_enable(iotjs_pwm_t* pwm) {
  aw_err_t ret;
  iotjs_pwm_platform_data_t* platform_data = pwm->platform_data;

  if (pwm->enable) {
    ret = aw_pwm_enable(platform_data->pid);
  } else {
    ret = aw_pwm_disable(platform_data->pid);
  }

  return ret == AW_OK ? true : false;
}

bool iotjs_pwm_close(iotjs_pwm_t* pwm) {
  pwm->platform_data->pid = -1;
  return true;
}

3.2 JS測(cè)試代碼

適配好后棠涮,我們編寫 JS 代碼測(cè)試一下,這里同樣借助蜂鳴器進(jìn)行測(cè)試蟆湖,可以用示波器抓取波形查看效果:

var pwm = require('pwm');  /* 導(dǎo)入pwm模塊 */

var dutyCycles = [0.25, 0.5, 0.75];  /* 高低平占比 */
var frequencies = [1, 10, 30];       /* 頻率,單位:kHz */

var configuration = {
  period: 0.001,  // 周期玻粪,單位:秒隅津,表示1kHz
  dutyCycle: dutyCycles[0],
  pin: 18  // 引腳
  pid: 0   // PWM ID
};

function initPwm(pwm) {
  pwm.setPeriodSync(0.001);
  pwm.setDutyCycleSync(0.5);
}

var pwm0 = pwm.openSync(configuration);
console.log('PWM initialized');

pwm0.setEnableSync(true);
dutyCycleTest();

function dutyCycleTest() {
  var loopCnt = 0;

  var loop = setInterval(function() {
    if (pwm0 === null) {
      return;
    }

    if (loopCnt >= dutyCycles.length) {
      clearInterval(loop);
      initPwm(pwm0);
      console.log('PWM duty-cycle test complete');
      frequencyTest();
      return;
    }
    console.log("dutycycle(%d)", dutyCycles[loopCnt]);
    pwm0.setDutyCycleSync(dutyCycles[loopCnt++]);
  }, 1000);
}

function frequencyTest() {
  var loopCnt = 0;

  var loop = setInterval(function() {
    if (loopCnt >= frequencies.length) {
      clearInterval(loop);
      pwm0.setEnableSync(false);
      pwm0.closeSync();
      console.log('PWM frequency test complete');
      return;
    }
    console.log("frequency(%d)", frequencies[loopCnt]);
    pwm0.setFrequencySync(frequencies[loopCnt++]);
  }, 2000);
}

輸出結(jié)果:

PWM initialized
dutycycle(0.25)
dutycycle(0.5)
dutycycle(0.75)
PWM duty-cycle test complete
frequency(1)
frequency(10)
frequency(30)
PWM frequency test complete
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市劲室,隨后出現(xiàn)的幾起案子伦仍,更是在濱河造成了極大的恐慌,老刑警劉巖很洋,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件充蓝,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡喉磁,警方通過查閱死者的電腦和手機(jī)谓苟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來协怒,“玉大人涝焙,你說我怎么就攤上這事≡邢荆” “怎么了仑撞?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長妖滔。 經(jīng)常有香客問我隧哮,道長,這世上最難降的妖魔是什么座舍? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任沮翔,我火速辦了婚禮,結(jié)果婚禮上曲秉,老公的妹妹穿的比我還像新娘鉴竭。我一直安慰自己歧譬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布搏存。 她就那樣靜靜地躺著瑰步,像睡著了一般。 火紅的嫁衣襯著肌膚如雪璧眠。 梳的紋絲不亂的頭發(fā)上缩焦,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音责静,去河邊找鬼袁滥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛灾螃,可吹牛的內(nèi)容都是我干的题翻。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼腰鬼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼嵌赠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起熄赡,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤姜挺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后彼硫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炊豪,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年拧篮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了词渤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡串绩,死狀恐怖掖肋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情赏参,我是刑警寧澤志笼,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站把篓,受9級(jí)特大地震影響纫溃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜韧掩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一紊浩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦坊谁、人聲如沸费彼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽箍铲。三九已至,卻和暖如春鬓椭,著一層夾襖步出監(jiān)牢的瞬間颠猴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來泰國打工小染, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留翘瓮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓裤翩,卻偏偏與公主長得像资盅,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子踊赠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

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