STM32的定时器_蓝桥杯信号测量与PWM输出实验_输入捕获/输出比较功能
定时器的PWM生成功能
PWM生成的配置与原理
-
PWM生成与输出比较
在STM8的教程中,我们通过配置TIM1_CCMR寄存器,启用TIM独立通道的PWM生成功能来生成PWM信号,需要注意的是,PWM生成模式属于输出比较功能的一个子集,两者有所不同
根据官方的数据手册对TIM1_CCMR_OCiM功能位的介绍:PWM模式1下计数值CNT>CCRx时通道x为有效电平,反之为无效电平;而输出比较模式的原理是每当CCR=CNT时对输出电平反转
简单来说,PWM生成模式下:通过ARR设定频率,通过CCR设置占空比,起始相位不可更改;输出比较模式则同样通过ARR设置频率,但通过CCR所修改的是起始相位,占空比不可更改(所谓不可更改指的是初始状态确定后,在初始化配置时是可以改变的)
一般来说我们不必了解这些理论也能使用PWM生成功能,如果需要更具体的介绍参阅这篇文章:https://shequ.stmicroelectronics.cn/thread-633899-1-1.html
-
启动PWM生成的各项配置
- 首先配置时钟,具体见前文,总之可得HCLK=80MHz
- 在左侧栏Timers中选择要使用的TIM,在右侧窗口上部分在Channe1菜单选项中选择功能为PWM Generation CH1(TIM15-17需要勾选Activated),让该计时器的通道1输出PWM
- 在右侧窗口的下部分Parameter Settings中配置Prescaler分频系数与Counter Period自动重装载系数(这两处数值对应PSC与ARR)
- 把引脚连接上刚刚启用的计数器独立通道,PA6引脚的功能选择TIM16_CH1,PA7通道选择TIM17_CH1
-
计算输出频率
前文已经写出定时器溢出频率公式(对PWM输出模式也即所生成输出信号的频率):F=CK_PSC/[(PSC+1)(ARR+1)]
根据要求的输出频率,可得ARR和PSC的配置值:
- ARR+1=CK_PSC/[f×(PSC+1)]
比如说,要求输出频率是100Hz,那么ARR+1=80M/[100×(PSC+1)]
可以得到PSC=8000-1,ARR=100-1
-
计算输出占空比
根据输出比较的原理,可知输出信号占空比=CCRx/(ARR+1)
上文在配置时将ARR配置为100-1,为的是方便得到CCRx的配置值:接续上文的配置,当CCRx=10时,输出的便是占空比为10%的100Hz信号
PWM生成的使用
-
使能PWM生成函数HAL_TIM_PWM_Start
void HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim,uint32_t Channel); /* 第一个参数是TIM的句柄,在使用的是TIM1时,此处写&htim1,以此类推 第二个参数是所使用的通道,这部分有宏定义,按TIM_CHANNEL_x格式写即可 */ HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1); //使用例:使用TIM3的CH1输出PWM -
修改频率与占空比
在程序中修改ARR、CCRx参数即可实现修改频率、占空比的效果,根据前文的介绍,实时调整ARR需要禁用auto-reload preload以降低延迟
例如,修改CCR可以使用捕获/比较寄存器设置函数
__HAL_TIM_SET_COMPARE(TIM_HandleTypeDef *htim,uint32_t Channel,__POLARITY__)由前一篇介绍的宏函数的原理可知,这样的修改可以通过直接读写寄存器实现,对于知晓寄存器原理的我们来说比使用前文使用宏函数更便捷
TIM2->ARR=//修改TIM2的ARR,从而修改频率 TIM2->CCR1=//修改TIM2的通道1的CCR,从而修改占空比 -
PWM生成的代码实现
让PA6(TIM16_CH1)输出100Hz(ARR=99,PSC=7999),PA7(TIM17_CH1)输出200Hz(ARR=99,PSC=3999)的信号,两者占空比默认为10%,使用按钮控制占空比,每按下一次增加10%,将两者的频率与占空比都显示到LCD上
uint16_t PA6_frq,PA7_frq; uint8_t PA6_duty=10,PA7_duty=10;//占空比默认为10 /* USER CODE END PV */ HAL_TIM_PWM_Start(&htim16,TIM_CHANNEL_1); //PA6 HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1); //PA7 PA6_frq=10000/(TIM16->ARR+1);//80 000 000/(PSC+1)/(ARR+1) PA7_frq=20000/(TIM17->ARR+1);//计算当前输出频率,单位Hz TIM16->CCR1=PA6_duty; TIM17->CCR1=PA7_duty;//设置当前占空比 /* USER CODE END 2 */ void Key_Proc(void) { key_val=Key_Scan(); key_down = key_val & (key_val^key_old); key_up = ~key_val & (key_val^key_old); key_old = key_val; if(key_down==1) { LCD_Clear(Black); LCD_SetBackColor(Black); LCD_SetTextColor(White); if(++lcd_page==3) lcd_page=0; } if(key_down==2&&lcd_page==0) { PA6_duty+=10; if(PA6_duty==100) PA6_duty=10; TIM16->CCR1=PA6_duty;//更改输出占空比 } if(key_down==3&&lcd_page==0) { PA7_duty+=10; if(PA7_duty==100) PA7_duty=10; TIM17->CCR1=PA7_duty; } } void Lcd_Proc(void) { if(lcd_page==0) { sprintf(buf,"PA6_frq:%dHz",PA6_frq); LCD_DisplayStringLine(Line4,(uint8_t*)buf); sprintf(buf,"PA7_frq:%dHz",PA7_frq); LCD_DisplayStringLine(Line5,(uint8_t*)buf); sprintf(buf,"PA6_duty:%d%%",PA6_duty); LCD_DisplayStringLine(Line7,(uint8_t*)buf); sprintf(buf,"PA7_duty:%d%%",PA7_duty); LCD_DisplayStringLine(Line8,(uint8_t*)buf); } }
定时器的输入捕获功能
信号频率测量的硬件结构
-
电路结构
在蓝桥杯开发板上的四个电位器,左边的R37和R38用于ADC电压测量,而右边的R39和R40便用于频率测量
这两个电位器连接在XL555的OUT与TRIG之间,通过调节电位器可以实现调整555信号发生器的频率,也即产生了待测信号
-
引脚连接
PB4经J9连接到R39的电刷与555的OUT,其可配置为TIM3_CH1的输入引脚
PA15经J10连接到R40的电刷与555的OUT,其可配置为TIM2_CH1的输入引脚
输入捕获的配置与原理
-
启动输入捕获的各项配置
-
在左侧栏Timers中选择TIM2与TIM3,并在Channel1菜单选项中选择功能为Input Capture direct mode(TIM15-17使用时要在右侧窗口勾选Activated)
-
在右侧窗口的下部分Parameter Settings中配置Prescaler分频系数,配置为79(运行频率80MHz减去1)
-
切换到各个TIM各自的NVIC Setting标签页,勾选NVIC Interrupt Table一栏下TIM2 global interrupt的Enabled选项,使能中断
-
把引脚连接上刚刚启用的计数器独立通道,配置PB4为TIM3_CH1,PA15为TIM2_CH1
如果需要测量占空比,还需要进一步配置:
- 将TIM的Channel2选择为Input Capture indirect mode
- 在Channel2的Parameter Settings窗口Polarity Selection中选择Falling Edge
-
-
计算输入频率
捕获开始后,检测到第一个上升沿时,CNT的值复制到CCRx中并开始计数,当再次捕捉到上升沿变化时,根据CNT中的值与上一次捕获的差值,即信号一个周期的持续时间,再根据计数频率可计算信号的频率
由此原理可知得到,一个周期持续时间t=CCRx/[CK_PSC/(PSC+1)]
将此式变形得测量到的频率f=CK_PSC/[(PSC+1)×CCRx]
-
计算输入占空比
在测量频率的基础上,新增另一通道来捕获下降沿(输入捕获功能可按需设置为上升沿捕获、下降沿捕获与都捕获),自第一个上升沿开始,到捕获到下降沿为高电平持续时间,再到捕获到第二个上升沿时为一个周期
因为计算占空比要使用两个通道,所以不能使用TIM15-17这些只有一个通道的定时器
(周期总用时-捕获到下降沿的时间)/周期总用时=占空比
输入捕获的使用
-
输入捕获中断方式启动函数HAL_TIM_IC_Start_IT
在使用此函数后,就会开启输入捕获的中断(务必记得要在初始化配置时开启NVIC中断),在捕获到设定的边沿信号后将会产生一个中断,也就是开启定时器
void HAL_TIM_IC_Start_IT(TIM_HandleTypeDef *htim,uint32_t Channel); /* 第一个参数是TIM的句柄 第二个参数是所使用的通道 */ HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1); //使用例:启用TIM2的CH1进行输入捕获 -
输入捕获中断方式停止函数HAL_TIM_IC_Stop_IT
禁止输入捕获中断,关闭输入捕获通道的函数,也就是把原本启动函数的Start改成Stop(或者说将IC替换为其他功能也就是关闭其它功能的函数)
但是启动与关闭是需要时间的,因此在实现按键或定时启停的功能时如无必要,最好不要直接执行此函数,而是设置一个启停标志位,在检测到标志位为关闭时停止获取到数值的显示
void HAL_TIM_IC_Stop_IT(TIM_HandleTypeDef *htim,uint32_t Channel); -
输入捕获中断回调函数HAL_TIM_IC_CaptureCallback
这个函数将会在捕获到设定边沿产生中断后执行,用于处理输入捕获时的数据
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { //捕获到设定的边沿后执行此函数中的语句 if(htim->Instance==TIM2) { //需要对句柄中的标志进行判断来找到中断来源 //例如此处语句表示TIM2捕获到对应边沿后需要执行对应操作 HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1); //重新开启中断以备下一次捕获 } } -
获取捕获值HAL_TIM_ReadCapturedValue
uint32_t HAL_TIM_ReadCapturedValue(TIM_HandleTypeDef *htim,uint32_t Channel) //该函数将返回捕获到设定边沿时的CCRx值 -
捕获边沿设置函数__HAL_TIM_SET_CAPTUREPOLARITY
宏函数,设置输入信号的捕获边沿
__HAL_TIM_SET_CAPTUREPOLARITY(__HANDLE__,__CHANNEL__,__POLARITY__) //__POLARITY__取值范围有: //TIM_INPUTCHANNELPOLARITY_RISING 上升沿捕获 //TIM_INPUTCHANNELPOLARITY_FALLING 下降沿捕获 //TIM_INPUTCHANNELPOLARITY_BOTHEDGE 双边沿捕获 -
测量频率的代码实现
uint32_t R40_frq,capture_val1=0; uint32_t R39_frq,capture_val2=0; /* USER CODE END PV */ HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1); HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1); /* USER CODE END 2 */ void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if(htim->Instance==TIM2)//如果是TIM2产生了输入捕获中断 { capture_val1=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1);//获取CCR捕获值 TIM2->CNT=0;//清除计数值,准备下一次计数 R40_frq=80000000/(80*capture_val1);//计算频率 HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);//重新开启中断 } if(htim->Instance==TIM3) { capture_val2=HAL_TIM_ReadCapturedValue(&htim3,TIM_CHANNEL_1); TIM3->CNT=0; R39_frq=80000000/(80*capture_val2); HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1); } } -
测量占空比的代码实现
double duty1=0.0; void Lcd_Proc(void) { if(lcd_page==0) { sprintf(buf,"duty1:%.2lf%% ",duty1); LCD_DisplayStringLine(Line5,(uint8_t*)buf); } } void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if(htim->Instance==TIM2) { if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1) //由于使用了两个独立通道来测量占空比,因此要选择中断来源于输入通道 { capture_val1=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1);//捕获上升沿 capture_val1_2=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_2);//捕获下降沿 TIM2->CNT=0; R40_frq=80000000/(80*capture_val1); //以上是计算频率的代码 duty1 = ((1.0*capture_val1_2)/capture_val1)*100; //计算占空比,乘1.0以转换数据类型,乘100以便用%表示 HAL_TIM_IC_Start(&htim2,TIM_CHANNEL_1); HAL_TIM_IC_Start(&htim2,TIM_CHANNEL_2); //重新打开中断 } } }
本文来自博客园,作者:无术师,转载请注明原文链接:https://www.cnblogs.com/artlessist/p/18880718
本文使用知识共享4.0协议许可 CC BY-NC-SA 4.0
特别说明版权归属的文章以及不归属于本人的转载内容(如引用的文章与图片)除外

浙公网安备 33010602011771号