STM32-10-定时器

STM32-01-认识单片机
STM32-02-基础知识
STM32-03-HAL库
STM32-04-时钟树
STM32-05-SYSTEM文件夹
STM32-06-GPIO
STM32-07-外部中断
STM32-08-串口
STM32-09-IWDG和WWDG

文章目录

一、STM32 基础定时器

STM32定时器分类
在这里插入图片描述

STM32定时器特性表:
在这里插入图片描述

主要功能:

定时器类型主要功能
基本定时器没有输入输出通道,常用作时基,即定时功能
通用定时器具有多路独立通道,可用于输入捕获/输出比较,也可用作时基
高级定时器除具备通用定时器所有功能外,还具备带死区控制的互补信号输出刹车输入等功能(可用于电机控制、数字电源设计等)

1. 基本定时器简介

  • STM32F103有两个基本定时器TIM6TIM7,它们的功能完全相同,资源是完全独立的,可以同时使用。

  • 主要特性

    • 16位自动重载递增计数器;
    • 16位可编程预分频器,预分频系数1~65536,用于对计数器时钟频率进行分频;
    • 触发DAC的同步电路;
    • 生成中断DMA请求
  • 定时原理:
    在这里插入图片描述

2. 基本定时器框图

在这里插入图片描述

  • 影子寄存器: 实际起作用的寄存器,用户不可直接访问。举个例子:我们可以把预分频系数写入预分频器寄存器(TIMx_PSC),但是预分频器寄存器只是起到缓存数据的作用,只有等到更新事件发生时,预分频器寄存器的值才会被自动写入其影子寄存器中,这时才真正起作用。

  • 自动重载寄存器:自动重载寄存器及其影子寄存器的作用在于自动重载寄存器是否具有缓冲作用还受到ARPE位的控制。

    • 当该位置0时,ARR寄存器不进行缓冲,写入新ARR值会立即传送到ARR影子寄存器从而直接生效;
    • 当该位置1时,ARR寄存器进行缓冲,写入新ARR值时不会立即被写入ARR影子寄存器,而是要等到更新事件发生才会被写入ARR影子寄存器,这时才生效。预分频器寄存器则没有这样相关的控制位,这就是它们不同点。
  • 更新事件的产生:

    1. 软件产生,将 TIMx_EGR 寄存器的位UG置 1,产生更新事件后,硬件会自动将 UG 位清零。
    2. 硬件产生,基本定时器的计数器(CNT)是一个递增的计数器,当寄存器(TIMx_CR1)的CEN位置 1,即使能定时器,每来一个CK_CNT脉冲,TIMx_CNT的值就会递增加1。当TIMx_CNT值与TIMx_ARR的设定值相等时,TIMx_CNT的值就会被自动清零并且会生成更新事件(如果开启相应的功能,就会产生DMA请求产生中断信号或者触发DAC同步电路),然后下一个 CK_CNT脉冲到来,TIMx_CNT的值就会递增加1,如此循环。在此过程中,TIMx_CNT等于 TIMx_ARR时,我们称之为定时器溢出,因为是递增计数,故而又称为定时器上溢。定时器溢出就伴随着更新事件的发生。
  • 计数模式与溢出条件:
    在这里插入图片描述

    • 递增计数模式实例说明
      在这里插入图片描述

    • 递减计数模式实例说明
      在这里插入图片描述

    • 中心对齐计数模式实例说明:
      在这里插入图片描述

3. 基本定时器相关寄存器

  • 控制寄存器 1(TIMx_CR1)
    在这里插入图片描述

  • DMA/中断使能寄存器(TIMx_DIER)
    在这里插入图片描述

  • 状态寄存器(TIMx_SR)
    在这里插入图片描述

  • 计数器(TIMx_CNT)
    在这里插入图片描述

  • 预分频器(TIMx_PSC)
    在这里插入图片描述

  • 自动重装载寄存器(TIMx_ARR)
    在这里插入图片描述

4. 定时器溢出时间计算

在这里插入图片描述

5. 基本定时器中断配置步骤

  1. 配置定时器基础工作参数

    HAL_TIM_Base_Init()
    
  2. 定时器基础MSP初始化

    HAL_TIM_Base_MspInit()     
    
  3. 使能更新中断并启动计数器

    HAL_TIM_Base_Start_IT()
    
  4. 设置优先级,使能中断

    HAL_NVIC_SetPriority()
    HAL_NVIC_EnableIRQ()    
    
  5. 编写中断服务函数

    TIMx_IRQHandler()
    HAL_TIM_IRQHandler()
    
  6. 编写定时器更新中断回调函数

    HAL_TIM_PeriodElapsedCallback()
    
  • 程序流程

    在这里插入图片描述

6. 代码实现

  • 功能:使用定时器中断实现LED灯闪烁.

  • 定时器中断初始化

    void btim_timx_int_init(uint16_t psc, uint16_t per)
    {
        g_timx_handle.Instance = TIM6;
        g_timx_handle.Init.Prescaler = psc;
        g_timx_handle.Init.Period = per;
        
        HAL_TIM_Base_Init(&g_timx_handle);
        
        HAL_TIM_Base_Start_IT(&g_timx_handle);
        
    }
    
  • 定时器基础MSP初始化函数

    void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
    {
        if (htim->Instance == TIM6)
        {
            __HAL_RCC_TIM6_CLK_ENABLE();
            HAL_NVIC_SetPriority(TIM6_IRQn, 1, 3);
            HAL_NVIC_EnableIRQ(TIM6_IRQn);
        }
    }
    
  • 定时器6中断服务函数

    void TIM6_IRQHandler()
    {
        HAL_TIM_IRQHandler(&g_timx_handle);
    }
    
  • 定时器溢出中断回调函数

    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
        if(htim->Instance == TIM6)
        {
            LED0_TOGGLE();
        }
    }
    
  • 主函数

    #include "./SYSTEM/sys/sys.h"
    #include "./SYSTEM/usart/usart.h"
    #include "./SYSTEM/delay/delay.h"
    #include "./BSP/LED/led.h"
    #include "./BSP/KEY/key.h"
    #include "./BSP/BEEP/beep.h"
    #include "./BSP/EXTI/exti.h"
    #include "./BSP/WDG/wdg.h"
    #include "./BSP/TIMER/btim.h"
    
    int main(void)
    {
        HAL_Init();                             /* 初始化HAL库 */
        sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
        delay_init(72);                         /* 延时初始化 */
        usart_init(115200);                     /* 串口初始化为115200 */
        led_init();                             /* 初始化LED */
        btim_timx_int_init(7199, 4999);
      
        while(1)
        {
            
        }
    } 
    

二、STM32通用定时器

1. 通用定时器简介

  1. 通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成。
  2. 它适用于多种场合,包括测量输入信号的脉冲宽度(输入捕获)或者产生输出波形(输出比较和PWM)。
  3. 使用定时器预分频器和RCC时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。
  4. 每个定时器都是完全独立的,没有互相共享任何资源,并且可以一起同步操作。
  • 主要特性

    • 16位递增、递减、中心对齐计数器(计数值:0~65535)

    • 16位预分频器(分频系数:1~65536)

    • 4个独立通道:输入捕获输出比较PWM生成单脉冲模式输出

    • 使用外部信号控制定时器且可实现多个定时器互连的同步电路

    • 支持针对定位的增量编码器和霍尔传感器电路

    • 触发输入作为外部时钟或者按周期的电流管理

2. 通用定时器框图

1. 时钟源

在这里插入图片描述

计数器时钟选择类型设置方法
内部时钟(CK_INT)设置TIMx_SMCR的SMS=000
外部时钟模式1:外部输入引脚(TIx)设置TIMx_SMCR的SMS=111
外部时钟模式2:外部触发输入(ETR)设置TIMx_SMCR的ECE=1
内部触发输入(ITRx)

在这里插入图片描述

