CW32F030C8T6 看门狗功能概述
- CW32F030C8T6 是芯源半导体(WCH)推出的 Cortex-M0 内核微控制器,内置独立看门狗(IWDG)和窗口看门狗(WWDG),用于检测和恢复系统异常状态。
一.独立看门狗(IWDG)
1.1功能特性:
- CW32x030 内部集成独立看门狗定时器 (IWDT),使用专门的内部 RC 时钟源 RC10K,可避免运行时受到外部因素 影响。一旦启动 IWDT,用户需要在规定时间间隔内对 IWDT 的计数器进行重载,否则计数器溢出会触发复位或 产生中断信号。IWDT 启动后,可停止计数。可选择在深度休眠模式下 IWDT 保持运行或暂停计数。 专门设置的键值寄存器,可以锁定 IWDT 的关键寄存器,防止寄存器被意外修改。
- 12bit 的向下计数器
- 独立内部低速 RC 振荡器
- 可编程时钟预分频周期
- 溢出可触发中断或复位
- 寄存器保护锁功能
- 深度休眠模式下可暂停计数
1.2功能框图:
1.3工作方式
- 启动 IWDT 的计数器,需要向键值寄存器 IWDT_KR 写入 0xCCCC,计数器开始从 0xFFF 向下计数。 在计数器减到 0 之前,向 IWDT_KR 寄存器写入 0xAAAA,会触发计数器重载,将 ARR 寄存器值加载到计数器。 当 IWDT 计数器值递减到 0,会产生溢出事件,溢出事件可触发 MCU 复位或产生 IWDT 中断信号,同时触发计数 器重载。控制寄存器 IWDT_CR 的 ACTION 和 IE 位域,用于控制看门狗溢出时是否产生中断和复位,如下表所示:
- IWDT 在 MCU 进入深度休眠模式时,可选择暂停 IWDT 计数,从而达到更低的系统整体功耗:控制寄存器 IWDT_CR 的 PAUSE 位域为 0,深度休眠模式时保持 IWDT 定时器运行;为 1 时暂停计数,当 MCU 退出深度休 眠模式时 IWDT 自动恢复计数。
1.4窗口选项
- 向窗口寄存器 IWDT_WINR 写入一个小于重载寄存器 IWDT_ARR 的值,可使 IWDT 工作于窗口看门狗模式。 IWDT_WINR 寄存器的默认值是 0x0FFF,即窗口选项默认是关闭的。 修改 IWDT_WINR 寄存器的值,会触发重载操作,将 ARR 寄存器值加载到计数器。 使用 IWDT 窗口功能,用户应在计数器值小于等于 IWDT_WINR 窗口值,并且递减到 0 之前进行重载操作,以避 免产生复位或看门狗定时器溢出。在看门狗计数器的值大于窗口值时进行重载操作,将触发系统复位。
1.5寄存器锁定功能
- 通过 IWDT 的键值寄存器 IWDT_KR,可配置 IWDT 的重要寄存器 IWDT_CR、IWDT_ARR、IWDT_WINR 为锁定状 态或解除锁定,锁定状态下不可对寄存器进行修改操作。 向 IWDT_KR 寄存器写入 0x5555,解除对 IWDT_CR、IWDT_ARR、IWDT_WINR 寄存器的锁定;向 IWDT_KR 寄 存器写入其他任何值,启动锁定保护。 CW32x030 在上电复位后,IWDT_CR、IWDT_ARR、IWDT_WINR 寄存器默认处于锁定状态,用户需先解除锁定, 才可对其进行修改操作。
1.6启动刷新与停止
- 配置 IWDT_KR 寄存器,可实现 IWDT 的启动、刷新和停止操作:
- 写入 0xCCCC,启动 IWDT。
- 写入 0xAAAA,重载计数器,即刷新 IWDT。
- 顺序写入 0x5A5A、0xA5A5,停止 IWDT。
- 注: 上述操作会同时启动 IWDT 寄存器锁定保护。
1.7状态寄存器 IWDT_SR,指示 IWDT 当前运行状态或寄存器更新状态,
- 如下表所示:
- 为 确 保 对 IWDT 寄 存 器 的 操 作 正 确 性, 用 户 在 更 新 IWDT_WINR、IWDT_ARR、IWDT_CR 之 后, 需 要 检 查 WINRF、ARRF、CRF 标志位是否为 0,以确认操作是否完成。 为确保对 IWDT 的重载操作,用户在进行重载操作后,应当检查 RELOAD 标志位是否为 0。
1.8定时时长设定
- IWDT 的计数时钟源为专用低速时钟 RC10K(时钟频率约为 10kHz,具体请参阅数据手册),通过控制寄存器 IWDT_CR 的 PRS 位域,可对其时钟源 RC10K 信号进行分频,如下表所示:
- 其中,f 为时钟源 RC10K 的频率,PRS 为预分频系数,ARR 为重载值。
- 故,当时钟源 RC10K 的频率为 10000Hz 时,IWDT 的最长和最短定时范围:
- IWDT 最短定时 = ( 4×20 / 10000 )×( 0x000 + 1 ) ≈ 400 μs
- IWDT 最长定时 = ( 4×27 / 10000 )×( 0xFFF + 1 ) ≈ 209.7 s
- 例:当时钟源 RC10K 的频率为 10000Hz 时,设置预分频值为 64,重载值为 512,则: IWDT 定时时长 = ( 4×24 / 10000 )×( 512 + 1 ) = 3.28 s
1.9 配置 IWDT 为独立看门狗
- 步骤 1:设置 SYSCTRL_APBEN1.IWDT 为 1,使能 IWDT 的配置时钟;
- 步骤 2:向 IWDT_KR 寄存器写入 0xCCCC,启动 IWDT; 注意: 需要启动 IWDT 后,才可对相关寄存器进行修改。
- 步骤 3:向 IWDT_KR 寄存器写入 0x5555,解除 IWDT 寄存器锁定功能;
- 步骤 4:配置 IWDT_CR,配置看门狗计数时钟与 RC10K 振荡器的预分频值、溢出后动作、深度休眠模式下是否 自动暂停;
- 步骤 5:配置 IWDT_ARR,配置看门狗的溢出周期;
- 步骤 6:等待 IWDT_SR.ARRF 和 IWDT_SR.CRF 变为 0,等待重载值和 CR 寄存器更新完成;
- 步骤 7:向 IWDT_KR 寄存器写入 0xAAAA,加载 ARR 到 IWDT 计数器。
1.10配置 IWDT 为窗口看门狗
- 步骤 1:设置 SYSCTRL_APBEN1.IWDT 为 1,使能 IWDT 的配置时钟;
- 步骤 2:向 IWDT_KR 寄存器写入 0xCCCC,启动 IWDT; 注意: 需要启动 IWDT 后,才可对相关寄存器进行修改。
- 步骤 3:向 IWDT_KR 寄存器写入 0x5555,解除 IWDT 寄存器锁定功能;
- 步骤 4:配置 IWDT_CR,配置看门狗计数时钟与 RC10K 振荡器的预分频值、溢出后动作、深度休眠模式下是否 自动暂停;
- 步骤 5:配置 IWDT_ARR,配置看门狗的重载值;
- 步骤 6:配置 IWDT_WINR,配置窗口大小,注意 IWDT_WINR 必须小于 IWDT_ARR 重载值;
- 步骤 7:等待 IWDT_SR.ARRF、IWDT_SR.WINRF 和 IWDT_SR.CRF 变为 0,等待重载值、窗口寄存器和 CR 寄存 器更新完成;
- 步骤 8:向 IWDT_KR 寄存器写入 0xAAAA,加载 ARR 到 IWDT 计数器。
1.11 刷新 IWDT(喂狗操作)
- 步骤 1:向 IWDT_KR 寄存器写入 0xAAAA,加载 ARR 到计数器;
- 步骤 2:等待 IWDT_SR.RELOAD 变为 0,等待重载操作完成。
二.独立看门狗寄存器列表
三.独立看门狗中断程序案例
#define IWDT_DURATION 250 // 独立看门狗定时器溢出时间设置为250毫秒
LED_InitTypeDef LED1, LED2; // 定义LED1和LED2的结构体变量
int32_t main(void)
{
IWDT_InitTypeDef IWDT_InitStruct = {0}; // 初始化独立看门狗配置结构体
/* 系统时钟配置 */
RCC_Configuration();
/* GPIO外设配置 */
GPIO_Configuration();
/* 配置SysTick定时器为250ms中断一次 */
SysTick_Config(SystemCoreClock / 1000 * 250);
/* LED1初始化配置 */
LED1.LED_Port = CW_GPIOB;
LED1.LED_Pin = GPIO_PIN_9;
LED_Init(&LED1);
LED1.LED_Off(&LED1);
/* LED2初始化配置 */
LED2.LED_Port = CW_GPIOB;
LED2.LED_Pin = GPIO_PIN_8;
LED_Init(&LED2);
LED2.LED_Off(&LED2);
/* 独立看门狗配置 */
IWDT_InitStruct.IWDT_ITState = ENABLE; // 使能看门狗中断
IWDT_InitStruct.IWDT_OverFlowAction = IWDT_OVERFLOW_ACTION_INT; // 溢出后产生中断而不复位系统
IWDT_InitStruct.IWDT_Pause = IWDT_SLEEP_PAUSE; // 睡眠模式下暂停看门狗
IWDT_InitStruct.IWDT_Prescaler = IWDT_Prescaler_DIV4; // 预分频系数为4
IWDT_InitStruct.IWDT_ReloadValue = (IWDT_FREQ >> 2) / 1000 * 280 - 1; // 设置重载值,实际溢出时间约256ms
IWDT_InitStruct.IWDT_WindowValue = 0xFFF; // 窗口值设置
IWDT_Init(&IWDT_InitStruct); // 初始化看门狗
IWDT_Cmd(); // 使能看门狗
/* NVIC中断控制器配置 */
NVIC_Configuration();
while (1)
{
}
}
/* 系统时钟配置函数 */
void RCC_Configuration(void)
{
/* 0. 启用HSI时钟并进行校准 */
RCC_HSI_Enable(RCC_HSIOSC_DIV6);
/* 1. 配置HCLK和PCLK分频系数 */
RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
/* 2. 使能PLL,将HSI时钟倍频至48MHz */
RCC_PLL_Enable(RCC_PLLSOURCE_HSI, 8000000, 6); // HSI默认频率8MHz
RCC_PLL_OUT();
/* 配置FLASH等待周期 */
__RCC_FLASH_CLK_ENABLE();
FLASH_SetLatency(FLASH_Latency_2); // 当HCLK在24-48MHz时设置为2周期
/* 3. 切换系统时钟源到PLL */
RCC_SysClk_Switch(RCC_SYSCLKSRC_PLL);
RCC_SystemCoreClockUpdate(48000000);
/* 4. 使能外设时钟 */
__RCC_IWDT_CLK_ENABLE(); // 使能独立看门狗时钟
__RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
__RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
}
/* GPIO配置函数 */
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* 配置PA1为下降沿触发的外部中断输入 */
GPIO_InitStruct.IT = GPIO_IT_FALLING;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pins = GPIO_PIN_1;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_Init(CW_GPIOA, &GPIO_InitStruct);
}
/* NVIC中断配置函数 */
void NVIC_Configuration(void)
{
__disable_irq(); // 全局禁用中断
NVIC_EnableIRQ(WDT_IRQn); // 使能看门狗中断
NVIC_SetPriority(GPIOA_IRQn, 0); // 设置GPIOA中断优先级为最高
NVIC_SetPriority(SysTick_IRQn, 1); // 设置SysTick中断优先级
NVIC_EnableIRQ(GPIOA_IRQn); // 使能GPIOA中断
__enable_irq(); // 全局使能中断
}
/* GPIOA中断回调函数 */
void GPIOA_IRQHandlerCallback(void)
{
// 空实现,可根据需要添加按键中断处理逻辑
}
/* SysTick中断回调函数 */
void SysTick_HandlerCallback(void)
{
LED1.LED_Toggle(&LED1); // 切换LED1状态
IWDT_Refresh(); // 喂狗操作,防止看门狗复位
}
/* 看门狗中断回调函数 */
void WDT_IRQHandlerCallback(void)
{
if (CW_IWDT->SR & IWDT_SR_OV_Msk) // 检查是否发生看门狗溢出
{
IWDT_ClearOVFlag(); // 清除溢出标志
LED2.LED_Toggle(&LED2); // 切换LED2状态指示溢出事件
}
}
四.独立看门狗复位程序案例
#define IWDT_DURATION 250 // 独立看门狗定时器超时时间设置为250毫秒
LED_InitTypeDef LED1, LED2; // 定义两个LED结构体变量
int32_t main(void)
{
IWDT_InitTypeDef IWDT_InitStruct = {0}; // 初始化独立看门狗配置结构体
/* 系统时钟配置 */
RCC_Configuration();
/* GPIO配置 */
GPIO_Configuration();
// 配置SysTick定时器为250毫秒中断一次
SysTick_Config(SystemCoreClock / 1000 * 250);
// 初始化LED1(GPIOB Pin9)
LED1.LED_Port = CW_GPIOB;
LED1.LED_Pin = GPIO_PIN_9;
LED_Init(&LED1);
LED1.LED_On(&LED1); // 点亮LED1
// 延时循环
for (volatile int32_t i = 5000000; i; i--);
LED1.LED_Off(&LED1); // 关闭LED1
// 初始化LED2(GPIOB Pin8)
LED2.LED_Port = CW_GPIOB;
LED2.LED_Pin = GPIO_PIN_8;
LED_Init(&LED2);
LED2.LED_Off(&LED2); // 初始状态关闭LED2
// 配置独立看门狗
IWDT_InitStruct.IWDT_ITState = DISABLE; // 禁用看门狗中断
IWDT_InitStruct.IWDT_OverFlowAction = IWDT_OVERFLOW_ACTION_RESET; // 溢出时复位系统
IWDT_InitStruct.IWDT_Pause = IWDT_SLEEP_PAUSE; // 睡眠模式下暂停看门狗
IWDT_InitStruct.IWDT_Prescaler = IWDT_Prescaler_DIV128; // 预分频128
IWDT_InitStruct.IWDT_ReloadValue = 800; // 重装载值(10秒超时)
IWDT_InitStruct.IWDT_WindowValue = 0xFFF; // 窗口值
IWDT_Init(&IWDT_InitStruct); // 初始化看门狗
IWDT_Cmd(); // 使能独立看门狗
// 等待看门狗运行
while (!CW_IWDT->SR_f.RUN);
IWDT_Refresh(); // 刷新看门狗计数器
/* 中断配置 */
NVIC_Configuration();
// 检测是否由看门狗复位引起
if (RCC_GetRstFlag(RCC_FLAG_IWDTRST))
{
LED2.LED_On(&LED2); // 如果是看门狗复位,点亮LED2
}
while (1) // 主循环
{
}
}
/* 系统时钟配置函数 */
void RCC_Configuration(void)
{
/* 0. 使能HSI时钟并校准 */
RCC_HSI_Enable(RCC_HSIOSC_DIV6);
/* 1. 配置HCLK和PCLK分频系数 */
RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
/* 2. 使能PLL,通过HSI倍频到48MHz */
RCC_PLL_Enable(RCC_PLLSOURCE_HSI, 8000000, 6); // HSI默认8MHz
RCC_PLL_OUT();
// 当HCLK在24M-48MHz时,设置FLASH等待周期为2
__RCC_FLASH_CLK_ENABLE();
FLASH_SetLatency(FLASH_Latency_2);
/* 3. 切换系统时钟到PLL */
RCC_SysClk_Switch(RCC_SYSCLKSRC_PLL);
RCC_SystemCoreClockUpdate(48000000);
/* 4. 外设时钟配置 */
__RCC_IWDT_CLK_ENABLE(); // 使能独立看门狗时钟
__RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
__RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
}
/* GPIO配置函数 */
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* 配置PA1为按键输入(下降沿触发) */
GPIO_InitStruct.IT = GPIO_IT_FALLING; // 下降沿中断
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 输入模式
GPIO_InitStruct.Pins = GPIO_PIN_1; // PA1引脚
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; // 高速
GPIO_Init(CW_GPIOA, &GPIO_InitStruct); // 初始化GPIOA
}
/* 中断配置函数 */
void NVIC_Configuration(void)
{
__disable_irq(); // 禁用所有中断
// 设置中断优先级
NVIC_SetPriority(GPIOA_IRQn, 0); // GPIOA中断最高优先级
NVIC_SetPriority(SysTick_IRQn, 1); // SysTick次高优先级
NVIC_EnableIRQ(GPIOA_IRQn); // 使能GPIOA中断
__enable_irq(); // 启用所有中断
}
/* GPIOA中断回调函数 */
void GPIOA_IRQHandlerCallback(void)
{
LED1.LED_Off(&LED1); // 关闭LED1
}
/* SysTick中断回调函数 */
void SysTick_HandlerCallback(void)
{
// 此处可添加周期性任务
}
/* 看门狗中断回调函数 */
void WDT_IRQHandlerCallback(void)
{
// 看门狗中断处理
}
该代码基于CW32微控制器的系统,主要功能包括:
- 系统时钟配置为48MHz
- 两个LED控制(GPIOB Pin8和Pin9)
- 独立看门狗定时器配置(10秒超时)
- GPIO中断配置(PA1按键输入)
- 系统复位检测功能
独立看门狗用于系统监控,如果10秒内没有刷新,将导致系统复位。复位后可通过LED2状态判断是否为看门狗复位。PA1按键触发中断会关闭LED1。