热度 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灯交替闪烁。 我并没有对捕获值作任何处理,因为我只是测试程序是否能顺利进入捕获中断。