单片机入门学习十四 STM32单片机学习十一 通用定时器二

上篇文章仅记录了通用定时器的时基单元的功能,本篇将记录其另外两个功能,输入和输出。

1、输入

输入
定时器使用其输入模块的功能需要如下步骤,在下面步骤中我也会分别将涉及到的寄存器一并列入在此。

1)步骤1:设置输入捕获滤波器(以上图的通道1为列)

捕获滤波器
从上图中知,该步骤涉及到的滤波器有
①TIMx_CCMR1(捕获/比较模式寄存器)
TIMx_CCMR1
②TIMx_CR1(控制寄存器)
TIMx_CR1

我们知道采用默认时钟初始化,Fck_int=72Mhz,Fdts由TIMx_CR1的CKD[1:0]的值来确定,若CKD[1:0]=00,那么Fdts=Fck_int。若ICIF[3:0]=0011,以Fck_int的频率采样,若连续采样8次,若都是高电平,则说明是一个有效的触发,就会触发输入捕获中断。这样就可以滤除那些高电平脉宽低于8个采样周期的脉冲信号,从而达到滤波的效果。若ICIF[3:0]=0000,则我们不做滤波处理,只要采集到上升沿就会触发捕获中断。

2)步骤2:设置输入捕获极性(通道1)

输入捕获
上图中涉及到的寄存器
TIMx_CCER(捕获/比较使能寄存器)
TIMx_CCER

3)设置输入捕获映射通道(通道1)

输入捕获
上图涉及到的寄存器
TIMx_CCMR1(捕获/比较模式寄存器)
TIMx_CCMR1

4)设置输入捕获分频器(通道1)

输入捕获分频器
上图中涉及到的寄存器
①TIMx_CCMR1(捕获/比较模式寄存器)
IC1PS
②TIMx_CCER(捕获/比较使能寄存器)
如步骤2中TIMx_CCER的第0位 CC1E描述。

5)步骤五:捕获到有效信号可以开启中断

使用到的寄存器为
DMA/中断使能寄存器(TIMx_DIER)
TIMx_DIER

6)步骤六:查看定时器通道对应引脚

STM32F103x数据手册中 我们来查看定时器对应引脚,从下图中我们可以看出 TIM5的通道1连接PA0,TIM5的通道2连接PA1等等。
通道引脚

7)总结

因此输入捕获的一般程序库函数配置步骤如下:
① 初始化定时器和通道对应IO的时钟。
② 初始化IO口,模式为输入:GPIO_Init();
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 输入
③初始化定时器ARR,PSC
TIM_TimeBaseInit();
④初始化输入捕获通道
TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
其中TIM_ICInitTypeDef的定义如下

typedef struct
{
  uint16_t TIM_Channel; //捕获通道1-4   
  uint16_t TIM_ICPolarity; //捕获极性(上升沿捕获[TIM_ICPolarity_Rising]、下降沿捕获[TIM_ICPolarity_Falling]、双边捕获[TIM_ICPolarity_BothEdge])
  uint16_t TIM_ICSelection; //映射关系
  uint16_t TIM_ICPrescaler; //分频系数
  uint16_t TIM_ICFilter;  //滤波器
} TIM_ICInitTypeDef;

⑤如果要开启捕获中断,
TIM_ITConfig();
NVIC_Init();
⑥使能定时器:TIM_Cmd();
⑦编写中断服务函数:TIMx_IRQHandler();

定时器用其输入捕获功能,常用函数如下:

  • 通道极性设置独立函数
    void TIM_OCxPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
  • 获取通道捕获值
    uint32_t TIM_GetCapture1(TIM_TypeDef* TIMx);
8)输入捕获示例代码

本示例代码完成的功能是 探测按键按下时间
u8 TIM5CH1_CAPTURE_STA;//自定义变量,用于记录高电平捕获时间值,其每一位的用途如下表:
TIM5CH1_CAPTURE_STA

//定时器5通道1输入捕获配置