1. 内部时钟(CK_INT
  • 定时器TIM2~TIM7挂接在APB1总线上,这些定时器的内部时钟(CK_INT)实际上来自APB1总线提供的时钟,然后经过一个倍频器之后,输出的时钟频率为72MHz。
2. 外部时钟模式1(TI1, TI2)

在这里插入图片描述

3. 外部时钟模式2(ETR)

在这里插入图片描述

4. 内部触发输入(ITRx)

在这里插入图片描述

2. 控制器

在这里插入图片描述

  • 控制器包括:触发控制器,从模式控制器,编码器接口

    • 触发控制器:用来提供触发信号给别的外设

    • 从模式控制器:可以控制计数器复位、启动、递增/递减、计数

    • 编码器接口:针对编码器计数

3. 时基单元

在这里插入图片描述

4. 输入捕获

在这里插入图片描述

  • TIMx_CH1~TIMx_CH4表示定时器的4个通道,4个通道独立工作。IO端口通过复用功能与这些通道相连。配置好IO端口的复用功能后,将需要测量的信号输入到相应的 IO端口,输入捕获部分可以对输入的信号的上升沿,下降沿或者双边沿进行捕获,常见的测量有:测量输入信号的脉冲宽度测量PWM输入信号的频率和占空比等。
    在这里插入图片描述

5. 捕获/比较(公共)

在这里插入图片描述

以通道1为例,对应CCR1寄存器,CC1G位可以产生软件捕获事件,那么硬件捕获事件如何产生的?以通道1输入为例,CC1S[1:0]=01,即IC1映射到TI1上;CC1E位置1,使能输入捕获;比如不滤波、不分频,ICF[3:0]=00,ICPS[1:0]=00;比如检测上升沿,CC1P位置0:接着就是等待测量信号的上升沿到来。当上升沿到来时,IC1PS信号就会触发输入捕获事件发生,计数器的值就会被锁存到捕获/比较影子寄存器里。当CCR1寄存器没有被进行读操作的时候,捕获/比较影子寄存器里的值就会锁存到CCR1寄存器中,那么程序员就可以读取CCR1寄存器,得到计数器的计数值。检测下降沿同理。

6. 输出比较

在这里插入图片描述

首先程序员写CCR1寄存器,即写入比较值。这个比较值需要转移到对应的捕获比较影子寄存器后才会真正生效。什么条件下才能转移?compare transfer旁边的与门,需要满足三个条件:CCR1不在写入操作期间CC1S[1:0]=0配置为输出OC1PE位置 0(或者OC1PE位置1,并且需要发生更新事件,这个更新事件可以软件产生或者硬件产生)。当CCR1寄存器的值转移到其影子寄存器后,新的值就会和计数器的值进行比较,它们的比较结果将会通过输出比较影响定时器的输出。
在这里插入图片描述

3. 通用定时器相关寄存器

  • 控制寄存器1TIMx_CR1
    在这里插入图片描述

  • 从模式控制寄存器TIMx_SMCR
    在这里插入图片描述

  • DMA/中断使能寄存器TIMx_DIER
    在这里插入图片描述

  • 状态寄存器TIMx_SR
    在这里插入图片描述

  • 计数寄存器TIMx_CNT
    在这里插入图片描述

  • 预分频寄存器TIMx_PSC
    在这里插入图片描述

  • 自动重载寄存器TIMx_ARR
    在这里插入图片描述

三、 通用定时器输出PWM波

  • 通用定时器输出模式

    PWM,即脉冲宽度调制,是利用微处理器的数字输出对模拟电路进行控制的一种非常有效的技术。让定时器产生PWM,在计数器频率固定时,PWM频率自动重载寄存器(TIMx_ARR)的值决定,其占空比由捕获/比较寄存器(TIMx_CCRx)的值决定。
    在这里插入图片描述

  • PWM模式
    在这里插入图片描述

  • PWM控制寄存器

    • 捕获/比较模式寄存器1/2TIMx_CCMR1/2
      在这里插入图片描述

    • 捕获/比较使能寄存器TIMx_CCER
      在这里插入图片描述

    • 捕获/比较寄存器TIMx_CCR1/2/3/4
      在这里插入图片描述

1. 通用定时器PWM输出实验配置步骤

  1. 配置定时器基础工作参数

    HAL_TIM_PWM_Init()
    
  2. 定时器PWM输出MSP初始化

    HAL_TIM_PWM_MspInit()     
    
  3. 配置PWM模式/比较值等

    HAL_TIM_PWM_ConfigChannel()
    
  4. 使能输出并启动计数器

    HAL_TIM_PWM_Start()
    
  5. 修改比较值控制占空比(可选)

    __HAL_TIM_SET_COMPARE()
    
  6. 使能通道预装载(可选)

    __HAL_TIM_ENABLE_OCxPRELOAD()
    
  • 相关库函数介绍

    函数主要寄存器主要功能
    HAL_TIM_PWM_Init()CR1、ARR、PSC初始化定时器基础参数
    HAL_TIM_PWM_MspInit()存放NVIC、CLOCK、GPIO初始化代码
    HAL_TIM_PWM_ConfigChannel()CCMRx、CCRx、CCER配置PWM模式、比较值、输出极性等
    HAL_TIM_PWM_Start()CCER、CR1使能输出比较并启动计数器
    __HAL_TIM_SET_COMPARE()CCRx修改比较值
    __HAL_TIM_ENABLE_OCxPRELOAD()CCER使能通道预装载

2. 代码实现

  • 功能:使用TM3通道2(由PB5复用)输出PWM,PB5引脚连接了LED0,从而实现PWM输出控制LED0亮度。

  • 程序流程:
    在这里插入图片描述

  • 通用定时器PWM输出初始化函数

    void gtim_timx_pwm_chy_init(uint16_t arr, uint16_t psc)
    {
        TIM_OC_InitTypeDef timx_oc_pwm_chy;
        
        g_timx_pwm_chy_handle.Instance = TIM3;                       //定时器选择
        g_timx_pwm_chy_handle.Init.Prescaler = psc;                  //定时器分频
        g_timx_pwm_chy_handle.Init.Period = arr;
        g_timx_pwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP; //定时器计数模式
        HAL_TIM_PWM_Init(&g_timx_pwm_chy_handle);   //初始化PWM
        
        timx_oc_pwm_chy.OCMode = TIM_OCMODE_PWM1;   //模式选择PWM1
        timx_oc_pwm_chy.Pulse = arr/2;              //占空比为50%
        timx_oc_pwm_chy.OCNPolarity = TIM_OCNPOLARITY_LOW;  //输出比较极性为低
        
        HAL_TIM_PWM_ConfigChannel(&g_timx_pwm_chy_handle, &timx_oc_pwm_chy, TIM_CHANNEL_2);   //配置定时器3通道2
        HAL_TIM_PWM_Start(&g_timx_pwm_chy_handle, TIM_CHANNEL_2);  //开启PWM通道  
    }
    
  • 定时器输出PWM MSP初始化函数

    void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
    {
        if(htim->Instance == TIM3)
        {
            GPIO_InitTypeDef gpio_init_struct;
            __HAL_RCC_GPIOB_CLK_ENABLE();
            __HAL_RCC_TIM3_CLK_ENABLE();
            
            gpio_init_struct.Pin = GPIO_PIN_5;
            gpio_init_struct.Mode = GPIO_MODE_AF_PP;
            gpio_init_struct.Pull = GPIO_PULLUP;
            gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
            HAL_GPIO_Init(GPIOB, &gpio_init_struct);
            
            __HAL_RCC_AFIO_CLK_ENABLE();
            __HAL_AFIO_REMAP_TIM3_PARTIAL();   //设置重映射,把TIM3通道2映射到PB5
        }
    }
    
  • 主函数

    int main(void)
    {
        uint16_t ledrpwmval = 0;
        uint8_t dir = 1;
        
        
        HAL_Init();                             /* 初始化HAL库 */
        sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
        delay_init(72);                         /* 延时初始化 */
        usart_init(115200);                     /* 串口初始化为115200 */
        led_init();                             /* 初始化LED */
        
        gtim_timx_pwm_chy_init(500 - 1, 72 - 1);
    
         
        while(1)
        {
            delay_ms(10);
            
            if(dir) ledrpwmval++;
            else ledrpwmval--;
            
            if(ledrpwmval > 300) dir = 0;
            if(ledrpwmval == 0) dir = 1;
            
            __HAL_TIM_SET_COMPARE(&g_timx_pwm_chy_handle, TIM_CHANNEL_2, ledrpwmval);
        }
    }
    

四、通用定时器输入捕获实验

1. 输入捕获测量脉冲宽度

在这里插入图片描述

测量方法:假定工作在递增计数模式,首先设置通道x为上升沿捕获,这样在t1时刻上升沿到来时,就会发生捕获事件(打开捕获中断,捕获事件发生时会触发捕获中断)。在捕获中断里将计数器值清零,并设置通道x为下降沿捕获,这样t2时刻下降沿到来时,就会发生捕获事件和捕获中断。捕获事件发生时,计数器的值会被锁存到捕获/比较寄存器中。在捕获中断内,读取捕获/比较寄存器就可以获取高电平脉冲时间,计数器计数的个数,从而可以算出高电平脉冲的时间(假定计数器没有溢出)。

但是在t1到t2时间段,定时器可能会产生N次溢出,需要对定时器溢出做相应的处理,防止高电平太长,导致测量出错。在t1到t2时间内,假定定时器溢出N次,那么高电平时间内,计数器计数的个数计算方法为:N * (ARR + 1) + CCRx2,CCRx2表示t2时间点,捕获/比较寄存器的值。经过计算得到高电平脉宽时间内计数器计数个数后,用这个个数乘以计数器的计数周期,就可以得到高电平持续的时间。就是输入捕获测量高电平脉宽时间的整个过程。

STM32F103的定时器除了TM6和TM7,其他定时器都有输入捕获功能。输入捕获,简单的说就是通过检测TIMx_CHy上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)时,会发生捕获事件,将当前定时器的值(TIMx_CNT)锁存到对应通道的捕获/比较寄存器(TIMx_CCRy)里,完成一次捕获。同时还可以配置捕获事件发生时是否触发捕获中断/DMA。另外还要考虑测量的过程中是否可能发生定时器溢出,如果可能溢出,还要做溢出处理。

