[STM32]通用定时器的使用

STM32的通用定时器的定时、输出PWM功能的使用

本次还是使用发光二极管来验证定时器的使用



前言

首先还是要了解STM32库函数中提供的使用定时器所需要配置的结构体成员的含义

typedef struct
{
  uint16_t TIM_Prescaler;         /*!< Specifies the prescaler value used to divide the TIM clock.
                                       This parameter can be a number between 0x0000 and 0xFFFF */

  uint16_t TIM_CounterMode;       /*!< Specifies the counter mode.
                                       This parameter can be a value of @ref TIM_Counter_Mode */

  uint32_t TIM_Period;            /*!< Specifies the period value to be loaded into the active
                                       Auto-Reload Register at the next update event.
                                       This parameter must be a number between 0x0000 and 0xFFFF.  */ 

  uint16_t TIM_ClockDivision;     /*!< Specifies the clock division.
                                      This parameter can be a value of @ref TIM_Clock_Division_CKD */

  uint8_t TIM_RepetitionCounter;  /*!< Specifies the repetition counter value. Each time the RCR downcounter
                                       reaches zero, an update event is generated and counting restarts
                                       from the RCR value (N).
                                       This means in PWM mode that (N+1) corresponds to:
                                          - the number of PWM periods in edge-aligned mode
                                          - the number of half PWM period in center-aligned mode
                                       This parameter must be a number between 0x00 and 0xFF. 
                                       @note This parameter is valid only for TIM1 and TIM8. */
} TIM_TimeBaseInitTypeDef;

typedef struct
{
  uint16_t TIM_OCMode;        /*!< Specifies the TIM mode.
                                   This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes */

  uint16_t TIM_OutputState;   /*!< Specifies the TIM Output Compare state.
                                   This parameter can be a value of @ref TIM_Output_Compare_State */

  uint16_t TIM_OutputNState;  /*!< Specifies the TIM complementary Output Compare state.
                                   This parameter can be a value of @ref TIM_Output_Compare_N_State
                                   @note This parameter is valid only for TIM1 and TIM8. */

  uint32_t TIM_Pulse;         /*!< Specifies the pulse value to be loaded into the Capture Compare Register. 
                                   This parameter can be a number between 0x0000 and 0xFFFF */

  uint16_t TIM_OCPolarity;    /*!< Specifies the output polarity.
                                   This parameter can be a value of @ref TIM_Output_Compare_Polarity */

  uint16_t TIM_OCNPolarity;   /*!< Specifies the complementary output polarity.
                                   This parameter can be a value of @ref TIM_Output_Compare_N_Polarity
                                   @note This parameter is valid only for TIM1 and TIM8. */

  uint16_t TIM_OCIdleState;   /*!< Specifies the TIM Output Compare pin state during Idle state.
                                   This parameter can be a value of @ref TIM_Output_Compare_Idle_State
                                   @note This parameter is valid only for TIM1 and TIM8. */

  uint16_t TIM_OCNIdleState;  /*!< Specifies the TIM Output Compare pin state during Idle state.
                                   This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State
                                   @note This parameter is valid only for TIM1 and TIM8. */
} TIM_OCInitTypeDef;


首先验证定时器的定时功能,再配置定时器使用输出比较功能来输出不同脉宽的方波

一、定时器详解

STM32F407ZGT6的定时器外设是由APB1提供的时钟,频率为主频率的二分之一,我们配置的系统时钟为168MHz,这里的APB1时钟频率就为84MHz,至于时间的算法,并不像51单片机那样复杂,我们只需要通过分频系数和计数器目标值来计算出频率就可以算出周期,例如我在原频率的基础上进行(8400-1)分频,计数器计数到(5000-1),至于这里为什么要-1,就和8位数据的最大值是255一样,从0开始算,一共有256个数,但是最大值是256-1;那么计算方法就是84000000/8400/5000 = 2,注意这里的2是频率并不是周期,那么我们换算成周期,1/2 = 0.5s;也就是500ms;基于以上的思想,我们来配置定时器的初始化结构体成员:

void Timer_TestInit(uint16_t arr, uint16_t psc)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    RCC_APB1PeriphClockCmd(TIMER_TEST_RCC_CLOCK, ENABLE);

    TIM_TimeBaseInitStructure.TIM_Period = arr;
    TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;

    TIM_TimeBaseInit(TIMER_TEST_NUM, &TIM_TimeBaseInitStructure);
    TIM_ITConfig(TIMER_TEST_NUM, TIM_IT_Update, ENABLE);

    NVIC_InitStructure.NVIC_IRQChannel = TIMER_TEST_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    TIM_Cmd(TIMER_TEST_NUM, ENABLE);
}

