STM32-10-定时器

news2024/11/18 1:38:39

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

文章目录

  • 一、STM32 基础定时器
    • 1. 基本定时器简介
    • 2. 基本定时器框图
    • 3. 基本定时器相关寄存器
    • 4. 定时器溢出时间计算
    • 5. 基本定时器中断配置步骤
    • 6. 代码实现
  • 二、STM32通用定时器
    • 1. 通用定时器简介
    • 2. 通用定时器框图
      • 1. 时钟源
        • 1. 内部时钟(`CK_INT`)
        • 2. 外部时钟模式1(TI1, TI2)
        • 3. 外部时钟模式2(ETR)
        • 4. 内部触发输入(ITRx)
      • 2. 控制器
      • 3. 时基单元
      • 4. 输入捕获
      • 5. 捕获/比较(公共)
      • 6. 输出比较
    • 3. 通用定时器相关寄存器
  • 三、 通用定时器输出PWM波
    • 1. 通用定时器PWM输出实验配置步骤
    • 2. 代码实现
  • 四、通用定时器输入捕获实验
    • 1. 输入捕获测量脉冲宽度
    • 2. 输入捕获实验相关寄存器
    • 3. 通用定时器输入捕获配置步骤
    • 4. 代码实现
  • 五、通用定时器脉冲计数实验
    • 1. 脉冲计数框图
    • 2. 相关寄存器
    • 3. 通用定时器脉冲计数配置步骤
    • 4. 代码实现
  • 六、高级定时器输出指定个数PWM实验
    • 1. 高级定时器框图
    • 2. 重复计数器特性
    • 3. 相关寄存器
    • 4. 高级定时器输出指定个数PWM实验配置步骤
    • 5. 代码实现
  • 七、高级定时器输出比较模式实验
    • 1. 输出比较模式功能
    • 2. 相关寄存器
    • 3. 高级定时器输出比较模式实验配置步骤
    • 4. 代码实现
  • 八、高级定时器互补输出带死区控制实验
    • 1. 互补死区的理解
    • 2. 相关寄存器
    • 3. 高级定时器互补输出带死区控制实验配置步骤
    • 4. 代码实现
  • 九、高级定时器PWM输入模式实验
    • 1. PWM输入工作原理
    • 2. 输入模式时序图
    • 3. 相关寄存器
    • 4. 高级定时器PWM输入模式实验配置步骤
    • 4. 代码实现

一、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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1713411.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

单链表,双向链表,循环链表

文章目录 链表单链表双向链表循环链表链表的底层结构链表的实现代码 链表 链表分为单链表&#xff0c;双向链表&#xff0c;循环链表。 单链表 如上图所示&#xff0c;链表是由多个节点组成&#xff0c;节点由数据域与指针域组成&#xff0c;数据域用于存储数据&#xff0c;指…

服务器主机托管一站式托管服务有哪些?

服务器主机托管一站式托管服务&#xff0c;作为现代企业信息化建设的重要一环&#xff0c;为企业提供了一种高效、安全、可靠的服务器运行环境。下面&#xff0c;我们将从多个方面详细介绍这一服务的内容。 一、硬件与基础设施 服务器主机托管服务首先涵盖了服务器硬件和网络基…

windows环境redis未授权利用手法总结

Redis未授权产生原因 1.redis绑定在0.0.0.0:6379默认端口&#xff0c;直接暴露在公网&#xff0c;无防火墙进行来源信任防护。 2.没有设置密码认证&#xff0c;可以免密远程登录redis服务 漏洞危害 1.信息泄露&#xff0c;攻击者可以恶意执行flushall清空数据 2.可以通过ev…

前端Vue自定义个性化导航栏菜单组件的设计与实现

摘要&#xff1a; 随着前端技术的飞速发展和业务场景的日益复杂&#xff0c;组件化开发已成为提升开发效率和降低维护成本的关键手段。本文将以Vue uni-app平台为例&#xff0c;介绍如何通过自定义导航栏菜单组件&#xff0c;实现业务逻辑与界面展示的解耦&#xff0c;以及如何…

25 使用MapReduce编程了解垃圾分类情况

测试数据中1表示可回收垃圾&#xff0c;2表示有害垃圾&#xff0c;4表示湿垃圾&#xff0c;8表示干垃圾。 统计数据中各类型垃圾的数量&#xff0c;分别存储可回收垃圾、有害垃圾、湿垃圾和干垃圾的统计结果。 &#xff08;存储到4个不同文件中&#xff0c;垃圾信息&#xff0…

高效记录收支明细,预设类别账户,智能统计财务脉络,轻松掌握个人财务!

收支明细管理是每位个人或企业都必须面对的财务任务&#xff0c;财务管理已经成为我们生活中不可或缺的一部分。如何高效记录收支明细&#xff0c;预设类别账户&#xff0c;智能统计财务脉络&#xff0c;轻松掌握个人财务&#xff1f;晨曦记账本为您提供了完美的解决方案&#…

JVM的垃圾回收机制--GC

垃圾回收机制&#xff0c;是java提供的对于内存自动回收的机制。java不需要像C/C那样手动free()释放内存空间&#xff0c;而是在JVM中封装好了。垃圾回收机制&#xff0c;不是java独创的&#xff0c;现在应该是主流编程语言的标配。GC需要消耗额外的系统资源&#xff0c;而且存…

