# 20.02_TIMER-1mS定时器

## 测试步骤

1. 初始化timer0，设置timer时钟为2分频（主频96M，分频后48M），周期为1ms
2. 在timer中断里翻转P0.0
3. 不配置timer分频的情况下，最大计数时间为0.682mS，大于该时间后需要设置时钟分频

## 实测数据

实测中断间隔时间为1.001958mS

## 参考代码

20.02_TIMER.c

    /**
     * @brief TIMER-1mS定时器
     * @details
     * 1. 初始化timer0，设置timer时钟为2分频（主频96M，分频后48M），周期为1ms
     * 2. 在timer中断里翻转P0.0
     * 3. 不配置timer分频的情况下，最大计数时间为0.682mS，大于该时间后需要设置时钟分频
     */
    #include "lks32mc09x_gpio.h"
    #include "lks32mc09x_sys.h"
    #include "lks32mc09x_timer.h"
    #include "lks32mc09x_user_manual.h"

    void Timer0_IRQHandler(void);

    int main(void)
    {
        GPIO_Config(GPIO0, GPIO_PinSource_0, GPIO_Mode_OUT, GPIO_AF_GPIO);

        // 初始化TIMER0
        TIMER_TimerInitTypeDef TIMER_InitStruct;
        TIMER_StructInit(&TIMER_InitStruct);

        // 配置TIMER0为比较模式，输出PWM
        TIMER_InitStruct.EN = ENABLE;                      ///< 定时器使能，ENABLE使能，DISABLE失能，影响timer的cnt是否计数
        TIMER_InitStruct.CAP1_CLR_EN = 0;                  ///< 当发生CAP1捕获事件时，清零 Timer 计数器，高有效
        TIMER_InitStruct.CAP0_CLR_EN = 0;                  ///< 当发生CAP0捕获事件时，清零 Timer 计数器，高有效
        TIMER_InitStruct.ONE_TRIG = 0;                     ///< 单次发送模式，此位需要在Timer比较模式下使用，且对应定时器使能需设置为0
        TIMER_InitStruct.CENTER = 0;                       ///< 中心计数模式使能,0:Timer向上从0计数至TH，然后回0，或Timer向下从TH计数至0，然后回到TH，1:Timer向上从0计数至TH，然后向下计数至0
        TIMER_InitStruct.DIR = 0;                          ///< 0:0->TH 递增计数，1：TH->递减计数
        TIMER_InitStruct.ClockDiv = 1;                     ///< Timer计数器频率配置,0:1分频,1:2分频,2:4分频,3:8分频,4:16分频,5:32分频,6:64分频,7:128分频
        TIMER_InitStruct.ETON = 0;                         ///< Timer计数器计数外部启动使能,0: 自动运行,1:外部事件触发计数
        TIMER_InitStruct.GATE_EN = 0;                      ///< Timer暂停使能，0：不暂停 1:当外部信号为低时，Timer暂停计数，外部信号根据TIMER1_EVT.EVT_SRC 进行选择
        TIMER_InitStruct.RL_EN = 0;                        ///< Timer重装使能,0：禁用外部事件重装，1：使能外部事件重装
        TIMER_InitStruct.XCLK_EN = 0;                      ///< Timer时钟源,0: 芯片内部时钟,1:外部时钟
        TIMER_InitStruct.SRC1 = 0;                         ///< Timer 捕获模式通道1信号来源,0:Timer通道0输入信号,1:Timer通道1输入信号,2:CLU0输出信号,3:CLU1输出信号,4:CLU2输出信号,5:CLU3输出信号,6:比较器0输出信号,7:比较器1输出信号,8:比较器2输出信号,9:Timer通道0和1的异或信号
        TIMER_InitStruct.CH1Output = 0;                    ///< Timer 通道1在比较模式下的输出极性控制，当计数器 CNT

## 使用到的库函数

库函数部分代码

    #define GPIO_AF_I2C 6
    #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_CLK_CFG_CLK_DIV_MASK (uint32_t)(0xFF << SYS_CLK_CFG_CLK_DIV_POS)
    #define SYS_CLK_CFG_CLK_DIV_POS 0
    #define SYS_CLK_CFG_CLK_SEL_MASK (uint32_t)(0x3 << SYS_CLK_CFG_CLK_SEL_POS)
    #define SYS_CLK_CFG_CLK_SEL_POS 8
    #define SYS_MODULE_GPIO BIT11
    #define SYS_MODULE_TIMER0 BIT4
    #define SYS_MODULE_TIMER1 BIT5
    #define SYS_MODULE_TIMER2 BIT6
    #define TIMER0 ((TIMER_TypeDef *)TIMER0_BASE)
    #define TIMER1 ((TIMER_TypeDef *)TIMER1_BASE)
    #define TIMER2 ((TIMER_TypeDef *)TIMER2_BASE)
    /**
     * @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 Timer初始化结构体
     */
    typedef struct TIMER_TimerInitTypeDef {
        uint8_t EN;
        uint32_t CAP1_CLR_EN;
        uint32_t CAP0_CLR_EN;
        uint32_t ONE_TRIG;
        uint32_t CENTER;
        uint32_t DIR;
        uint32_t ClockDiv;
        uint32_t ETON;
        uint32_t GATE_EN;
        uint32_t RL_EN;
        uint32_t XCLK_EN;
        uint32_t SRC1;
        uint32_t CH1Output;
        uint32_t CH1_WorkMode;
        uint32_t CH1_FE_CAP_EN;
        uint32_t CH1_RE_CAP_EN;
        uint32_t SRC0;
        uint32_t CH0Output;
        uint32_t CH0_WorkMode;
        uint32_t CH0_FE_CAP_EN;
        uint32_t CH0_RE_CAP_EN;
        uint32_t TH;
        uint32_t CNT;
        uint32_t CHN0;
        uint32_t CHN1;
        uint32_t EVT;
        uint32_t FLT;
        uint32_t IE;
        uint32_t CMP0_TRIGGER_MODE;
        uint32_t CMP1_TRIGGER_MODE;
        uint32_t SHADOW;
        uint32_t CH1_DEFAULT;
        uint32_t CH0_DEFAULT;
        uint32_t HALT_PRT;
        uint32_t FAIL_SEL;
        uint32_t FAIL_POL;
        uint32_t FAIL_EN;
        uint32_t MOE;
    };
    /**
     * @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 Timer初始化
     * @param TIMERx Timer实例指针
     * @param TIMER_InitStruct Timer初始化结构体指针
     */
    void TIMER_Init(TIMER_TypeDef *TIMERx, TIMER_TimerInitTypeDef *TIMER_InitStruct)
    {
        uint32_t th = TIMER_InitStruct->TH;
        TIMER_Enable(TIMERx);
        if (TIMERx != TIMER2) // 只有timer2是32位定时器
        {
            if (th > 0xffff)
            {
                th = 0xffff;
            }
            else
            {
                __NOP();
            }
        }
        else
        {
            __NOP();
        }
        // 配置Timer寄存器
        TIMERx->CFG2 = (TIMER_InitStruct->EN << 15) |
                       (TIMER_InitStruct->ONE_TRIG << 9) |
                       (TIMER_InitStruct->CENTER << 8) |
                       (TIMER_InitStruct->DIR << 6) |
                       (TIMER_InitStruct->ClockDiv << 4) |
                       (TIMER_InitStruct->ETON << 3) |
                       (TIMER_InitStruct->GATE_EN << 2) |
                       (TIMER_InitStruct->RL_EN << 1) |
                       TIMER_InitStruct->XCLK_EN;

        TIMERx->CFG0 = (TIMER_InitStruct->CMP0_TRIGGER_MODE << 9) |
                       (TIMER_InitStruct->CAP0_CLR_EN << 8) |
                       (TIMER_InitStruct->SRC0 << 4) |
                       (TIMER_InitStruct->CH0Output << 3) |
                       (TIMER_InitStruct->CH0_WorkMode << 2) |
                       (TIMER_InitStruct->CH0_FE_CAP_EN << 1) |
                       (TIMER_InitStruct->CH0_RE_CAP_EN << 0);

        TIMERx->CFG1 = (TIMER_InitStruct->CMP1_TRIGGER_MODE << 9) |
                       (TIMER_InitStruct->CAP1_CLR_EN << 8) |
                       (TIMER_InitStruct->SRC1 << 4) |
                       (TIMER_InitStruct->CH1Output << 3) |
                       (TIMER_InitStruct->CH1_WorkMode << 2) |
                       (TIMER_InitStruct->CH1_FE_CAP_EN << 1) |
                       (TIMER_InitStruct->CH1_RE_CAP_EN << 0);

        TIMERx->CHN0 = TIMER_InitStruct->CHN0;
        TIMERx->CHN1 = TIMER_InitStruct->CHN1;
        TIMERx->TH   = th;
        TIMERx->CNT  = TIMER_InitStruct->CNT;
        TIMERx->EVT  = TIMER_InitStruct->EVT;
        TIMERx->FLT  = TIMER_InitStruct->FLT;
        TIMERx->IE   = TIMER_InitStruct->IE;

        TIMERx->IO = (TIMER_InitStruct->HALT_PRT << 7) |
                     (TIMER_InitStruct->CH0_DEFAULT << 8) |
                     (TIMER_InitStruct->CH1_DEFAULT << 9) |
                     (TIMER_InitStruct->MOE << 6) |
                     (TIMER_InitStruct->FAIL_SEL << 2) |
                     (TIMER_InitStruct->FAIL_POL << 1) |
                     (TIMER_InitStruct->FAIL_EN << 0);

        // 影子寄存器配置
        TIMERx->CFG2 |= (TIMER_InitStruct->SHADOW << 10);
    }
    /**
     * @brief Timer结构体初始化
     * @param TIMER_InitStruct Timer初始化结构体指针
     */
    void TIMER_StructInit(TIMER_TimerInitTypeDef *TIMER_InitStruct)
    {
        for (int i = 0; i < sizeof(TIMER_TimerInitTypeDef) / sizeof(uint32_t); i++)
        {
            ((uint32_t *)TIMER_InitStruct)[i] = 0;
        }
    }
    /**
     * @brief 使能Timer
     * @param TIMERx Timer实例指针
     */
    void TIMER_Enable(TIMER_TypeDef *TIMERx)
    {
        if (TIMERx == TIMER0)
        {
            SYS_ModuleClockCmd(SYS_MODULE_TIMER0, ENABLE);
        }
        else if (TIMERx == TIMER1)
        {
            SYS_ModuleClockCmd(SYS_MODULE_TIMER1, ENABLE);
        }
        else if (TIMERx == TIMER2)
        {
            SYS_ModuleClockCmd(SYS_MODULE_TIMER2, ENABLE);
        }
        else
        {
            __NOP();
        }
    }
    /**
     * @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 数字模块时钟使能
     * @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 清除Timer中断标志
     * @param TIMERx Timer实例指针
     * @param tempFlag 要清除的中断标志
     */
    void TIMER_ClearIRQFlag(TIMER_TypeDef *TIMERx, uint32_t tempFlag)
    {
        TIMERx->IF = tempFlag;
    }
    /**
     * @brief 翻转指定的GPIO引脚位
     * @param GPIOx GPIO模块指针
     * @param GPIO_Pin 要翻转的引脚
     */
    void GPIO_ToggleBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    {
        GPIOx->PDO = GPIOx->PDO ^ GPIO_Pin;
    }
    /**
     * @brief 获取当前MCU时钟
     * @return 当前MCU时钟频率
     */
    uint32_t SYS_ReadMcuClk(void)
    {
        uint32_t clk    = 96000000;
        uint32_t clkdiv = (SYS_CLK_CFG & SYS_CLK_CFG_CLK_DIV_MASK) >> SYS_CLK_CFG_CLK_DIV_POS;
        uint32_t clksel = (SYS_CLK_CFG & SYS_CLK_CFG_CLK_SEL_MASK) >> SYS_CLK_CFG_CLK_SEL_POS;
        switch (clksel) //  0: HRC 1: PLL 2: LRC
        {
            case 0: // HRC
                clk = 8000000;
                break;
            case 1: // PLL
                switch (clkdiv)
                {
                    case 0xff: // 96M
                        clk = 96000000;
                        break;
                    case 0x55: // 48M
                        clk = 48000000;
                        break;
                    case 0x11: // 24M
                        clk = 24000000;
                        break;
                    case 0x01: // 12M
                        clk = 12000000;
                        break;
                    case 0x00: // 12M
                        clk = 12000000;
                        break;
                    default: // 逐位计算
                        clk = 0;
                        for (int i = 0; i < 8; i++)
                        {
                            if (clkdiv & (1 << i))
                            {
                                clk += 12000000;
                            }
                            else
                            {
                                __NOP();
                            }
                        }
                        break;
                }
                break;
            case 2: // LRC
                clk = 32000;
                break;
            default:
                break;
        }
        return clk;
    }
    /**
     * @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;
        }
    }