因为最近的工作任务忙的比狗还惨,所以更新也就慢了一点点,不过最近一个关于F4DISCO学习的东西,怎么说呢,其实就是买其他开发板,技术支持就那么一家,买官方板倒不错,全世界都承认.
首先定时器还是有那么几种,有 TIM1 和 TIM8 等高级定时器,也有 TIM2~TIM5,TIM9~TIM14 等通用定时 器,还有 TIM6 和 TIM7 等基本定时器,总共达 14 个定时器之多,反正万变不离其宗啦.
如果只是做一下定时器中断,就没必要浪费一个很高级的定时器了,当然,SysTick还一句话搞定呢?呵呵,不过这里要讲定时器,肯定不用SysTick实现啊.这里就有TIM7吧,因为TIM8还要跟DAC中断复用,还是避免一下吧.如此珍贵的资源呢.
其中我们需要填充的值,大概也就只有TIM_Period,TIM_Prescaler,TIM_ClockDivision.绝对不能学着别人一样嗮寄存器,我们就不嗮,我就告诉你这个是怎么算出来的.
溢出时间 = ((TIM_Period+1)*(TIM_Prescaler+1)/CLK)CLK_DIV.
不过,大多数教程为了简化,都不要CLK_DIV,直接把CLK_DIV设置成1.比如我们计算周期是500ms来算.先设置TIM_Prescaler = 8399,让他变成1kHz,方便后面计算TIM_Period,这时候,TIM_Period = 4999啦,如果这时候要得到800ms呢,既可以设置TIM_Period = 7999,也可以设置CLK_DIV = 4,TIM_Period = 999.后者功耗可能更低吧.
具体实例是这么干的:
void TIM7_Int_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE); ///使能TIM7时钟 TIM_TimeBaseInitStructure.TIM_Period = 8399; //自动重装载值 TIM_TimeBaseInitStructure.TIM_Prescaler = 9999; //定时器分频 TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM7, &TIM_TimeBaseInitStructure); //初始化TIM7 TIM_ITConfig(TIM7, TIM_IT_Update, ENABLE); //允许定时器7更新中断 TIM_Cmd(TIM7, ENABLE); //使能定时器7 NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn; //定时器7中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01; //抢占优先级1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } //定时器7中断服务函数 void TIM7_IRQHandler(void) { if(TIM_GetITStatus(TIM7, TIM_IT_Update) == SET) //溢出中断 { } TIM_ClearITPendingBit(TIM7, TIM_IT_Update); //清除中断标志位 }
这个我是1s一次,可以在中断里面翻转LED等其他东西看看.中断定时非常简单,那么PWM输出和捕获呢?首先我们要在一个板子上完成这两件事,先决条件是...PWM发生速度较慢,捕获速度较快.他们初始化都差不多,区别在于,一个是TIM_OCInit,一个是TIM_ICInit,这里的I,O意思就是Input,Output.他们的速度快,和速度慢,就是TimeBase的初始化,而TimeBase,又实践过了,所以~ 只要讲的PWM/IC与普通定时的区别就可以了.
PWM发生我们用TIM14,基础定时器可是不能发生硬件PWM的哦.比如TIM_OCPolarity = TIM_OCPolarity_Low,就是说,在达到CCR时候,电平就变高了.直到走完整个周期.而如果我们设置的是OC1,就用TIM_SetCompare1修改CCR,当然直接写寄存器也可以的.
void TIM14_PWM_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE); //TIM14时钟使能 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能PORTF时钟 GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_TIM14); //GPIOF9复用为定时器14 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //GPIOF9 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PF9 TIM_TimeBaseStructure.TIM_Prescaler = 1000; //定时器分频 这里决定了,PWM频率是84MHz/84=1MHz TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseStructure.TIM_Period = 5000; //自动重装载值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM14, &TIM_TimeBaseStructure); //初始化定时器14 //初始化TIM14 Channel1 PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低 TIM_OC1Init(TIM14, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM1 4OC1 TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable); //使能TIM14在CCR1上的预装载寄存器 TIM_ARRPreloadConfig(TIM14, ENABLE); //ARPE使能 TIM_Cmd(TIM14, ENABLE); //使能TIM14 }
现在可以接一个LED到面包板看到LED在呼吸,当然,要有一些引脚电容才行哦,你直接焊接在LED上无济于事哦.
既然PWM已经发生,然后打算用TIM13来捕获,为什么,因为TIM13用到的引脚是PA6,虽然他跟TIM8 中断上复用,但完全不是一个定时器,不会乱的.一般在捕获时候,整个捕获周期都是0xFFFFFFFF,但是如果定时器位数不足,就按最大位数选取.现在可以用短路帽把他们两个IO短路起来.然后既然是捕获,就应该设置一下触发,而且,可能一次没捕捉到呢?那么还要捕捉两次,就是先捕捉TIM_ICPolarity_Rising,让电平从0,变成1,再捕捉TIM_ICPolarity_Falling,从1变成0,就知道高电平长度了.
void TIM13_CAP_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; TIM_ICInitTypeDef TIM13_ICInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM13, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_TIM13); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; GPIO_Init(GPIOA, &GPIO_InitStructure); TIM_TimeBaseStructure.TIM_Prescaler = 83; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period = 0XFFFFFFFF; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM13, &TIM_TimeBaseStructure); TIM13_ICInitStructure.TIM_Channel = TIM_Channel_1; TIM13_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM13_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM13_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM13_ICInitStructure.TIM_ICFilter = 0x00; TIM_ICInit(TIM13, &TIM13_ICInitStructure); TIM_ITConfig(TIM13, TIM_IT_Update | TIM_IT_CC1, ENABLE); TIM_Cmd(TIM13, ENABLE ); NVIC_InitStructure.NVIC_IRQChannel = TIM8_UP_TIM13_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } uint8_t TIM13CH1_CAPTURE_STA = 0; uint32_t TIM13CH1_CAPTURE_VAL; void TIM8_UP_TIM13_IRQHandler(void) { if((TIM13CH1_CAPTURE_STA & 0X80) == 0) //还未成功捕获 { if(TIM_GetITStatus(TIM13, TIM_IT_Update) != RESET)//溢出 { if(TIM13CH1_CAPTURE_STA & 0X40) //已经捕获到高电平了 { if((TIM13CH1_CAPTURE_STA & 0X3F) == 0X3F) //高电平太长了 { TIM13CH1_CAPTURE_STA |= 0X80; //标记成功捕获了一次 TIM13CH1_CAPTURE_VAL = 0XFFFFFFFF; } else TIM13CH1_CAPTURE_STA++; } } if(TIM_GetITStatus(TIM13, TIM_IT_CC1) != RESET)//捕获1发生捕获事件 { if(TIM13CH1_CAPTURE_STA & 0X40) //捕获到一个下降沿 { TIM13CH1_CAPTURE_STA |= 0X80; //标记成功捕获到一次高电平脉宽 TIM13CH1_CAPTURE_VAL = TIM_GetCapture1(TIM13); //获取当前的捕获值. TIM_OC1PolarityConfig(TIM13, TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获 } else //还未开始,第一次捕获上升沿 { TIM13CH1_CAPTURE_STA = 0; //清空 TIM13CH1_CAPTURE_VAL = 0; TIM13CH1_CAPTURE_STA |= 0X40; //标记捕获到了上升沿 TIM_Cmd(TIM13, DISABLE ); //关闭定时器5 TIM_SetCounter(TIM13, 0); TIM_OC1PolarityConfig(TIM13, TIM_ICPolarity_Falling); //CC1P=1 设置为下降沿捕获 TIM_Cmd(TIM13, ENABLE ); //使能定时器5 } } } TIM_ClearITPendingBit(TIM13, TIM_IT_CC1 | TIM_IT_Update); //清除中断标志位 }
在主函数中,写一些测试,就可以看看效果了:
#include "stm32f4xx.h" #include "SysTick.h" #include "DUART.h" #include "TimerPWM.h" #include "TimerCAP.h" int main(void) { uint8_t PWM_Dir = 1; uint16_t PWM_Val = 200; __int64 CAP_Temp; SysTick_Init(); DUSART_Init(); TIM14_PWM_Init(); TIM13_CAP_Init(); while (1) { Delay(10); if(PWM_Dir)PWM_Val++; else PWM_Val--; if(PWM_Val > 350)PWM_Dir = 0; if(PWM_Val < 10)PWM_Dir = 1; TIM_SetCompare1(TIM14, PWM_Val * 10); if(TIM_GetCapture1(TIM14) == 300)TIM_SetCompare1(TIM14, 0); if(TIM13CH1_CAPTURE_STA & 0X80) //成功捕获到了一次高电平 { CAP_Temp = TIM13CH1_CAPTURE_STA & 0X3F; CAP_Temp *= 0XFFFFFFFF; //溢出时间总和 CAP_Temp += TIM13CH1_CAPTURE_VAL; //得到总的高电平时间 printf("HIGH:%lld us ", CAP_Temp); //打印总的高点平时间 TIM13CH1_CAPTURE_STA = 0; //开启下一次捕获 } } }
如图: