1.定时器的输入捕获可以用来测量脉冲宽度或者测量频率。输入捕获的原理图如下:
假设定时器是向上计数。在图中,t1~t2之间的便是我们要测量的高电平的时间(脉冲宽度)。首先,设置定时器为上升沿捕获,如此一来,在t1时刻可以捕获到当前定时器的计数值CNT。然后,清零CNT,并设置定时器为下降沿捕获,这样,在t2时刻,又会发生捕获事件,得到此时CNT的值,记为CCRx2。最后,根据定时器的计数频率,便可以计数出t1~t2的时间,从而得到高电平的脉冲宽度。
这里的上升沿捕获是指,当定时器复用的IO口,被输入高电平时,即t1时刻产生捕获。下降沿捕获同理。
此外,在t1~t2之间,可能产生N次的定时器溢出。为了使数据准确,需要对定时器的溢出做出处理,防止高电平的时间过长。本次实验中的处理方式为,当溢出的次数达到一定值时,强制认为已经捕获完成(捕获到上升沿、捕获到下降沿)。
CNT的计数次数:N*ARR+CRRx2。
用CNT的计数次数乘以计数周期,便可以得到t1~t2的时间长度,即高电平持续的时间。
2.修改寄存器介绍:TIMx_ARR、 TIMx_PSC、TIMx_CCMR1、TIMx_CCER、TIMx_DIER、TIMx_CR1、TIMx_CCR1等寄存器的介绍可以在前面的博客中定时器介绍部分查看。此处只介绍TIMx_CCMR1寄存器和TIMx_CCER寄存器。
(1)捕获/比较寄存器(TIMx_CCMR1):
当在输入捕获模式下使用时,对于着图中的第二行。低八位[7:0]用于捕获/比较寄存器通道1的控制。高八位[15:8]用于捕获/比较寄存器通道2的控制。本次实验中使用的通道1,因此,只介绍TIMx_CCMR1的低八位。
[7:0]各位的描述如下:
(2)捕获/比较使能寄存器(TIMx_CCER):
要想使能输入捕获,必须设置CC1E为1。
3.设计思路:
(1)使能TIM2时钟,配置PA0为下拉输入。(开发版中PA0与TIM2_CH1复用,并外接了按键。)
(2)初始化TIM2,设置TIM2的ARR和PSC。
(3)设置TIM2的输入比较参数,开启输入捕获。
4.代码:通过按键的状态获取高低电平的时间,并将时间通过串口打印。同时,使用到了定时器2的中断服务函数。
(1)usart:
#ifndef __USART_H
#define __USART_H
#include "stm32f10x.h"
#include <stdio.h>
#define RX_LEN 100 //Äܹ»½ÓÊܵÄ×î´ó×Ö½ÚÊý
extern u8 RX_BUF[RX_LEN];
extern int len;
extern u8 RX_FLAG;
void usart_init(u32 bound);
#endif
#include "USART.h"
int len = 0;
u8 data;
u8 RX_BUF[RX_LEN];
u8 RX_FLAG = 0;
//´®¿Ú³õʼ»¯º¯Êý
void usart_init(u32 bound)
{
//1.¶¨ÒåÒý½Å¡¢USART¡¢ÖжϽṹÌ壺
GPIO_InitTypeDef GPIO_Initstruct;
USART_InitTypeDef USART_Initstruct;
NVIC_InitTypeDef NVIC_Initstruct;
//2.ʹÄܶ˿ںÍUSARTµÄʱÖÓ£º
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1,ENABLE);
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1 | RCC_APB2Periph_AFIO,ENABLE);
//3.ÅäÖÃÒý½Å£º
//PA9£º
GPIO_Initstruct.GPIO_Pin = GPIO_Pin_9;
GPIO_Initstruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Initstruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_Initstruct);
//PA10:
GPIO_Initstruct.GPIO_Pin = GPIO_Pin_10;
GPIO_Initstruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Initstruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_Initstruct);
//4.ÅäÖÃUSART1:
USART_Initstruct.USART_BaudRate = bound;
USART_Initstruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Initstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Initstruct.USART_Parity = USART_Parity_No;
USART_Initstruct.USART_StopBits = USART_StopBits_1;
USART_Initstruct.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1,&USART_Initstruct); //½«Ïà¹ØÊý¾ÝдÈëUSARTµÄ¼Ä´æÆ÷
USART_Cmd(USART1,ENABLE); //ʹÄÜUSART¼Ä´æÆ÷
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //ʹÄܽÓÊÕÖжÏ
//5.ÅäÖÃÖжϣº
NVIC_Initstruct.NVIC_IRQChannel = USART1_IRQn;
NVIC_Initstruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Initstruct.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_Initstruct.NVIC_IRQChannelSubPriority = 3;
NVIC_Init(&NVIC_Initstruct);
}
//void USART1_IRQHandler(void)
//{
// //uint16_t x[] = {1,2,3};
// //ÅжÏÊÇ·ñ²úÉú´®¿ÚÊý¾Ý½ÓÊÜÖжÏ
// if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)
// {
// data = USART_ReceiveData(USART1);
// RX_BUF[len++] = data;
// RX_FLAG = 1;
// }
//}
//Öض¨Ïòfputcº¯Êý£º
//int fputc(int ch,FILE *f)
//{
// //1.Åжϴ®¿ÚÊÇ·ñ·¢ËÍÍê³É£º
// while((USART1->SR & 0x40) == 0);
//
// //2.·¢ËÍÒ»¸ö×Ö½Ú£¬½«Êý¾ÝдÈëµ½¼Ä´æÆ÷£º
// USART1->DR = (u8) ch;
// return ch;
//}
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//Ñ»··¢ËÍ,Ö±µ½·¢ËÍÍê±Ï
USART1->DR = (u8) ch;
return ch;
}
(2)delay:
#ifndef __DELAY_H
#define __DELAY_H
#include "stm32f10x.h"
void delay_us(uint32_t us); //ÑÓʱ΢Ãë
void delay_ms(uint32_t ms); //ÑÓʱºÁÃë
#endif
#include "delay.h"
void delay_us(uint32_t us)
{
uint32_t i;
//1.Ñ¡ÔñHCLKʱÖÓ£¬²¢ÉèÖõδðʱÖÓ¼ÆÊýÖµ
SysTick_Config(72);
for(i = 0;i < us;i++)
{
while(!((SysTick->CTRL) & (1 << 16))); //µÈ´ý¼ÆÊýÍê³É
}
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //Ñ¡ÔñSTCLKʱÖÓÔ´£¬²¢Ê§Äܶ¨Ê±Æ÷
}
void delay_ms(uint32_t ms)
{
uint32_t i;
//1.Ñ¡ÔñHCLKʱÖÓÔ´£¬²¢ÉèÖõδðʱÖÓ¼ÆÊýÖµ
SysTick_Config(72000);
for(i = 0;i < ms;i++)
{
while(!((SysTick->CTRL) & (1 << 16))); //µÈ´ý¼ÆÊýÍê³É
}
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //Ñ¡ÔñSTCLKʱÖÓÔ´£¬²¢Ê§Äܶ¨Ê±Æ÷
}
(3) time_capture:
#ifndef __TIME_CAPTURE_H
#define __TIME_CAPTURE_H
#include "stm32f10x.h"
extern u8 TIM2_CAPTURE_STATU; //ÊäÈ벶»ñ״̬
extern u16 TIM2_CAPTURE_VALUE;
void TIME_CAPTURE_Init(u16 arr,u16 psc);
#endif
#include "time_capture.h"
/*TIM2_CAPTURE_STATUµÄµÚ7Ϊ²¶»ñÍê³É±êÖ¾£¬
µÚ6λΪ²¶»ñµ½¸ßµçƽ±êÖ¾£¬
µÚ0-5λ벶»ñ¸ßµçƽºó¶¨Ê±Æ÷µÄÒç³ö´ËÊý*/
u8 TIM2_CAPTURE_STATU = 0; //ÊäÈ벶»ñ״̬
u16 TIM2_CAPTURE_VALUE = 0; //ÊäÈ벶»ñÖµ
void TIME_CAPTURE_Init(u16 arr,u16 psc)
{
//¶¨ÒåÏà¹Ø½á¹¹Ì壺
GPIO_InitTypeDef GPIO_InitStrcture;
TIM_TimeBaseInitTypeDef TIME_TimeBaseStructure;
TIM_ICInitTypeDef TIM2_ICInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//1.ʹÄÜʱÖÓ£º
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//2.ÅäÖÃÏà¹ØµÄ½á¹¹ÌåÐÅÏ¢£º
GPIO_InitStrcture.GPIO_Mode = GPIO_Mode_IPD;
GPIO_InitStrcture.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStrcture.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStrcture);
GPIO_ResetBits(GPIOA,GPIO_Pin_0); //³õʼʱ½«PA0ÖÃΪ0
//3.ÅäÖö¨Ê±Æ÷2£º
TIME_TimeBaseStructure.TIM_Period = arr;
TIME_TimeBaseStructure.TIM_Prescaler = psc;
TIME_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//ÉèÖÃʱÖÓ·Ö¸î
TIME_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Down;
TIM_TimeBaseInit(TIM2,&TIME_TimeBaseStructure);
//4.ÅäÖö¨Ê±Æ÷2µÄÊäÈ벶»ñ²ÎÊý£º
TIM2_ICInitStructure.TIM_Channel = TIM_Channel_1; //ÉèÖÃÊäÈëͨµÀ
TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //ÉèÖÃÉÏÉýÑز¶»ñ
TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //Ó³Éäµ½TI1ÉÏ
TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //ÉèÖÃÊäÈë·ÖƵ
TIM2_ICInitStructure.TIM_ICFilter = 0; //ÉèÖÃÂ˲¨Æ÷
TIM_ICInit(TIM2,&TIM2_ICInitStructure);
//5.ÖжϹÜÀíÅäÖãº
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStructure);
//6.ʹÄܶ¨Ê±Æ÷µÄ¸üÐÂÖжϡ¢²¶»ñÖжϺͶ¨Ê±Æ÷£º
TIM_ITConfig(TIM2,TIM_IT_Update | TIM_IT_CC1,ENABLE);
TIM_Cmd(TIM2,ENABLE);
}
//ÖØд¶¨Ê±Æ÷2µÄÖжϷþÎñº¯Êý£º
void TIM2_IRQHandler(void)
{
if((TIM2_CAPTURE_STATU & 0x80) ==0) //»¹Î´²¶»ñÍê³É
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update) != RESET)
{
if(TIM2_CAPTURE_STATU & 0x40) //²¶»ñµ½¸ßµçƽ
{
//´Ë´¦Êǵ±Á¬Ðø²¶»ñµ½¸ßµçƽµÄ´ÎÊý>= 0x3Fʱ£¬Ç¿ÖÆÈÏΪ²¶»ñ³É¹¦
if((TIM2_CAPTURE_STATU & 0x3F) == 0x3F)//¸ßµçƽ̫³¤
{
TIM2_CAPTURE_STATU |= 0x80; //±ê¼Ç²¶»ñ³É¹¦
TIM2_CAPTURE_VALUE = 0xFFFF;
}
else
{
TIM2_CAPTURE_STATU ++;
}
}
}
}
if(TIM_GetITStatus(TIM2,TIM_IT_CC1) != RESET) //·¢Éú²¶»ñʼþ
{
if(TIM2_CAPTURE_STATU & 0x40) //²¶»ñµ½Ò»¸öϽµÑØ
{
TIM2_CAPTURE_STATU |= 0X80; //±ê¼Ç²¶»ñ³É¹¦
TIM2_CAPTURE_VALUE = TIM_GetCapture1(TIM2); //»ñÈ¡¼ÆÊýcntÖµ
TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Rising); //Ï´β¶×½ÉÏÉýÑØ
}
else
{
TIM2_CAPTURE_STATU = 0;
TIM2_CAPTURE_VALUE = 0;
TIM_SetCounter(TIM2,0); //ÉèÖüÆÊý¼Ä´æÆ÷µÄÖµ
TIM2_CAPTURE_STATU |= 0x40; //±ê¼Ç²¶×½µ½ÁËÉÏÉýÑØ
TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Falling); //ÉèÖÃÏ´β¶×½Ï½µÑØ
}
}
TIM_ClearITPendingBit(TIM2,TIM_IT_CC1 | TIM_IT_Update); //Çå³ýÖжϱê־λ
}
(4) main:
#include "stm32f10x.h"
#include "USART.h"
#include "led.h"
#include "delay.h"
#include "time_capture.h"
#include <stdio.h>
extern u8 data;
int main(void)
{
int i;
u32 temp = 0;
//ÖжÏÓÅÏȼ¶·Ö×飺
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//³õʼ»¯´®¿ÚUSART1:
usart_init(9600);
//LED_Init();
//KEY_Init();
TIME_CAPTURE_Init(0xFFFF,72 - 1);
//GPIO_ResetBits(GPIOA,GPIO_Pin_8);
//GPIO_ResetBits(GPIOD,GPIO_Pin_2);
printf("½ÓÊܵ½µÄÊý¾Ý:\r\n");
delay_ms(10);
while(1)
{
//printf("test\r\n");
delay_ms(10);
if(TIM2_CAPTURE_STATU & 0X80) //²¶×½µ½¸ßµçƽ
{
//printf("test2.0\r\n");
temp = TIM2_CAPTURE_VALUE & 0x3F;
temp *= 65536;
temp += TIM2_CAPTURE_VALUE;
printf("high = %d us\r\n",temp);
TIM2_CAPTURE_STATU = 0; //¿ªÆôÏÂÒ»´Î²¶»ñ
}
}
}
5.运行结果:根据按键按键的时间,计数高电平的时间。
6.总结:通过定时器捕获可以测量脉冲宽度或者测量频率。定时器的捕获配置,主要是配置相关的寄存器,并重写定时器中断服务函数,在中断服务函数中书写数据捕获的逻辑。