Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
WDT
nuc980
NK-980IOT测评之WDT测试
发布于 2022-03-09 14:01:22 浏览:879
订阅该版
[tocm] # NK-980IOT之WDT测试 本文对NK-980IOT的看门狗进行了详细介绍,基于寄存器配置和驱动API分别编写了测试代码,进行WDT功能测试,对驱动的实现原理也进行了详细的介绍。 ## 基本特征 硬件上板子有32.768k的外部晶振,测试时使用该时钟作为看门狗时钟源。 - 复位 - Idle/Power-down模式唤醒(此时只能选择 LXT时钟源,看门狗超时中断唤醒) - 20位递增计数器 - 超时时间2^4~2^20,输入时钟32.768kHz时对应0.48828125 ms ~ 32 s,注意只有8个档位,见TOUTSEL - 复位保持时间 (1 / WDT_CLK) * 63,输入时钟32.768kHz时对应1.922607421875mS - 可配置 延迟1026、130、18、 3 个WDT_CLK之后复位。 - 可配置PG.3拉高即WDTON (SYS_PWRON[3]),默认使能看门狗,此时WDTCKEN只读且为1 ![图片1.png](https://oss-club.rt-thread.org/uploads/20220309/8fc50239b9a31698a0d733cedce442fc.png.webp) ## 配置 - 使能时钟WDTCKEN (CLK_PCLKEN0[0]),如果复位时PG.3拉高则自动使能 - 配置看门狗复位信号是否连接到nRESET引脚,WDTRST (SYS_APBIPRST0[0]) - 时钟 ![图片2.png](https://oss-club.rt-thread.org/uploads/20220309/d41bea6a32741a8a38bb65192c182f19.png) ## 功能 1.使能 WDTEN (WDT_CTL[7])设置为1使能看门狗,计数器开始计数, SYNC (WDT_CTL[30])用于表示是否使能,需要2 * WDT_CLK 个时钟才能使能。 2.设置超时时间 TOUTSEL(WDT_CTL[11:8])分别是 0000 = 24 *TWDT. 0001 = 26 *TWDT. 0010 = 28 *TWDT. 0011 = 210 *TWDT. 0100 = 212 *TWDT. 0101 = 214 *TWDT. 0110 = 216 *TWDT. 0111 = 218 *TWDT. 1000 = 220 *TWDT. 3.中断 超时时IF (WDT_CTL[3]) 置位(不使能中断也会置位),如果 INTEN (WDT_CTL[6])使能中断,则进入中断。 4.延迟复位 当超时设置IF (WDT_CTL[3])后, TRSTD时间后才会复位,在 TRSTD时间前, WDT_RSTCNT[31:0]写0x5AA5可以复位计数器避免复位。 TRSTD由RSTDSEL(WDT_ALTCTL [1:0])设置。 00 = WDT Reset Delay Period is 1026 * WDT_CLK. 01 = WDT Reset Delay Period is 130 * WDT_CLK. 10 = WDT Reset Delay Period is 18 * WDT_CLK. 11 = WDT Reset Delay Period is 3 * WDT_CLK. 如果在 TRSTD时间前没有喂狗,且RSTEN (WDT_CTL[1])设置为1则,RSTF (WDT_CTL[2]) 置位进入复位状态。 复位保持TRST时间,即63 WDT个时钟,后执行(0x0000_0000)中断向量处指令。 5.复位原因 复位后RSTF (WDT_CTL[2]) 保持1,软件可以查看RSTF (WDT_CTL[2])知道是否是看门狗复位。 注:为了下次再次判断一般读完后写1清0. 6.仿真 默认仿真时看门狗不计数,可以设置ICEDEBUG (WDT_CTL[31])为1在仿真时看门狗也工作. 7.唤醒 看门狗中断产生时,且WKEN (WDT_CTL[4]) 使能,能从Power-down模式唤醒。 进入休眠前必须使能XTAL_EN (CLK_PMCON[0]),使用外部32k晶振,使能中断。 软件可以通过查询WKF (WDT_CTL[5]) 知道是不是看门狗唤醒。 复位过程如下: ![图片3.png](https://oss-club.rt-thread.org/uploads/20220309/0b6a976509b210776ffdb30c24377ba0.png.webp) ## 寄存器 - 有些寄存器值访问受保护参考 REGWRPROT - WDT_CTL 配置仿真时看门狗是否工作,查看使能状态,配置超时时间,使能看门狗配置, 如果CWDTEN不是111则WDTEN 时钟是1,且不能改为0. 使能中断配置,唤醒标志,唤醒使能配置,中断标志,看门狗复位标志,复位使能。 - WDT_ALTCTL 设置复位延迟时间 - WDT_RSTCNT 写 0x00005AA5喂狗.写需要 2 * WDT_CLK时间生效。 ## 直接操作寄存器编写测试代码 寄存器相关定义在nuc980.h中,包含头文件 `\bsp\nuvoton\libraries\nuc980\Driver\Include\nuc980.h` 代码见main.c ### 测试狗叫是否生效 WDT_USEAPI宏定义为0;注释掉wdt_handler函数中的M32(REG_WDT_RSTCNT) = 0x00005AA5; 取消注释main函数中的M32(REG_WDT_RSTCNT) = 0x00005AA5; 分别设置rt_thread_mdelay(400)和rt_thread_mdelay(600); 看是否分别狗不叫和狗叫。 ### 测试狗超时时间是否准确 WDT_USEAPI宏定义为0; 取消注释掉wdt_handler函数中的M32(REG_WDT_RSTCNT) = 0x00005AA5; 注释main函数中的M32(REG_WDT_RSTCNT) = 0x00005AA5; 查看红色LED灯是否按照亮0.5秒灭0.5周期闪烁。 用示波器或者逻辑分析仪查看红色LED的引脚,查看是否是0.5秒高0.5秒低。 ![image.png](https://oss-club.rt-thread.org/uploads/20220309/c18c95f3419d1e6a6e9fa532229f2864.png) ![微信图片_20220309214644.jpg](https://oss-club.rt-thread.org/uploads/20220309/ac3aeae5224de26236b9212e946e0aee.jpg.webp) [85bd6084ce5c5bed645c3271dbf06e68.mp4](https://oss-club.rt-thread.org/uploads/20220309/a4a108c5cb162a4507d7abb92ec81f83.mp4) ## 使用驱动API测试 WDT_USEAPI宏定义为1; 分别设置`rt_thread_mdelay(900)`和`rt_thread_mdelay(1100)`; 看是否分别狗不叫和狗叫。 ## 看门狗设备驱动代码分析 rt的设备驱动有点类似uboot和linux 的实现方式,分析其实现可以对自己的开发提供一些参考。 代码在 `\bsp\nuvoton\libraries\nuc980\rtt_port\drv_wdt.c` ### 初始化过程 设备驱动函数的入口是rt_hw_wdt_init 这个函数会在板级初始化时会自动调用,怎么实现自动调用的呢,实际上用到了连接脚本或者分散加载机制。 看到有如下语句 `INIT_BOARD_EXPORT(rt_hw_wdt_init);` `INIT_BOARD_EXPORT`宏的定义如下: 在`\rt-thread\include\redef.h`中 `#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")` 再往上看`INIT_EXPORT`宏的定义如下 这里根据编译器内置宏进行了区分,非_MSC_VER定义如下: ![图片4.png](https://oss-club.rt-thread.org/uploads/20220309/3a0624579af2c6034b3becfe38d89e37.png) 默认`RT_DEBUG_INIT`未定义,那么就是 `#define INIT_EXPORT(fn, level) RT_USED const init_fn_t __rt_init_##fn RT_SECTION(".rti_fn." level) = fn` 其中 `typedef int (*init_fn_t)(void)`; `#define RT_USED __attribute__((used))` 说明该段总是保留,避免链接时删除 `#define RT_SECTION(x) __attribute__((section(x))) `表示代码放在段x里 `INIT_BOARD_EXPORT(rt_hw_wdt_init)`; 合起来就是`__attribute__((used)) const init_fn_t __rt_init_rt_hw_wdt_init __attribute__((section(.rti_fn.1))) = rt_hw_wdt_init` 也就是定义了变量`__rt_init_rt_hw_wdt_init` 放在了`.rti_fn.1`段,变量的类型是 `int (*init_fn_t)(void)`型的函数指针,值是`rt_hw_wdt_init`,即该函数的地址。 那么这个变脸就指向了函数`rt_hw_wdt_init`。 查看map文件`\rt-thread\bsp\nuvoton\nk-980iot\Listings\rethread.map` `__rt_init_rti_board_start~__rt_init_rti_board_end` 如下也可以看到变量`__rt_init_rt_hw_wdt_init`确实放在了`.rti_fn.1`段 ![图片5.png](https://oss-club.rt-thread.org/uploads/20220309/625a09c9b263bc94cb8e29cf57613f8c.png) 可以看到`.rti_fn.1`段放了很多的驱动模块,`__rt_init_rti_board_start~__rt_init_rti_board_end`分别对应段的开始和结束地址。 同样的方式`\env\rt-thread\src\components.c` `INIT_EXPORT(rti_board_start, "0.end")`; `INIT_EXPORT(rti_board_end, "1.end")`; 定义了变量`__rt_init_rti_board_start`到`rti_fn.0.end` `__rt_init_rti_board_end`到`rti_fn.1.end` `rt_components_board_init`中 ``` for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++) { (*fn_ptr)(); } ``` 即可遍历`.rti_fn.1`中的函数指针。 整个调用过程如下: ``` $Sub$$main -> rtthread_startup -> rt_hw_board_init -> rt_components_board_init-> 遍历回调段.rti_fn.1中的函数指针 ->rt_hw_wdt_init ``` 最开始的入口点不同环境可能不一样,上面的是MDK的比如IAR就是`__low_level_init,GCC`就是`entry`。 初始化过程和linux uboot等实现方式一样,这样的实现使得驱动解耦,需要对应的驱动只需要编译对应的驱动文件即可,无需其他任何修改。使用也是通过查找驱动再使用的方式,耦合性小。是嵌入式设置可以参考的一种设计模式。 驱动的注册等其他实现查看代码即可不再赘述。 ### 使用 参见`wdt_api_init` `rt_device_find`先查找对应的驱动 `rt_device_open`找到后`open` `rt_device_control`然后配置参数,启动,喂狗也使用该接口。 ## 代码地址 https://gitee.com/qinyunti/nk-980-iot-test.git ## 总结 使用驱动API开发比较方便快捷,为了了解外设模块最好还是直接写寄存器有调试过程才能理解更深刻。 驱动的实现方式也可以参考用到自己的设计中去。 ## 附录主要代码 main.c ``` /**************************************************************************//** * * @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2020-12-12 Wayne First version * ******************************************************************************/ #include
#include
#include "nuc980.h" #include "nu_sys.h" #include
#if defined(RT_USING_PIN) #include
/* defined the LED_R pin: PB13 */ #define LED_R NU_GET_PININDEX(NU_PB, 13) /* defined the LED_Y pin: PB8 */ #define LED_Y NU_GET_PININDEX(NU_PB, 8) #if !defined(BSP_USING_USBH) /* defined the Key1 pin: PE10 */ #define KEY_1 NU_GET_PININDEX(NU_PE, 10) /* defined the Key2 pin: PE12 */ #define KEY_2 NU_GET_PININDEX(NU_PE, 12) static uint32_t u32Key1 = KEY_1; static uint32_t u32Key2 = KEY_2; void nu_button_cb(void *args) { uint32_t u32Key = *((uint32_t *)(args)); switch (u32Key) { case KEY_1: rt_pin_write(LED_Y, PIN_HIGH); break; case KEY_2: rt_pin_write(LED_Y, PIN_LOW); break; } } #endif #endif #define WDT_USEAPI 0 #if WDT_USEAPI static rt_device_t wdt_device = RT_NULL; void wdt_api_init(void) { rt_err_t result; wdt_device = rt_device_find("wdt"); if (!wdt_device) { return; } result = rt_device_open(wdt_device, 0); if (result != RT_EOK) { return; } uint32_t timeout = 1; /* 单位秒 */ 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_device_close(wdt_device); } #else static void wdt_handler(int vector, void *param) { /* 设置IO低 用于测试看门狗超时时间 */ static int flag = 0; if(flag == 0) { flag = 1; rt_pin_write(LED_R, PIN_HIGH); } else { flag = 0; rt_pin_write(LED_R, PIN_LOW); } M32(REG_WDT_CTL) |= (1u<<3) | (1u<<5); M32(REG_WDT_RSTCNT) = 0x00005AA5; /* 喂狗 如果要测不喂狗是否能复位注释掉这句; 保留这句方便用示波器查看引脚波形,查看狗时间,此时要注释掉main中的喂狗 */ } /** * 前置条件外32.768kHz * 超时周期 2^14/32768S = 0.5秒 * 延迟复位时间 1026/32768S = 31.31mS */ void wdt_init(void) { /* CLK_DIVCTL8 WDTSEL[9:8] 00: WDT_SrcCLK = XIN. 01: WDT_SrcCLK = XIN/512. 10: WDT_SrcCLK = PCLK2/4096. 11: WDT_SrcCLK = 32.768 kHz. */ M32(REG_CLK_DIVCTL8) = (M32(REG_CLK_DIVCTL8) & (~(0x03<<8))) | (3u<<8); /* 设置时钟源为32.768 kHz */ M32(REG_CLK_PCLKEN0) |= (1u<<0); /*WDTCKEN[0] 设置为1使能时钟 */ /* 必须保证三条语句连续执行 */ do { M32(REG_SYS_REGWPCTL) = 0x59; /* 解除写保护 */ M32(REG_SYS_REGWPCTL) = 0x16; M32(REG_SYS_REGWPCTL) = 0x88; }while(M32(REG_SYS_REGWPCTL) == 0u); M32(REG_WDT_CTL) &= ~(1u<<7); /* WDTEN[7]=0 先关闭再配置 */ M32(REG_WDT_CTL) &= ~(1u<<31); /* ICEDEBUG[31]: 0仿真时狗有效 1仿真时狗停止*/ /*TOUTSEL[11:8] 0000 = 2^4*TWDT. 0001 = 2^6*TWDT. 0010 = 2^8*TWDT. 0011 = 2^10*TWDT. 0100 = 2^12*TWDT. 0101 = 2^14*TWDT. 0110 = 2^16*TWDT. 0111 = 2^18*TWDT. 1000 = 2^20*TWDT. */ M32(REG_WDT_CTL) = (M32(REG_WDT_CTL) & (~(0x0F<<8))) | (5u<<8); /* 设置超时时间2^14*TWDT */ M32(REG_WDT_CTL) |= 1u<<6; /* INTEN[6]=1 使能中断 0不使能 */ M32(REG_WDT_CTL) |= 1u<<5; /* WKF[5]=1 清除唤醒标志 写1清0 */ M32(REG_WDT_CTL) |= 1u<<4; /* WKEN[4]=1 使能唤醒 */ M32(REG_WDT_CTL) |= 1u<<3; /* IF[3]=1 清除中断标志 写1清0 */ M32(REG_WDT_CTL) |= 1u<<2; /* RSTF[2]=1 清除看门狗复位标志 写1清0 */ M32(REG_WDT_CTL) |= 1u<<1; /* RSTEN[1]=1使能狗叫复位 */ M32(REG_WDT_RSTCNT) = 0x00005AA5; /* 清计数器 */ /* 00 = WDT Reset Delay Period is 1026 * WDT_CLK. 01 = WDT Reset Delay Period is 130 * WDT_CLK. 10 = WDT Reset Delay Period is 18 * WDT_CLK. 11 = WDT Reset Delay Period is 3 * WDT_CLK. */ M32(REG_WDT_ALTCTL) = (M32(REG_WDT_ALTCTL) & (~(0x03<<0))) | (0u<<0); /* 设置延迟复位时间1026*TWDT */ /*注册中断服务函数 */ rt_hw_interrupt_install(IRQ_WDT, wdt_handler, RT_NULL, (char *)"dummy"); rt_hw_interrupt_umask(IRQ_WDT); M32(REG_WDT_CTL) |= (1u<<7); /* WDTEN=1 使能看门狗 */ while(M32(REG_WDT_CTL) & (1u<<30)); /* 等待SYNC变为0, 0表示就绪,1表示使能还未完成 */ M32(REG_SYS_REGWPCTL) = 0x00; /* 写非0x59 0x16 0x88值 写保护 */ } #endif void wdt_init(void); int main(int argc, char **argv) { #if defined(RT_USING_PIN) int counter = 10; /* set LED_R pin mode to output */ rt_pin_mode(LED_R, PIN_MODE_OUTPUT); /* set LED_Y pin mode to output */ rt_pin_mode(LED_Y, PIN_MODE_OUTPUT); #if !defined(BSP_USING_USBH) /* set KEY_1 pin mode to input */ rt_pin_mode(KEY_1, PIN_MODE_INPUT_PULLUP); /* set KEY_2 pin mode to output */ rt_pin_mode(KEY_2, PIN_MODE_INPUT_PULLUP); rt_pin_attach_irq(KEY_1, PIN_IRQ_MODE_FALLING, nu_button_cb, &u32Key1); rt_pin_irq_enable(KEY_1, PIN_IRQ_ENABLE); rt_pin_attach_irq(KEY_2, PIN_IRQ_MODE_FALLING, nu_button_cb, &u32Key2); rt_pin_irq_enable(KEY_2, PIN_IRQ_ENABLE); #endif while (counter--) { rt_pin_write(LED_R, PIN_HIGH); rt_thread_mdelay(100); rt_pin_write(LED_R, PIN_LOW); rt_thread_mdelay(100); } #endif #if WDT_USEAPI wdt_api_init(); #else wdt_init(); #endif while(1) { #if WDT_USEAPI /* 延长该值测试看是否狗叫 对于USP_API=1时设置的狗超时时间是1000mS,这里设置为1100触发狗叫,设置为900不会狗叫 */ rt_thread_mdelay(900); rt_device_control(wdt_device, RT_DEVICE_CTRL_WDT_KEEPALIVE, RT_NULL); #else /* 延长该值测试看是否狗叫 对于USP_API=0时设置的狗超时时间是500mS,这里设置为600触发狗叫,设置为400不会狗叫 测试狗叫时要注释掉wdt_handler里的喂狗 */ rt_thread_mdelay(400); ///M32(REG_WDT_RSTCNT) = 0x00005AA5; /* 喂狗 */ #endif } } ```
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
qinyunti
这家伙很懒,什么也没写!
文章
30
回答
1
被采纳
0
关注TA
发私信
相关文章
1
RT-Thread Studio 开启WDT编译出错
2
stm32g0 设备驱动框架 wdt adc 报错
3
看门狗不能关闭,STOP控制字没有实现
4
使用看门狗时有while(1)线程,程序运行就卡死
5
为什么rt-thread没有看门狗服务?
6
创建开门狗时出现系统卡死
7
[已解决]KEIL debug没法暂停是为什么?
8
看门狗(watchdog)
9
ENV下的NUC980能移动到RT STUDIO上吗?
10
NUC980 生成 UFFS 文件系统镜像
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
freemodbus
PWM
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
a1012112796
10
个答案
1
次被采纳
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
YZRD
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部