• 合宙AIR105(三): 定时器, 定时器中断和PWM输出


    目录

    Air105 的 Timer

    定时器

    • 1 个 Timer 单元,包含 8 个独立定时器: Timer0 - Time7
    • 8 个定时器中断源独立,每个定时器单独占 1 个中断源
    • 使用 PCLK 时钟频率作为定时器计时钟源
    • 定时器采用向下计数方式

    定时器的两种运行模式

    • user-defined: 定时器计数值载入TimerNLoadCount寄存器设定值, 使用用户模式可以产生固定时间的定时器中断
    • free-runing: 定时器计数值会载入其允许的最大值, 即0xFFFFFFFF. 在定时器产生中断(计数到0)前, 用户可以再编程或禁止定时器中断. 使用这个模式, 定时器只产生1次中断, 中断产生后计数重置为 0xFFFFFFFF 并向下计数, 但不会再产生中断.

    PWM

    • 每个 Timer 单元定时器都支持 PWM 模式
    • PWM 模式最高频率 PCLK/2
    • PWM 单次触发(one shot)功能

    定时器相关代码

    以下代码基于 air105_project https://gitee.com/iosetting/air105_project 的库函数

    定时器模块结构

    在Air105中, 全局只有一个定时器模块, TIMM0

    typedef struct
    {
        TIM_TypeDef TIM[TIM_NUM];
        __I  uint32_t TIM_IntStatus;
        __I  uint32_t TIM_EOI;
        __I  uint32_t TIM_RawIntStatus;
        __I  uint32_t TIM_Comp;
        __IO uint32_t TIM_ReloadCount[TIM_NUM];
    } TIM_Module_TypeDef;
    

    这个 TIMM0 的地址定义在 air105.h 中

    #define TIMM0                                   ((TIM_Module_TypeDef *)TIMM0_BASE)
    
    #define AIR105_PERIPH_BASE                      (0x40000000UL)   /*!< (Peripheral) Base Address */
    #define AIR105_APB0_BASE                        (AIR105_PERIPH_BASE + 0x10000)
    #define TIMM0_BASE                              (AIR105_APB0_BASE + 0x3000)
    
    • 地址 = 0x40000000UL + 0x10000 + 0x3000 = 0x4001 3000
    • 范围 [0x4001_3000, 0x4001_3FFF]

    定时器初始化

    定时器的初始化只需要两个参数: TIMx, 周期(时钟数), 为配合定时器使用, 还需要定义中断

    void Timer_Init(void)
    {
        TIM_InitTypeDef TIM_InitStruct;
        NVIC_InitTypeDef NVIC_InitStructure;
    
        // 开启定时器的外设时钟
        SYSCTRL_APBPeriphClockCmd(SYSCTRL_APBPeriph_TIMM0, ENABLE);
        SYSCTRL_APBPeriphResetCmd(SYSCTRL_APBPeriph_TIMM0, ENABLE);
    
        // 定时器的时钟是 PCLK, 计数间隔为 1ms 对应的时钟数
        TIM_InitStruct.TIM_Period = SYSCTRL->PCLK_1MS_VAL;
        // 使用 定时器0
        TIM_InitStruct.TIMx = TIM_0;
        // 初始化
        TIM_Init(TIMM0, &TIM_InitStruct);
        // 开启定时器0的中断
        TIM_ITConfig(TIMM0, TIM_InitStruct.TIMx, ENABLE);
    
        //NVIC
        NVIC_SetPriorityGrouping(NVIC_PriorityGroup_0);
    
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_InitStructure.NVIC_IRQChannel = TIM0_0_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    
        NVIC_Init(&NVIC_InitStructure);
    
        // 启动定时器0
        TIM_Cmd(TIMM0, (TIM_NumTypeDef)TIM_0, ENABLE);
    }
    

    在库函数中, 会将模式设为 user-defined, 即自动循环, 重复载入周期并产生中断.

    /**
      * @brief  Initializes the TIMx Unit peripheral according to the specified parameters.
      * @param  TIMMx: x can be 0 to select the TIM peripheral
      * @param  TIM_InitStruct: pointer to a TIM_InitTypeDef structor that contains the configuration information
      * @retval None
      */
    void TIM_Init(TIM_Module_TypeDef *TIMMx, TIM_InitTypeDef *TIM_InitStruct)
    {
        TIM_Cmd(TIMMx, TIM_InitStruct->TIMx, DISABLE);
        
        TIMMx->TIM[TIM_InitStruct->TIMx].ControlReg = 0;
        TIMMx->TIM[TIM_InitStruct->TIMx].ControlReg |= TIMER_CONTROL_REG_TIMER_MODE;
        TIMMx->TIM[TIM_InitStruct->TIMx].ControlReg &= ~TIMER_CONTROL_REG_TIMER_PWM;
    
        TIMMx->TIM[TIM_InitStruct->TIMx].LoadCount = TIM_InitStruct->TIM_Period;
    }
    

    定时器中断处理

    Air105对应每个定时器, 各有一个中断处理函数, 可以查看 startup.air105.s 中的中断向量定义

    TIM0_0_IRQHandler
    TIM0_1_IRQHandler
    TIM0_2_IRQHandler
    TIM0_3_IRQHandler
    TIM0_4_IRQHandler
    TIM0_5_IRQHandler
    TIM0_6_IRQHandler
    TIM0_7_IRQHandler
    

    对应 Timer0 的中断处理, 写在 air105_it.c. TIM_ClearITPendingBit 和 NVIC_ClearPendingIRQ 是必须调用的, 用于清除中断

    void TIM0_0_IRQHandler(void)
    {
        TIM_ClearITPendingBit(TIMM0, TIM_0);
        NVIC_ClearPendingIRQ(TIM0_0_IRQn);
    }
    

    下面加入处理逻辑的例子, 每秒调用一次 timer_handler(), 注意不要在中断处理中使用耗时的工作

    extern uint32_t timer_count;
    extern void timer_handler(void);
    
    void TIM0_0_IRQHandler(void)
    {
        timer_count++;
        if (timer_count >= 1000)
        {
            timer_count = 0;
            timer_handler();
        }
    
        TIM_ClearITPendingBit(TIMM0, TIM_0);
        NVIC_ClearPendingIRQ(TIM0_0_IRQn);
    }
    

    定时器示例代码

    使用Timer0控制板载LED每隔一秒闪烁

    https://gitee.com/iosetting/air105_project/tree/master/Demos/Timer/Timer_Blink

    Air105 的 PWM

    Air105 的8个独立定时器均可编程产生PWM信号. 当用户设定TimerNControlReg中PWM比特位为1后,定时器进入PWM工作模式. 此时 PWM 由 TimerNLoadCount2 和 TimerNLoadCount 寄存器分别控制高电平及低电平周期翻转输出.

    频率和占空比设置

    • 高电平周期 = (TimerNLoadCount2 + 1) * PCLK_Period
    • 低电平周期 = (TimerNLoadCount + 1) * PCLK_Period

    PWM 相关代码

    PWM初始化也只需要三个参数 TIMx 和高低电平两个周期, 两者之和就是一个PWM周期

    typedef struct 
    {
    	TIM_NumTypeDef TIMx;
    	uint32_t TIM_LowLevelPeriod;
    	uint32_t TIM_HighLevelPeriod;
    }TIM_PWMInitTypeDef;
    

    用Timer5初始化

    void TimerPWM_Init(void)
    {
        TIM_PWMInitTypeDef TIM_PWMInitStruct;
    
        SYSCTRL_APBPeriphClockCmd(SYSCTRL_APBPeriph_TIMM0, ENABLE);
        SYSCTRL_APBPeriphResetCmd(SYSCTRL_APBPeriph_TIMM0, ENABLE);
    
        //Timer5 -> PWM5
        TIM_PWMInitStruct.TIM_HighLevelPeriod = SYSCTRL->PCLK_1MS_VAL;
        TIM_PWMInitStruct.TIM_HighLevelPeriod = 0;
        TIM_PWMInitStruct.TIMx = TIM_5;
        TIM_PWMInit(TIMM0, &TIM_PWMInitStruct);
        TIM_Cmd(TIMM0, TIM_5, ENABLE);
    }
    

    在初始化PWM的库函数中, 默认将模式设为 user-defined, 自动循环载入周期, 并屏蔽中断

    /**
      * @brief  Initializes the TIMx PWM Unit peripheral according to the specified parameters.
      * @param  TIMMx: x can be 0 to select the TIM peripheral
      * @param  TIM_PWMInitStruct: pointer to a TIM_PWMInitTypeDef structor that contains the configuration information
      * @retval None
      */
    void TIM_PWMInit(TIM_Module_TypeDef *TIMMx, TIM_PWMInitTypeDef *TIM_PWMInitStruct)
    {
        TIM_Cmd(TIMMx, TIM_PWMInitStruct->TIMx, DISABLE);
    
        TIMMx->TIM[TIM_PWMInitStruct->TIMx].ControlReg = 0;
        TIMMx->TIM[TIM_PWMInitStruct->TIMx].ControlReg |= TIMER_CONTROL_REG_TIMER_MODE;
        TIMMx->TIM[TIM_PWMInitStruct->TIMx].ControlReg |= TIMER_CONTROL_REG_TIMER_PWM;
        TIMMx->TIM[TIM_PWMInitStruct->TIMx].ControlReg |= TIMER_CONTROL_REG_TIMER_INTERRUPT;
        TIMMx->TIM[TIM_PWMInitStruct->TIMx].LoadCount = TIM_PWMInitStruct->TIM_LowLevelPeriod;
        TIMMx->TIM_ReloadCount[TIM_PWMInitStruct->TIMx] = TIM_PWMInitStruct->TIM_HighLevelPeriod;
    }
    

    将 PB5 功能复用为 PWM5

    GPIO_InitTypeDef gpio;
    gpio.GPIO_Pin = GPIO_Pin_5;
    gpio.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio.GPIO_Remap = GPIO_Remap_2;
    GPIO_Init(GPIOB, &gpio);
    printf("GPIO Init\r\n");
    

    实时调节占空比, 后两个参数代表PCLK时钟周期个数

    TIM_SetPWMPeriod(TIMM0, TIM_5, period - high_period, high_period);
    

    PWM示例代码

    使用PWM5(Timer5)控制LED产生呼吸灯效果

    https://gitee.com/iosetting/air105_project/tree/master/Demos/PWM/PWM_FadeLED

    示例接线:

    根据 开发板的BOM PCB 查看 https://wiki.luatos.com/_static/bom/Air105.html
    示例中使用Timer4, Timer5对应的PWM4和PWM5输出, 使用的是PB4和PB5, 对应开发板的SP2_MOSP2_MI, 开发板上的PWM5对应的是PC7, 要注意, 别接错了.
    运行示例, 将两个LED各自串接一个1-5K的电阻, 分别接GND后接在SP2_MOSP2_MI上, 就能看到呼吸灯的效果了

  • 相关阅读:
    css
    js -【 数组】判断一个变量是数组类型的几种方法
    【消灭代办】第2周
    【本周面试题】第2周
    【本周面试题】第1周
    【消灭代办】第1周
    echarts
    css
    js
    JS方法
  • 原文地址:https://www.cnblogs.com/milton/p/16389098.html
Copyright © 2020-2023  润新知