一、定时器
设置一个5秒的定时器,每隔5秒从串口发送“hello windows!”;同时设置一个2秒的定时器,让LED等周期性地闪烁,实现一个多任务并发运行的功能。
(1)定时器介绍
1、定时器定义
1.能够对内部时钟信号或外部输入信号进行计数,数值达到设定要求时,向CPU发起中断请求,完成外部程序的运行。
2.本质就是进行计数,选择内部时钟脉冲,作为计数器时,技术信号的来源选择非周期脉冲信号。
STM32中定时器可分为高级定时器、通用定时器、基本定时器三类,他们都是由一个可编程的16位预分频器(TIMX_PSC)驱动的16位。
在大容量的 STM32F103xx增强型系列产品包含最多2个高级控制定时器、4个普通定时器和2个基本定时器,以及2个看门狗定时器和1个系统嘀嗒定时器,本次的目标芯片(STM32F103C8T6)也是大容量系列,所以具备以上所述的定时器种类
2、定时器的分类
1.基本定时器:功能最少,只能充当基本的时基,甚至都没有外部引脚
2.通用定时器:拥有基本定时器的全部功能,同时有输入捕获模式,用以接收外部的PWM,脉冲之类的信息
3.高级定时器:又有通用定时器的全部功能,又有互补输出模式,功能最为强大
通常我们使用的都是通用定时器
通用定时器特点:
1.位于ABP1低速总线上
2.16位向下,向上/向下(中心对齐模式)计数模式,自动重装载计数器(TIMx_CNT)
3.16位可编程(可以实现修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为1~65535任意数值
4.四个独立通道(TIMx_CH1~4),通道用来支持:
①输入捕获
②输出比较
③PWM生成
④单脉冲模式输出
5.可使用外部信号(TIM_ETR)控制定时器和定时器互连的同步电路
3、定时时钟计算方法
Tout = ((arr+1)(psc+1))/Tclk
其中:
Tclk:定时器的输入时钟频率(单位MHZ)
Tout:定时器溢出时间(单位为us)
arr: 计数装载值
psc: 时钟分频系数
4、工作过程
在选定的时钟源(可以是内部的也可以是外部的)和预分频器TIMX_PSC的驱动下,根据设置的计数模式(向上、向下、中央对齐)自动。
装载计数器TIMX_CNT开始计数;如果使能了相应的事件(更新事件、触发事件、输入捕获、输出比较)则会产生相应的中断。
如果没有开启输入和输出,只使能了计数器计数溢出后自动装载,可以做为一个简单定时器使用,计数器自己开始周期计数
如果开启了通道输入捕获,当检测到ICx信号上相应的边沿后,计数器(CNT)的当前值被锁存到捕获/比较寄存器(TIMx_CCRx)中,通过中断的方式可以读取出来假设为n1,然后更改输入捕获的信号级性(上升沿或下降沿),当再次检测到ICx信号上相应的边沿后,计数器(CNT)的当前值再次被锁存到捕获/比较寄存器(TIMx_CCRx)中假设为n2;n2 -n1节可算出电平的持续时间
如果开启了输出控制,可以产生一个由TIMx_ARR寄存器确定频率、由TIMx_CCRx寄存器确定占空比的PWM信号。
如果选择外部的同步时钟信号(TI1F_ED、TI1FP1、TI2FP2)作为计数器的时钟源,可以用来统计脉冲,实现脉冲频率采集功能
(2)使用STM32CubeMX创建项目
①新建工程
选择芯片STM32F103C8T6
②RCC配置
③SYS配置
④GPIO配置
⑤配置定时器2和定时器3
分频系数虽然写的是71,但系统处理的时候会自动加上1,所以实际进行的是72分频。由于时钟我们一般会配置为72MHZ,所以72分频后得到1MHZ的时钟。1MHZ的时钟,计数5000次,得到时间5000/1000000=0.005秒。也就是每隔0.005秒定时器2会产生一次定时中断
⑥设置USART
⑦配置中断
⑦设置USART
⑧Clock Configuration设置
⑨Project Manager设置
⑩创建完成并用keil打开
(3)编写代码
①启动定时器
在main函数中添加代码
HAL_TIM_Base_Start_IT(&htim2);
HAL_TIM_Base_Start_IT(&htim3);
该函数表示启动相应的定时器,“h”表示HAL库,“tim2”表示定时器2。所以这行代码的意思就是启动定时器2和定时器3。
②在main.c中定义STM32需要给上位机发送的消息
uint8_t hello[20]="hello windows!\r\n";
③定时器中断回调函数
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。
例如time_cnt,当它大于等于100的时候,才会执行if里面的代码。也就是说需要发生400次中断,才会让LED的状态翻转。前面已经算过了,一次定时中断的时间是0.005秒,所以400次中断的时间是0.005400=2秒。也就是说每隔2秒,LED的状态翻转一次。
(4)编译与烧录
(5)结果演示
1.串口调试
结果一
2.LED灯
结果二
(6)观察波形
设置Debug
调试
选择逻辑分析仪
选择要观察的引脚
UART #1输出设置
运行
观察输出波形
思考,如果不采用定时器,如何同时完成上面两个周期性任务?
①基于忙等待(循环计数)的方法
可以通过循环计数来模拟时间的流逝。假设我们知道处理器执行一次循环所花费的大致时间,就可以通过循环一定的次数来实现定时功能。例如,先通过实验或者查阅芯片手册等方式,估算出处理器执行一个简单循环(如 for (i = 0; i < N; i++))的时间。如果这个循环执行一次的时间是 t 毫秒,那么要实现 2 秒(2000 毫秒)的定时,就需要循环 2000/t 次。
②基于系统时间戳的方法
许多嵌入式系统或者计算机系统都有获取系统时间的功能。可以获取系统的当前时间,然后通过比较当前时间和上一次执行任务的时间来判断是否到达了定时时间。例如,在 C 语言中可以使用time()函数(在支持该函数的系统中)来获取从某个起始时间以来经过的秒数。
③基于操作系统的任务调度机制(以 FreeRTOS 为例)
如果在支持操作系统的环境下,可以利用操作系统的任务调度功能。创建两个任务,一个任务负责串口发送,另一个任务负责 LED 闪烁。每个任务在自己的任务函数中实现周期性执行的逻辑,通过操作系统的时间片轮转或者优先级调度等机制,使两个任务能够并发执行,达到同时完成两个周期性任务的目的。
二、PWM
使用TIM3和TIM4,分别输出一个PWM波形,PWM的占空比随时间变化,去驱动你外接的一个LED以及最小开发板上已焊接的LED(固定接在 PC13 GPIO端口),实现2个 LED呼吸灯的效果。
(1)PWM介绍
1、PWM定义
PWM(Pulse Width Modulation)即脉冲宽度调制,简称脉宽调制。它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术;它是一种模拟控制方式,根据相应载荷的变化来调制晶体管基极或MOS管栅极的偏置,来实现晶体管或MOS管导通时间的改变,从而实现开关稳压电源输出的改变。
2、基本原理
PWM就是对逆变电路开关器件的通断进行控制,使输出端得到一系列幅值相等的脉冲,用这些脉冲来代替正弦波或所需要的波形。也可以这样理解,PWM是一种对模拟信号电平进行数字编码的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。PWM信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的。只要带宽足够,任何模拟值都可以使用 PWM 进行编码。
该信号在预定义的时间和速度中设置为高(5v或3.3v)和低(0v)。通常,我们将PWM的高电平称为1,低电平为0。
3、主要参数
PWM占空比:
PWM信号保持高电平的时间百分比称为占空比。如果信号始终为高电平,则它处于100%占空比,如果它始终处于低电平,则占空比为0%。如图1所示,T1为占空比,T为一个PWM周期。
PWM的频率:
PWM信号的频率决定PWM完成一个周期的速度。STM32的MDK编译器可以选择5MHZ,10MHZ,20MHZ和50MHZ
4、STM32上的PWM
PWM产生
STM32的定时器除了TIM6和7,其他的定时器都可以用来产生PWM输出。其中高级定时器TIM1和TIM8可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4路的 PWM 输出,这样,STM32 最多可以同时产生 30 路 PWM 输出。
脉冲宽度调制模式可以产生一个由TIMx_ARR寄存器确定频率、由TIMx_CCRx寄存器确定占空比的信号。通用定时器产生PWM 的定时器框图如下:(其他定时器框图类似)
5、PWM输出的模式区别
PWM模式1:在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为有效电平,否则为无效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)
PWM模式2:在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为无效电平,否则为有效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为有效电平,否则为无效电平
注意:PWM的模式只是区别什么时候是有效电平,但并没有确定是高电平有效还是低电平有效。这需要结合CCER寄存器的CCxP位的值来确定。
例如:若PWM模式1,且CCER寄存器的CCxP位为0,则当TIMx_CNT<TIMx_CCR1时,输出高电平;同样的,若PWM模式1,且CCER寄存器的CCxP位为2,则当TIMx_CNT<TIMx_CCR1时,输出低电平。
6、PWM的计数模式
向上计数模式:
下面是一个PWM模式1的例子。当TIMx_CNT<TIMx_CCRx时PWM信号参考OCxREF为高,否则为低。如果TIMx_CCRx中的比较值大于自动重装载值(TIMx_ARR),则OCxREF保持为’1’。如果比较值为0,则OCxREF保持为’0
向下计数模式:
在PWM模式1,当TIMx_CNT>TIMx_CCRx时参考信号OCxREF为低,否则为高。如果TIMx_CCRx中的比较值大于TIMx_ARR中的自动重装载值,则OCxREF保持为’1’。该模式下不能产生0%的PWM波形。
中央对齐模式:
当TIMx_CR1寄存器中的CMS位不为’00’时,为中央对齐模式(所有其他的配置对OCxREF/OCx信号都有相同的作用)。根据不同的CMS位设置,比较标志可以在计数器向上计数时被置’1’、在计数器向下计数时被置’1’、或在计数器向上和向下计数时被置’1’。TIMx_CR1寄存器中的计数方向位(DIR)由硬件更新,不要用软件修改它。
7、PWM相关配置寄存器
包含三个寄存器:捕获/比较模式寄存器(TIMx_CCMR1/2)、捕获/比较使能寄存器(TIMx_CCER)、捕获/比较寄存器(TIMx_CCR1~4)。设置TIMx_CCMRx寄存器OCxPE位以使能相应的预装载寄存器,最后还要设置TIMx_CR1寄存器的ARPE位,(在向上计数或中心对称模式中)使能自动重装载的预装载寄存器。在TIMx_CCMRx寄存器中的OCxM位写入110(PWM模式1)或111(PWM模式2),能够独立地设置每个OCx输出通道产生一路PWM。
①捕获/比较模式寄存器(TIMx_CCMR1)
作用:在PWM输出模式下,确定PWM的模式、使能相应的预装载寄存器等操作
②捕获/比较使能寄存(TIMx_CCER)
作用:在PWM输出模式下,确定PWM的输出极性和输出使能
③捕获/比较寄存器(TIMx_CCR1)
(2)使用STM32CubeMX创建项目
①新建工程
选择芯片STM32F103C8T6
②RCC配置
③SYS配置
④配置定时器3和定时器4
选择定时器3和定时器4来实现定时的功位置3,分频系数为71,向上计数模式,计数周期为500,使能自动重载模式。通道1选择:PWM Generation CH1(PWM输出通道1)
⑤Clock Configuration设置
⑥Project Manager设置
⑥创建完成并用keil打开
(3)编写代码
1.设置占空比
打开工程,主要修改main.c文件。首先定义一个变量,用来存储占空比:初值设为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.调用代码
在main函数里while循环中添加代码
设置为每隔50毫秒,占空比加10,如果超过500(也就是PWM周期),自动变成0。(即灯会从亮倒暗,逐渐变化)
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);
}
(4)硬件连接
1.线路连接
根据查询数据手册可发现定时器3,4的一通道分别对应引脚PA6 PB6,然后题目要求还需要直接驱动PC13(最小开发板上已焊接的LED(固定接在 PC13 GPIO端口)),故而我们将对应PWM波作为输入,连接其对应引脚即可实现所需完成功能。
LED灯短脚 —> A6
LED灯长脚 —> 3V3
PB6 —> PC13
(5)编译与烧录
(6)结果演示
结果三
三、总结
通过本次对 STM32 定时器与 PWM 功能的深入学习与实践,我深刻体会到了嵌入式系统中硬件与软件知识的紧密融合。在理解定时器和 PWM 原理的过程中,不仅需要掌握数字电路中的计数原理、脉冲调制技术,还需熟悉微处理器的中断处理机制、外设配置等软件方面的知识。这种跨领域知识的综合运用,极大地拓宽了我的知识面,使我对嵌入式系统的整体架构和工作原理有了更全面、深入的理解。
掌握了定时器的配置与使用,能够根据实际需求准确计算定时参数,实现从微秒级到秒级的精准定时任务。这对于实时性要求较高的应用场景,如工业自动化中的数据采集周期控制、通信协议中的时序控制等,具有重要意义。同时,通过 PWM 功能的实践,学会了如何灵活调整脉冲宽度来精确控制外部设备,如实现 LED 灯的渐变效果、电机的平滑调速等,提升了对模拟量的数字化控制能力。
在代码编写过程中,深入了解了 STM32 的 HAL 库函数的使用方法,学会了如何正确初始化定时器和 PWM 相关外设,编写中断服务程序和回调函数,以及在主程序中合理控制定时任务和 PWM 输出。
在项目实践过程中,不可避免地遇到了各种问题,如硬件连接问题、软件配置错误、逻辑错误等。通过对这些问题的逐一排查和解决,我逐渐培养了自己的问题解决能力和逻辑思维能力。面对复杂的问题时,学会了从多个角度进行分析,逐步缩小问题范围,最终找到解决方案。同时,在解决问题的过程中,也会尝试不同的方法和思路,这有助于拓展自己的思维方式,提高创新能力。
四、参考
https://blog.csdn.net/weixin_64559251/article/details/127581276
https://blog.csdn.net/qq_45237293/article/details/111997424
https://blog.csdn.net/weixin_64559251/article/details/127561710
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/2401_87430833/article/details/144384654
|