void TIM5_Cap_Init(u16 arr,u16 psc)
{    
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
    TIM_ICInitTypeDef  TIM5_ICInitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);    //使能TIM5时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA时钟

    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;  //PA0 清除之前设置  
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 输入  
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_ResetBits(GPIOA,GPIO_Pin_0);                        //PA0 下拉

    //初始化定时器5 TIM5   
    TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值 
    TIM_TimeBaseStructure.TIM_Prescaler =psc;   //预分频器   
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

    //初始化TIM5输入捕获参数
    TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01     选择输入端 IC1映射到TI1上
    TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;    //上升沿捕获
    TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
    TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;   //配置输入分频,不分频 
    TIM5_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波
    TIM_ICInit(TIM5, &TIM5_ICInitStructure);

    //中断分组初始化
    NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;  //TIM3中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先占优先级2级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级0级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
    NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 

    TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断 ,允许CC1IE捕获中断    

    TIM_Cmd(TIM5,ENABLE );  //使能定时器5
}


u8  TIM5CH1_CAPTURE_STA=0;  //输入捕获状态                            
u16 TIM5CH1_CAPTURE_VAL;    //输入捕获值

//定时器5中断服务程序     
void TIM5_IRQHandler(void)
{ 
    if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获   
    {     
        if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
        {       
            if(TIM5CH1_CAPTURE_STA&0X40)//已经捕获到高电平了
            {
                if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
                {
                    TIM5CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次
                    TIM5CH1_CAPTURE_VAL=0XFFFF;
                }else TIM5CH1_CAPTURE_STA++;
            }    
        }
        if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)//捕获1发生捕获事件
        {   
            if(TIM5CH1_CAPTURE_STA&0X40)        //捕获到一个下降沿      
            {               
                TIM5CH1_CAPTURE_STA|=0X80;      //标记成功捕获到一次高电平脉宽
                TIM5CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5);
                TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
            }else                               //还未开始,第一次捕获上升沿
            {
                TIM5CH1_CAPTURE_STA=0;          //清空
                TIM5CH1_CAPTURE_VAL=0;
                TIM_SetCounter(TIM5,0);
                TIM5CH1_CAPTURE_STA|=0X40;      //标记捕获到了上升沿
                TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);     //CC1P=1 设置为下降沿捕获
            }           
        }                                              
    }

    TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位

}

int main(void)
{       
    u32 temp=0; 
    delay_init();            //延时函数初始化    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
    uart_init(115200);   //串口初始化为115200


    TIM3_PWM_Init(899,0);       //不分频。PWM频率=72000/(899+1)=80Khz
    TIM5_Cap_Init(0XFFFF,72-1); //以1Mhz的频率计数 
    while(1)
    {
        delay_ms(10);
        TIM_SetCompare2(TIM3,TIM_GetCapture2(TIM3)+1);

        if(TIM_GetCapture2(TIM3)==300)TIM_SetCompare2(TIM3,0);  

        if(TIM5CH1_CAPTURE_STA&0X80)//成功捕获到了一次上升沿
        {
            temp=TIM5CH1_CAPTURE_STA&0X3F;
            temp*=65536;//溢出时间总和
            temp+=TIM5CH1_CAPTURE_VAL;//得到总的高电平时间
            printf("HIGH:%d us\r\n",temp);//打印总的高点平时间
            TIM5CH1_CAPTURE_STA=0;//开启下一次捕获
        }
    }
}

上面程序的思路是:(中断TIM5_IRQHandler(void)里的思路)TIM5配置为上升沿触发后,等待上升沿中断到来,当捕获到上升沿中断,若TIM5CH1_CAPTURE_STA的bit6=0,则表示还没哟捕获到新的上升沿,那么令TIM5CH1_CAPTURE_STA=0、TIM5CH1_CAPTURE_VAL=0、TIM5->CNT=0,同时设置TIM5CH1_CAPTURE_STA的bit6=1标记捕获到高电平,同时再设置为下降沿捕获,等待下降沿到来。在等待下降沿期间会有定时器溢出中断,若定时器发生了溢出中断,TIM5CH1_CAPTURE_STA++对溢出次数进行记录,若溢出次数是最大溢出值了,就强制标记捕获完成。当下降沿到来时,先设置TIM5CH1_CAPTURE_STA的bit7=1标识捕获一次高电平完成,然后将定时器值读到TIM5CH1_CAPTURE_VAL中,最后设置为上升沿捕获,回到初始状态。

