一、前言
近期因工作需求學(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平臺(tái)通用外設(shè)接口(1):ADC衡蚂;
- 【工作筆記】IOT.js適配AWorks平臺(tái)通用外設(shè)接口(2):GPIO窿克;
- 【工作筆記】IOT.js適配AWorks平臺(tái)通用外設(shè)接口(3):I2C骏庸;
- 【工作筆記】IOT.js適配AWorks平臺(tái)通用外設(shè)接口(4):PWM;
- 【工作筆記】IOT.js適配AWorks平臺(tái)通用外設(shè)接口(5):SPI年叮;
- 【工作筆記】IOT.js適配AWorks平臺(tái)通用外設(shè)接口(6):UART具被;
備注: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