2. 输入捕获实验相关寄存器

  • 捕获/比较模式寄存器1/2TIMx_CCMR1/2
    在这里插入图片描述

  • 捕获/比较使能寄存器TIMx_CCER
    在这里插入图片描述

3. 通用定时器输入捕获配置步骤

  1. 配置定时器基础工作参数

    HAL_TIM_IC_Init()
    
  2. 定时器输入捕获MSP初始化

    HAL_TIM_IC_MspInit() 
    
  3. 配置输入通道映射、捕获边沿等

    HAL_TIM_IC_ConfigChannel()
    
  4. 设置优先级,使能中断

    HAL_NVIC_SetPriority()
    HAL_NVIC_EnableIRQ()
    
  5. 使能定时器更新中断

    __HAL_TIM_ENABLE_IT()
    
  6. 使能捕获、捕获中断及计数器

    HAL_TIM_IC_Start_IT()
    
  7. 编写中断服务函数

    TIMx_IRQHandler()
    HAL_TIM_IRQHandler()
    
  8. 编写更新中断和捕获回调函数

    HAL_TIM_PeriodElapsedCallback() 
    HAL_TIM_IC_CaptureCallback()
    
  • 相关函数的功能

    函数主要寄存器主要功能
    HAL_TIM_IC_Init()CR1、ARR、PSC初始化定时器基础参数
    HAL_TIM_IC_MspInit()存放NVIC、CLOCK、GPIO初始化代码
    HAL_TIM_IC_ConfigChannel()CCMRx、CCER配置通道映射、捕获边沿、分频、滤波等
    __HAL_TIM_ENABLE_IT()DIER使能更新中断等
    HAL_TIM_IC_Start_IT()CCER、DIER、CR1使能输入捕获、捕获中断并启动计数器
    HAL_TIM_IRQHandler()SR定时器中断处理公用函数,处理各种中断
    HAL_TIM_PeriodElapsedCallback()定时器更新中断回调函数,由用户重定义
    HAL_TIM_IC_CaptureCallback()定时器输入捕获回调函数,由用户重定义

4. 代码实现

  • 功能

    1、使用TM5CH1来做输入捕获,捕获PA0上的高电平脉宽,并将脉宽时间通过串口打印出来,然后通过按WKUP按键,模拟输入高电平,例程中能测试的最长高电平脉宽时间为: 4194303us.

    2、LED0闪烁指示程序运行。

  • 通用定时器TIM5通道1输入捕获初始化函数

    void gtim_timx_cap_chy_init(uint16_t arr, uint16_t psc)
    {
        TIM_IC_InitTypeDef timx_ic_cap_chy = {0};
        
        //初始化定时器参数 
        g_timx_cap_chy_handle.Instance = TIM5;        //定时器5的基地址
        g_timx_cap_chy_handle.Init.Prescaler = psc;   //定时器的分频系数
        g_timx_cap_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;   //递增计数模式
        g_timx_cap_chy_handle.Init.Period = arr;    //自动重装载值
        HAL_TIM_IC_Init(&g_timx_cap_chy_handle);    //初始化
        
        //配置通道映射、捕获边沿、分频、滤波等
        timx_ic_cap_chy.ICPolarity = TIM_ICPOLARITY_RISING;     //配置捕获极性为上升沿
        timx_ic_cap_chy.ICSelection = TIM_ICSELECTION_DIRECTTI; //映射到通道1
        timx_ic_cap_chy.ICPrescaler = TIM_ICPSC_DIV1;           //不进行分频
        timx_ic_cap_chy.ICFilter = 0;                           //不进行滤波
        HAL_TIM_IC_ConfigChannel(&g_timx_cap_chy_handle, &timx_ic_cap_chy, TIM_CHANNEL_1);   //初始化
        
        __HAL_TIM_ENABLE_IT(&g_timx_cap_chy_handle, TIM_IT_UPDATE);  //使能更新中断
        HAL_TIM_IC_Start_IT(&g_timx_cap_chy_handle, TIM_CHANNEL_1);  //开始捕获TIM5的通道1
    }
    
  • 通用定时器输入捕获初始化函数

    void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
    {
         if(htim->Instance == TIM5)    //判断定时器是否正确
         {
             GPIO_InitTypeDef gpio_init_struct;     //GPIO配置结构体
             
             __HAL_RCC_TIM5_CLK_ENABLE();    //使能定时器时钟
             __HAL_RCC_GPIOA_CLK_ENABLE();   //使能GPIOA时钟
             
             gpio_init_struct.Pin = GPIO_PIN_0;              //配置引脚号
             gpio_init_struct.Mode = GPIO_MODE_AF_PP;        //配置复用推挽输出
             gpio_init_struct.Pull = GPIO_PULLDOWN;          //配置下拉模式
             gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;  //配置输出速度
             HAL_GPIO_Init(GPIOA, &gpio_init_struct);   //初始化
             
             HAL_NVIC_SetPriority(TIM5_IRQn, 1, 3);          //设置中断优先级
             HAL_NVIC_EnableIRQ(TIM5_IRQn);                  //使能TIM5中断
         }
    }
    
  • 定时器TIM5中断服务函数

    //定时器5中断服务函数
    void TIM5_IRQHandler(void)
    {
        //HAL库中断公共处理函数
        HAL_TIM_IRQHandler(&g_timx_cap_chy_handle);
    }
    
  • 定时器输入捕获中断处理回调函数

    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
    {
        if(htim->Instance == TIM5)
        {
            if((g_timxchy_cap_sta & 0x80) == 0)  //未成功捕获
            {
                if(g_timxchy_cap_sta & 0x40)  //捕获下降沿
                {
                    g_timxchy_cap_sta |= 0x80;   //位7置1 代表成功捕获
                    g_timxchy_cap_val = HAL_TIM_ReadCapturedValue(&g_timx_cap_chy_handle, TIM_CHANNEL_1);   //保存当前计数器的值
                    TIM_RESET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, TIM_CHANNEL_1);   //失能通道
                    TIM_SET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, TIM_CHANNEL_1, TIM_ICPOLARITY_RISING);   //使能通道1为上降沿捕获
                }
                else  //捕获上升沿
                {
                    g_timxchy_cap_sta = 0;
                    g_timxchy_cap_val = 0;
                    g_timxchy_cap_sta |= 0x40;
                    __HAL_TIM_DISABLE(&g_timx_cap_chy_handle);   //失能定时器5
                    __HAL_TIM_SET_COUNTER(&g_timx_cap_chy_handle, 0);   //计数器的值清0
                    TIM_RESET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, TIM_CHANNEL_1);   //失能通道
                    TIM_SET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); //使能通道1为下降沿捕获
                    __HAL_TIM_ENABLE(&g_timx_cap_chy_handle);   //使能定时器5
                }
            }
        }
    }
    
  • 定时器更新中断回调函数

    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
        if(htim->Instance == TIM5)
        {
            if((g_timxchy_cap_sta & 0x80) == 0)  //未成功捕获
            {
                if(g_timxchy_cap_sta & 0x40)     //已经成功捕获
                {
                    if((g_timxchy_cap_sta & 0X3F) == 0x3f)  //高电平时间过长
                    {
                        TIM_RESET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, TIM_CHANNEL_1);   //失能通道
                        TIM_SET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); //使能通道1为下降沿捕获
                        g_timxchy_cap_sta |= 0x80;    //标记成功捕获一次
                        g_timxchy_cap_val = 0xFFFF;
                    }
                    else   //累积定时器溢出次数
                    {
                        g_timxchy_cap_sta++;
                    }
                }
            }
        }
        
        if(htim->Instance == TIM6)
        {
            LED0_TOGGLE();
        }
        
        if(htim->Instance == TIM7)
        {
            LED1_TOGGLE();
        }
    }
    
  • 主函数

    int main(void)
    {
        uint32_t temp = 0;
        uint8_t t= 0;
        
        
        HAL_Init();                             /* 初始化HAL库 */
        sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
        delay_init(72);                         /* 延时初始化 */
        usart_init(115200);                     /* 串口初始化为115200 */
        led_init();                             /* 初始化LED */
        
        gtim_timx_cap_chy_init(0xFFFF, 72 - 1);  //1MHz
    
         
        while(1)
        {
            if(g_timxchy_cap_sta & 0x80)
            {
                temp = g_timxchy_cap_sta & 0x3F;
                temp = temp * 65536;    //溢出时间总和
                temp += g_timxchy_cap_val;   //溢出的总时间
                printf("HIGH:%d us\r\n", temp);
                g_timxchy_cap_sta = 0;  //开启下一次捕获
            }
            
            t++;
            
            if(t > 20)
            {
                t = 0;
                LED0_TOGGLE();
            }
            delay_ms(10);
        }
    }  
    
  • 主程序运行流程
    在这里插入图片描述

  • 中断程序运行流程
    在这里插入图片描述

