一.定时器原理
1. stm32f407定时器
2. 定时器的功能
2.1 基本定时器TIM功能
● 主要运用于定时计数以及驱动DAC
2.2 通用定时器TIM功能
● 定时器定时计数
● 输入捕获
● 输出比较
● PWM输出
● 使用外部信号控制定时器和定时器互连的同步电路
2.3 高级定时器TIM功能
● 通用定时器的有功能
● 带死区控制和紧急刹车,可用于PWM控制电机
3. 定时器计数功能
● 向上计数模式
计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。
● 向下计数模式
计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。
● 中央对齐模式(向上/向下计数)
计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。
4. 定时器时钟的频率设定
注意:不是连接了APB2或者APB1,时钟频率就是对应路的频率,可能时2倍
● 定时器如何确定1倍还是2倍?
如果APBx Prescaler为/1,那么时钟频率就是与路段频率一样(即等于HCLK),如果APBx Prescaler不为/1,那么时钟频率就是APBx Prescaler的2倍.
5. 计数器会从0开始计时
二.基本定时器使用
1. 实验一: 利用基本定时器实现定时1秒中断,并在中断处理函数中打印输出字符
2. cubemx配置
(1)tim6配置<注意:TIM6的NVIC不要忘记使能>
(2)RCC设置为外部晶振,RCC配置成高效率分频
设置的原因:使TIM6的输入时钟频率为84HZ(如下图)
如何确定上述第二章图Prescaler与counter Period的数值?(如下图)
实验步骤:
- HAL_TIM_Base_Start_IT(&htim6);函数使用TIM
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)重写回调函数
注意:由于使用的是更新中断,所以找Update
(1)在tim.c中重写回调函数
由于我们设置TIM的是一秒一个中断
疑问:只需要使能一次就可以重复触发中断了,有些怎么不可以,每次都需要使能
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM6)
{
printf("tim6 int\n");
}
}
三.高级定时器的功能分析
0. 总图
- 时钟源
2. 控制器
3. 时基单元
4. 输入捕获
5. 公共部分
6. 输出比较
实验二:利用定时器2的输入捕获功能测量按下KEY6键后低电平持续的时间
- 看原理图:key6对应PA0,TIM2在数据参考手册中对应APB1频率84MHZ
- Cubemx创建工程
(1)TIM2定时器配置
(2) RCC时钟配置(HSE设为晶振,时钟按25,336,/4,/2配置)
(3)USART1串口异步通信
(4)TIM时基单元的控制(TIM2->configuration->TIM2 control)
(5)NVIC中断优先级的设定
3. 实验步骤
(1)main.c
HAL_TIM_Base_Start_IT(&htim2);//更新溢出中断
HAL_TIM_IC_Start_IT (&htim2, TIM_CHANNEL_1);//输入捕获中断
(2)tim.c中更新溢出中断(回调函数)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
if(htim->Instance==TIM2){
if(fall_flag){
cap_value+=1000000;//每次溢出加一个ARR(单位毫秒) printf("updata int\n");
}
}
}
(3)tim.c的输入捕获中断(回调函数)
uint8_t fall_flag = 0; //下降沿捕获标记位
uint32_t cap_value = 0;
注意:下面有复制版
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){
if(!fall_flag){
printf("捕获下降沿\n");
HAL_Delay(20);//防抖
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET){
fall_flag=1;
__HAL_TIM_DISABLE(htim); //关闭定时器
__HAL_TIM_SET_COUNTER(htim,0); //设置计数器值为0
TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1); //清除原来的设置
TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_RISING);
//设置为上升沿捕获
__HAL_TIM_ENABLE(htim); //使能定时器
}
}
else{
printf("捕获上升沿\n");
fall_flag = 0;
//最终时间
cap_value+=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1)+20000;
//HAL_TIM_ReadCapturedValue()读取当前计数器的值
printf("低电平持续时间= %d秒:%d毫秒:%d微秒\n",
cap_value/1000000,cap_value%1000000/1000,cap_value%1000);
cap_value=0;
__HAL_TIM_DISABLE(htim); //关闭定时器
TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1); //清除原来的设置 TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING); //设置为下降沿捕获
__HAL_TIM_ENABLE(htim); //使能定时器
HAL_Delay(20);
}
}
复制版
uint8_t fall_flag = 0; //ϽµÑز¶»ñ±ê¼Çλ
uint32_t cap_value = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2)
{
if(fall_flag)
{
cap_value += 1000000; //ÿ´ÎÒç³ö×öÒ»¸ö¼ÓARRµÄ¼Ç¼
printf("update int \n");
}
}
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2)
{
if(!fall_flag) //µ±Ç°²¶»ñµ½µÄÊÇϽµÑØ
{
printf("²¶»ñµ½ÁËϽµÑØ\n");
HAL_Delay(20); //ÑÓʱ20msÏû¶¶
if( HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
{
fall_flag = 1;
__HAL_TIM_DISABLE(htim); //¹Ø±Õ¶¨Ê±Æ÷
__HAL_TIM_SET_COUNTER(htim,0); //ÉèÖüÆÊýÆ÷µÄֵΪ0
TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1); //Çå³ýÔÀ´µÄÉèÖÃ
TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING); //ÉèÖÃΪÉÏÉýÑز¶»ñ
__HAL_TIM_ENABLE(htim); //ʹÄܶ¨Ê±Æ÷
}
}
else //µ±Ç°²¶»ñµ½µÄÊÇÉÏÉýÑØ£¬±¾´Î²¶»ñ½áÊø
{
printf("²¶»ñµ½ÁËÉÏÉýÑØ\n");
fall_flag = 0;
//×îÖÕµÄʱ¼ä= Òç³öµÄʱ¼ä + Á½´Î¼ÆÊý²îÖµ + ÑÓʱ¶¶¶¯µÄʱ¼ä
cap_value = cap_value + HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1) + 20000 ;
printf("µÍµçƽ³ÖÐøµÄʱ¼ä= %dÃ룺%dºÁÃ룺%d΢Ãî\n",cap_value/1000000,cap_value%1000000/1000,cap_value%1000);
cap_value = 0;
__HAL_TIM_DISABLE(htim); //¹Ø±Õ¶¨Ê±Æ÷
TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1); //Çå³ýÔÀ´µÄÉèÖÃ
TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING); //ÉèÖÃΪϽµÑز¶»ñ
__HAL_TIM_ENABLE(htim); //ʹÄܶ¨Ê±Æ÷
HAL_Delay(20);
}
}
}
注意:下面为固定的函数
//关闭定时器
__HAL_TIM_DISABLE(htim);
//设置计数器值为0
__HAL_TIM_SET_COUNTER(htim,0);
//清除原来的设置
TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1);
//设置为上升沿捕获
TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_RISING);
//使能定时器
__HAL_TIM_ENABLE(htim);
实验三:定时器pwm呼吸灯
1.实验要求,D7(对应PF7)灯实现呼吸灯.
2.cubemx创建工程
(1)RCC时钟使用外部晶振,配置(25,336,/4,/2)
(2)PF7设置TIM11,TIM11设置pwm模式
(3)TIM11配置
(4)没有使用到中断,所以没有使用NVIC
3. 步骤
(1)main.c中
//改变CCR(用来确定有效范围)的值
void TIM11_Set_Compare(uint32_t compare)
{
TIM11->CCR1 = compare;
}
(2)main.c中
第一步:打开PWM,while()循环之外
HAL_TIM_PWM_Start(&htim11, TIM_CHANNEL_1); //Æô¶¯PWM¶¨Ê±Æ÷
第二步:
int32_t compare = 0; //设置CCR
uint8_t dir = 0; //设置方向
while(1){
if(!dir){ //向上增长(变亮)
compare += 10;
if(compare>=1000)
{
compare = 1000-1;
dir = 1;
}
}
else //向下减少(变暗)
{
compare -= 10;
if(compare<=0)
{
compare = 0;
dir = 0;
}
}
TIM11_Set_Compare(compare); //改变CCR
HAL_Delay(20);
}
(4)运行结果:D6灯由亮到灭,渐进转变