“2024深圳数字能源展”共同探讨数字能源未来发展方向和挑战

在数字化浪潮的推动下&#xff0c;新一轮科技革命与产业革命正以前所未有的速度加速兴起。在这个时代背景下&#xff0c;数字化技术与能源行业的高度融合&#xff0c;使得能源数字化成为未来发展的必然趋势。数字经济浪潮的蓬勃兴起&#xff0c;为能源行业的数字化转型提供了强…

网络安全-钓鱼篇-利用cs进行钓鱼

一、环境 自行搭建&#xff0c;kill&#xff0c;Windows10&#xff0c;cs 二、原理 如图所示 三、钓鱼演示 首先第一步&#xff1a;打开System Profiler-分析器功能 选择克隆www.baidu.com页面做钓鱼 之后我们通过包装域名&#xff0c;各种手段让攻击对象访问&#xff1a;h…

XXE漏洞详解——进阶篇

读取文件时有特殊符号 在读取文件时&#xff0c;文件中包含"<,>,&"等这些特殊符号时&#xff0c;会被xml解析器解析&#xff0c;报错从而导致读取失败&#xff0c;例如尝试读取以下文件 C:\test.txt 内容&#xff1a; <Baize Sec> payload: <…

搜维尔科技:Movella Xsens用于动画,CG,短视频制作案例

用户名称 广州百漫文化传播有限公司 应用场景 基于Xsens MVN Link 动作捕捉系统的动画制作、CG制作、短视频制作、快速动画MAYA插件、影视动漫实时合成预渲染。 现场照片 《西行纪》内容简介&#xff1a;在远古神明的年代&#xff0c;世间存在着天众、龙众、阿修罗等八部众…

探索 ChatboxAI:智能对话的新时代

在人工智能迅速发展的今天&#xff0c;智能对话已经成为了我们日常生活中不可或缺的一部分。从智能助理到聊天机器人&#xff0c;AI 技术正在改变我们与世界互动的方式。今天&#xff0c;我们要介绍的是一个全新且功能强大的平台——ChatboxAI。 什么是 ChatboxAI&#xff1f;…

S32K --- FLS MCAL配置

一、前言 二、MCAL配置 添加一个Mem_43_infls的模块, infls是访问内部flash, exfls是访问外部flash 2.1 General 这边暂时保持的默认,还没详细的去研究,等有空研究了,我再来更新 2.2 MemInstance 双金“index”的下标“0”可以进里面详细配置,这个是基本操作了。 2.2.1 G…

为什么说想当产品经理,最好的时候就是现在?

今年,随着人工智能(AI)技术的火热,AI产品经理岗位的需求也一路暴涨,薪资也同步水涨船高。 根据美国招聘社交媒体Glassdoor的数据,AI产品经理年收入高达125万元,是普通产品经理年收入的1.43倍,更是项目经理年收入的2.14倍。在中国,大厂AI产品经理的月收入也高达3到7万左右。但即…

国家首批20个“数据要素×”典型案例发布,珈和科技助力金融、气象两案例入榜

5月24日&#xff0c;国家数据局会同生态环境部、交通运输部、金融监管总局、中国科学院、中国气象局、国家文物局、国家中医药局等部门在第七届数字中国峰会主论坛上发布首批20个“数据要素”典型案例。 其中&#xff0c;珈和科技以其专业的产品和服务助力金融服务领域的浙江网…

Linux 系统编程笔记--基本概念(一)

操作系统&#xff1a; 管理计算机硬件和软件资源的计算机程序。 内核&#xff1a; 操作系统的核心&#xff0c;是应用程序连接硬件设备的桥梁。 CPU 可以在两种状态下运行:用户态和内核态&#xff0c;在用户态下运行时&#xff0c;CPU 只能访问用户空间的内存;在内核态运行时&…

【Pandas】深入解析`pd.read_json()`函数

【Pandas】深入解析pd.read_json()函数 &#x1f308; 欢迎莅临我的个人主页&#x1f448;这里是我深耕Python编程、机器学习和自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;并乐于分享知识与经验的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#x…

学习笔记|如何移植NCNN

在计算机视觉和深度学习领域&#xff0c;NCNN&#xff08;Netural Network Computer Vision&#xff09;是一个轻量级的神经网络计算框架&#xff0c;被广泛应用于各类嵌入式设备和移动平台。今天&#xff0c;让我们一同跟随共创社团队的步伐&#xff0c;揭秘他们如何进行NCNN的…

React Hooks是如何保存的

React 函数式组件是没有状态的&#xff0c;需要 Hooks 进行状态的存储&#xff0c;那么状态是怎么存储的呢&#xff1f;Hooks是保存在 Fiber 树上的&#xff0c;多个状态是通过链表保存&#xff0c;本文将通过源代码分析 Hooks 的存储位置。 创建组件 首先我们在组件中添加两…

净念——雷江书画艺术汇报展暨作品捐赠仪式在合阳博物馆盛大开幕

古莘初夏万物秀,丹青妙笔谱华章&#xff01;5月25日上午9时&#xff0c;由合阳县委宣传部、合阳文化和旅游局主办&#xff0c;合阳历史文化博物馆、合阳县旅游发展有限公司承办的净念雷江书画艺术汇报展暨作品捐赠仪式在合阳县历史博物馆盛大开幕。 陕西省原副省长魏增军&#…