五、通用定时器脉冲计数实验

1. 脉冲计数框图

脉冲计数实验使用的时钟源是外部时钟模式1,外部输入引脚(TIx)作为定时器的时钟源。实验中使用WK_UP按键按下产生的高电平脉冲作为定时器的计数器时钟,每按下一次按键产生一次高电平脉冲,计数器的值加一。
在这里插入图片描述

外部时钟模式1的外部输入引脚只能是通道1或者通道2对应的IO,通道3或者通道4不可以。以通道1输入为例,外部时钟源信号通过通道1输入后,TI1分别要经过滤波器边沿检测器后,来到TI1FP1,被触发输入选择器选择为触发源,接着来到从模式控制器。从模式选择为外部时钟模式1,这时候外部时钟源信号就会到达时基单元的预分频器,后面就是经过分频后就作为计数器的计数时钟。

2. 相关寄存器

  • 捕获/比较模式寄存器1/2TIMx_CCMR1/2

    与输入捕获配置相同

  • 捕获/比较使能寄存器TIMx_CCER

    与输入捕获配置相同

  • 从模式控制寄存器TIMx_SMCR
    在这里插入图片描述

3. 通用定时器脉冲计数配置步骤

  1. 配置定时器基础工作参数

    HAL_TIM_IC_Init()
    
  2. 定时器输入捕获MSP初始化

    HAL_TIM_IC_MspInit()
    
  3. 配置定时器从模式等

    HAL_TIM_SlaveConfigSynchro()
    
  4. 使能输入捕获并启动计数器

    HAL_TIM_IC_Start()
    
  5. 获取计数器的值

    __HAL_TIM_GET_COUNTER()
    
  6. 设置计数器的值

    __HAL_TIM_SET_COUNTER()
    
  • 相关库函数

    函数主要寄存器主要功能
    HAL_TIM_IC_Init()CR1、ARR、PSC初始化定时器基础参数
    HAL_TIM_IC_MspInit()存放NVIC、CLOCK、GPIO初始化代码
    HAL_TIM_SlaveConfigSynchro()SMCR、CCMRx、CCER配置定时器从模式、触发选择、分频、滤波等
    HAL_TIM_IC_Start()CCER、CR1使能输入捕获、启动计数器
    __HAL_TIM_GET_COUNTER()CNT获取计数器当前值
    __HAL_TIM_SET_COUNTER()CNT设置计数器的值

4. 代码实现

  • 功能

    使用TM2CH1做输入捕获,我们将捕获PA0上的高电平脉宽,并将脉宽进行计数,通过串口打印出来。大家可以通过按WKUP按键,输入高电平脉冲,通过按KEY0重设当前计数。 LED0闪烁,提示程序运行。

  • 通用定时器脉冲计数初始化函数

    void gtim_timx_cnt_chy_init(uint16_t psc)
    {
        TIM_SlaveConfigTypeDef timx_slave_config = {0};
        
        
        g_timx_cnt_chy_handle.Instance = TIM2;                       //定时器选择
        g_timx_cnt_chy_handle.Init.Prescaler = psc;                  //定时器分频
        g_timx_cnt_chy_handle.Init.Period = 65535;
        g_timx_cnt_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP; //定时器计数模式
        HAL_TIM_IC_Init(&g_timx_cnt_chy_handle);   //初始化PWM
        
        timx_slave_config.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;
        timx_slave_config.InputTrigger = TIM_TS_TI1F_ED;
        timx_slave_config.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;
        timx_slave_config.TriggerFilter = 0;
        HAL_TIM_SlaveConfigSynchro(&g_timx_cnt_chy_handle, &timx_slave_config);
        
        HAL_TIM_IC_Start(&g_timx_cnt_chy_handle, TIM_CHANNEL_1);
    }
    
  • 主函数

    int main(void)
    {
        uint16_t curcnt;  //存储当前的计数值
        uint16_t oldcnt;   //存储旧的计数值
        uint8_t key;      //按键的键值
        uint8_t t;        //延时时间
        
        
        HAL_Init();                             /* 初始化HAL库 */
        sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
        delay_init(72);                         /* 延时初始化 */
        usart_init(115200);                     /* 串口初始化为115200 */
        led_init();                             /* 初始化LED */
        key_init();
        gtim_timx_cnt_chy_init(0);  
    
         
        while(1)
        {
            key = key_scan(0);
            if(key == 1)
            {
                __HAL_TIM_SET_COUNTER(&g_timx_cnt_chy_handle, 0);
            }
            
            curcnt = __HAL_TIM_GET_COUNTER(&g_timx_cnt_chy_handle);
            
            if(oldcnt != curcnt)
            {
                oldcnt = curcnt;
                printf("CNT:%d\r\n", oldcnt);
            }
            
            t++;
            
            if(t > 20)
            {
                t = 0;
                LED0_TOGGLE();
            }
            delay_ms(10);
        }
    }  
    

六、高级定时器输出指定个数PWM实验

1. 高级定时器框图

在这里插入图片描述

  1. 重复计数器

    在基本定时器和通用定时器中,定时器发生上溢或者下溢时,会直接生成更新事件。但有重复计数器的定时器并不完全这样,在定时器每次发生上溢或下溢时,重复计数器的值会减一,当重复计数器的值为0时,再发生一次上溢或者下溢才会生成定时器更新事件。如果我们设置重复计数器寄存器的RCR的值为N,那么更新事件将在定时器发生N+1次上溢或下溢时发生。

  2. 输出比较

    与通用定时器相比,高级定时器多了带死区控制的互补输出功能。通道1、2、3都有互补输出通道,DTG死区发生器,死区时间由DTG[7:0]位来配置。如果不使用互补通道和死区时间控制,那么高级定时器TIM1和TIM8和通用定时器的输出比较部分使用方法基本一样,只是要注意MOE位得置1定时器才能输出。

  3. 断路功能

    断路功能也称刹车功能,一般用于控制电机的刹车。F1系列有一个断路通道,断路源可以是刹车输入引脚TIMx_BKIN),也可以是一个时钟失败事件。时钟失败事件由复位时钟控制器中的时钟安全系统产生。系统复位后,断路功能默认被禁止,MOE位为低。

2. 重复计数器特性

在这里插入图片描述

  1. 计数器每次上溢或下溢都能使重复计数器减1,减到0时,再发生一次溢出就会产生更新事件。

  2. 如果设置RCR为N,更新事件将在N+1次溢出时发生。

3. 相关寄存器

  • 控制寄存器1TIMx_CR1
    在这里插入图片描述

  • 捕获/比较模式寄存器1/2TIMx_CCMR1/2
    在这里插入图片描述

  • 捕获/比较使能寄存器TIMx_CCER
    在这里插入图片描述

  • 事件产生寄存器TIMx_EGR
    在这里插入图片描述

  • 重复计数器寄存器TIMx_RCR
    在这里插入图片描述

  • 捕获/比较寄存器1/2/3/4TIMx_CCR1/2/3/4
    在这里插入图片描述

  • 断路和死区寄存器TIMx_BDTR
    在这里插入图片描述

