主控板:STM32G431RBT6
板载下载器:DAPLINK
软件:STM32CubeMX、Keil MDK5

一、定时器相关

  • STM32G4系列共有10个定时器:
      TIM可以对输入的时钟进行计数,并在数值达到设定值时触发中断。在STM32中定时器的基准时钟一般都是主频72MHz,并且以16位计数器,预分频器,自动重装寄存器为时基单元,在72MHz计数时钟下可以实现最大59.65s的定时。
定时器类型定时器计数器分辨率计数器类型预分频系数DMA请求生成捕捉/比较通道互补输出
高级控制定时器TIM1,TIM816-bitUp,down,Up/down1-65536Yes44
全功能通用定时器TIM232-bitUp,down,Up/down1-65536Yes4No
全功能通用定时器TIM3,TIM416-bitUp,down,Up/down1-65536Yes4No
通用定时器TIM1516-bitUp1-65536Yes21
通用定时器TIM16,TIM1716-bitUp1-65536Yes11
基本定时器TIM6,TIM716-bitUp1-65536Yes0No
  • 定时器计数模式

    • 向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。
    • 向下计数模式:计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。
    • 中央对齐模式(向上/向下计数):计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。
  • 基本定时器(TIM6和TIM7)
      功能:当累加的时钟脉冲数超过预定值时,能触发中断或者触发DMA请求。是专门用于驱动数模转换器(DAC)。

  • 定时器时间
    定时器时钟频率 f = 最大频率 S Y S C L K / (计数器上限 A R R + 1 )(预分频系数 P S C + 1 ) 定时器时钟频率f=最大频率SYSCLK/(计数器上限ARR+1)(预分频系数PSC+1) 定时器时钟频率f=最大频率SYSCLK/(计数器上限ARR+1)(预分频系数PSC+1
    定时器时间(秒) = 1 / f 定时器时间(秒)=1/f 定时器时间(秒)=1/f

二、PWM输出

1、基本原理

  脉冲宽度调制(PWM),简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。 ​

  • PWM工作模式

    • PWM模式1(向上计数) :计数器从0计数加到自动重装载值(TIMx_ARR),然后重新从0开始计数,并且产生一个计数器溢出事件;
    • PWM模式2(向下计数) :计数器从自动重装载值(TIMx_ARR)减到0,然后重新从重装载值(TIMx_ARR)开始递减,并且产生一个计数器溢出事件。
  • 向上计数模式的PWM工作原理
      STM32的每个通用定时器都有独立的4个通道可以用来作为:输入捕获、输出比较、PWM输出、单脉冲模式输出等。将寄存器值和计数器值比较,通过比较结果输出高低电平,便可以实现脉冲宽度调制模式(PWM信号)。
      在PWM输出模式下,除了CNT(计数器当前值)ARR(自动重装载值) 之外,还多了一个值CCRx(捕获/比较寄存器值)。当CNT小于CCRx时,TIMx_CHx通道输出低电平;当CNT等于或大于CCRx时,TIMx_CHx通道输出高电平。
       TIMx_ARR寄存器确定PWM频率,TIMx_CCRx寄存器确定占空比。

  • 计算公式

    • PWM频率:
      F p w m = S Y S C L K / ( ( a r r + 1 ) ∗ ( p s c + 1 ) ) ( 单位: H z ) Fpwm =SYSCLK / ((arr+1)*(psc+1))(单位:Hz) Fpwm=SYSCLK/((arr+1)(psc+1))(单位:Hz)
      arr - 计数器值,psc - 预分频值
    • 占空比:
      d u t y . c i r c l e = T I M x − > C C R x / a r r duty.circle = TIMx->CCRx / arr duty.circle=TIMx>CCRx/arr
      TIMx->CCRx - 用户设定值
  • 时钟引脚
    在这里插入图片描述

2、工程配置

  • 在前篇配置的工程中继续配置。根据下图中的要求选择PA6,PA7两个引脚完成PWM输出的功能;
    在这里插入图片描述

  • 对比时钟引脚图可知这两个引脚分别有两个定时器通道可以利用,由于在上篇的按键中使用了TIM3定时器,所以在实现PWM输出功能时:PA6->TIM16_CH1PA7->TIM17_CH1
    在这里插入图片描述

  • PA6引脚处的输出频率固定为100Hz,而系统时钟为80MHz,因此需要PSC * ARR * 100=80MHz,如下图所示:在这里插入图片描述

  • PA7处的配置与PA6类似,但是由于PA7处的固定输出频率为200Hz,因此设置PSC值为4000-1

3、代码编写

  • key_change():按键改变函数,B1键切换界面,B2、B3键分别增加PA6、PA7引脚输出PWM的占空比,
    void key_change(){
    //	if(key[0].state==Short_Press){
    if(key[0].single_flag==1){
    	view =!view;
    	key[0].single_flag=0;
    //key[0].state=No_Press;
     }
    
    //if(key[1].state==Short_Press){
     if(key[1].single_flag==1){
    	if(PA6_duty>=90)
    			PA6_duty=10;
    	else
    			PA6_duty+=10;
    	__HAL_TIM_SetCompare(&htim16,TIM_CHANNEL_1,PA6_duty);
    //key[1].state==No_Press;
    	key[1].single_flag=0;
     }
    // if(key[2].state==Short_Press){
     if(key[2].single_flag==1){
    	if(PA6_duty>=90)
    			PA7_duty=10;
    	else
    			PA7_duty+=10;
    	__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,PA7_duty);
    //key[2].state==No_Press;
    	key[2].single_flag=0;
      }
    }
    
  • disp_proc():页面显示函数,
    void disp_proc(){
      	if(view==0){
      		//LCD_Clear(0x000000);
      		LCD_DisplayStringLine(Line1, (uint8_t *)"       Data");
      	}
      	else if(view==1){
      		//LCD_Clear(0x000000);
      		LCD_DisplayStringLine(Line1, (uint8_t *)"       Para");
      		char text[30];
      		sprintf(text,"    PA6:%d %     ",PA6_duty);
      		LCD_DisplayStringLine(Line2, (uint8_t *)text);
      		sprintf(text,"    PA7:%d       ",PA7_duty);
      		LCD_DisplayStringLine(Line4, (uint8_t *)text);
      	}
    }
    

