# 14.01_IWDG-看门狗复位时间

## 测试步骤

1. 配置看门狗2S进行复位，P0.6作为指示IO。
2. 初始化完成之后0.6输出低电平
3. no_feed_dog_flg 等于0时正常喂狗，等于1时将0.6设置为高电平，并停止喂狗。
4. feed_dog_time_mS 不等于-1时会更新喂狗时间为feed_dog_time_mS毫秒
5. 给P0.6接一个下拉(芯片复位后所有IO都是低电平，看门狗的复位到GPIO初始化需要大约2mS)。
6. 读取P0.6的高电平时间即可确认实际看门狗复位时间。

## 实测数据

看门狗最小复位时间为8mS，最大复位时间为64S。
误差主要由量化误差和LRC时钟本身的误差决定。

## 参考代码

14.01_IWDG_Test.c

    /**
     * @brief IWDG-看门狗复位时间
     * @details
     * 1. 配置看门狗2S进行复位，P0.6作为指示IO。
     * 2. 初始化完成之后0.6输出低电平
     * 3. no_feed_dog_flg 等于0时正常喂狗，等于1时将0.6设置为高电平，并停止喂狗。
     * 4. feed_dog_time_mS 不等于-1时会更新喂狗时间为feed_dog_time_mS毫秒
     * 5. 给P0.6接一个下拉(芯片复位后所有IO都是低电平，看门狗的复位到GPIO初始化需要大约2mS)。
     * 6. 读取P0.6的高电平时间即可确认实际看门狗复位时间。
     */
    #include "lks32mc09x_gpio.h"
    #include "lks32mc09x_iwdg.h"
    volatile int32_t no_feed_dog_flg = 0;
    volatile int32_t feed_dog_time_mS = -1;

    /**
     * @brief 主函数
     */
    int main(void)
    {
        // IWDG初始化结构体
        IWDG_InitTypeDef IWDG_InitStruct;

        // 硬件初始化
        // 初始化GPIO
        GPIO_ResetBits(GPIO0, GPIO_Pin_6);
        GPIO_Config(GPIO0, GPIO_PinSource_6, GPIO_Mode_OUT, GPIO_AF_GPIO);

        // 初始化IWDG
        IWDG_StrutInit(&IWDG_InitStruct);
        IWDG_InitStruct.WDG_EN = 1;
        IWDG_InitStruct.WTH = IWDG_SECOND2IWDGCNT(2); // 设置看门狗定时唤醒门限值为2秒
        IWDG_Init(&IWDG_InitStruct);
        IWDG_ENABLE(); // 使能看门狗

        while (1)
        {
            if (feed_dog_time_mS != -1)
            {
                IWDG_Update(IWDG_SECOND2IWDGCNT((float)feed_dog_time_mS / 1000.0f));
                feed_dog_time_mS = -1;
            }
            if (no_feed_dog_flg == 0)
            {
                IWDG_Feed();
            }
            else
            {
                GPIO_SetBits(GPIO0, GPIO_Pin_6);
            }
        }
    }

## 使用到的库函数