4. 高级定时器输出指定个数PWM实验配置步骤

  1. 配置定时器基础工作参数

    HAL_TIM_PWM_Init()
    
  2. 定时器PWM输出MSP初始化

    HAL_TIM_PWM_MspInit()   
    
  3. 配置PWM模式/比较值等

    HAL_TIM_PWM_ConfigChannel()
    
  4. 设置优先级,使能中断

    HAL_NVIC_SetPriority()
    HAL_NVIC_EnableIRQ()
    
  5. 使能定时器更新中断

    __HAL_TIM_ENABLE_IT()
    
  6. 使能输出、主输出、计数器

    HAL_TIM_PWM_Start()
    
  7. 编写中断服务函数

    TIMx_IRQHandler()
    HAL_TIM_IRQHandler()
    
  8. 编写更新中断回调函数

    HAL_TIM_PeriodElapsedCallback()
    
  • 具体函数功能

    函数主要寄存器主要功能
    HAL_TIM_PWM_Init()CR1、ARR、PSC初始化定时器基础参数
    HAL_TIM_PWM_MspInit()存放NVIC、CLOCK、GPIO初始化代码
    HAL_TIM_PWM_ConfigChannel()CCMRx、CCRx、CCER配置PWM模式、比较值、输出极性等
    __HAL_TIM_ENABLE_IT()CCER使能更新中断等
    HAL_TIM_PWM_Start()CCER、CR1使能输出、主输出、启动计数器
    HAL_TIM_IRQHandler()SR定时器中断处理公用函数,处理各种中断
    HAL_TIM_PeriodElapsedCallback()定时器更新中断回调函数,由用户重定义
    HAL_TIM_GenerateEvent()EGR通过软件产生事件
    __HAL_TIM_ENABLE()CR1启动计数器

5. 代码实现

  • 功能:

    通过TIM8CH1(由PC6复用)输出PWM,然后为了指示PWM的输出情况,我们用杜邦线将PC6和PE5引脚的排针连接起来,从而实现PWM输出控制LED1(硬件已连接在PPE5引脚上)的亮灭。注意的点是:PE5要设置成浮空输入,避免引脚冲突,我们在main函数中设置好了,请看源码。上电默认输出5个PWM波,连接好杜邦线后可以看见LED1亮灭五次。之后按一下按键KEYO,就会输出5个PWM波控制LED1亮灭五次。LED0闪烁提示系统正在运行。

  • 高级定时器输出指定个数PWM初始化

    void atim_timx_npwm_chy_init(uint16_t arr, uint16_t psc)
    {
        TIM_OC_InitTypeDef timx_oc_npwm_chy = {0};
        
        g_timx_npwm_chy_handle.Instance = TIM8;
        g_timx_npwm_chy_handle.Init.Prescaler = psc;
        g_timx_npwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;  //向上计数模式
        g_timx_npwm_chy_handle.Init.Period = arr;
        g_timx_npwm_chy_handle.Init.RepetitionCounter = 0;  //重复计数器初始值
        g_timx_npwm_chy_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; /*使能TIMx_ARR进行缓冲 */
        HAL_TIM_PWM_Init(&g_timx_npwm_chy_handle);
        
        timx_oc_npwm_chy.OCMode = TIM_OCMODE_PWM1;  //使用PWM1工作模式
        timx_oc_npwm_chy.Pulse = arr / 2;           //占空比设置为50%
        timx_oc_npwm_chy.OCPolarity = TIM_OCPOLARITY_HIGH;   //高电平有效
        HAL_TIM_PWM_ConfigChannel(&g_timx_npwm_chy_handle, &timx_oc_npwm_chy, TIM_CHANNEL_1);  //进行初始化
        
        __HAL_TIM_ENABLE_IT(&g_timx_npwm_chy_handle, TIM_IT_UPDATE);  //允许更新中断
        
        HAL_TIM_PWM_Start(&g_timx_npwm_chy_handle, TIM_CHANNEL_1);    //开启对应的PWM通道
    }
    
  • 高级定时器输出指定个数PWM MSP回调函数

    void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
    {
        if(htim->Instance == TIM8)
        {
            GPIO_InitTypeDef gpio_init_struct;
            
            __HAL_RCC_GPIOC_CLK_ENABLE();
            __HAL_RCC_TIM8_CLK_ENABLE();
            
            gpio_init_struct.Pin = GPIO_PIN_6;
            gpio_init_struct.Pull = GPIO_PULLUP;
            gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
            gpio_init_struct.Mode = GPIO_MODE_AF_PP;
            HAL_GPIO_Init(GPIOC, &gpio_init_struct);
            
            HAL_NVIC_SetPriority(TIM8_UP_IRQn, 1, 3);
            HAL_NVIC_EnableIRQ(TIM8_UP_IRQn);
        }
    }
    
  • 高级定时器设置PWM个数函数

    void atim_timx_npwm_chy_set(uint8_t npwm)
    {
        if(npwm == 0) return;
        
        g_npwm_remain = npwm;   //保存脉冲个数
        HAL_TIM_GenerateEvent(&g_timx_npwm_chy_handle, TIM_EVENTSOURCE_UPDATE);  //产生一次软件更新事件,在中断里面处理脉冲输出
        __HAL_TIM_ENABLE(&g_timx_npwm_chy_handle);   //使能定时器TIM8
    }
    
  • 定时器8中断服务函数

    void TIM8_UP_IRQHandler(void)
    {
        HAL_TIM_IRQHandler(&g_timx_npwm_chy_handle);
    }
    
  • 定时器更新中断回调函数

    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
        if(htim->Instance == TIM8)
        {
            if(g_npwm_remain)
            {
                TIM8->RCR = g_npwm_remain - 1;
                HAL_TIM_GenerateEvent(&g_timx_npwm_chy_handle, TIM_EVENTSOURCE_UPDATE);
                __HAL_TIM_ENABLE(&g_timx_npwm_chy_handle);
                g_npwm_remain = 0;
            }
            else
            {
                TIM8->CR1 &= ~(1 << 0);
            }
        }
    }
    
  • 主函数

    void LED_init(void)
    {
        GPIO_InitTypeDef gpio_init_struct;
        
        gpio_init_struct.Pin = GPIO_PIN_5;
        gpio_init_struct.Mode = GPIO_MODE_INPUT;      //设置为输入,防止冲突(PC6)
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_LOW;
        HAL_GPIO_Init(GPIOE, &gpio_init_struct);
    }
    
    int main(void)
    {
        uint8_t t = 0;
        uint8_t key = 0;
        
        HAL_Init();                             /* 初始化HAL库 */
        sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
        delay_init(72);                         /* 延时初始化 */
    //    usart_init(115200);                     /* 串口初始化为115200 */
        led_init();                             /* 初始化LED */
        key_init();
        
        LED_init();   //需要重新配置PE5为输入模式防止冲突
     
        atim_timx_npwm_chy_init(5000 -1, 7200 - 1);
        
        atim_timx_npwm_chy_set(5); 
    
         
        while(1)
        {
            key = key_scan(0);
            
            if(key == 4)
            {
                atim_timx_npwm_chy_set(6);
            }
            
            t++;
            if(t > 50)
            {
                t = 0;
                LED0_TOGGLE();
            }
            delay_ms(10);
        }
    }  
    

七、高级定时器输出比较模式实验

1. 输出比较模式功能

输出比较模式下翻转功能作用是:当计数器的值等于捕获/比较寄存器影子寄存器的值时, OC1REF发生翻转,进而控制通道输出(OCx)翻转。通过翻转功能实现输出PWM的具体原理如下:PWM频率自动重载寄存器(TIMx_ARR)的值决定,在这个过程中,只要自动重载寄存器的值不变,那么PWM占空比就固定为50%。可以通过捕获/比较寄存器(TIMx_CCRx)的值改变PWM的相位
在这里插入图片描述

2. 相关寄存器

  • 控制寄存器1TIMx_CR1
    在这里插入图片描述

  • 捕获/比较模式寄存器1/2TIMx_CCMR1/2
    在这里插入图片描述

  • 捕获/比较使能寄存器TIMx_CCER
    在这里插入图片描述

  • 捕获/比较寄存器1/2/3/4TIMx_CCR1/2/3/4
    在这里插入图片描述

  • TIM1/TIM8断路和死区寄存器TIMx_BDTR
    在这里插入图片描述

3. 高级定时器输出比较模式实验配置步骤

  1. 配置定时器基础工作参数

    HAL_TIM_OC_Init()
    
  2. 定时器输出比较MSP初始化

    HAL_TIM_OC_MspInit() 
    
  3. 配置输出比较模式等

    HAL_TIM_OC_ConfigChannel()
    
  4. 使能通道预装载

    __HAL_TIM_ENABLE_OCxPRELOAD()
    
  5. 使能输出、主输出、计数器

    HAL_TIM_OC_Start()
    
  6. 修改捕获/比较寄存器的值

    __HAL_TIM_SET_COMPARE()
    
  • 具体函数功能

    函数主要寄存器主要功能
    HAL_TIM_OC_Init()CR1、ARR、PSC初始化定时器基础参数
    HAL_TIM_OC_MspInit存放NVIC、CLOCK、GPIO初始化代码
    HAL_TIM_OC_ConfigChannel()CCMRx、CCRx、CCER设置输出比较模式、比较值、输出极性等
    __HAL_TIM_ENABLE_OCxPRELOAD()CCMRx使能通道预装载
    HAL_TIM_OC_Start()CR1、CCER、BDTR使能输出比较、主输出、启动计数器
    __HAL_TIM_SET_COMPARE()CCRx修改捕获/比较寄存器的值

