tag 标签: 输入捕获

相关博文
  • 热度 21
    2013-1-19 18:59
    2330 次阅读|
    3 个评论
    第十五章 输入捕获实验         上一章,我们介绍了STM32的通用定时器作为PWM输出的使用方法,这一章,我们将向大家介绍通用定时器作为输入捕获的使用。在本章中,我们将用TIM5的通道1(PA0)来做输入捕获,捕获PA0上高电平的脉宽(用WK_UP按键输入高电平),通过串口打印高电平脉宽时间,从本章分为如下几个部分: 15.1 输入捕获简介 15.2 硬件设计 15.3 软件设计 15.4 下载验证 15.1 输入捕获简介 输入捕获模式可以用来测量脉冲宽度或者测量频率。STM32的定时器,除了TIM6和TIM7,其他定时器都有输入捕获功能。STM32的输入捕获,简单的说就是通过检测TIMx_CHx上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的通道的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。同时还可以配置捕获时是否触发中断/DMA等。 本章我们用到TIM5_CH1来捕获高电平脉宽,也就是要先设置输入捕获为上升沿检测,记录发生上升沿的时候TIM5_CNT的值。然后配置捕获信号为下降沿捕获,当下降沿到来时,发生捕获,并记录此时的TIM5_CNT值。这样,前后两次TIM5_CNT之差,就是高电平的脉宽,同时TIM5的计数频率我们是知道的,从而可以计算出高电平脉宽的准确时间。 接下来,我们介绍我们本章需要用到的一些寄存器配置,需要用到的寄存器有:TIMx_ARR、TIMx_PSC、TIMx_CCMR1、TIMx_CCER、TIMx_DIER、TIMx_CR1、TIMx_CCR1这些寄存器在前面2章全部都有提到(这里的x=5),我们这里就不再全部罗列了,我们这里针对性的介绍这几个寄存器的配置。 首先TIMx_ARR和TIMx_PSC,这两个寄存器用来设自动重装载值和TIMx的时钟分频,用法同前面介绍的,我们这里不再介绍。 再来看看捕获/比较模式寄存器1:TIMx_CCMR1,这个寄存器在输入捕获的时候,非常有用,有必要重新介绍,该寄存器的各位描述如图15.1.1所示:     图15.1.1 TIMx_CCMR1寄存器各位描述        当在输入捕获模式下使用的时候,对应图15.1.1的第二行描述,从图中可以看出,TIMx_CCMR1明显是针对2个通道的配置,低八位 用于捕获/比较通道1的控制,而高八位 则用于捕获/比较通道2的控制,因为TIMx还有CCMR2这个寄存器,所以可以知道CCMR2是用来控制通道3和通道4(详见《STM32参考手册》290页,14.4.8节)。        这里我们用到的是TIM5的捕获/比较通道1,我们重点介绍TIMx_CMMR1的 位(其实高8位配置类似),TIMx_CMMR1的 位详细描述见图15.1.2所示:     图15.1.2 TIMx_CMMR1 位详细描述 其中CC1S ,这两个位用于CCR1的通道配置,这里我们设置IC1S =01,也就是配置IC1映射在TI1上(关于IC1,TI1不明白的,可以看《STM32参考手册》14.2节的图98-通用定时器框图),即CC1对应TIMx_CH1。 输入捕获1预分频器IC1PSC ,这个比较好理解。我们是1次边沿就触发1次捕获,所以选择00就是了。 输入捕获1滤波器IC1F ,这个用来设置输入采样频率和数字滤波器长度。其中, 是定时器的输入频率(TIMxCLK),一般为72Mhz,而 则是根据TIMx_CR1的CKD 的设置来确定的,如果CKD 设置为00,那么 = 。N值就是滤波长度,举个简单的例子:假设IC1F =0011,并设置IC1映射到通道1上,且为上升沿触发,那么在捕获到上升沿的时候,再以 的频率,连续采样到8次通道1的电平,如果都是高电平,则说明却是一个有效的触发,就会触发输入捕获中断(如果开启了的话)。这样可以滤除那些高电平脉宽低于8个采样周期的脉冲信号,从而达到滤波的效果。这里,我们不做滤波处理,所以设置IC1F =0000,只要采集到上升沿,就触发捕获。 再来看看捕获/比较使能寄存器:TIMx_CCER,该寄存器的各位描述见图14.1.2(在第14章)。本章我们要用到这个寄存器的最低2位,CC1E和CC1P位。这两个位的描述如图15.1.3所示:       图15.1.3 TIMx_CCER最低2位描述        所以,要使能输入捕获,必须设置CC1E=1,而CC1P则根据自己的需要来配置。 接下来我们再看看DMA/中断使能寄存器:TIMx_DIER,该寄存器的各位描述见图13.1.2(在第13章),本章,我们需要用到中断来处理捕获数据,所以必须开启通道1的捕获比较中断,即CC1IE设置为1。 控制寄存器:TIMx_CR1,我们只用到了它的最低位,也就是用来使能定时器的,这里前面两章都有介绍,请大家参考前面的章节。 最后再来看看捕获/比较寄存器1:TIMx_CCR1,该寄存器用来存储捕获发生时,TIMx_CNT的值,我们从TIMx_CCR1就可以读出通道1捕获发生时刻的TIMx_CNT值,通过两次捕获(一次上升沿捕获,一次下降沿捕获)的差值,就可以计算出高电平脉冲的宽度。 至此,我们把本章要用的几个相关寄存器都介绍完了,本章要实现通过输入捕获,来获取TIM5_CH1(PA0)上面的高电平脉冲宽度,并从串口打印捕获结果。下面我们介绍输入捕获的配置步骤: 1 )开启TIM5 时钟,配置PA0 为下拉输入。 要使用TIM5,我们必须先开启TIM5的时钟(通过APB1ENR设置)。这里我们还要配置PA0为下拉输入,因为我们要捕获TIM5_CH1上面的高电平脉宽,而TIM5_CH1是连接在PA0上面的。 2 )设置TIM5 的ARR 和PSC 。 在开启了TIM5的时钟之后,我们要设置ARR和PSC两个寄存器的值来设置输入捕获的自动重装载值和计数频率。 3 )设置TIM5 的CCMR1 TIM5_CCMR1寄存器控制着输入捕获1和2的模式,包括映射关系,滤波和分频等。这里我们需要设置通道1为输入模式,且IC1映射到TI1(通道1)上面,并且不使用滤波(提高响应速度)器。 4 )设置TIM5 的CCER ,开启输入捕获,并设置为上升沿捕获。 TIM5_CCER寄存器是定时器的开关,并且可以设置输入捕获的边沿。只有TIM5_CCER寄存器使能了输入捕获,我们的外部信号,才能被TIM5捕获到,否则一切白搭。同时要设置好捕获边沿,才能得到正确的结果。 5 )设置TIM5 的DIER ,使能捕获和更新中断,并编写中断服务函数 因为我们要捕获的是高电平信号的脉宽,所以,第一次捕获是上升沿,第二次捕获时下降沿,必须在捕获上升沿之后,设置捕获边沿为下降沿,同时,如果脉宽比较长,那么定时器就会溢出,对溢出必须做处理,否则结果就不准了。这两件事,我们都在中断里面做,所以必须开启捕获中断和更新中断。 设置了中断必须编写中断函数,否则可能导致死机。我们需要在中断函数里面完成数据处理和捕获设置等关键操作,从而实现高电平脉宽统计。 6 )设置TIM5 的CR1 ,使能定时器 最后,必须打开定时器的计数器开关,通过设置TIM5_CR1的最低位为1,启动TIM5的计数器,开始输入捕获。 通过以上6步设置,定时器5的通道1就可以开始输入捕获了,同时因为还用到了串口输出结果,所以还需要配置一下串口。 15.2 硬件设计 本实验用到的硬件资源有: 指示灯DS0 WK_UP按键 串口 定时器TIM3 定时器TIM5         前面4个,在之前的章节均有介绍。本节,我们将捕获TIM5_CH1(PA0)上的高电平脉宽,通过WK_UP按键输入高电平,并从串口打印高电平脉宽。同时我们保留上节的PWM输出,大家也可以通过用杜邦线连接PB5和PA0,来测量PWM输出的高电平脉宽。 详细内容和源码,见附件!  
  • 热度 26
    2012-12-12 09:43
    17524 次阅读|
    12 个评论
      这两天学习了一下stm32通用定时器的输入捕获功能。在网上看到很多网友说触发中断程序进不了,于是自己也测试了个小程序,还好能够进入中断。呵呵~   实现功能:PA8随意延时驱动led灯闪烁,并且将PA8用杜邦线连接到PA7口,PA7是通用定时器TIM3的2通道,在TIM3_CH2触发中断程序中取反连接到PD2口的led灯,指示中断程序运行,并且每次进入中断后改变触发捕获的极性。实现两个led灯会交替闪烁。   先有必要了解stm32定时器的输入触发模块,如下图:   需要注意的是,一眼望去一个定时器似乎有8个通道,左边四个,右边四个,但其实左边和右边是共用相同的IO引脚,所以名称标注是一模一样。也就是说,每个通用定时器都只有四个独立通道,当某一通道作为了输入触发功能那就不能再作为输出匹配功能。这一点我们也可以从其他地方找到印证。比如TIM_ITConfig()函数中如下: void TIM_ITConfig ( TIM_TypeDef *  TIMx,     uint16_t  TIM_IT,     FunctionalState  NewState    )     Enables or disables the specified TIM interrupts. Parameters: TIMx,: where x can be 1 to 17 to select the TIMx peripheral. TIM_IT,: specifies the TIM interrupts sources to be enabled or disabled. This parameter can be any combination of the following values: TIM_IT_Update: TIM update Interrupt source TIM_IT_CC1: TIM Capture Compare 1 Interrupt source TIM_IT_CC2: TIM Capture Compare 2 Interrupt source TIM_IT_CC3: TIM Capture Compare 3 Interrupt source TIM_IT_CC4: TIM Capture Compare 4 Interrupt source TIM_IT_COM: TIM Commutation Interrupt source TIM_IT_Trigger: TIM Trigger Interrupt source TIM_IT_Break: TIM Break Interrupt source 我们可以看到此函数TIM_IT参数的取值范围如下: TIM_IT_Update: TIM update Interrupt source TIM_IT_CC1: TIM Capture Compare 1 Interrupt source TIM_IT_CC2: TIM Capture Compare 2 Interrupt source TIM_IT_CC3: TIM Capture Compare 3 Interrupt source TIM_IT_CC4: TIM Capture Compare 4 Interrupt source TIM_IT_COM: TIM Commutation Interrupt source TIM_IT_Trigger: TIM Trigger Interrupt source TIM_IT_Break: TIM Break Interrupt source 也就是说每个通道的捕获和比较功能是共用一个中断标志。     stm32定时器输入触发功能其实挺简单的,与AVR单片机几乎一样。就是单片机引脚上一旦出现一个有效边沿(可以配置为上升、下降或者上升下降均触发),那么定时器计数器CNT里面的值就会被相应的Capture/Compare X Register保存下来。这里X可以是1,2,3,4任何一个。并且中断标志位被置位。但是此时TIM的计数寄存器CNT却不管这一事件的发生,继续自己的计数。此功能可以用来测量外部信号的脉宽或者是周期。   对于定时器的时基单元TIM_TimeBaseStructure就不作说明了,在我前面的文章有专门介绍。下面就重点讲解输入触发单元TIM_ICInitStructure。 首先看次结构体原型的定义如下: typedef struct {    uint16_t TIM_Channel;      /*! Specifies the TIM channel.                                   This parameter can be a value of @ref TIM_Channel */    uint16_t TIM_ICPolarity;   /*! Specifies the active edge of the input signal.                                   This parameter can be a value of @ref TIM_Input_Capture_Polarity */    uint16_t TIM_ICSelection;  /*! Specifies the input.                                   This parameter can be a value of @ref TIM_Input_Capture_Selection */    uint16_t TIM_ICPrescaler;  /*! Specifies the Input Capture Prescaler.                                   This parameter can be a value of @ref TIM_Input_Capture_Prescaler */   uint16_t TIM_ICFilter;     /*! Specifies the input capture filter.                                   This parameter can be a number between 0x0 and 0xF */ } TIM_ICInitTypeDef;     它一共有5个成员,5个成员具体作用,我们只要看看3.5版本固件库的说明就清楚了。   uint16_t TIM_ICInitTypeDef::TIM_Channel Specifies the TIM channel. This parameter can be a value of TIM_Channel 其中TIM_Channel的取值范围如下: TIM_Channel_1. TIM_Channel_2 TIM_Channel_3 TIM_Channel_4 uint16_t TIM_ICInitTypeDef::TIM_ICFilter Specifies the input capture filter. This parameter can be a number between 0x0 and 0xF 说实话这个成员具体作用我没有深入了解,仅仅知道是作为对输入信号的滤波作用,估计是让用户设定用多少个采样时钟来确定最终输入信号,起到滤波作用,避免高频信号干扰,反正不管它了。 uint16_t TIM_ICInitTypeDef::TIM_ICPolarity Specifies the active edge of the input signal. This parameter can be a value of TIM_Input_Capture_Polarity 这个就是触发边沿的极性选择了,取值范围如下: TIM_ICPolarity_BothEdge TIM_ICPolarity_Rising TIM_ICPolarity_Falling uint16_t TIM_ICInitTypeDef::TIM_ICPrescaler Specifies the Input Capture Prescaler. This parameter can be a value of TIM_Input_Capture_Prescaler 这个成员是对外部信号进行分频,也即是设置上图中的Prescaler,可以设置为1/2/4/8分频。 uint16_t TIM_ICInitTypeDef::TIM_ICSelection Specifies the input. This parameter can be a value of TIM_Input_Capture_Selection 这个成员的作用就必须要对照上面的示意图才能明白。仔细看上面的图,可以发现定时器的4个通道并不是完全独立的,而是1、2一组,3、4一组,同组之间的通道是有联系的。也就是可以出现交叉触发。而TIM_ICSelection就是选择要不要使用交叉来触发,如果不明白可以看固件库的说明文档,如下是此结构体成员的取值范围: #define TIM_ICSelection_DirectTI   ((uint16_t)0x0001) TIM Input 1, 2, 3 or 4 is selected to be connected to IC1, IC2, IC3 or IC4, respectively #define TIM_ICSelection_IndirectTI   ((uint16_t)0x0002) TIM Input 1, 2, 3 or 4 is selected to be connected to IC2, IC1, IC4 or IC3, respectively. #define TIM_ICSelection_TRC   ((uint16_t)0x0003) TIM Input 1, 2, 3 or 4 is selected to be connected to TRC. 也就是说,根据不同的取值,可以讲外部引脚的触发信号连到内部不同的单元,这样就使得单片机更加灵活了。      下面是main.c文件 #include "stm32f10x.h"  GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef  TIM_ICInitStructure; NVIC_InitTypeDef NVIC_InitStructure;  void delay() { u32 i,j; for(i=0;i1000;i++) for(j=0;j5000;j++) ; }  void rcc_cfg() {  ; }  void gpio_cfg() {   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOA, ENABLE);     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       //随意延时led取反,且将PA8作为触发定时器电平   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   GPIO_Init(GPIOA, GPIO_InitStructure);     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;                       //触发中断时,取反PD2   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   GPIO_Init(GPIOD, GPIO_InitStructure);     /* TIM3 channel 2 pin (PA.07) configuration */    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;    GPIO_Init(GPIOA, GPIO_InitStructure); }   void nvic_cfg() {         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);               /* Enable the TIM3 global Interrupt */        NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能        NVIC_Init(NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 }   void tim3_cfg() {   RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);    TIM_DeInit(TIM3);   TIM_InternalClockConfig(TIM3);   //预分频系数为36000-1,这样计数器时钟为72MHz/36000 = 2kHz   TIM_TimeBaseStructure.TIM_Prescaler = 36000 - 1;   //设置时钟分割   TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;   //设置计数器模式为向上计数模式   TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;   //设置计数溢出大小,每计2000个数就产生一个更新事件   TIM_TimeBaseStructure.TIM_Period = 2000 - 1;   //将配置应用到TIM2中   TIM_TimeBaseInit(TIM3,TIM_TimeBaseStructure);    //禁止ARR预装载缓冲器   TIM_ARRPreloadConfig(TIM3, DISABLE);    //下面是对 TIM_ICInitStructure的配置    TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;        TIM_ICInitStructure.TIM_ICFilter = 0x0;       /*选择输入比较滤波器,滤波设置,经历几个周期跳变认定波形稳定0x0~0xF*/     TIM_ICInit(TIM3, TIM_ICInitStructure);     //开启TIM2的中断   TIM_ClearFlag(TIM3, TIM_IT_CC2);   TIM_ITConfig(TIM3,TIM_IT_CC2,ENABLE);     TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设 }   /**   * @brief  Main program.   * @param  None   * @retval None   */ int main(void) {       rcc_cfg();       gpio_cfg();       nvic_cfg();       tim3_cfg();   while (1)   {     /* Set PA8 */     GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_SET);        delay();     /* Reset PA8 */     GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_RESET);        delay();   } }   注意定时器3通道2引脚设置为 浮空输入 。       下面是stm32f10x_it.c文件   #include "stm32f10x_it.h"  u8 flag=0; extern TIM_ICInitTypeDef  TIM_ICInitStructure; void TIM3_IRQHandler(void) {   if(TIM_GetITStatus(TIM3, TIM_IT_CC2) == SET)   {     /* Clear TIM3 Capture compare interrupt pending bit */     TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);       //每次进入中断就改变捕获触发方式,且翻转PD2的电平        if(flag==0)               {               flag=1;               TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;               TIM_ICInit(TIM3, TIM_ICInitStructure);               GPIO_WriteBit(GPIOD, GPIO_Pin_2, Bit_RESET);               }        else               {               flag=0;               TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;               TIM_ICInit(TIM3, TIM_ICInitStructure);               GPIO_WriteBit(GPIOD, GPIO_Pin_2, Bit_SET);               }    } }   程序运行后,可以看到板子上两个led灯交替闪烁。 我并没有对捕获值作任何处理,因为我只是测试程序是否能顺利进入捕获中断。    
相关资源