开发环境
RT-Thread Studio 2.2.1
RT-Thread 4.1.0
Xshell 7软件
NUMAKER-IOT-M487开发板 V1.3
开发板介绍
开发板很不错,板载了丰富的资源,作为物联网开发非常合适,如果再搭配一个显示屏的话就非常完美了。开发板外观图:
创建工程
这个非常简单,首先确认安装好了NUMAKER-IOT-M487开发板的开发包。
然后创建RT-Thread项目,选择基于开发板,选择NUMAKER-IOT-M487开发板。
项目创建完成后会发现软件版本不是最新的,更新系统版本。右键刚创建的工程->修改工程->系统版本,选择4.1.0,或者latest。
因为4.1.0版本和开发板软件包使用的4.0.4有一些区别,因此需要根据编译的错误提示进行修改。
M487开发板WDT介绍
看门狗定时器(WDT)的作用是当系统运行到未知状态时,用于执行系统重置。这可以防止系统无限期地挂起。此外,该看门狗定时器支持将系统从空闲/断电模式唤醒的功能。
特点:
18位WDT超时间隔运行计数器。
可选择的超时间隔(2的4次方2的18次方),WDT_CLK=10kHz时超时间隔为1.6ms26.214s。
系统在重置状态下保持(1/WDT_CLK)*63。
支持可选WDT重置延迟周期,包括1026、130、18或3 WDT_CLKreset延迟周期。
通过在Config0寄存器中设置CWDTEN[2:0],支持在芯片通电或复位后强制启用WDT。
仅当WDT时钟源选择为10kHz或LXT时,才支持WDT超时唤醒功能。
框图
WDT时钟选择
因为开发板官方已经给写好了WDT驱动,而且默认时钟选择了32.768晶振时钟。
直接使用官方驱动进行测试非常方便,创建WDT线程进行看门狗测试。
static struct rt_thread wdt_thread;
static rt_uint8_t rt_wdt_thread[2048];
static void wdt_thread_entry(void* parameter);
int main(int argc, char *argv)
{
rt_thread_init(&wdt_thread, / 线程控制块 /
"wdt", / 线程名字 /
wdt_thread_entry, / 线程入口函数 /
RT_NULL, / 线程入口函数参数 /
&rt_wdt_thread[0], / 线程栈起始地址 /
sizeof(rt_wdt_thread), / 线程栈大小 /
11, / 线程的优先级 /
20); / 线程时间片 /
rt_thread_startup(&wdt_thread); / 启动线程,开启调度 /
while (1)
{
rt_thread_mdelay(10000);
}
return 0;
}
static rt_device_t wdt_device = RT_NULL;
static void wdt_thread_entry(void parameter) /WDT任务线程/
{
uint32_t timeout = 1; // 设置看门狗超时时间,单位:秒
uint32_t time_delay=1100;
wdt_device = rt_device_find("wdt");
rt_device_control(wdt_device, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, &timeout);
rt_device_control(wdt_device, RT_DEVICE_CTRL_WDT_START, RT_NULL);
rt_kprintf("Watchdog stat...");
while (1)
{
if(!key2_toggle) //启动默认看门狗超时,按下KEY2恢复喂狗
{
time_delay=1100;
//rt_kprintf("WDT Delay Time:%d\r\n",time_delay);
rt_thread_mdelay(time_delay); //延时,测试看门狗
rt_device_control(wdt_device, RT_DEVICE_CTRL_WDT_KEEPALIVE, RT_NULL);
}
else
{
time_delay=900;
//rt_kprintf("WDT Delay Time:%d\r\n",time_delay);
rt_thread_mdelay(time_delay); //延时,测试看门狗
rt_device_control(wdt_device, RT_DEVICE_CTRL_WDT_KEEPALIVE, RT_NULL);
}
}
}
这里使用了按键来切换看门狗测试延时时间,按键线程使用的软件包,非常方便,直接再RT-Thread Settings里面添加就可以了,不用重复造轮子了。部分代码如下:
static void btn_click_event_cb(agile_btn_t *btn)
{
rt_pin_write(LED_Y, PIN_LOW);
rt_thread_mdelay(100);
rt_pin_write(LED_Y, PIN_HIGH);
//rt_kprintf("[button click event] pin:%d repeat:%d, hold_time:%d\r\n", btn->pin, btn->repeat_cnt, btn->hold_time);
if(btn==key1)
{
key1_toggle++;
if(key1_toggle>1)
{key1_toggle=0;}
rt_kprintf("key1 pressed\r\n");
}
if(btn==key2)
{
key2_toggle++;
if(key2_toggle>1)
{key2_toggle=0;}
if(key2_toggle==0)
{
rt_kprintf("WDT Delay Time:1100\r\n");
}
if(key2_toggle==1)
{
rt_kprintf("WDT Delay Time:900\r\n");
rt_device_control(wdt_device, RT_DEVICE_CTRL_WDT_KEEPALIVE, RT_NULL);
}
rt_kprintf("key2 pressed\r\n");
}
}
插上电源启动,连上串口使用Xshell串口查看开发板不停重启,这是因为开始定义延时时间1.1秒再喂狗,而看门狗设置的超时时间时1秒。按键KEY2按下后延时时间修改为0.9秒,这样程序就可以正常运行了。
WDT在程序设计时已经是必不可少的功能了,因为有了官方驱动,再结合RT-Thread使用非常方便。
M487开发板RTC介绍
实时时钟(RTC)控制器提供实时和日历消息。RTC提供可编程的时间刻度和报警匹配中断。时间和日历消息的数据格式以BCD格式表示。数字频率补偿功能可用于补偿外部晶体振荡器的频率精度。
特点:
支持带有外部电源引脚VBAT的独立RTC电源域。(M48xGC/M48xE8)
支持RTC时间(小时、分钟、秒)中的实时计数器和RTC时间中的日历计数器RTC_CAL(年、月、日)用于RTC时间和日历检查。
支持中的报警时间(小时、分钟、秒)和日历(年、月、日)设置。
支持报警时间(小时、分钟、秒)和日历(年、月、日)掩码启用。
RTC_CLKFMT寄存器中可选择的12小时或24小时时间模式。
支持RTC_闰年寄存器中的闰年指示。
支持RTC_工作日寄存器中的星期日计数器。
支持具有8个周期间隔选项1/128、1/64、1/32、1/16、1/8、1/4、1/2和1秒。
当RTC中断信号被激活时,支持芯片从空闲或断电模式唤醒。
框图
从特点描述中可以知道NUMAKER-IOT-M487开发板使用的芯片M487JIDAE没有独立的外部RTC电源引脚,也就是不能直接接单独为RTC供电的外部电源,例如电池。
也可以使用RTC寄存器来配置RTC,既然官方给出了软件包,使用现成的驱动就非常方便了。创建RTC线程测试RTC。
static struct rt_thread rtc_thread;
static rt_uint8_t rt_rtc_thread[2048];
static void rtc_thread_entry(void parameter);
int main(int argc, char **argv)
{
rt_thread_init(&rtc_thread, / 线程控制块 */
"rtc",
rtc_thread_entry,
RT_NULL,
&rt_rtc_thread[0],
sizeof(rt_rtc_thread),
3,
20);
rt_thread_startup(&rtc_thread); / 启动线程,开启调度 /
while (1)
{
rt_thread_mdelay(10000);
}
return 0;
}
static void rtc_thread_entry(void parameter) /RTC任务线程*/
{
time_t now;
while (1)
{
now = time(RT_NULL);
if(key1_toggle)
{
rt_kprintf("RTC Current Time:%.*s", 25, ctime(&now));
}
rt_thread_mdelay(1000);
}
}
程序中使用KEY1来控制是否打印输出RTC时间,KEY1按键按下每隔1秒打印输出当前RTC时间。
之所以使用按键控制输出主要是在操作命令时方便,尝试进行msh命令进行日期和时间的设置,但是发现日期和时间无法设置。因为有了NUC980开发板测试RTC的经验,这里是同样的问题。
修改RTC自带驱动即可,在drv_rtc.c里面的static rt_err_t nu_rtc_is_date_valid(const time_t *const t)函数里:
/ check the date is supported by rtc. /
if (((uint32_t)t > (uint32_t)t_upper) || ((uint32_t)t < (uint32_t)t_lower))
return -(RT_EINVAL);
既然开发板自带WIFI模块,又可以有线方式联网,那就可以很方便的使用NTP获取网络时间,进行NTP对时,然后同步到RTC,这样RTC时间就会和网络时间保持一致。
添加网络工具,使能NTP和同步至RTC。
设置完之后可以看到NTP对时成功后会自动同步至RTC时间。
开始的错误和超时是由于没有联网成功和对时太频繁造成的,为了测试效果吧NTP对时时间设置了每10秒对一次,实际应用中不需要这么短的时间对时的。
因为RTC的精度已经比较高了,离线状态下一天也就误差差不多4秒,所以基本每隔一个小时或者半个小时对时一次即可。
总结
NUMAKER-IOT-M487开发板功能非常强大了,板载资源非常丰富,可以完成物联网的很多实验,值得推荐。
RT-Thread实时系统使用也非常方便高效,针对物联网又非常多的软件包和工具使用,大大缩短了开发周期。
原作者:Kan