我们这里也使用到了中断,目的是使用定时器500ms中断一次,我们在中断内进行发光二极管的状态翻转;所以下面我们要编写中断的服务函数:

void TIM3_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET) //溢出中断
    {
        LED_NUM_0 = !LED_NUM_0;
    }
    TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除中断标志位
}

记得一定要清除中断标志位;

二、定时器的输出比较功能

1.什么是PWM

所谓PWM,就是脉冲宽度调制,学过电子技术的达瓦里式多多少少有过耳闻,我们在学习DCDC电路中曾经了解过一个Buck斩波电路,通过控制MOSFET的导通关断的时间不同,使本来连续的电压波形变成脉冲宽度不同的方波,因为由电感和大电容的存在,输出电压并不会大幅度波动,而是神奇的输出一个小于输入电压的值,这里控制MOSFET的信号也是PWM,与之不同的是这里MOSFET导通关断造成的是模拟信号,但我们控制MOSFET的是数字信号。也就是说我们能够通过PWM来控制导通关断的时间以达到控制器件的状态,映射到发光二极管上就是它的亮灭程度了,联想到直流电动机上那就是可以控制它的转速了,注意STM32的引脚不能直接驱动直流电机!那么STM32定时器的输出比较功能怎么去理解呢,简单的来说,就是我们通过分频系数和计数器确定PWM的频率后,它就是不变的了,我们能改变的是脉宽在一个周期内的时间,这是不能改变它的周期和频率的,假设我的周期是1,那么我设定比较值为0.5,那么计数器计数器到0.5后则会翻转一次电平,形成一个周期内的电平跳变,通过这样循环往复就形成频率恒定一个周期内脉冲宽度可以不同的方波。基于以上所有的思想,我们来配置STM32定时器的输出比较功能:

void Timer_TestPWMInit(uint16_t arr, uint16_t psc)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;

    RCC_APB1PeriphClockCmd(TIMER_TEST_PWM_RCC_CLOCK, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
    GPIO_PinAFConfig(GPIOF, GPIO_PinSource9, GPIO_AF_TIM14);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_Init(GPIOF, &GPIO_InitStructure);

    TIM_TimeBaseInitStructure.TIM_Period = arr;
    TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInit(TIMER_TEST_PWM_NUM, &TIM_TimeBaseInitStructure);

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
    TIM_OC1Init(TIMER_TEST_PWM_NUM, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(TIMER_TEST_PWM_NUM, TIM_OCPreload_Enable);
    TIM_ARRPreloadConfig(TIMER_TEST_PWM_NUM, ENABLE);
    TIM_Cmd(TIMER_TEST_PWM_NUM, ENABLE);
}

我们在LED初始化这个地方进行一些小小的改动:

void Led_Init(LedStatus_TypeDef_t InitState)
{
#if LED_MODE
    GPIO_InitTypeDef GPIO_InitSturcture;
    RCC_AHB1PeriphClockCmd(LEDx_RCC_CLOCK, ENABLE);

    GPIO_InitSturcture.GPIO_Pin = LED0_PIN_NUM | LED1_PIN_NUM;
    GPIO_InitSturcture.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitSturcture.GPIO_OType = GPIO_OType_PP;
    GPIO_InitSturcture.GPIO_PuPd = GPIO_PuPd_DOWN;
    GPIO_InitSturcture.GPIO_Speed = GPIO_Speed_2MHz;

    GPIO_Init(LEDx_GPIO_PROT, &GPIO_InitSturcture);
    LED_NUM_0 = InitState;
    LED_NUM_1 = InitState;
#else
    Timer_TestPWMInit(500 - 1, 84 - 1);
    TIM14->CCR1 = 0;
#endif
}

上面我们的定时器输出比较通道使用的的是通道一,通过TIM14的CCR1寄存器直接操作,使用库函数所提供的改变比较值的函数也是对操作的寄存器进行封装,在这里我们自己也对这一操作进行封装:

void Led_SetPWMDuty(uint32_t duty)
{
    TIM14->CCR1 = duty;
}

2.通过改变脉宽来改变亮灭程度

老样子还是呼吸灯:

for (; i < 500; i++)
{
	Led_SetPWMDuty(i);
	delay_ms(2);
}
for (; i > 0; i--)
{
	Led_SetPWMDuty(i);
	delay_ms(2);
}

最后烧录进去验证即可


总结

这里第一个使用定时器固定时间改变LED状态就不演示了,如果呼吸灯不能形象的去理解PWM的功能可以用示波器测一下输出的波形;
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值