写在前面——
《STM32单片机开发应用教程(HAL库版)—基于国信长天嵌入式竞赛实训平台(CT117E-M4)》第四章4.8 TIM—PWM输出实验,讲解TIM 定时与PWM输出的STM32CubeMX配置和程序设计方法
官方例程下载:https://pan.baidu.com/s/1QC5BnDgY1m1me6-ihQ_OUQ?pwd=nqb1
提取码:nqb1
定时器(Timer)最基本的功能就是定时,可以延伸到测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)等。
STM32G4 的定时器功能十分强大,共有10个定时器:
表1 定时器类型
查看STM32G431RB数据手册(资料包芯片手册中),可获得各个定时器通道的引脚分布,如
可以汇总为表2:
STM32G431的通用 TIMx定时器功能特点包括:
16 /32 位向上、向下、向上/向下(中心对齐)计数模式,自动装载计数器(TIMx_CNT)。
16 位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数 为 1~65535 之间的任意数值。
4 个独立通道(TIMx_CH1~4)
计数器模式
通用定时器可以向上计数、向下计数、向上向下双向计数模式。
向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。
向下计数模式:计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。
中央对齐模式(向上/向下计数):计数器从0开始计数到最大值,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。
定时时间计算
基本定时器计数过程主要涉及到三个寄存器内容,分别是计数器寄存器(TIMx_CNT)、预分频器寄存器(TIMx_PSC)、自动重载寄存器(TIMx_ARR),这三个寄存器都是 16 位有效数字,即可设置值为 0 至 65535。
CNT表示当前计数值,ARR是自动重载的最大计数值。PSC是分频系数,对定时器时钟AB1或AB2进行分频,得到当前计数器的频率。)
首先设置预分频器寄存器TIMx_PSC (简写成PSC),得到分频后的频率,频率的倒数就是周期时长。如,定时器时钟fCK_PSC =80MKz、(PSC+1)设为(7999+1),这样fCK_CNT /(PSC+1)分频后的频率值就为10kHz,每一个周期时长也就是0.1ms。
fCK_CNT = fCK_PSC/(PSC+1) // fCK_PSC为定时器时钟频率,来自APB1或者APB2。
再把把(ARR+1)设为(9999+1)。这样我们得到的定时时长就是1s。
Tout(定时时间)=(ARR+1)(PSC+1)/ fCK_PSC
PWM波输出
PWM 输出就是产生一个周期的方波信号,频率、占空比(高电平占总周期的比值)是它的关键参数。
如下图,假定定时器工作在向上计数PWM模式,且当 CNT(当前计数值)
STM32G431 定时器可以用来产生 PWM 输出,其中高级定时器 TIM1 可以输出 4 路。而通用定时器(TIM2、TIM3、TIM4)也可同时产生多达 4 路的 PWM 输出。
通常单片机GPIO口只能输出高电平和低电平,可以利用PWM波,通过改变PWM方波的占空比,从而获得不同的模拟电压值。如,100%对应5v,0%对应0v,50%对应2.5v。
因此,利用PWM波,可以实现呼吸灯(电压值变化,导致LED亮度强弱变化)、电机调速(电压值变化,导致电机速度变化)等。
定时器(Timer)最基本的功能就是定时,这里以官方例程HAL_10_TIM_BASE为例,阐述TIM定时作用的设计方法,实现流水灯。
定时器的定时设计方法:
参数配置:PSC、ARR,开启定时中断。可采用STM32CubeMX或者程序设计来实现。
main.c中调用TIM初始化语句、开启定时中断HAL_TIM_Base_Start_IT(&htim1)。
编写中断回调函数HAL_TIM_PeriodElapsedCallback,实现功能要求。
定时器的参数配置,可以首先使用STM32CubeMX进行初步设置。还可以在程序设计时,进行二次修改。
以TIM3为例,配置出1HZ的频率,也就是1s发生一次溢出中断。
80MHZ经过PSC分频,80MHZ/(7999+1) ,得到10KHZ的时钟频率(周期0.0001s)。ARR设为10K-1,则计数10K次,产生溢出中断,即实现1s的计时 。
STM32CubeMX配置,如下图:
1)定时器参数配置二次修改、初始化
如上,STM32CubeMX参数配置完成后,在工程项目中生成对应的初始化函数MX_TIM1_Init(),并在main.c中自动调用。该函数包含了CubeMX所配置的各项参数。
PSC、ARR等参数,还可以通过对函数MX_TIM1_Init()内部参数进行二次修改,如,官方例程HAL_10_TIM_BASE中:
如上图,其与前述STM32CubeMX中TIM1设置不一样,也就是说MX_TIM1_Init()内部参数可以进行二次修改。PSC=1000,ARR=79,也就是实现近似1/1000s=0.001s计时。
2)开启定时器中断
定时器初始化配置后,还需要开启定时器中断,使用函数 HAL_TIM_Base_Start_IT (&htim1);其中htim1表示定时器TIM1,定义方法:TIM_HandleTypeDef htim1。通常,通过STM32CubeMX配置TIM1后,在time.c文件中自动定义,直接使用它即可。
3)编写中断回调函数
中断回调函数HAL_TIM_PeriodElapsedCallback()是实现TIM定时的关键。按照其设计框架,需要手动编写其代码,实现不同功能要求。
从定时器中断原理来说,每个定时器中断,就执行其中断服务函数HAL_TIM_IRQHandler(在库stm32g4xx_hal_tim.c中已定义)。中断服务函数HAL_TIM_IRQHandler,由HAL库提供,无需用户编辑,但其内部自动执行中断回调函数HAL_TIM_PeriodElapsedCallback。
因此,我们需要编写中断回调函数HAL_TIM_PeriodElapsedCallback,以实现不同功能要求。如官方例程HAL_10_TIM_BASE中,使用TIM1,实现0.1s流水灯效果。中断回调函数HAL_TIM_PeriodElapsedCallback代码编辑为:
/* USER CODE BEGIN 0 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint16_t counter = 0;
static uint16_t led = 0x0001, i = 0, dir = 0;
if(++counter == 100){
counter = 0;
GPIOC->ODR = ~(led << (8 + i));
GPIOD->BSRR |= GPIO_PIN_2;
__nop();__nop();__nop();
GPIOD->BRR |= GPIO_PIN_2;
if(dir == 0){
if(++i == 7){
dir = 1;
}
}else{
if(--i == 0){
dir = 0;
}
}
}
//HAL_TIM_Base_Start_IT(&htim1);
}
/* USER CODE END 0 */
前面参数配置中,实现了定时器0.001s计时,也就是说0.001s发生一次中断,每0.001s执行一次中断回调函数HAL_TIM_PeriodElapsedCallback。因此,中断回调函数内部每100次执行一次LED显示、移位操作,从而实现0.001s *100=0.1s流水灯。
这里,要求实现(固定频率,占空比可调:呼吸灯):初始化定时器定时10ms,输出PWM,使LED1闪烁。
STM32CubeMX中,对定时器PWM方式配置分成2个方面:
1)PWM输出引脚的设定
LED1对应的GPIO是PC8,查阅STM32G431RB数据手册,PC8可用的定时器可用TIM3的通道3。若选用其他GPIO输出PWM,同理,还得查阅该手册,找到合适的TIM通道:
STM32CubeMX中设置PC8输出方式为TIM3_CH3。PD2设置为Output。
其他GPIO、SYS、时钟树(80MHz)配置参考前述。(图略)
2)TIM3的PWM方式配置
如下图,STM32CubeMX中对TIM3的PWM设置。
其中,TIM3的定时周期配置成10ms(PSC=7999,ARR=99),才能达到呼吸灯显示效果,否则呼吸灯显示不明显。
其中,PWM mode1和mode2的概念:
模式1和模式2区别不大,选用模式1即可。
上图中,占空比默认为0,后面在程序设计中可修改。
3)main.c程序设计
与PWM相关的函数有4个:
回调函数: void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
开启PWM: HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
停止PWM :HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1);
配置占空比:__HAL_TIM_SetCompare (&htim3,TIM_CHANNEL_3, i);//i取值0~ARR
设置自动重装值: __HAL_TIM_SET_AUTORELOAD(htim, arr)
这里,主要使用开启PWM、配置占空比2个函数,实现呼吸灯效果。main函数的程序设计如下:
int main(void)
{
HAL_Init();
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM3_Init();
/* USER CODE BEGIN 2 */
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);//关闭锁存器
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);//使能定时器3的通道3,产生PWM
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);//开启锁存器
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_All, 1);//先全部关闭LED
uint16_t i=0;
for(i=0;i<100;i++){__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_3,i);HAL_Delay(20);}
for(i=100;i>0;i--) {__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_3,i); HAL_Delay(20);}
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);//关闭锁存器
}
}
其中,HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3)开启PWM输出,就是在引脚上输出所设定的PWM波。
__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_3,i),通过i的变化,使PWM波占空比变化,,LED显示出呼吸灯效果。
编译、下载该程序,可实现固定频率,占空比可调的PWM波输出,显示呼吸灯效果。
部分题目要求实现频率和占空比都可调节的PWM波,那么,在STM32CubeMX配置中略作修改。
如下图,在STM32CubeMX中,对TIM设置成可修改频率和占空比:
则,程序设计可使用以下2个宏定义函数,进行参数修改:
如,在main.c中编辑函数TIM_Proc():
void TIM_Proc(void)
{
__HAL_TIM_SET_AUTORELOAD (&htim3, 9999);
__HAL_TIM_SetCompare (&htim3, TIM_CHANNEL_3, 2000);
}
最后在while中调用TIM_Proc(),就可以实现TIM3_CH1输出的PWM波,频率为100Hz(PSC=79,ARR=9999),占空比为20%(2000/(ARR+1))。
一般题目中测量PWM波要求使用PA6或PA7引脚。查询TIM引脚分布,可选择PA6–》TIM3_CH1,PA7–》TIM3_CH2。STM32CubeMX配置和程序设计方法,参看前述。
使用示波器,就可以从PA6、PA7观测PWM波,或者打开KEIL仿真功能,检测PA6、PA7的输出波形。
第十二届蓝桥杯嵌入式省赛对PWM的考察为例,PWM部分要求:
设计思路:
1) STM32CubeMX中配置PA7–》TIM3_CH2输出PWM波:2KHz(PSC=399,ARR=99)
2)利用函数__HAL_TIM_SetCompare (&htim3,TIM_CHANNEL_2,20),实现20%占空比。
注意开启PWM:HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1)
3) 按键切换控制,参看4.3节按键控制程序设计(按键扫描函数Key_scan,按键服务函数Key_proc)
STM32CubeMX配置:输出引脚,TIMx_CHx,PSC,ARR
TIM定时和PWM输出,对应的函数有所不同。