STM32F4-DISCO 学习之 定时器中断 + PWM 生成 + 定时器捕获

/ 0评 / 1

因为最近的工作任务忙的比狗还惨,所以更新也就慢了一点点,不过最近一个关于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;			   //开启下一次捕获
        }
    }
}

如图:

QQ截图20150905151550

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注