库函数部分代码

    #define GPIO_AF_I2C 6
    #define IWDG IWDG0
    #define IWDG_SECOND2IWDGCNT(x) IWDG_SECOND2IWDGCNT_SAT((int32_t)(32000 * (float)x + 0x40), 0x100, 0x1fff00)
    #define REG_RESET(reg,mask) reg &= ~(mask)
    #define REG_SET(reg,mask) reg |= (mask)
    #define REG_WRITE(reg,mask) reg = (mask)
    #define SYS0 ((SYS_TypeDef *)(SYS_BASE))
    #define SYS_MODULE_GPIO BIT11
    /**
     * @brief GPIO功能配置结构体句柄
     */
    typedef struct GPIO_InitTypeDef {
        uint32_t GPIO_Pin;
        GPIO_Mode_TypeDef GPIO_Mode;
        GPIO_PuPd_TypeDef GPIO_PuPd;
        uint32_t GPIO_PODEna;
        uint32_t GPIO_PFLT;
    };
    /**
     * @brief GPIO模式选择枚举
     */
    typedef enum GPIO_Mode_TypeDef {
        GPIO_Mode_IN = 0,
        GPIO_Mode_OUT = 1,
        GPIO_Mode_ANA = 2,
        GPIO_Mode_IO = 3,
    }} {enum_name};
    /**
     * @brief IWDG初始化结构体句柄
     */
    typedef struct IWDG_InitTypeDef {
        uint32_t DWK_EN;
        uint32_t WDG_EN;
        uint32_t WTH;
        uint32_t RTH;
    };
    /**
     * @brief  GPIO配置函数
     * @param  GPIOx: GPIO端口
     * @param  GPIO_PinSource: GPIO引脚
     * @param  mode: GPIO模式
     * @param  GPIO_AF_x: GPIO复用功能选择
     */
    void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
    {
        GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
        GPIO_InitTypeDef GPIO_InitStructure;
        GPIO_StructInit(&GPIO_InitStructure);
        GPIO_InitStructure.GPIO_Pin  = 1 << GPIO_PinSource;
        GPIO_InitStructure.GPIO_Mode = mode;
        if (GPIO_AF_x == GPIO_AF_I2C)
        {
            GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
        }
        else
        {
            __NOP();
        }
        GPIO_Init(GPIOx, &GPIO_InitStructure);
    }
    /**
     * @brief IWDG初始化
     * @param IWDG_InitStruct 指向IWDG初始化结构体的指针
     */
    void IWDG_Init(IWDG_InitTypeDef *IWDG_InitStruct)
    {
        IWDG_InitStruct->RTH = (IWDG_InitStruct->RTH > BIT21) ? BIT21 - 1 : IWDG_InitStruct->RTH;
        IWDG_InitStruct->WTH = (IWDG_InitStruct->WTH > BIT21) ? BIT21 - 1 : IWDG_InitStruct->WTH;
        IWDG->CFG            = (IWDG_InitStruct->DWK_EN << 4) | (IWDG_InitStruct->WDG_EN);
        IWDG->PSW            = PSW_IWDG_PRE;
        IWDG->RTH            = IWDG_InitStruct->RTH;
        if (IWDG_InitStruct->RTH > IWDG_InitStruct->WTH)
        {
            IWDG->WTH = IWDG_InitStruct->RTH - IWDG_InitStruct->WTH;
        }
        else
        {
            IWDG->WTH = 0x001000;
        }
        IWDG->PSW = PSW_IWDG_PRE;
        IWDG->CLR = PSW_IWDG_CLR;
    }
    /**
     * @brief IWDG配置结构体初始化
     * @param IWDG_InitStruct 指向IWDG初始化结构体的指针
     */
    void IWDG_StrutInit(IWDG_InitTypeDef *IWDG_InitStruct)
    {
        IWDG_InitStruct->DWK_EN = DISABLE;                // 深度休眠定时唤醒使能
        IWDG_InitStruct->WDG_EN = DISABLE;                // 独立看门狗使能
        IWDG_InitStruct->WTH    = IWDG_SECOND2IWDGCNT(1); // 看门狗定时唤醒时间,大于复位时间后无效
        IWDG_InitStruct->RTH    = IWDG_SECOND2IWDGCNT(2); // 看门狗超时复位时间（21位计数器，但低12恒位0）
                                                          // SECOND2IWDGCNT输入参数，秒
    }
    /**
     * @brief 使能看门狗
     */
    void IWDG_ENABLE(void)
    {
        IWDG->CFG = 0x1;
    }
    /**
     * @brief 初始化GPIO
     * @param GPIOx GPIO模块指针
     * @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
     */
    void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
    {
        // 使能GPIO时钟
        SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);

        // 配置引脚模式
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
        {
            GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin;  // 打开输入使能
            GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
            GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
        }
        else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
        {
            GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
            GPIOx->POE |= GPIO_InitStruct->GPIO_Pin;  // 使能输出
        }
        else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
        {
            GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
            GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
        }
        else // GPIO_Mode_ANA
        {
            GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
            GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
            GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
        }

        if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
        {
            GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
        }
        else
        {
            GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
        }
        // 配置开漏使能
        if (GPIO_InitStruct->GPIO_PODEna)
        {
            GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
        }
        else
        {
            GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
        }

        // 配置滤波使能
        if (GPIO_InitStruct->GPIO_PFLT)
        {
            GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
        }
        else
        {
            GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
        }
    }
    /**
     * @brief 初始化GPIO结构体为默认值
     * @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
     */
    void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
    {
        for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
        {
            ((uint8_t *)GPIO_InitStruct)[i] = 0;
        }
    }
    /**
     * @brief 喂狗，重置看门狗计数器
     */
    void IWDG_Feed(void)
    {
        IWDG->PSW = PSW_IWDG_PRE;
        IWDG->CLR = PSW_IWDG_CLR;
    }
    /**
     * @brief 复位指定的GPIO引脚位
     * @param GPIOx GPIO模块指针
     * @param GPIO_Pin 要复位的引脚
     */
    void GPIO_ResetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    {
        GPIOx->PDO &= ~GPIO_Pin;
    }
    /**
     * @brief 数字模块时钟使能
     * @param nModule 模块编号
     * @param state 使能或禁用状态
     */
    void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
    {
        REG_WRITE(SYS0->PROTECT, 0x7a83);
        if (state)
        {
            REG_SET(SYS0->CLK_FEN, nModule);
        }
        else
        {
            REG_RESET(SYS0->CLK_FEN, nModule);
        }
        REG_WRITE(SYS0->PROTECT, 0);
    }
    /**
     * @brief 设置指定的GPIO引脚位
     * @param GPIOx GPIO模块指针
     * @param GPIO_Pin 要设置的引脚
     */
    void GPIO_SetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    {
        GPIOx->PDO |= GPIO_Pin;
    }
    /**
     * @brief 设置超时复位时间
     * @param rth 超时复位时间，单位1/64k 秒，建议用 SECOND2IWDGCNT 宏将秒转换
     */
    void IWDG_Update(uint32_t rth)
    {
        IWDG->PSW = PSW_IWDG_PRE;
        IWDG->RTH = rth;
        IWDG->CLR = PSW_IWDG_CLR;
    }
    /**
     * @brief 配置GPIO引脚复用功能
     * @param GPIOx GPIO模块指针
     * @param GPIO_PinSource 引脚源
     * @param GPIO_AF 复用功能选择
     */
    void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
    {
        uint8_t offset;
        uint8_t pins = GPIO_PinSource >> 2;
        offset       = ((GPIO_PinSource & 0x3) * 4);
        switch (pins)
        {
            case 0:
                GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
                break;
            case 1:
                GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
                break;
            case 2:
                GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
                break;
            case 3:
                GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
                break;
            default:
                break;
        }
    }