一.定时器基本原理
1.定时器概念
定时器是微控制器中不可缺少的外设,它主要用于定时、测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)等,本质就是进行计数,选择内部时钟脉冲,作为计数器时,技术信号的来源选择非周期脉冲信号。
2.定时器分类
STM32定时器种类多,功能 强大,这些定时器完全独立、 互不干扰,可以同步操作。分为高级定时器、通用定时器和基本定时器三种,它们之间存在功能上的包含关系,即基本定时器有的功能通用定时器都有,通用定时器又包含了高级定时器的部分功能,而高级定时器则增加了如死区互补输出、刹车信号等更高级的功能。
我们最常用的就是通用定时器,TIM2、TIM3、TIM4、TIM5 为STM32的4个独立的16位通 用定时器,具有定时、测量 输入信号的脉冲长度(输入 捕获)、输出所需波形(输出比较、产生PWM、单脉冲 输出等)等功能。
3.时钟计算方法
定时器的定时时间主要取决于定时周期和预分频因子,计算公式为:
定时时间=(ARR+1)×(预分频值PSC+1)/输入时钟频率
或 T=(TIM_Period +1)*(TIM_Prescaler +1)/TIMxCLK 这里ARR+1是因为计数器都是从0开始计数的。
4.工作原理
(1)时钟产生与选择
通用定时器的时钟源有多种选择,主要包括内部时钟(CK_INT)、外部时钟模式(外部输入脚TIx、外部触发输入ETR)和内部触发输入(ITRx)等。时钟源的选择决定了定时器的计数频率。
(2)计数过程
通用定时器内部有一个计数器,它根据所选的时钟源进行计数。计数器的计数模式可以是向上计数、向下计数或者向上/向下双向计数(中心对齐模式)。
a.向上计数模式:计数器从0开始计数,当计数到自动加载值(TIMx_ARR)时,计数器溢出并重新从0开始计数,同时产生一个计数器溢出事件。
b.向下计数模式:计数器从自动加载值(TIMx_ARR)开始向下计数,当计数到0时,计数器重新从自动加载值开始计数,并产生一个计数器向下溢出事件。
c.中心对齐模式:计数器从0开始计数到自动加载值-1时,产生一个计数器溢出事件,然后向下计数到1时,再次产生一个计数器溢出事件。之后计数器从0开始重新计数。
(3)输入捕获与输出比较
通用定时器具有输入捕获和输出比较功能,这些功能通过定时器的输入捕获通道和输出比较通道实现。
a.输入捕获:输入捕获通道可以捕获外部信号的边沿,并记录捕获时刻计数器的值。这可以用于测量输入信号的脉冲宽度或频率。
b.输出比较:输出比较通道可以将计数器的值与预设的比较值进行比较,当两者相等时,可以产生输出信号或触发中断。这可以用于生成PWM波形或控制外部设备的开关。
二.定时器实验
1.任务要求
之前作业中的延时功能都是通过循环、delay/Hal_delay函数等实现,本次作业通过定时器Timer方式实现时间的精准控制,相当于给CPU上了一个闹钟,CPU平时处理其它任务,当定时时间到了以后,处理定时相关的任务。请设置一个5秒的定时器,每隔5秒从串口发送“hello windows!”;同时设置一个2秒的定时器,让LED等周期性地闪烁,实现一个多任务并发运行的功能。思考,如果不采用定时器,如何同时完成上面两个周期性任务?
2.HAL库—工程创建
通过STM32CubeMx创建新工程,点击左上角的File文件选择“New Project”,在弹出的界面中,下拉列表输入芯片名称“STM32F103C8T6”,右下角选中后,点击“Start Project”,
配置“RCC”,RCC中, HSE选中"Crystal/Ceramic Resonator"项,LSE选择Disable,界面如下:
配置“SYS”,下拉Debug模式 ,选中"Serial Wire"Timebase Source选择“SysTick”,界面如下:
然后进行GPIO口的管脚配置,这里我选择了PA1作为LED指示灯,配置如下:
然后进行定时器配置,选择TIM2和TIM3 选中定时器2;配置定时器2的时钟源为内部时钟;分频系数为71,向上计数模式,计数周期为5000,使能自动重载模式。定时器3参照定时器2的配置。
(注意;分频系数那里虽然写的是71,但系统处理的时候会自动加上1,所以实际进行的是72分频。由于时钟我们一般会配置为72MHZ,所以72分频后得到1MHZ的时钟。1MHZ的时钟,计数5000次,得到时间5000/1000000=0.005秒。也就是每隔0.005秒定时器2会产生一次定时中断。)
接着配置NVIC,开启定时器2的中断:
然后选择USART1,选择USART1进行传输,将串口设置为异步工作模式,
点击Clock Configuration 选择HSE和PLLCLK,进行时钟配置,
设置Project Manager,这里特别注意,代码的生成路径不要有中文,点击“Code Generator”,勾选图示内容,再点击右上角"GENERATE CODE",生成文件。
3.相关代码编写
3.1定时器启用
在主函数启动相应的定时器,代码如下:
HAL_TIM_Base_Start_IT(&htim2);
HAL_TIM_Base_Start_IT(&htim3);
3.2串口通信
在主函数中编写MCU给上位机发送的消息,代码如下:
uint8_t hello[20]="hello windows!\r\n";
3.3定时器中断回调
在主函数中编写中断回调函数,代码如下:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint32_t time_cnt =0;
static uint32_t time_cnt3 =0;
if(htim->Instance == TIM2)
{
if(++time_cnt >= 400)
{
time_cnt =0;
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_1);
}
}
if(htim->Instance == TIM3)
{
if(++time_cnt3 >= 1000)
{
time_cnt3 =0;
HAL_UART_Transmit(&huart1,hello,20,100000);
}
}
}
该函数为定时器的中断回调函数,当产生定时中断的时候,会自动调用这个函数。在函数内部定义了定时器的一个静态变量:time_cnt与定时器3 的time_cnt3。
如果不采用定时器,可以通过循环加延时的方法来实现。但这种方法通常效率较低,并且对于实时性要求较高的任务来说,不是最佳实践。
4.整体编译及串口烧录
先进行编译,确认无误,双击点开“FlyMcu”,然后把MCU的boot0置1,boot1置0,接线时注意TXD->A10;RXD->A9;3V3->3V3;GND->G开始烧录代码,注意更新.hex文件。
5.效果展示
硬件图
最后的效果视频
dingshiqi
三.PWM基本原理
1.PWM介绍
PWM(脉冲宽度调制)是一种通过改变脉冲的宽度来控制模拟电路的有效技术。它的核心在于调节一系列固定频率脉冲的占空比,即高电平时间与整个周期时间的比例,从而模拟出连续的模拟信号。这种技术使得数字硬件如微控制器能够精确控制模拟电路,广泛应用于电机控制、LED调光等领域。
PWM的基本组成
PWM信号由以下几个关键参数组成:
频率(Frequency):指定一秒钟内信号从高电平到低电平再回到高电平的次数,即一秒钟内PWM周期的数量。
占空比(Duty Cycle):一个脉冲周期内,高电平的时间与整个周期时间的比例。占空比的大小决定了PWM等效出来的模拟电压的大小。
分辨率(Resolution):占空比变化的细腻程度,即占空比变化的快慢。
2.PWM的工作原理
PWM通过控制信号的脉冲宽度来实现对电压或电流的精确控制。在具有惯性的系统中,如LED灯或电机,可以通过调制一系列脉冲的宽度来等效地获得所需的模拟参量。例如,通过调节PWM信号的占空比,可以控制流经LED的平均电流,从而调节灯光的亮度。
四.PWM实验
1.任务要求
使用TIM3和TIM4,分别输出一个PWM波形,PWM的占空比随时间变化,去驱动你外接的一个LED以及最小开发板上已焊接的LED(固定接在 PC13 GPIO端口),实现2个 LED呼吸灯的效果。
2.HAL库—创建工程
通过STM32CubeMx创建新工程,点击左上角的File文件选择“New Project”,在弹出的界面中,下拉列表输入芯片名称“STM32F103C8T6”,右下角选中后,点击“Start Project”
配置“RCC”,RCC中, HSE选中"Crystal/Ceramic Resonator"项,LSE选择Disable
配置“SYS”,下拉Debug模式 ,选中"Serial Wire"Timebase Source选择“SysTick”
然后对定时器3和定时器4配置,对于TIM3,设置分频系数为71,向上计数模式,计数周期为500,使能自动重载模式。通道1选择:PWM Generation CH1(PWM输出通道1),设置分频系数为71,计数周期为500,其它默认,定时器四仍然也选择PWM Generation CH1(PWM输出通道1),计数周期根据自己需要进行设置,具体设置见下图:
点击Clock Configuration 选择HSE和PLLCLK,进行时钟配置,
设置Project Manager,这里特别注意,代码的生成路径不要有中文,点击“Code Generator”,勾选图示内容,再点击右上角"GENERATE CODE",生成文件。
最后用Keil打开新建的HAL库工程
3.相关代码编写
(1)找到主函数,设置占空比,定义一个变量,用来存储占空比:初值设为10,代码如下:
uint16_t duty_num3 = 10;
uint16_t duty_num4 = 10;
(2)开启PWM通道:开始TIM3通道3,输出PWM;开始TIM4通道4,输出PWM,代码如下:
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_1);
(3)然后在while(1)中设置每隔50毫秒,占空比加10,如果超过500,自动变成低电平,代码如下:
while (1)
{
/* USER CODE END WHILE */
HAL_Delay(50);
duty_num3 = duty_num3 + 10;
duty_num4 = duty_num4 + 10;
if(duty_num3 > 500)
{
duty_num3 = 0;
}
__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,duty_num3);
if(duty_num4 > 500)
{
duty_num4 = 0;
}
__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_1,duty_num4);
/* USER CODE BEGIN 3 */
}
4.整体编译及串口烧录
先进行编译,确认无误,双击点开“FlyMcu”,然后把MCU的boot0置1,boot1置0,接线时注意TXD->A10;RXD->A9;3V3->3V3;GND->GND开始烧录代码,注意更新.hex文件。
5.效果展示
定时器3,4的一通道分别对应引脚PA6和PB6,其中PB6已经连接了LED灯,但是PA6却是空的,而另一个LED是MCU最小开发板上已焊接的LED(固定接在 PC13 GPIO端口),找一根杜邦线把PA6和P13连在一起后,就可以了,具体硬件连接见下图
效果演示
pwm
五.总结
第一部分:定时器实现多任务并发
之前的实验中,延时功能依赖循环或delay/Hal_delay函数,这种方法效率低,且在延时期间CPU处于阻塞状态,无法处理其他任务。本次实验通过定时器Timer实现了精准的定时中断,相当于为CPU设置了一个闹钟。CPU可以处理其他任务,当定时时间到达时,中断服务程序会执行相应的操作。
我设置了一个5秒定时器(例如TIM2),每隔5秒通过串口发送“hello windows!”;同时设置了一个2秒定时器(例如TIM3),控制LED周期性闪烁。这实现了两个周期性任务的并发运行。
如果不采用定时器,要同时完成这两个周期性任务,只能通过轮询的方式,即在主循环中不断检测时间,如果达到5秒则发送数据,达到2秒则切换LED状态。这种方法效率极低,且时间精度难以保证,尤其当需要处理其他任务时,时间精度会进一步下降,甚至可能导致任务错乱。定时器中断机制则有效避免了这些问题,实现了更精准、更高效的多任务并发。
第二部分:PWM波形控制LED呼吸灯
本部分实验使用TIM3和TIM4分别产生PWM波形,并通过改变占空比来控制两个LED的亮度,实现呼吸灯效果。TIM3控制外接LED,TIM4控制PC13端口的LED。 PWM波形的占空比随时间变化,例如采用正弦波变化,可以产生柔和的呼吸灯效果。 这部分实验需要对PWM的原理以及定时器的配置有深入的理解,包括预分频、自动重装载值、比较值等参数的设置。
心得体会:
通过本次实验,我深刻理解了STM32定时器的强大功能以及在嵌入式系统中的重要作用。定时器不仅可以实现精准的延时控制,更重要的是可以实现多任务的并发运行,提高了系统的效率和实时性。 PWM波形的应用也让我对模拟信号的产生和控制有了更深入的认识。 在实验过程中,我遇到了不少问题,例如定时器配置的细节、PWM波形的产生以及代码调试等,通过查阅资料和不断尝试,最终解决了这些问题,也提升了我的问题解决能力和编程能力。 未来我会继续深入学习STM32的更多功能,并将其应用到更复杂的项目中。