当完成了一次捕获(TIM5CH1_CAPTURE_STA的bit7=1),在main函数中我们即可计算出高电平的时间。若TIM5CH1_CAPTURE_STA的bit7一直为1,就不会进行第二次捕获,因此在main函数中,计算出捕获的高电平时间后,将TIM5CH1_CAPTURE_STA=0,即将TIM5CH1_CAPTURE_STA的bit7=0,就开启了第二次捕获了。

2、输出

定时器的输出可以用于PWM(脉冲宽度调制,即控制输出方波的占空比)
STM32PWM工作过程
通道1
PWM
上面两幅图表示(第一幅图以通道1位例):
当ARR>CCR1时,若CCER:CC1P位=0并且CCER:CC1E位=0,则输出高电平。占空比的控制可以通过设置CCR1寄存器的值来确定
下面是图1涉及到的寄存器的值的含义:

  • TIMx_CCR1:捕获比较(值)寄存器(x=1,2,3,4):设置比较值。
    CCR1
  • TIMx_CCMR1: OC1M[2:0]位,对于PWM方式下,用于设置PWM模式1【110】或者PWM模式2【111】
    OC1M[
  • TIMx_CCER:CC1P位:输入/捕获1输出极性。0:高电平有效,1:低电平有效。
  • TIMx_CCER:CC1E位:输入/捕获1输出使能。0:关闭,1:打开。
1)常用库函数
  • 设置比较值函数
    void TIM_SetCompareX(TIM_TypeDef* TIMx, uint16_t Compare2);
  • 使能输出比较预装载
    void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
  • 使能自动重装载的预装载寄存器允许位
    void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
2)PWM输出配置步骤

① 使能定时器3和相关IO口时钟。
使能定时器3时钟:RCC_APB1PeriphClockCmd();
使能GPIOB时钟:RCC_APB2PeriphClockCmd();
② 初始化IO口为复用功能输出。函数:GPIO_Init();
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
这里我们是要把PB5用作定时器的PWM输出引脚,所以要重映射配置,所以需要开启AFIO时钟。同时设置重映射。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);
④ 初始化定时器:ARR,PSC等:TIM_TimeBaseInit();
⑤ 初始化输出比较参数:TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

typedef struct
{
  uint16_t TIM_OCMode;  //PWM模式1或者模式2
  uint16_t TIM_OutputState; //输出使能 OR失能
  uint16_t TIM_OutputNState;
  uint16_t TIM_Pulse; //比较值,写CCRx
  uint16_t TIM_OCPolarity; //比较输出极性
  uint16_t TIM_OCNPolarity; 
  uint16_t TIM_OCIdleState;  
  uint16_t TIM_OCNIdleState; 
} TIM_OCInitTypeDef;

⑥ 使能预装载寄存器: TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
⑦ 使能定时器。TIM_Cmd();
⑧ 不断改变比较值CCRx,达到不同的占空比效果:TIM_SetCompare2();

3)PWM程序示例代码

使用定时器3的PWM功能,输出占空比可变的PWM波,用来驱动LED灯,从而达到LED[PB5]亮度由暗变亮,又从亮变暗,如此循环。

//TIM3 PWM部分初始化 
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u16 arr,u16 psc)
{  
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;


    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);    //使能定时器3时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟

    GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射  TIM3_CH2->PB5    

   //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形    GPIOB.5
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO

   //初始化TIM3
    TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
    TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 
    TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

    //初始化TIM3 Channel2 PWM模式     
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
    TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC2

    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR2上的预装载寄存器

    TIM_Cmd(TIM3, ENABLE);  //使能TIM3
}

int main(void)
{       
    u16 led0pwmval=0;
    u8 dir=1;   
    delay_init();            //延时函数初始化    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);      //设置NVIC中断分组2:2位抢占优先级,2位响应优先级

    LED_Init();              //LED端口初始化
    TIM3_PWM_Init(899,0);    //不分频。PWM频率=72000000/900=80Khz
    while(1)
    {
        delay_ms(10);    
        if(dir)led0pwmval++;
        else led0pwmval--;

        if(led0pwmval>300)dir=0;
        if(led0pwmval==0)dir=1;                                      
        TIM_SetCompare2(TIM3,led0pwmval);          
    }    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风水月

从心底相信自己是成功的第一步

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值