输入捕获就是用定时器检测引脚上的电平时间,可以检测高电平时间和低电平时间,然后可以算引脚上信号的频率和占空比。
基本思路就是利用定时器的输入捕获功能。
定时器捕获到高电平或低电平就会进入捕获中断
例如:
我们要捕获高电平时间
0 设置定时器计数频率和装载值,一般设置1MHz,65535
1 设置定时器捕获为高电平捕获
2 进入捕获中断后,获取CNT计数值或CCRx值,定时器捕获到电平后会把CNT的值保存到CCRx。
设置成低电平捕获。
3 再次进入捕获中断,获取CNT计数值-上次的CNT值=总高电平时间。
设置成高电平捕获。
4 重复2-3即可完成下一次捕获。当然还要考虑溢出的情况,代码里有处理。
下面我写的一个实例:
1 定时器1 PA8产生PWM信号,可改变占空比,检测高电平时间
2 捕获定时器是定时器2,初始化如上例。
3 仿真改变PA8占空比,查看捕获出的高电平时间。
实测:
duty=1000就是高电平维持1000us,
捕获时间也是对应的1000us,一点误差都没有。
代码如下:
//高电平标志
u8 gao_flag=0;
//高电平时间
u32 gao_timer=0;
//捕获成功标志
u8 buhuo_flag=0;
//溢出次数
u8 yichu_c=0;
//定时器 5 中断服务程序
extern "C" void TIM2_IRQHandler(void)
{
if((TIM2CH1_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) //这个是溢出中断
{
//如果已经得到高电平了,CNT溢出了
if(gao_flag==1)
{
yichu_c++; //溢出加1
}
}
if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)//捕获 1 发生捕获事件
{
if(0==gao_flag&&buhuo_flag==0) //说明之前没有捕获到高电平
{
gao_timer=TIM2->CCR1; //获取高电平时间
yichu_c=0; //溢出清零
gao_flag=1; //高电平标志
TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Falling); //改成下降沿捕获
}
//再一次进入捕获中断说明是捕获到下降沿了
else
{
if(buhuo_flag==0) //判断是否捕获成功了,如果捕获成功了就不在捕获了
{
gao_timer=TIM2->CCR1-gao_timer; //获取捕获到的高电平时间
gao_timer+=yichu_c*65536; //加上溢出时间
TIM2->CNT=0; //计数清零
gao_flag=0; //高电平标志清零
buhuo_flag=1; //标志捕获成功了
TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Rising); //改成上沿捕获
}
}
}
}
TIM_ClearITPendingBit(TIM2, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位
}
void TIM1_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStrue;
TIM_OCInitTypeDef TIM_OCInitStrue;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrue;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE); //使能TIM3和相关GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);// 使能GPIOB时钟(LED在BP5引脚),使能AFIO时钟(定时器3通道2需要重映射到BP5引脚)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStrue.GPIO_Pin=GPIO_Pin_8; // TIM_CH2
GPIO_InitStrue.GPIO_Mode=GPIO_Mode_AF_PP; // 复用推挽
GPIO_InitStrue.GPIO_Speed=GPIO_Speed_50MHz; //设置最大输出速度
GPIO_Init(GPIOA,&GPIO_InitStrue); //GPIO端口初始化设置
//TIM1->CCMR1&=0xF7F7; //关闭事件更新值
//TIM1->CCMR1|=0x808; //开启事件更新值
TIM_TimeBaseInitStrue.TIM_Period=50000; //设置自动重装载值
TIM_TimeBaseInitStrue.TIM_Prescaler=71; //预分频系数
TIM_TimeBaseInitStrue.TIM_CounterMode=TIM_CounterMode_Up; //计数器向上溢出
TIM_TimeBaseInitStrue.TIM_ClockDivision=TIM_CKD_DIV1; //时钟的分频因子,起到了一点点的延时作用,一般设为TIM_CKD_DIV1
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStrue); //TIM3初始化设置(设置PWM的周期)
TIM_OCInitStrue.TIM_OCMode=TIM_OCMode_PWM2; // PWM模式2:CNT>CCR时输出有效
TIM_OCInitStrue.TIM_OCPolarity=TIM_OCPolarity_Low;// 设置极性-有效为高电平
TIM_OCInitStrue.TIM_OutputState=TIM_OutputState_Enable;// 输出使能
TIM_OCInitStrue.TIM_Pulse=100;
TIM_OC1Init(TIM1,&TIM_OCInitStrue); //TIM3的通道2PWM 模式设置
TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable); //使能预装载寄存器
TIM_Cmd(TIM1,ENABLE); //使能TIM3
TIM_CtrlPWMOutputs(TIM1, ENABLE);
TIM1->CCMR1&=0xF7F7; //关闭事件更新值
}
void capture_init(u16 arr,u16 psc)
{
TIM_OCInitTypeDef TIM2_OCInitStructure;
TIM_ICInitTypeDef TIM2_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能 TIM2 时钟
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 下拉
//初始化定时器 2 TIM2
TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //预分频器
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //初始化 TIMx 的时间基数单位
//初始化 TIM2 输入捕获参数
TIM2_ICInitStructure.TIM_Channel = TIM_Channel_1; //选择输入端 IC1 映射到 TI1 上
TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到 TI1 上
TIM2_ICInitStructure.TIM_ICPrescaler = TIM_CKD_DIV1; //配置输入分频,不分频
TIM2_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波
TIM_ICInit(TIM2, &TIM2_ICInitStructure);
//中断分组初始化
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //TIM2 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级 2 级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级 0 级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能
NVIC_Init(&NVIC_InitStructure); //初始化外设 NVIC 寄存器
TIM_ITConfig(TIM2,TIM_IT_Update|TIM_IT_CC1,ENABLE);
TIM_Cmd(TIM2,ENABLE ); //使能定时器3
}
u16 duty=1000;
int main(void)
{
serial_init(115200); //串口初始化为 9600
TIM1_PWM_Init(1000,71); //
PAout(1)=1;
capture_init(0XFFFF,72-1); //以 1Mhz 的频率计数
printf("init\r\n");
while(1)
{
//测试位运算和逻辑运算的速度
PAout(1)=1;
TIM1->CCR1=duty;
delay_ms(10);
if(buhuo_flag==1)
{
printf("HIGH1:%d us \r\n",gao_timer); //打印总的高点平时间
buhuo_flag=0; //重新捕获
}
PAout(1)=0;
delay_ms(10);
}
}