本文目的是主要介绍通过STM32F103C8T6采用定时器Timer方式实现时间的精准控制,相当于给CPU上了一个闹钟,CPU平时处理其它任务,当定时时间到了以后,处理定时相关的任务。请设置一个5秒的定时器,每隔5秒从串口发送“hello windows!”;同时设置一个2秒的定时器,让LED等周期性地闪烁。
前言
※ 在stm32CubeMX环境配置下,延时功能都是通过循环、delay/Hal_delay函数等实现,可参考博主的博客进行学习:https://blog.csdn.net/qq_52199251/article/details/127274537
※ 本文主要讲解采用定时器Timer方式实现时间的精准控制,并且利用定时器实现串口通信以及LED周期闪烁。
加油,看到这个博客的人,祝你开心一整天
(一)需求分析
※ 通过定时器Timer方式实现时间的精准控制,相当于给CPU上了一个闹钟,CPU平时处理其它任务,当定时时间到了以后,处理定时相关的任务。请设置一个5秒的定时器,每隔5秒从串口发送“hello windows!”;同时设置一个2秒的定时器,让LED等周期性地闪烁。
所需工具:
1、芯片: STM32F103C8T6
2、STM32CubeMx软件
3、IDE: MDK-Keil软件
4、STM32F1xx/STM32F4xxHAL库
(二)STM32定时器介绍
1.什么是定时器?
● 单片机的定时/计数器,故名思义就是用于精确定时一段时间后
,执行特定代码程序的用途。使用定时器会比使用软件延时函数(delay)更加精准
,举例应用如,基于STM32的频率计。定时器往往结合中断程序一起使用。
● 以STM32F103系列产品为例,其拥有:
4个通用定时器(TIM2~TIM5)
2个高级控制定时器(TIM1和TIM8),功能更强大
2个基本定时器(TIM6和TIM7),主要用于产生DAC触发信号 1个实时时钟(RTC)
2个看门狗定时器
1个系统滴答定时器(SysTick时钟),主要用于精确延时(delay函数)
通用定时器结构:
定时器时钟源结构图:
将八个定时器进行划分如下:
定时器名称 | 功能等级 |
---|---|
TIM1 | 高级控制定时器 |
TIM8 | 高级控制定时器 |
TIM2 | 通用定时器 |
TIM3 | 通用定时器 |
TIM4 | 通用定时器 |
TIM5 | 通用定时器 |
TIM6 | 基本定时器 |
TIM7 | 基本定时器 |
2.高级控制定时器(TIM1 & TIM8)
※ TIM1和ITIM8定时器的功能包括:
● 16位向上、向下、向上/下自动装载计数器
● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65535之间的任意数值
● 多达4个独立通道:
输入捕获
输出比较
PWM生成(边缘或中间对齐模式)
单脉冲模式输出
● 死区时间可编程的互补输出
● 使用外部信号控制定时器和定时器互联的同步电路
● 允许在指定数目的计数器周期之后更新定时器寄存器的重复计数器
● 刹车输入信号可以将定时器输出信号置于复位状态或者一个已知状态
※ 如下事件发生时产生中断/DMA:
更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
输入捕获
输出比较
刹车信号输入
● 支持针对定位的增量(正交)编码器和霍尔传感器电路
● 触发输入作为外部时钟或者按周期的电流管理
3.通用定时器(TIM2 - TIM5)
※ 通用TIMx (TIM2、TIM3、TIM4和TIM5)定时器功能包括:
● 16位向上、向下、向上/向下自动装载计数器
● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意数值
● 4个独立通道:
输入捕获
输出比较
PWM生成(边缘或中间对齐模式)
单脉冲模式输出
● 使用外部信号控制定时器和定时器互连的同步电路
● 如下事件发生时产生中断/DMA:
更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
输入捕获
输出比较
● 支持针对定位的增量(正交)编码器和霍尔传感器电路
● 触发输入作为外部时钟或者按周期的电流管理
4.基本定时器(TIM6 & TIM7)
※ TIM6和TIM7定时器的主要功能包括:
● 16位自动重装载累加计数器
● 16位可编程(可实时修改)预分频器,用于对输入的时钟按系数为1~65536之间的任意数值分频
● 触发DAC的同步电路
● 在更新事件(计数器溢出)时产生中断/DMA请求
5.定时器的计数值设置
※ 我们用定时器来定时1s,那么这个计数值我们应该设置为多少?
● 先来设置16位预分频的分频系数,我们设置为7200。
● 所以 = 10000HZ,经过预分频后72Mhz变成了10000hz
● 所以,T = =,即:在10000hz频率下,计数10000次,需要花费1s的时间。换而言之就是,在10000hz频率下,想要延时1s就需要计数10000次。所以,此时计数值我们设置为10000。
※ 所以,计算值设置为多少,需要先知道预分频的系数,才能计算出来。
相应库函数:
6.技术模式介绍
🎈 向上计数:从0计数到计数值
🎈 向下计数:从计数值计数到0
🎈 中央对模式1:从0计数到计数值 又 从计数值计数到0
🎈 中央对模式2:从计数值计数到0 又 从0计数到计数值
🎈 中央对模式3:与中央对模式2一样。
6.1定时器中断源
● TIM_IT_Update 更新中断:即计数值计数完了,溢出了,此时产生中断。也就是计数溢出中断。注意:只有这个中断源是基本定时器的,其他的都是通用定时器的。
标志位:
7.“小试牛刀”——定时器中断实例
● 下面是使用定时器中断的代码,我们设置为每500ms中断一次
,中断服务函数控制LED实现LED状态取反。时间计算方法为:
Tout(溢出时间)=(ARR+1)(PSC+1)/Tclk
编写时钟.c文件:
//timer.c源文件
#include "timer.h"
#include "led.h"
//通用定时器3中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
//定时器TIM3初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
//中断优先级NVIC设置
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIMx
}
//定时器3中断服务程序
void TIM3_IRQHandler(void) //TIM3中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查TIM3更新中断发生与否
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx更新中断标志
LED1=!LED1;
}
}
编写时钟.h文件:
//timer.h头文件
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
void TIM3_Int_Init(u16 arr,u16 psc);
#endif
编写主函数文件:
//main.c源文件
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "timer.h"
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init(); //LED端口初始化
TIM3_Int_Init(4999,7199);//10Khz的计数频率,计数到5000为500ms
while(1)
{
LED0=!LED0;
delay_ms(200);
}
}
(三)STM32F103C8T6下工程实现
1.实现目的
🎈 设置一个5秒的定时器,每隔5秒从串口发送“hello windows!”。
🎈 设置一个2秒的定时器,让LED等周期性地闪烁。
2.创建项目
3.选择C8T6芯片
● 选择C8T6芯片,开启我们的控制点灯旅程。
3.引脚排列和配置
3.1 配置RCC
●点System Cor
,选择RCC
,在右侧弹出的菜单栏中选Crystal/Ceramic Resonator
:
3.2 配置SYS
● 选择调试接口,点System Cor
,选择SYS
。在右侧弹出的菜单栏中选Serial Wire
:
3.3 配置端口输出
● 选择PA5
作为LED灯的输出
,将其选为GPIO-OUT
:
3.4 配置定时器
● 这里使用定时器2和定时器3
来实现定时的功能。选中定时器2
;配置定时器2的时钟源为内部时钟
;设置分频系数为71
,向上计数模式,计数周期为5000
:
● 再对TIM3进行相应的配置
※ 注意:分频系数虽然是71,但系统处理的时候会自动加上1,所以实际进行的是72分频。由于时钟一般会配置为72MHZ,所以72分频后得到1MHZ的时钟;1MHZ的时钟,计数5000次,得到时间5000/1000000=0.005秒;也就是每隔0.005秒定时器2会产生一次定时中断。
● 这时可以发现芯片的部分引脚变亮。
4.配置中断与USART1
4.1 配置中断
● 配置TIM2
和TIM3
的中断:
4.2 配置USART1
● 选择Connectivity
,点开USART1
,Mode选择异步通信Asynchronous
:
5.配置时钟
● 配置为7MHZ
6.项目配置生成
● 具体配置如下:
(四)Keil编译程序
1.工程项目
● 用keil打开刚刚在CubeMX生成的项目文件。
2.代码编写
2.1 定时器启动
● 在main.c文件中补充定时器启动代码。
HAL_TIM_Base_Start_IT(&htim2);
HAL_TIM_Base_Start_IT(&htim3);
2.1 串口输出
● 在main.c文件中添加串口输出代码。
uint8_t hello[20]="hello windows!\r\n";
2.3 定时器中断回调函数
● 在main.c文件中编写回调函数。
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_5);
}
}
if(htim->Instance == TIM3)
{
if(++time_cnt3 >= 1000)
{
time_cnt3 =0;
HAL_UART_Transmit(&huart1,hello,20,100000);
}
}
}
3.编译文件
● 发现没有报错
(五)烧录实现
1.硬件搭建
● 硬件电路搭建如下:
2.打开FlyMcu
2.串口助手实现
● 最终实现效果如下,五秒一次“hello windows”:
串口
3.LED周期闪烁实现
● 可以观察到LED实现了2秒的周期闪烁
(六)总结
本文介绍了通过STM32F103C8T6采用定时器Timer方式实现时间的精准控制,相当于给CPU上了一个闹钟,CPU平时处理其它任务,当定时时间到了以后,处理定时相关的任务;并设置一个5秒的定时器,每隔5秒从串口发送“hello windows!”;同时也设置一个2秒的定时器,让LED等周期性地闪烁。
同时,本次操作不仅回顾了以往的知识,还掌握了定时器Timr方式去实现精准控制,LED的周期闪烁与hello windows输出,也让我们掌握了如何通过定时器去实现嵌入式开发。
为自己鼓掌。
寄语:不伤心不失心不碎心
梦想之路必定风雪交加
坚定年少时候的信念与梦啊
你是少年啊!失去什么又何妨,去爱去追去梦,去成就你的奇迹!
(七)参考文献
[1]https://blog.csdn.net/weixin_52071377/article/details/126461270
[2]https://blog.csdn.net/qq_42992084/article/details/104099659
[3]https://blog.csdn.net/qq_39577221/article/details/125360536