STM32CubMX自学笔记(五)---定时器
前景回顾
上一节主要介绍了在STM32CubMx中如何配置中断,并且如何使用其中的中断函数,首次提到了回调函数这个概念,这一小节,我们在此基础上实现通过定时器来实现。。。。。
主要介绍
定时器功能:定时、输出比较、输入捕获、互补输出
定时器分类:基本定时器、通用定时器、高级定时器
定时器资源:STM32F429有2个高级定时器、10个通用定时器、2个基本定时器
基本定时器功能简介
1-计数器16bit,只能向上计数,只有TIM6和TIM7
2-没有外部的GPIO,是内部资源,只能用来定时
3-时钟来自PCLK1,可实现1~65536分频
我们知道时钟就是单片机的心脏,并且,使用单片机万万离不开时钟,可以说时钟是单片机内容的重中之重,之前配置文件时,我们都没有介绍时钟这一模块,在这里,将要花费大量篇幅介绍STM32中的时钟小节。关于时钟的基本概念请转至时钟超详细讲解。这里还是主要对STM32CubMx中的时钟进行介绍。
工程配置-基本定时器
首先进入软件界面,使能HSE(高速时钟)和LSE(低速时钟),使能完成之后进入Clock Configuration界面。
系统时钟图介绍
下图为STM32F407ZGT6的时钟树界面。以系统时钟SYSCLK为界限划分左中右。我们可以清晰的看到左侧部分系统的时钟源有LSE(低速外部时钟)、HSE(高速外部时钟),系统内部有LSI(低速内部时钟)、HSI(高速内部时钟)。箭头指向不同的地方,是为不同的外设提供时钟信号。
中间部分系统时钟源(SYSCLK)来源有3处,HSI,HSE,PLLCLK锁相环。PLL锁相环是一个倍频器,可以把频率低的时钟经过乘除运算放大成为频率高的时钟。下图中PLL锁相环将25MHz的HSE倍频成为了168MHz的高速时钟。
右侧将得到的系统时钟在经过预分频器AHB处理得到了HCLK,HCLK再通过APB1和APB2预分频器将时钟分配给外设。如APB1外设时钟,APB1定时器时钟、APB2外设时钟、APB2定时器时钟。
配置时钟有一个原则,就是用到哪些外设就配置哪些时钟。下图为定时器1-14所挂载在时钟树上的位置,在本次实验中我们用到了timer6,可以看到timer6挂载在APB1上,而配置的APB1时钟为84MHz。
系统时钟设置
通用定时器的工作原理:
首先,定时器的时钟信号送入16位可编程预分频器(Prescaler),该预分频器的系数为0-65535之间的任意数值,预分配器溢出之后,会向16位的主计数器(Counter Period)发送一个脉冲信号。
预分频器,实质上是一个加法计数器,预分频系数就是加计数的溢出值。
定时器一个周期产生的时间我们如何计算呢?下面以timer6为例,具体配置细节。
先介绍定时器发生中断时间的计算方法:
定时器时间=(Prescaler+1)X(Counter Period)X1/定时器时钟频率 单位:秒/s
这里我们配置Prescaler为8400-1,Counter Period为5000-1,可以得到0.5s的计时时间,同时也要使能自动重装载。
同样的这里我们配置Prescaler为8400-1,Counter Period为2000-1,可以得到0.2s的计时时间。
时钟初始化配置完成,紧接着要使能两个定时器中断,如下图所示,至此,工程配置完毕,可以生成系统文件了。
系统代码编写
观察生成的系统文件。
void MX_TIM6_Init(void)
{
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim6.Instance = TIM6;
htim6.Init.Prescaler = 8399;
//预分频设置,就是将84MHz的系统时钟均分成8400份,得到一份的时间为0.1ms
htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
htim6.Init.Period = 1999; //计数器设置为2000,2000*0.1ms=0.2s
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
}
依照惯例,首先打开stm32f4xx__it.c文件,在中断函数里面追溯根源,找到中断回调函数。
或者在hal库的定时器的头文件找到回调函数,复制到主函数对它进行重写。
在 main.c
函数中添加如下代码:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM6) //是否是定时器6触发中断
{
HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);//翻转LED0的电平
}
if(htim->Instance==TIM7)//是否是定时器7触发中断
{
HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);//翻转LED1的电平
}
}
在main函数中添加:
HAL_TIM_Base_Start_IT(&htim6);//启动定时器中断6
HAL_TIM_Base_Start_IT(&htim7);//启动定时器中断7
完成配置
代码验证
我们将代码下载到开发板上,观察现象,发现LED0按照定时器6设置的时间翻转。LED1按照定时器7设置的时间翻转。
工程配置2-高级定时器输入捕获(来自野火)
【 !】功能简介:
使用STM32的高级控制定时器PWM输入捕获模式,测量PWM波形频率和周期。
学习目的:学会使用高级控制定时器输入捕获功能,以及通用定时器PWM输出功能。
【 !】实验操作:
电脑端使用串口调试助手,选择电脑与STM32相连的COM口,设置为115200-N-8-1,
根据硬件设计内容结合软件设计的引脚宏定义参数,用杜邦线连接通用定时器
PWM输出引脚和高级控制定时器的输入捕获引脚。
【*】 引脚分配
高级控制定时器捕获输入引脚:
IC <—> PC6
通用定时器PWM输出引脚:
OC <—> PA15
时钟配置
TIM2生成PWM定时器配置:
时钟来源选择的是内部时钟,产生PWM的是定时器2的通道1。时钟源是APB2 84Mhz。
TIM8定时器输入捕获配置:
1、复位模式:当触发信号产生变化的时候,计数器与预分频器可以重新初始化。
2、选择触发源TI1FP1,是因为通道有两个触发源还有一个是2的。通道1配置为输入捕获上升沿,通道2配置为输入捕获下降沿。
系统代码生成
PWM输出配置:
static void TIM_PWMOUTPUT_Config(void)
{
TIM_OC_InitTypeDef TIM_OCInitStructure;
// 开启TIMx_CLK,x[2,3,4,5,12,13,14]
GENERAL_TIM_CLK_ENABLE();
/* 定义定时器的句柄即确定定时器寄存器的基地址*/
TIM_PWMOUTPUT_Handle.Instance = GENERAL_TIM;
/* 累计 TIM_Period个后产生一个更新或者中断*/
//当定时器从0计数到9999,即为10000次,为一个定时周期
TIM_PWMOUTPUT_Handle.Init.Period = 10000-1;
// 高级控制定时器时钟源TIMxCLK = HCLK=108MHz
// 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=100KHz
TIM_PWMOUTPUT_Handle.Init.Prescaler = 108-1;
// 采样时钟分频
TIM_PWMOUTPUT_Handle.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
// 计数方式
TIM_PWMOUTPUT_Handle.Init.CounterMode=TIM_COUNTERMODE_UP;
// 重复计数器
TIM_PWMOUTPUT_Handle.Init.RepetitionCounter=0;
// 初始化定时器TIMx, x[1,8]
HAL_TIM_PWM_Init(&TIM_PWMOUTPUT_Handle);
/*PWM模式配置*/
//配置为PWM模式1
TIM_OCInitStructure.OCMode = TIM_OCMODE_PWM1;
TIM_OCInitStructure.Pulse = 5000;
TIM_OCInitStructure.OCPolarity = TIM_OCPOLARITY_HIGH;
TIM_OCInitStructure.OCNPolarity = TIM_OCNPOLARITY_HIGH;
TIM_OCInitStructure.OCIdleState = TIM_OCIDLESTATE_SET;
TIM_OCInitStructure.OCNIdleState = TIM_OCNIDLESTATE_RESET;
//初始化通道1输出PWM
HAL_TIM_PWM_ConfigChannel(&TIM_PWMOUTPUT_Handle,&TIM_OCInitStructure,TIM_CHANNEL_1);
/* 定时器通道1输出PWM */
HAL_TIM_PWM_Start(&TIM_PWMOUTPUT_Handle,TIM_CHANNEL_1);
}
PWM输入捕获配置:
static void TIM_PWMINPUT_Config(void)
{
TIM_IC_InitTypeDef TIM_ICInitStructure;
TIM_SlaveConfigTypeDef TIM_SlaveConfigStructure;
// 开启TIMx_CLK,x[1,8]
ADVANCE_TIM_CLK_ENABLE();
/* 定义定时器的句柄即确定定时器寄存器的基地址*/
TIM_PWMINPUT_Handle.Instance = ADVANCE_TIM;
TIM_PWMINPUT_Handle.Init.Period = 0xFFFF;
// 高级控制定时器时钟源TIMxCLK = HCLK=216MHz
// 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=1MHz
TIM_PWMINPUT_Handle.Init.Prescaler = 216-1;
// 采样时钟分频
TIM_PWMINPUT_Handle.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
// 计数方式
TIM_PWMINPUT_Handle.Init.CounterMode=TIM_COUNTERMODE_UP;
// 初始化定时器TIMx, x[1,8]
HAL_TIM_IC_Init(&TIM_PWMINPUT_Handle);
/* IC1捕获:上升沿触发 TI1FP1 */
TIM_ICInitStructure.ICPolarity = TIM_ICPOLARITY_RISING;
TIM_ICInitStructure.ICSelection = TIM_ICSELECTION_DIRECTTI;
TIM_ICInitStructure.ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.ICFilter = 0x0;
HAL_TIM_IC_ConfigChannel(&TIM_PWMINPUT_Handle,&TIM_ICInitStructure,ADVANCE_IC1PWM_CHANNEL);
/* IC2捕获:下降沿触发 TI1FP2 */
TIM_ICInitStructure.ICPolarity = TIM_ICPOLARITY_FALLING;
TIM_ICInitStructure.ICSelection = TIM_ICSELECTION_INDIRECTTI;
TIM_ICInitStructure.ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.ICFilter = 0x0;
HAL_TIM_IC_ConfigChannel(&TIM_PWMINPUT_Handle,&TIM_ICInitStructure,ADVANCE_IC2PWM_CHANNEL);
/* 选择从模式: 复位模式 */
TIM_SlaveConfigStructure.SlaveMode = TIM_SLAVEMODE_RESET;
/* 选择定时器输入触发: TI1FP1 */
TIM_SlaveConfigStructure.InputTrigger = TIM_TS_TI1FP1;
HAL_TIM_SlaveConfigSynchronization(&TIM_PWMINPUT_Handle,&TIM_SlaveConfigStructure);
/* 使能捕获/比较2中断请求 */
HAL_TIM_IC_Start_IT(&TIM_PWMINPUT_Handle,TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&TIM_PWMINPUT_Handle,TIM_CHANNEL_2);
}
main代码编写
中断配置:
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
/* 获取输入捕获值 */
IC1Value = HAL_TIM_ReadCapturedValue(&TIM_PWMINPUT_Handle,ADVANCE_IC1PWM_CHANNEL);
IC2Value = HAL_TIM_ReadCapturedValue(&TIM_PWMINPUT_Handle,ADVANCE_IC2PWM_CHANNEL);
if (IC1Value != 0)
{
/* 占空比计算 */
DutyCycle = (float)((IC2Value+1) * 100) / (IC1Value+1);
/* 频率计算 */
Frequency = 216000000/216/(float)(IC1Value+1);
}
else
{
DutyCycle = 0;
Frequency = 0;
}
}
}
int main(void)
{
/* 初始化系统时钟为216MHz */
SystemClock_Config();
/* 初始化串口 */
DEBUG_USART_Config();
/* 初始化基本定时器定时,1s产生一次中断 */
TIMx_Configuration();
while(1)
{
HAL_Delay(500);
printf("IC1Value = %d IC2Value = %d ",IC1Value,IC2Value);
printf("占空比:%0.2f%% 频率:%0.2fHz\n",DutyCycle,Frequency);
}
}
实验显示:
可以查看到定时器2产生的PWM方波的占空比,频率。定时器8捕获的2次捕获的数值。
结语
本节介绍了STM32中基本定时器6&7的使用,通过配置定时器6&7,LED灯精准时间翻转。另外,完成了高级定时器的输入捕获实验。