本次实验学习通过定时器Timer方式实现对时间的精准控制,相当于给CPU上了一个闹钟,CPU平时处理其它任务,当定时时间到了以后,处理定时相关的任务。设置一个5秒的定时器,每隔5秒从串口发送“hello windows!”;同时设置一个2秒的定时器,让LED等周期性地闪烁。
本文目录
一、定时器概念
1. 定时器的分类
定时器可分为
- 高级定时器 TIM1 TIM8
- 通用定时器 TIM2 TIM3 TIM4 TIM5
- 基本定时器 TIM6 TIM7
定时器中断是由单片机中的定时器溢出而申请的中断。STM32中共有11个定时器,具体如下:
1、两个高级定时器:TIM1、TIM8-------------------------APB2
2、四个通用定时器:TIM2~TIM5-------------------------APB1
3、两个基本定时器:TIM6、TIM7-------------------------APB1 4、两个看门狗
5、一个系统嘀嗒定时器(SysTick)
2. 高级定时器、通用定时器和基本定时器的区别
TIM1和TIM8定时器的功能包括【增强型】:
● 16位向上、向下、向上/下自动装载计数器
●16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65535之间的任意数值
● 多达4个独立通道: ─输入捕获 ─ 输出比较 ─ PWM生成(边缘或中间对齐模式) ─ 单脉冲模式输出
● 死区时间可编程的互补输出
●使用外部信号控制定时器和定时器互联的同步电路
● 允许在指定数目的计数器周期之后更新定时器寄存器的重复计数器
●刹车输入信号可以将定时器输出信号置于复位状态或者一个已知状态
● 如下事件发生时产生中断/DMA: ─更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发) ─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数) ─ 输入捕获 ─ 输出比较─ 刹车信号输入
● 支持针对定位的增量(正交)编码器和霍尔传感器电路
●触发输入作为外部时钟或者按周期的电流管理
TIMx主要功能通用TIMx (TIM2、TIM3、TIM4和TIM5)定时器功能包括【通用型】:
● 16位向上、向下、向上/向下自动装载计数器
●16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意数值
● 4个独立通道: ─ 输入捕获 ─输出比较 ─ PWM生成(边缘或中间对齐模式) ─ 单脉冲模式输出
● 使用外部信号控制定时器和定时器互连的同步电路
●如下事件发生时产生中断/DMA: ─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发) ─
触发事件(计数器启动、停止、初始化或者由内部/外部触发计数) ─ 输入捕获 ─ 输出比较
●支持针对定位的增量(正交)编码器和霍尔传感器电路
● 触发输入作为外部时钟或者按周期的电流管理
TIM6和TIM7定时器的主要功能包括【精简型】:
● 16位自动重装载累加计数器
● 16位可编程(可实时修改)预分频器,用于对输入的时钟按系数为1~65536之间的任意数值分频
●触发DAC的同步电路 注:此项是TIM6/7独有功能.
● 在更新事件(计数器溢出)时产生中断/DMA请求
二、定时器功能
1. 定时器结构图
定时器结构框图分为5部分
- 时钟产生部分
- 定时器部分
- 输入比较部分
- 输出比较部分
- 寄存器部分
具体结构如图所示:
2. STM32定时器功能—计时&&中断
定时器的计时我个人理解为控制计数的数量来控制定时的时间,由于定时器的计数频率和计数量可调,所以可以控制定时器的计时时间、计数频率和系统时钟、重装载值(arr)和预分频系数(psc)有关。
系统时钟在前面关于时钟的学习总结中已经讲解,我们会在主函数的最开始设置系统时钟,STM32单片机的时钟一般设置为最高的72M。预分频系数是指系统时钟经过几分频作为定时器的时钟,预分频系数设置为0的时候就是1分频,也就是不分频,设置为1时候是2分频,3的时候就是4分频,4的时候…总的来说就是PSC+1倍分频。
最大重装载值就是定时器的最大计数值,STM32定时器有一个十六位的计数寄存器,最大计数值就是65535。每次配置定时器都要设置arr和psc,下面给出一段定时器配置的程序。
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period=arr;
TIM_TimeBaseInitStruct.TIM_Prescaler=psc;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);//定时器初始化,配置参数
定时器中断就是在定时器的计数值达到想要的最大计数值时(可以理解为爆表),也就是想要的计时时间后,进入一次中断,在中断中执行特定的代码。
以下代码为定时器中断的配置,设置了定时器的计数时间,中断的触发方式(更新中断,也就是爆表),以及中断的优先级,以及最重要的使能中断。
void TIM2_Int_Init(u16 arr,u16 psc)//定时器2中断初始化 arr为重装载值 psc为分频系数 主函数要进行中断分组
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//定时器时钟使能
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period=arr;
TIM_TimeBaseInitStruct.TIM_Prescaler=psc;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);//定时器初始化,配置参数
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//设置更新中断
NVIC_InitStruct.NVIC_IRQChannel=TIM2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStruct);
TIM_ARRPreloadConfig(TIM2,ENABLE);
TIM_Cmd(TIM2,ENABLE);//使能TIM2定时器
}
3. 定时时钟计算方法和计数模式
定时器的时钟计算公式为:
Tout = ((arr+1)(psc+1))/Tclk
- Tclk:定时器的输入时钟频率(单位MHZ)
- Tout:定时器溢出时间(单位为us)
- arr: 计数装载值
- psc: 时钟分频系数
定时器的计数模式分为以下三种:
向上计数模式: 计数值从0计数到自动加载值(TIM_ARR),产生一个计数溢出事件,然后重新从0开始计数
向下计数模式:计数器从自动装入的值(TIM_ARR)开始向下计数到0,产生一个计数溢出事件,然后从计数装入值重新开始。
中央对齐模式: 计数器从O开始到(自动装入值-1),产生计数溢出事件,然后向下计数到1,产生定时器溢出事件,然后从0开始计数。
三、CubeMX初始化配置
1. 选择芯片开始创建项目
选择STM32F103C8T6——>开始项目
2. 配置时钟
选择外部时钟HSE
配置时钟树
3. TIM2中断相关配置
TIM2选择外部时钟作为时钟源,APB1时钟频率为72M,经过PSC72分频后定时器时钟频率为1Mhz,计数周期5000计满时间为0.005S,计数方式为向上计数。
定时器中断
4. 外设引脚配置
初始化USART1串口
5. 项目命名与工程导出
文件命名为 timer interrupt
四、 代码编写
1. 启动TIM2定时器中断
HAL_TIM_Base_Start_IT(&htim2); //启动定时器2中断 //PAB1总线时钟频率72M,分频系数72-1
写在while()函数之前
2. 初始化定时器TIM2
代码CubeMX自动配置好了
static void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 71;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 5000;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
}
3. 回调函数
回调函数,计时满2s时,LED电平翻转一次;计时满5s时,串口发送一个字符串到上位机。
__weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(htim);
time_count1++;
time_count2++;
if(time_count1==400) //200次定时器中断计时满2s,计满2s控制PA3的LED灯电平翻转
{
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_3);
time_count1=0;
}
if(time_count2==1000)
{
HAL_UART_Transmit(&huart1,Tx_buf,sizeof(Tx_buf),100); //1000次定时器中断计时满5s,计满串口发送字符串
time_count2=0;
}
/* NOTE : This function should not be modified, when the callback is needed,
the HAL_TIM_PeriodElapsedCallback could be implemented in the user file
*/
}
实验结果见视频:
定时器中断
五、总结
本次实验学习了通过定时器Timer方式实现时间的精准控制,设置一个5秒的定时器,每隔5秒串口发送“hello windows!”;同时设置一个2秒的定时器,让LED等周期性地闪烁。
实验过程中,对定时器的配置是最开始需要注意的,不然CubeMX导出的文件将出现问题。相比于Delay函数,定时器中断能更准确地把握时间,中断一触发,就执行中断服务函数中的功能。定时器中断与串口通信的结合,完成了中断控制串口通信的功能,这也让我对所学知识有了更深一步的认识。
六、参考资料
http://www.mcublog.cn/stm32/2021_01/stm32cubemx-dingshiqi-led/