4、硬件实现

  由于手边没有示波器无法观测到输出波形,所以可以通过LED灯的显示亮度观测占空比。按下B1键可以切换界面,按下B2和B3键可以分别增加PA6和PA7的占空比。
在这里插入图片描述

三、输入捕获

1、工程配置

  • 在上述工程中配置,选择PA15和PB4作为数据输入引脚,PB4->TIM3_CH1PA15->TIM2_CH1(由于PB4和按键同时使用了TIM3定时器,会导致按键失效,因此后续需要将按键对应的定时器更换为其他空闲的定时器。);
  • 配置定时器TIM2和TIM3的channel1为直接输入捕获并使能中断:
    在这里插入图片描述
  • 将按键的定时器更换为TIM4:
    在这里插入图片描述

2、代码编写

  • interrupt.c:增加中断回调函数

    //另外需要将按键对应的中断回调函数中的TIM3更改为TIM4
    
    unsigned int ccrl_val1=0,ccrl_val2=0;
    unsigned int frq1=0,frq2=0;
    
    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){
      if(htim->Instance==TIM2){
      	ccrl_val1=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);
      	__HAL_TIM_SetCounter(htim,0);
      	frq1=(80000000/80)/ccrl_val1;
      	HAL_TIM_IC_Start(htim,TIM_CHANNEL_1);
      }
      
      if(htim->Instance==TIM3){
      	ccrl_val2=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);
      	__HAL_TIM_SetCounter(htim,0);
      	frq2=(80000000/80)/ccrl_val2;
      	HAL_TIM_IC_Start(htim,TIM_CHANNEL_1);
      }
    }
    
  • interrupt.h:增加上述函数的声明

    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);
    
  • main.c:增加定义变量freq1、freq2并修改界面显示内容,同时还需要开启定时器TIM4

    extern unsigned int frq1,frq2;
    
    HAL_TIM_Base_Start_IT(&htim4);
    /开启定时器/
    HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);
    HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);
    void disp_proc(){
    	if(view==0){
    		//LCD_Clear(0x000000);
    		LCD_DisplayStringLine(Line1, (uint8_t *)"       Data");
    		char text[30];
    		sprintf(text,"    frq1=%d     ",frq1);
    		LCD_DisplayStringLine(Line2, (uint8_t *)text);
    		sprintf(text,"    frq2=%d     ",frq2);
    		LCD_DisplayStringLine(Line4, (uint8_t *)text);
    	}
    	else if(view==1){
    		//LCD_Clear(0x000000);
    		LCD_DisplayStringLine(Line1, (uint8_t *)"       Para");
    		char text[30];
    		sprintf(text,"    PA6:%d %     ",PA6_duty);
    		LCD_DisplayStringLine(Line2, (uint8_t *)text);
    		sprintf(text,"    PA7:%d       ",PA7_duty);
    		LCD_DisplayStringLine(Line4, (uint8_t *)text);
    	}
    }
    

