1.资源的获取:
关注
博主并点赞
对应博客,并在文章下留言
邮箱索取;
2.资源的类型:获取资源仅限于免费公开文章或粉丝免费专享文章,不包括付费文章;
博主分享不易,请给三连
哦(关注 + 点赞 + 收藏),你的鼓励是博主分享的动力。
STM32CubeMX 实战教程:看门狗实验(LL 库)
1 前言
1.1 STM32CubeMX
1.2 教程介绍
STM32CubeMX 实战教程 基于正点原子 STM32F1 精英板(STM32F103ZET6)平台,旨在通过对板载资源的使用将各种外设的配置方法讲述清楚。更多教程请订阅 [专栏]STM32CubeMX 。
本教程是 STM32CubeMX 实战教程中的第五篇,主要通过看门狗实验,将 STM32CubeMX IWDG & WWDG 的配置方法讲述清楚。
1.3 准备工作
系统版本:Windows 10 专业版 64 位
软件版本:STM32CubeMX V6.0.0 ( STM32CubeMX 下载及安装教程 )
硬件平台:正点原子 STM32F1 精英板
编译环境:MDK_ARM V5.29 ( MDK_ARM 下载及安装教程 )
调试工具:ST-LINK/V2
1.4 硬件资源
本节教程使用的硬件资源分配:
序号 | 引脚 | 引脚功能 | 硬件连接 | 备注 |
---|---|---|---|---|
01 | PA0 | Input | 轻触按键(KEY_UP) | 按下高电平 |
02 | PE3 | Input | 轻触按键(KEY1) | 按下低电平 |
03 | PE4 | Input | 轻触按键(KEY0) | 按下低电平 |
04 | PB5 | Output | LED 灯(DS0) | 低电平亮 |
05 | PB8 | Output | 蜂鸣器(BEEP) | 高电平响 |
06 | PE5 | Output | LED 灯(DS1) | 低电平亮 |
2 配置项目
2.1 项目导入
将第三篇的 USART_Test 的项目另存作为 WDG_Test 的项目初始模板。
工程模板修改及项目导入,请参考:STM32CubeMX 实战教程:LED 灯、蜂鸣器、按键输入实验。这里就不重复了,项目目录如下:
2.2 IWDG 配置
- 点击 IWDG,勾选 Activated 使能 IWDG,配置所需的分频系数和计数器重装载值;
参数 | 描述 | 取值范围 | 备注 |
---|---|---|---|
counter clock prescaler | 计数器时钟分频系数 | 4,8,16,32,64,128,256 | - |
down-counter reload value | 向下计数器重装载值 | 0 ~ 4095 | - |
上图配置的 IWDG 超时时间为 0.8s。(以 HSI = 40KHz 计算,实际上 MCU 内部 HSI = 30 ~ 60 KHz)
具体计算方法请参考:附录2 IWDG 超时时间计算
2.3 WWDG & NVIC 配置
- 点击 IWDG,勾选 Activated 使能 WWDG,配置所需的分频系数、上窗口值、计数器重装载值及使能 EWI 中断;
参数 | 描述 | 取值范围 | 备注 |
---|---|---|---|
WWDG counter clock prescaler | 计数器时钟分频系数 | 1,2,4,8 | - |
WWDG window value | (上)窗口值 | 0x40 ~0x7F | - |
WWDG free-running downcounter value | 向下计数器重装载值 | 0x40 ~ 0x7F | - |
Early wakeup interrupt | (EWI)提前唤醒中断 | - | 使能后,向下计数器在 0x40 时触发中断 |
上图配置的 WWDG 相关时间见下表。
参数 | 描述 | 计数器值 | 时间 | 备注 |
---|---|---|---|---|
不允许刷新时间 | 重装载后不允许重装载的时间 | 0x7F ~ 0x5F | 29.127 ms | 在该时间内,如果强行喂狗,MCU 将强制复位 |
允许刷新时间 | 不允许刷新时间后的允许重装载的时间 | 0x5F ~ 0x3F | 29.127 ms | 在该时间内,可以正常喂狗 |
超时时间 | 不允许刷新时间 + 允许刷新时间 | 0x7F ~ 0x3F | 58.254 ms | 在该时间内,如未进行喂狗,MCU 将强制复位 |
EWI 中断时间 | 重装载后(不重装载)触发中断的时间 | 0x7F ~ 0x40 | 57.344 ms | 使能中断后,将在复位前触发中断 |
(PS:重装载 就是我们俗称的 “喂狗”)
具体计算方法请参考:附录3 WWDG 超时时间计算
- 点击 NVIC,使能 Window watchdog 中断,优先级设置为 1,0;
- 点击 Code generation,勾选 Generate IRQ handler 生成中断服务函数,勾选 Call HAL handler;
(关于 Code generation 窗口具体介绍,请参考:STM32CubeMX 实战教程:外部中断实验 > 附录2 NVIC Code generation 配置窗口介绍 )
2.4 Clock 配置
Clock 不需要配置,可直接继续使用。可参考:STM32CubeMX 实战教程:新建项目和生成 MDK_ARM 工程、STM32CubeMX 实战教程:SysTick 实验。
外设 | 描述 | 时钟来源 | 备注 |
---|---|---|---|
IWDG | 独立看门狗 | HSI | HSI 时钟频率在 30KHz ~ 60KHz 范围内波动,一般按 40KHz 计算超时时间 |
WWDG | 窗口看门狗 | PCLK1 | PCLK1 时钟频率最大为 36MHz (STM32F1,其他系列请参考芯片参考手册) |
2.5 生成代码
项目管理配置及生成代码请参考:STM32CubeMX 实战教程:新建项目和生成 MDK_ARM 工程、STM32CubeMX 项目配置窗口介绍(一)、STM32CubeMX 项目配置窗口介绍(二)。
点击 GENERATE CODE 生成代码。
为了方便测试代码的执行,选择每次复位只执行一种看门狗的任务,所以要勾选 Do Not Generate Function Call 不在 main 函数初始化代码中调用看门狗的初始化函数。
3 代码测试
- 打开 MDK_ARM 工程;
- 查看
MX_IWDG_Init()
和MX_WWDG_Init()
函数源码;
- 在 main.h 文件的用户代码区域加入宏定义;
宏定义简单,就不做注释了,代码如下:
/* USER CODE BEGIN Private defines */
#define BEEP_ON LL_GPIO_SetOutputPin(BEEP_GPIO_Port, BEEP_Pin)
#define BEEP_OFF LL_GPIO_ResetOutputPin(BEEP_GPIO_Port, BEEP_Pin)
#define BEEP_TOG LL_GPIO_TogglePin(BEEP_GPIO_Port, BEEP_Pin)
#define DS0_ON LL_GPIO_ResetOutputPin(DS0_GPIO_Port, DS0_Pin)
#define DS0_OFF LL_GPIO_SetOutputPin(DS0_GPIO_Port, DS0_Pin)
#define DS0_TOG LL_GPIO_TogglePin(DS0_GPIO_Port, DS0_Pin)
#define DS1_ON LL_GPIO_ResetOutputPin(DS1_GPIO_Port, DS1_Pin)
#define DS1_OFF LL_GPIO_SetOutputPin(DS1_GPIO_Port, DS1_Pin)
#define DS1_TOG LL_GPIO_TogglePin(DS1_GPIO_Port, DS1_Pin)
#define KEY_UP_Press LL_GPIO_IsInputPinSet(KEY_UP_GPIO_Port, KEY_UP_Pin) == SET
#define KEY_UP_UnPress LL_GPIO_IsInputPinSet(KEY_UP_GPIO_Port, KEY_UP_Pin) == RESET
#define KEY0_Press LL_GPIO_IsInputPinSet(KEY0_GPIO_Port, KEY0_Pin) == RESET
#define KEY0_UnPress LL_GPIO_IsInputPinSet(KEY0_GPIO_Port, KEY0_Pin) == SET
#define KEY1_Press LL_GPIO_IsInputPinSet(KEY1_GPIO_Port, KEY1_Pin) == RESET
#define KEY1_UnPress LL_GPIO_IsInputPinSet(KEY1_GPIO_Port, KEY1_Pin) == SET
/* USER CODE END Private defines */
- 在 stm32f1xx_it.c 文件
WWDG_IRQHandler()
函数的用户代码区域加入中断处理代码;
WWDG_IRQHandler
函数代码如下:
/**
* @brief This function handles Window watchdog interrupt.
*/
void WWDG_IRQHandler(void)
{
/* USER CODE BEGIN WWDG_IRQn 0 */
LL_WWDG_ClearFlag_EWKUP(WWDG); // Clear Flag
// 这里重载计数器值
// 如果没有保护程序, 可以不重载计数器值, 让 MCU 直接自动复位.
// 如果 有保护程序, 必须 重载计数器值, 否则没有足够的时间执行保护程序.
LL_WWDG_SetCounter(WWDG, 0x7F); // Set Counter
// 这里可以添加用户的保护程序, 如参数保存,执行机构失控保护等.
// 用户保护程序执行完成后, 需用户自行软件复位 MCU.
// 为了体现测试效果, 用蜂鸣器的开/关来代替保护程序.
{
BEEP_TOG;
LL_mDelay(30); // 加入 30ms 延时, 避免 main -> while 函数中重载计数器函数
// 在上窗口值以上 (0x7F - 0x5F) 进行重载时而导致的复位.
BEEP_TOG;
// NVIC_SystemReset(); // 软件复位, 这里为了体现测试效果,不进行复位
}
/* USER CODE END WWDG_IRQn 0 */
/* USER CODE BEGIN WWDG_IRQn 1 */
/* USER CODE END WWDG_IRQn 1 */
}
- 在
main
函数中加入看门狗初始化函数及模拟任务代码;
main
函数代码如下:
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
uint8_t iwdg_flag, wwdg_flag;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_AFIO);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
/* System interrupt init*/
/** NONJTRST: Full SWJ (JTAG-DP + SW-DP) but without NJTRST
*/
LL_GPIO_AF_Remap_SWJ_NONJTRST();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
SysTick->CTRL = SysTick_CTRL_ENABLE_Msk | // SysTick Timer clock source = HCLK_DIV8 (HCLK/8 = 9MHz)
SysTick_CTRL_TICKINT_Msk; // SysTick Timer ENABLE | SysTick Timer Interrupt ENABLE
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
LL_RCC_GetSystemClocksFreq(&get_rcc_clock);
LL_GPIO_SetOutputPin(DS0_GPIO_Port, DS0_Pin);
LL_GPIO_SetOutputPin(DS1_GPIO_Port, DS1_Pin);
LL_USART_EnableIT_RXNE(USART1); // ENABLE USART1 RXNE Interrupt
LL_USART_EnableIT_IDLE(USART1); // ENABLE USART1 IDLE Interrupt
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
BEEP_ON;
LL_mDelay(300);
BEEP_OFF;
iwdg_flag = 0;
wwdg_flag = 0;
while(KEY0_UnPress && KEY1_UnPress)
{
DS0_ON;
DS1_ON;
}
DS0_OFF;
DS1_OFF;
if(KEY0_Press)
{
MX_IWDG_Init();
iwdg_flag = 1;
wwdg_flag = 0;
}
else if(KEY1_Press)
{
MX_WWDG_Init();
wwdg_flag = 1;
iwdg_flag = 0;
}
while (1)
{
// IWDG Test Task
if(iwdg_flag == 1)
{
if(KEY_UP_UnPress)
{
// 0.5s, 按时喂狗
LL_mDelay(500);
LL_IWDG_ReloadCounter(IWDG);
DS0_TOG;
}
else
{
// 1.5s, 超时喂狗.
LL_mDelay(500);
LL_mDelay(500);
LL_mDelay(500);
LL_IWDG_ReloadCounter(IWDG);
}
}
// WWDG Test Task
if(wwdg_flag == 1)
{
static uint8_t err_run = 0;
if(KEY_UP_UnPress)
{
// err_run = 0, 40ms, 正常喂狗.
// err_run = 1, 20ms, 提前喂狗, 会导致 MCU 复位.
if(err_run == 1) // 程序运行逻辑出错,比预计运行时间少
{
LL_mDelay(20);
}
else if(err_run == 0) // 程序运行正常,需要 40ms
{
LL_mDelay(20);
LL_mDelay(20);
}
LL_WWDG_SetCounter(WWDG, 0x7F);
DS1_TOG;
}
else
{
// 80ms, 超时, 会触发 EWI 提前唤醒中断.
DS1_OFF;
LL_mDelay(80); // 程序运行逻辑死机,比预计运行时间长
}
if(KEY0_UnPress)
{
err_run = 0; // 程序运行错误标志清除
}
else
{
err_run = 1; // 程序运行错误标志使能
}
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
- 编译工程;
- 使用 ST-LINK/V2 下载程序,按下复位按键,若按照下面操作看到正常现象,则说明程序正常运行,看门狗配置成功。
Steps | 操作 | 正常现象 | 备注 |
---|---|---|---|
1 | 按下 复位 按键,松开 | 蜂鸣器响 300ms 后,DS0 和 DS1 亮 | - |
2 | 按下 KEY0 按键,松开 | DS0 和 DS1 灭,IWDG 开始初始化,进入 IWDG Test Task | - |
3 | 按下 KEY1 按键,松开 | DS0 和 DS1 灭,WWDG 开始初始化,进入 WWDG Test Task | - |
Steps | 操作 | 正常现象 | 备注 |
---|---|---|---|
1 | 无操作 | 程序正常喂狗,DS0 每 500ms 电平翻转一次 | DS1 默认不亮 |
2 | 按下 KEY_UP 按键,不动 | 程序超时喂狗,0.8s 后 MCU 强制 复位 | - |
Steps | 操作 | 正常现象 | 备注 |
---|---|---|---|
1 | 无操作 | 程序正常(40ms > 29ms)喂狗,DS1 每 40ms 电平翻转一次 | DS0 默认不亮 |
2 | 按下 KEY_UP 按键,不动 | 程序超时喂狗,58ms 后进入 EWI 中断,蜂鸣器每 58ms 响 30ms | 中断中 DS1 不亮 |
3 | 松开 KEY_UP 按键 | 程序继续正常(40ms > 29ms)喂狗,DS1 每 40ms 电平翻转一次 | - |
4 | 按下 KEY0 按键 | 程序提前喂狗(20ms < 29ms),MCU 在喂狗后强制 复位 | - |
4 结束
本教程由 Brendon Tan 原创发布,版权所有。该文档仅供个人学习交流使用,不得用于其他用途, 禁止商用, 转载或公开使用请联系作者授权。
此教程由本人独立整理,如有不当之处,欢迎指正。
附录
附录1 相关教程
STM32Cube 系列软件教程总目录请参考文章:STM32Cube 生态系统之网站、视频、文档及教程汇总 。
该文章还提供:
- STM32Cube 生态系统相关文章
- STM32Cube 生态系统相关软件更新介绍
- STM32Cube 生态系统相关教程汇总
- STM32Cube 生态系统相关网站汇总
- 该文章会不定期修改更新,更多信息请进入文章查看
附录2 IWDG 超时时间计算
查询参考手册:
参考手册 Table 96 给出了基于 HSI=40KHz (HSI = 30 ~60KHz)的各分频系数下的最大或最小超时时间,我们可以根据自己的配置进行计算。
例如,
本实验 IWDG 的配置参数为:分频系数 64,重装载值 500,则计算如下:
我们也可以自己根据频率和分频系数进行计算:
设 HSI = 40KHz,分频系数 64,计数器重装载值 500。
则 IWDG 的时钟周期为 40KHz/64 = 0.625KHz
;
所以 IWDG 计数器计数1(减 1)所需的时间为 1/0.625KHz = 1.6ms
;
因此 IWDG 超时时间为 1.6ms x 500 = 800ms
。
附录3 WWDG 超时时间计算
查询参考手册:
根据上图可知:
- WWDG 的时钟来源是 PCLK1,并有一个预分频系数 4096,即时钟源为 PCLK1/4096;
- 只有当计数器值小于窗口值(上窗口值,下窗口值固定为 0x3F,可看下一张图片理解)时,才能进行喂狗操作,否则 MCU 将会强制复位;(本实验中有相关测试程序)
- 系统复位后,WWDG 处于关闭状态,若 WWDG 被开启,
在下一次复位之前,WWDG 不能通过软件关闭
。
参考手册 Table 98 给出了基于 PCLK1=36MHz (STM32F1 中,PCLK1 最大为 36MHz)的各分频系数下的最大或最小超时时间,我们可以根据自己的配置进行计算。
例如,
本实验 IWDG 的配置参数为:分频系数 8,重装载值 0x7F(127),窗口值为 0x5F(95),下窗口值固定为0x3F(63),则计算如下:
我们也可以自己根据频率和分频系数进行计算:
设 PCLK1 = 36MHz,分频系数 8,计数器重装载值 0x7F(127),窗口值为 0x5F(95),下窗口值固定为0x3F(63);
则 WWDG 的时钟周期为 36000KHz/4096/8 = 1.099KHz
;(4096 是内部固定预分频系数)
所以 WWDG 计数器计数1(减 1)所需的时间为 1/1.099KHz = 0.910ms
;
因此 WWDG 超时时间为 (127-63) x 0.91ms = 58.24ms
。(如果上面的式子用分式表示,最后再计算,得出的时间应该是 58.25ms)