国产MCU学习Day4——CW32F030C8T6:独立看门狗功能全解析

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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值