IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> [STM32]通用定时器的使用 -> 正文阅读

[嵌入式][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的功能可以用示波器测一下输出的波形;
在这里插入图片描述

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-09-10 11:01:09  更:2021-09-10 11:02:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 0:42:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码