4. 代码实现

  • 功能:

    使用输出比较模式的翻转功能,通过定时器8的4路通道输出占空比固定为50%、相位分别是25%、50%、75%和100%的PWM。

  • 高级定时器输出比较初始化

    void atim_timx_comp_pwm_init(uint16_t arr, uint16_t psc)
    {
        TIM_OC_InitTypeDef timx_oc_comp_pwm = {0};
        
        g_timx_comp_pwm_handle.Instance = TIM8;
        g_timx_comp_pwm_handle.Init.Prescaler = psc;
        g_timx_comp_pwm_handle.Init.CounterMode = TIM_COUNTERMODE_UP;  //向上计数模式
        g_timx_comp_pwm_handle.Init.Period = arr;
        g_timx_comp_pwm_handle.Init.RepetitionCounter = 0;  //重复计数器初始值
        HAL_TIM_PWM_Init(&g_timx_comp_pwm_handle);
        
        timx_oc_comp_pwm.OCMode = TIM_OCMODE_TOGGLE;  //模式选择翻转
        timx_oc_comp_pwm.OCPolarity = TIM_OCPOLARITY_HIGH;   //极性选择高电平
        HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &timx_oc_comp_pwm, TIM_CHANNEL_1);
        HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &timx_oc_comp_pwm, TIM_CHANNEL_2);
        HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &timx_oc_comp_pwm, TIM_CHANNEL_3);
        HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &timx_oc_comp_pwm, TIM_CHANNEL_4);
        
        //使能通道预装载
        __HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_1);
        __HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_2);
        __HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_3);
        __HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_4);
        
        //使能输出、主输出、计数器
        HAL_TIM_OC_Start(&g_timx_comp_pwm_handle, TIM_CHANNEL_1);
        HAL_TIM_OC_Start(&g_timx_comp_pwm_handle, TIM_CHANNEL_2);
        HAL_TIM_OC_Start(&g_timx_comp_pwm_handle, TIM_CHANNEL_3);
        HAL_TIM_OC_Start(&g_timx_comp_pwm_handle, TIM_CHANNEL_4);
    }
    
  • 定时器输出比较MSP函数

    void HAL_TIM_OC_MspInit(TIM_HandleTypeDef *htim)
    {
        if(htim->Instance == TIM8)
        {
            GPIO_InitTypeDef gpio_init_struct;
            
            __HAL_RCC_TIM8_CLK_ENABLE();
            __HAL_RCC_GPIOC_CLK_ENABLE();
                   
            gpio_init_struct.Mode = GPIO_MODE_AF_PP;  //推挽复用
            gpio_init_struct.Pin = GPIO_PIN_6;
            gpio_init_struct.Pull = GPIO_NOPULL;      //没有上下拉
            gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
            HAL_GPIO_Init(GPIOC, &gpio_init_struct);
            
            gpio_init_struct.Pin = GPIO_PIN_7;
            HAL_GPIO_Init(GPIOC, &gpio_init_struct);
            
            gpio_init_struct.Pin = GPIO_PIN_8;
            HAL_GPIO_Init(GPIOC, &gpio_init_struct);
            
            gpio_init_struct.Pin = GPIO_PIN_9;
            HAL_GPIO_Init(GPIOC, &gpio_init_struct);
        }
    }
    
  • 主函数

    int main(void)
    {
        uint8_t t = 0;
     
        
        HAL_Init();                             /* 初始化HAL库 */
        sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
        delay_init(72);                         /* 延时初始化 */
        usart_init(115200);                     /* 串口初始化为115200 */
        led_init();                             /* 初始化LED */
        key_init();
     
        atim_timx_comp_pwm_init(1000 - 1, 72 - 1);
        
        __HAL_TIM_SET_COMPARE(&g_timx_comp_pwm_handle, TIM_CHANNEL_1, 250 - 1);
        __HAL_TIM_SET_COMPARE(&g_timx_comp_pwm_handle, TIM_CHANNEL_2, 500 - 1);
        __HAL_TIM_SET_COMPARE(&g_timx_comp_pwm_handle, TIM_CHANNEL_3, 750 - 1);
        __HAL_TIM_SET_COMPARE(&g_timx_comp_pwm_handle, TIM_CHANNEL_3, 1000 - 1);
       
        while(1)
        {     
            t++;
            if(t > 50)
            {
                t = 0;
                LED0_TOGGLE();
            }
            delay_ms(10);
        }
    }  
    
  • 程序运行结果
    在这里插入图片描述

八、高级定时器互补输出带死区控制实验

1. 互补死区的理解

  • 互补输出
    在这里插入图片描述

​ CH1输出黄色的PWM,互补通道CH1N输出绿色的PWM,两个信号恰好是相反的,CH1的PWM为高电平期 间,CH1N的PWM则是低电平,反之亦然,这就是互补输出

  • 带死区控制的互补输出
    在这里插入图片描述

    CH1输出的PWM和CH1N输出的PWM在高低电平转换期间,插入了一段时间才实现互补输出。这段时间称为死区时间。上图箭头所指的区域就是死区时间,两段时间相同。

  • 死区互补控制H桥
    在这里插入图片描述

    • 当Q1和Q4导通,电流的方向是从左到右;当Q2和Q3导通,电流的方向是从右到左。
    • 因为元器件具有延时特性,控制信号经过OC1传输到电机需要一定的时间。由于元器件的特性,就会直接导致直接使用互补输出信号驱动H桥存在短路现象。为了避免这种情况,就有了带死区控制的互补输出来驱动H桥。用户必须根据与输出相连接的器件及其特性(电平转换器的固有延迟、开关器件产生的延迟)来调整死区时间。
  • 死区时间计算
    在这里插入图片描述

2. 相关寄存器

  • 控制寄存器1TIMx_CR1
    在这里插入图片描述

  • 捕获/比较模式寄存器1/2TIMx_CCMR1/2
    在这里插入图片描述

  • 捕获/比较使能寄存器TIMx_CCER
    在这里插入图片描述

  • 捕获/比较寄存器1/2/3/4TIMx_CCR1/2/3/4
    在这里插入图片描述

  • TIM1/TIM8断路和死区寄存器TIMx_BDTR
    在这里插入图片描述

    对应的结构框图
    在这里插入图片描述

  • 刹车断路功能
    在这里插入图片描述

    发生刹车后:
    在这里插入图片描述

3. 高级定时器互补输出带死区控制实验配置步骤

  1. 配置定时器基础工作参数

    HAL_TIM_PWM_Init()
    
  2. 定时器PWM输出MSP初始化

    HAL_TIM_PWM_MspInit()  
    
  3. 配置PWM模式/比较值等

    HAL_TIM_PWM_ConfigChannel()
    
  4. 配置刹车功能、死区时间等

    HAL_TIMEx_ConfigBreakDeadTime()
    
  5. 使能输出、主输出、计数器

    HAL_TIM_PWM_Start()
    
  6. 使能互补输出、主输出、计数器

    HAL_TIMEx_PWMN_Start()
    
  • 相关函数功能

    函数主要寄存器主要功能
    HAL_TIM_PWM_Init()CR1、ARR、PSC初始化定时器基础参数
    HAL_TIM_PWM_MspInit()存放NVIC、CLOCK、GPIO初始化代码
    HAL_TIM_PWM_ConfigChannel()CCMRx、CCRx、CCER配置PWM模式、比较值、输出极性等
    HAL_TIMEx_ConfigBreakDeadTime()BDTR配置刹车功能、死区时间等
    HAL_TIM_PWM_Start()CCER、CR1使能输出、主输出、启动计数器
    HAL_TIMEx_PWMN_Start()CCER、CR1使能互补输出、主输出、启动计数器