3、硬件实现

  • 旋转下面的电位器可以调节频率;
    在这里插入图片描述

  • 将PA6、PA7与PB4、PA15连接起来就可以读取到上述程序中设定好的PWM频率。
    在这里插入图片描述

四、问题与总结

  实现各个功能时最好不要复用定时器,否则可能会导致一部分功能失效。第二是在实现界面切换是需要注意进行清屏操作,否则可能会有上一个界面的残留显示,另外还需要注意在识别按键按下并完成相应的操作后,需要将标志按键按下的标志位清零,否则为会识别为按键反复被按下从而影响显示结果。在配置引脚过程中不选择带N的定时器,是因为TIM1_BKIN这种带N的定时器通道是用来生成互补PWM的。

  下面是几个工程中用到的HAL库函数:

//用于启动定时器的中断模式,使用之前需要先初始化好定时器的配置,并将相关的中断处理函数注册到对应的中断向量中
HAL_TIM_Base_Start_IT(&htim4);
//用于在TIM17的CHANNEL1输出占空比为PA7_duty的PWM
__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,PA7_duty);
//用于在中断方式下启动定时器的输入捕获功能:使能定时器的捕获中断,并启动定时器运行
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);
//用于启动定时器输入捕获模式但不会启用输入捕获中断,上一个会启用
HAL_TIM_IC_Start(htim,TIM_CHANNEL_1);
//输入捕获终端回调,用于处理所有定时器的输入捕获中断,用户在该函数内编写实际的任务处理程序
HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);
//用于启用定时器PWM输出通道
HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1);
//在定时器周期性中断事件发生时被调用
HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
//读取当前定时器计数器的值,
HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1)

参考资料
(1)B站学习视频:【备战2024蓝桥杯 嵌入式组】CT117E-M4 新款开发板 3小时省赛模块 速成总结
(2)【STM32】HAL库 STM32CubeMX教程七—PWM输出(呼吸灯)
(3)stm32学习之定时器中断时间设置与计算
(4)【经验分享】STM32G4之基本定时器
(5)HAL库的TIM中断和输入捕获
(6)【STM32学习】——TIM定时中断
(7)STM32基础:定时器输入捕获功能

Logo

欢迎加入西安开发者社区!我们致力于为西安地区的开发者提供学习、合作和成长的机会。参与我们的活动,与专家分享最新技术趋势,解决挑战,探索创新。加入我们,共同打造技术社区!

更多推荐