4. 代码实现

  • 功能:

    1. 利用TIM1_CH1(PE9)输出70%占空比的PWM波,它的互补输出通道(PE8)则是输出30%占空比的PWM波。
    2. 刹车功能,当给刹车输入引脚(PE15)输入高电平时,进行刹车,即PE8和PE9停止输出PWM波。
    3. LED0闪烁指示程序运行。
  • 高级定时器互补输出初始化函数

    void atim_timx_cplm_pwm_init(uint16_t arr, uint16_t psc)
    {
        TIM_OC_InitTypeDef timx_oc_cplm_pwm = {0};
        
        g_timx_cplm_pwm_handle.Instance = TIM1;                       //定时器选择
        g_timx_cplm_pwm_handle.Init.Prescaler = psc;                  //定时器分频
        g_timx_cplm_pwm_handle.Init.Period = arr;
        g_timx_cplm_pwm_handle.Init.CounterMode = TIM_COUNTERMODE_UP; //定时器计数模式
        g_timx_comp_pwm_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV4;  //CKD[1:0] = 10
        HAL_TIM_PWM_Init(&g_timx_cplm_pwm_handle);   //初始化PWM
        
        
        timx_oc_cplm_pwm.OCMode = TIM_OCMODE_PWM1;             //模式选择PWM模式1
        timx_oc_cplm_pwm.OCPolarity = TIM_OCPOLARITY_HIGH;     //OC1高电平有效
        timx_oc_cplm_pwm.OCNPolarity = TIM_OCPOLARITY_HIGH;    //OC1N高电平有效
        timx_oc_cplm_pwm.OCIdleState = TIM_OCIDLESTATE_RESET;  //当MOE = 0, OC1 = 1;
        timx_oc_cplm_pwm.OCNIdleState = TIM_OCNIDLESTATE_RESET;//当MOE = 0, OC1x = 1;
        HAL_TIM_PWM_ConfigChannel(&g_timx_cplm_pwm_handle, &timx_oc_cplm_pwm, TIM_CHANNEL_1);
        
        //设置死区参数
        g_sbreak_dead_time_config.OffStateRunMode = TIM_OSSR_DISABLE;           //运行模式下的关闭状态选择
        g_sbreak_dead_time_config.OffStateIDLEMode = TIM_OSSI_DISABLE;          //空闲模式下的关闭状态选择
        g_sbreak_dead_time_config.LockLevel = TIM_LOCKLEVEL_OFF;                //寄存器锁定配置
        g_sbreak_dead_time_config.BreakState = TIM_BREAK_ENABLE;                //断路输入使能控制
        g_sbreak_dead_time_config.BreakPolarity = TIM_BREAKPOLARITY_HIGH;       //断路输入极性
        g_sbreak_dead_time_config.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE; //自动恢复输出使能控制
        HAL_TIMEx_ConfigBreakDeadTime(&g_timx_cplm_pwm_handle, &g_sbreak_dead_time_config);
        
        HAL_TIM_PWM_Start(&g_timx_cplm_pwm_handle, TIM_CHANNEL_1);    //启动定时器的正常输出
        HAL_TIMEx_PWMN_Start(&g_timx_cplm_pwm_handle, TIM_CHANNEL_1); //启动定时器的互补输出
    }
    
  • 定时器设置输出比较值死区时间

    void atim_timx_cplm_pwm_set(uint16_t ccr, uint8_t dtg)
    {
        //设置比较寄存器的值
        __HAL_TIM_SET_COMPARE(&g_timx_cplm_pwm_handle, TIM_CHANNEL_1, ccr);
        
        //单独进行死区时间的设置
        g_sbreak_dead_time_config.DeadTime = dtg;
        HAL_TIMEx_ConfigBreakDeadTime(&g_timx_cplm_pwm_handle, &g_sbreak_dead_time_config);
    }
    
  • 主函数

    int main(void)
    {
        uint8_t t = 0;
     
        
        HAL_Init();                             /* 初始化HAL库 */
        sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
        delay_init(72);                         /* 延时初始化 */
        usart_init(115200);                     /* 串口初始化为115200 */
        led_init();                             /* 初始化LED */
        key_init();
     	
        //设置1khz的周期
        atim_timx_cplm_pwm_init(1000 - 1, 72 - 1);
        
        //设置TIM捕获比较寄存器值,从而控制占空比为70%
        atim_timx_cplm_pwm_set(700 - 1, 100);
       
        while(1)
        {     
            t++;
            if(t > 50)
            {
                t = 0;
                LED0_TOGGLE();
            }
            delay_ms(10);
        }
    }  
    
  • 程序运行流程
    在这里插入图片描述

  • 程序运行结果
    在这里插入图片描述

九、高级定时器PWM输入模式实验

1. PWM输入工作原理

在这里插入图片描述

  1. 确定定时器的时钟源:本代码使用内部时钟(CK_INT),时钟频率为72MHz,计数频率确定了测量精度。
  2. 确定PWM输入的通道:PWM输入模式下测量PWM,PWM信号输入只能从通道1或通道2输入。
  3. 确定IC1和IC2的捕获边沿:以通道 1(CH1)输入 PWM 为例,一般设置 IC1 捕获边沿为上升沿捕获,IC2 捕获边沿为下降沿捕获。
  4. 选择触发输入信号:选择TI1FP1或TI2FP2。
  5. 从模式选择:复位模式,在出现所选触发输入(TRGI)上升沿时,重新初始化计数器并生成一个寄存器更新事件。
  6. 读取一个PWM周期内计数器的计数个数,以及高电平期间计数个数,再结合计数器的计数周期,最终通过计算得到输入的PWM周期和占空比等参数。

2. 输入模式时序图

在这里插入图片描述

假设计数器的计数频率是72MHz,那我们就可以计算出PWM的周期、频率和占空比等参数。由计数器的计数频率为72Hz,可以得到计数器计一个数的时间是13.8ns(即测量的精度是13.8ns)。知道了测量精度,再来计算PWM的周期,PWM周期=(4+1)*(1/72000000)=69.4ns,那么PWM的频率就是14.4MHz。占空比= (2+1)/(4+1)=3/5(即占空比为60%)。

3. 相关寄存器

  • 从模式控制寄存器TIMx_SMCR
    在这里插入图片描述

  • 捕获/比较模式寄存器1/2TIMx_CCMR1/2
    在这里插入图片描述

  • 捕获/比较使能寄存器TIMx_CCER
    在这里插入图片描述

  • 捕获/比较寄存器1/2/3/4TIMx_CCR1/2/3/4
    在这里插入图片描述

  • DMA中断使能寄存器TIMx_DIER
    在这里插入图片描述

4. 高级定时器PWM输入模式实验配置步骤

  1. 配置定时器基础工作参数

    HAL_TIM_IC_Init()
    
  2. 定时器捕获输入MSP初始化

    HAL_TIM_IC_MspInit()
    
  3. 配置IC1/2映射、捕获边沿等

    HAL_TIM_IC_ConfigChannel()
    
  4. 配置从模式,触发源等

    HAL_TIM_SlaveConfigSynchro()
    
  5. 设置优先级,使能中断

    HAL_NVIC_SetPriority() 
    HAL_NVIC_EnableIRQ()
    
  6. 使能捕获、捕获中断及计数器

    HAL_TIM_IC_Start_IT() 
    HAL_TIM_IC_Start()
    
  7. 编写中断服务函数

    TIMx_IRQHandler()
    
  8. 编写输入捕获回调函数

    HAL_TIM_IC_CaptureCallback()
    
  • 函数具体功能

    函数主要寄存器主要功能
    HAL_TIM_IC_Init()CR1、ARR、PSC初始化定时器基础参数
    HAL_TIM_IC_MspInit()存放NVIC、CLOCK、GPIO初始化代码
    HAL_TIM_IC_ConfigChannel()CCMRx、CCER配置通道映射、捕获边沿、分频、滤波等
    HAL_TIM_SlaveConfigSynchro()SMCR、CCER配置从模式、触发源、触发边沿等
    HAL_TIM_IC_Start_IT()CCER、DIER、CR1使能输入捕获、捕获中断并启动计数器
    HAL_TIM_IRQHandler()SR定时器中断处理公用函数,处理各种中断
    HAL_TIM_IC_CaptureCallback()定时器输入捕获回调函数,由用户重定义

4. 代码实现

  • 功能:

    首先通过TM3_CH2(PB5)输出PWM波。然后把PB5输出的PWM波用杜邦线接入PC6(定时器8通道1),最后通过串口打印PWM波的脉宽和频率等信息。通过LED1闪烁来提示程序正在运行。

  • 通用定时器PWM输出初始化函数

    void gtim_timx_pwm_chy_init(uint16_t arr, uint16_t psc)
    {
        TIM_OC_InitTypeDef timx_oc_pwm_chy;
        
        g_timx_pwm_chy_handle.Instance = TIM3;
        g_timx_pwm_chy_handle.Init.Prescaler = psc;
        g_timx_pwm_chy_handle.Init.Period = arr;
        g_timx_pwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
        HAL_TIM_PWM_Init(&g_timx_pwm_chy_handle);
        
        timx_oc_pwm_chy.OCMode = TIM_OCMODE_PWM1;
        timx_oc_pwm_chy.Pulse = arr / 2;
        timx_oc_pwm_chy.OCPolarity = TIM_OCPOLARITY_LOW;
        HAL_TIM_PWM_ConfigChannel(&g_timx_pwm_chy_handle, &timx_oc_pwm_chy, TIM_CHANNEL_2);
        HAL_TIM_PWM_Start(&g_timx_pwm_chy_handle, TIM_CHANNEL_2);
    }
    
  • 定时器输出PWM MSP初始化函数

    void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
    {
        if(htim->Instance == TIM3)
        {
            GPIO_InitTypeDef gpio_init_struct;
            __HAL_RCC_GPIOB_CLK_ENABLE();
            __HAL_RCC_TIM3_CLK_ENABLE();
    
            gpio_init_struct.Pin = GPIO_PIN_5;
            gpio_init_struct.Mode = GPIO_MODE_AF_PP;            /* 推挽复用 */
            gpio_init_struct.Pull = GPIO_PULLUP;                /* 上拉 */
            gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;      /* 高速 */
            HAL_GPIO_Init(GPIOB, &gpio_init_struct);
            
            __HAL_RCC_AFIO_CLK_ENABLE();
            __HAL_AFIO_REMAP_TIM3_PARTIAL();
        }
    }
    
  • PWM输入模式初始化函数

    void atim_timx_pwmin_chy_init(void)
    {
        TIM_SlaveConfigTypeDef slave_config = {0};
        TIM_IC_InitTypeDef tim_ic_pwmin_chy = {0};
    
        g_timx_pwmin_chy_handle.Instance = TIM8;                        /* 定时器8 */
        g_timx_pwmin_chy_handle.Init.Prescaler = 0;                     /* 定时器预分频系数 */
        g_timx_pwmin_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;  /* 递增计数模式 */
        g_timx_pwmin_chy_handle.Init.Period = 65535;                    /* 自动重装载值 */
        HAL_TIM_IC_Init(&g_timx_pwmin_chy_handle);
        
        /* 从模式配置,IT1触发更新 */
        slave_config.SlaveMode = TIM_SLAVEMODE_RESET;                   /* 从模式:复位模式 */
        slave_config.InputTrigger = TIM_TS_TI1FP1;                      /* 定时器输入触发源:TI1FP1 */
        slave_config.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;      /* 上升沿检测 */
        slave_config.TriggerFilter = 0;                                 /* 不滤波 */
        HAL_TIM_SlaveConfigSynchro(&g_timx_pwmin_chy_handle, &slave_config);
    
        /* IC1捕获:上升沿触发TI1FP1 */
        tim_ic_pwmin_chy.ICPolarity = TIM_ICPOLARITY_RISING;            /* 上升沿检测 */
        tim_ic_pwmin_chy.ICSelection = TIM_ICSELECTION_DIRECTTI;        /* 选择输入端IC1映射到TI1 */
        tim_ic_pwmin_chy.ICPrescaler = TIM_ICPSC_DIV1;                  /* 不分频 */
        tim_ic_pwmin_chy.ICFilter = 0;                                  /* 不滤波 */
        HAL_TIM_IC_ConfigChannel(&g_timx_pwmin_chy_handle, &tim_ic_pwmin_chy, TIM_CHANNEL_1);
    
        /* IC2捕获:下降沿触发TI1FP2 */
        tim_ic_pwmin_chy.ICPolarity = TIM_ICPOLARITY_FALLING;           /* 下降沿检测 */
        tim_ic_pwmin_chy.ICSelection = TIM_ICSELECTION_INDIRECTTI;      /* 选择输入端IC2映射到TI1 */
        HAL_TIM_IC_ConfigChannel(&g_timx_pwmin_chy_handle, &tim_ic_pwmin_chy, TIM_CHANNEL_2);
    
        HAL_TIM_IC_Start_IT(&g_timx_pwmin_chy_handle, TIM_CHANNEL_1);
        HAL_TIM_IC_Start(&g_timx_pwmin_chy_handle, TIM_CHANNEL_2);
    }
    
  • 定时器输入捕获MSP初始化函数

    void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
    {
        if(htim->Instance == TIM8)
        {
            GPIO_InitTypeDef gpio_init_struct = {0};
    
            __HAL_RCC_TIM8_CLK_ENABLE();
            __HAL_RCC_GPIOC_CLK_ENABLE();
    
            gpio_init_struct.Pin = GPIO_PIN_6;
            gpio_init_struct.Mode = GPIO_MODE_AF_PP;
            gpio_init_struct.Pull = GPIO_PULLDOWN;
            gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
            HAL_GPIO_Init(GPIOC, &gpio_init_struct);
            
            /* TIM1/TIM8有独立的输入捕获中断服务函数 */
            HAL_NVIC_SetPriority(TIM8_CC_IRQn, 1, 3);
            HAL_NVIC_EnableIRQ(TIM8_CC_IRQn);
        }
    }
    
  • 定时器8输入捕获中断服务函数

    void TIM8_CC_IRQHandler(void)
    {
        HAL_TIM_IRQHandler(&g_timx_pwmin_chy_handle); /* 定时器共用处理函数 */
    }
    
  • PWM输入模式重新启动捕获函数

    void atim_timx_pwmin_chy_restart(void)
    {
        sys_intx_disable();                     /* 关闭中断 */
    
        g_timxchy_pwmin_sta = 0;                /* 清零状态,重新开始检测 */
        g_timxchy_pwmin_hval=0;
        g_timxchy_pwmin_cval=0;
    
        sys_intx_enable();                      /* 打开中断 */
    }
    
  • 定时器输入捕获中断处理回调函数

    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
    {
        if(htim->Instance == TIM8)
        {
            if(g_timxchy_pwmin_sta == 0)
            {
                if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
                {
                    g_timxchy_pwmin_hval = HAL_TIM_ReadCapturedValue(&g_timx_pwmin_chy_handle, TIM_CHANNEL_2) + 1 + 1;
                    g_timxchy_pwmin_cval = HAL_TIM_ReadCapturedValue(&g_timx_pwmin_chy_handle, TIM_CHANNEL_1) + 1 + 1;
                    g_timxchy_pwmin_sta = 1;
                }
            }
        }
    }
    
  • 主函数

    int main(void)
    {
        uint8_t t = 0;
        double ht, ct, f, tpsc;
    
        HAL_Init();                                 /* 初始化HAL库 */
        sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟, 72Mhz */
        delay_init(72);                             /* 延时初始化 */
        usart_init(115200);                         /* 串口初始化为115200 */
        led_init();                                 /* 初始化LED */
        gtim_timx_pwm_chy_init(10 - 1, 72 - 1);
        
        TIM3->CCR2 = 3;
        
        atim_timx_pwmin_chy_init();
        
        while (1)
        {
            delay_ms(10);
            t++;
    
            if (t >= 20)    /* 每200ms输出一次结果,并闪烁LED0,提示程序运行 */
            {
                if (g_timxchy_pwmin_sta)    /* 捕获了一次数据 */
                {
                    printf("\r\n");                                     /* 输出空,另起一行 */
                    printf("PWM PSC  :%d\r\n", g_timxchy_pwmin_psc);    /* 打印分频系数 */
                    printf("PWM Hight:%d\r\n", g_timxchy_pwmin_hval);   /* 打印高电平脉宽 */
                    printf("PWM Cycle:%d\r\n", g_timxchy_pwmin_cval);   /* 打印周期 */
                    tpsc = ((double)g_timxchy_pwmin_psc + 1) / 72;      /* 得到PWM采样时钟周期时间 */ 
                    ht = g_timxchy_pwmin_hval * tpsc;                   /* 计算高电平时间 */
                    ct = g_timxchy_pwmin_cval * tpsc;                   /* 计算周期长度 */
                    f = (1 / ct) * 1000000;                             /* 计算频率 */
                    printf("PWM Hight time:%.3fus\r\n", ht);            /* 打印高电平脉宽长度 */
                    printf("PWM Cycle time:%.3fus\r\n", ct);            /* 打印周期时间长度 */
                    printf("PWM Frequency :%.3fHz\r\n", f);             /* 打印频率 */ 
                    atim_timx_pwmin_chy_restart(); /* 重启PWM输入检测 */
                } 
    
                LED1_TOGGLE();  /* LED1(GREEN)闪烁 */
                t = 0;
            }
        }
    }
    

声明:资料来源(战舰STM32F103ZET6开发板资源包)

  1. Cortex-M3权威指南(中文).pdf
  2. STM32F10xxx参考手册_V10(中文版).pdf
  3. STM32F103 战舰开发指南V1.3.pdf
  4. STM32F103ZET6(中文版).pdf
  5. 战舰V4 硬件参考手册_V1.0.pdf
  • 13
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值