完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
以前就纠结过能不能一个定时器进行多路的输入捕获,因为毕竟输出四路的PWM是轻松随意的,当时大概想了一下觉得可能会比较麻烦就一直没去尝试,最近组里的同学做方波测频和测占空比遇到了问题,又提到了这个,今天仔细想了一下有了思路就写程序然后上板子试了一下,解决了这个问题。
首先我们先看单路的输入捕获是怎么实现的 定时器的初始化函数: //定时器2通道1输入捕获配置 //arr:自动重装值 //psc:时钟预分频数 void TIM2_Cap_Init(u16 arr,u16 psc) { RCC->APB1ENR|=1<<0; //TIM2 时钟使能 RCC->APB2ENR|=1<<2; //使能PORTA时钟 GPIOA->CRL&=0XFFFFFFF0; //PA0 清除之前设置 GPIOA->CRL|=0X00000008; //PA0 输入 GPIOA->ODR|=0<<0; //PA0 下拉 TIM2->ARR=arr; //设定计数器自动重装值 TIM2->PSC=psc; //预分频器 TIM2->CCMR1|=1<<0; //CC1S=01 选择输入端 IC1映射到TI1上 TIM2->CCMR1|=1<<4; //IC1F=0001 配置输入滤波器 以Fck_int采样,2个事件后有效 TIM2->CCMR1|=0<<10; //IC2PS=00 配置输入分频,不分频 TIM2->CCER|=0<<1; //CC1P=0 上升沿捕获 TIM2->CCER|=1<<0; //CC1E=1 允许捕获计数器的值到捕获寄存器中 TIM2->DIER|=1<<1; //允许捕获中断 TIM2->DIER|=1<<0; //允许更新中断 TIM2->CR1|=0x01; //使能定时器2 MY_NVIC_Init(2,0,TIM2_IRQn,2);//抢占2,子优先级0,组2 } 输入捕获的中断函数: //捕获状态 //[7]:0,没有成功的捕获;1,成功捕获到一次. //[6]:0,还没捕获到高电平;1,已经捕获到高电平了. //[5:0]:捕获高电平后溢出的次数 u8 TIM2CH1_CAPTURE_STA=0; //输入捕获状态 u16 TIM2CH1_CAPTURE_VAL; //输入捕获值 //定时器2中断服务程序 void TIM2_IRQHandler(void) { u16 tsr; tsr=TIM2->SR; if((TIM2CH1_CAPTURE_STA&0X80)==0) //还未成功捕获 { if(tsr&0X01)//溢出 { if(TIM2CH1_CAPTURE_STA&0X40) //已经捕获到高电平了 { if((TIM2CH1_CAPTURE_STA&0X3F)==0X3F) //高电平太长了 { TIM2CH1_CAPTURE_STA|=0X80; //标记成功捕获了一次 TIM2CH1_CAPTURE_VAL=0XFFFF; }else TIM2CH1_CAPTURE_STA++; } } if(tsr&0x02)//捕获1发生捕获事件 { if(TIM2CH1_CAPTURE_STA&0X40) //捕获到一个下降沿 { TIM2CH1_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽 TIM2CH1_CAPTURE_VAL=TIM2->CCR1; //获取当前的捕获值. TIM2->CCER&=~(1<<1); //CC1P=0 设置为上升沿捕获 }else //还未开始,第一次捕获上升沿 { TIM2CH1_CAPTURE_VAL=0; TIM2CH1_CAPTURE_STA=0X40; //标记捕获到了上升沿 TIM2->CNT=0; //计数器清空 TIM2->CCER|=1<<1; //CC1P=1 设置为下降沿捕获 } } } TIM2->SR=0;//清除中断标志位 } 主函数读取并显示: int main(void) { u32 temp=0; Stm32_Clock_Init(9); //系统时钟设置 uart_init(72,9600); //串口初始化为9600 delay_init(72); //延时初始化 LED_Init(); //初始化与LED连接的硬件接口 TIM1_PWM_Init(899,36-1); //不分频。PWM频率=72000/(899+1)=80Khz TIM2_Cap_Init(0XFFFF,72-1); //以1Mhz的频率计数 while(1) { delay_ms(10); LED0_PWM_VAL++; if(LED0_PWM_VAL==300)LED0_PWM_VAL=0; if(TIM2CH1_CAPTURE_STA&0X80)//成功捕获到了一次高电平 { temp=TIM2CH1_CAPTURE_STA&0X3F; temp*=65536; //溢出时间总和 temp+=TIM2CH1_CAPTURE_VAL; //得到总的高电平时间 printf("HIGH:%d usrn",temp); //打印总的高点平时间 TIM2CH1_CAPTURE_STA=0; //开启下一次捕获 } } } 具体的原理我就不细说了,如果不了解可以去找一下原子的教程,那里面讲得是很详细的,要实现四通道的输入捕获初始化是肯定要修改的,但是最为重要的是修改中断函数中的内容。 在单路输出中,中断的执行流程是,当新开始一次捕获并且捕获到高电平(就是上升沿),将TIM2->CNT寄存器置0,并且将捕获电平变为低电平(去捕获下降沿),表示新开始一次捕获,然后如果持续高电平使定时器溢出,TIMCH1_CAPTURE_STA会自增1,然后捕获到下降沿以后就完成了一次捕获,这时去读取当前CCR寄存器的值,然后在加上溢出次数*65536得到的就是整个高电平器件定时器总计数值,通过计算计数频率就可以得到高脉冲的时间。 四通道输入捕获原理 要实现四路的输入捕获如果每开始一次新的捕获就将CNT值置零是肯定不可以的,一个定时器只有一个CNT寄存器这样会是捕获完全混乱,所以,我的解决方法是:用一个变量Date1来存当开始一次新的捕获时CNT寄存器的值,然后中间步骤和单通道是完全相同的,完成捕获后将那时CCR寄存器的值写到Date2。计算总计数值就是溢出次数*65536+Date2-Date1 四路输入捕获初始化函数 注意:我这里将TIM2进行了部分重映射,在不进行重映射的情况下(PA0,PA1,PA2,PA3)通道四无法触发捕获中断)进行部分重映射(PA0,PA1,PB10,PB11)后就可以正常使用,我觉得可能是板子的问题 //定时器2通道1-4输入捕获配置 //arr:自动重装值 //psc:时钟预分频数 void TIM2_Cap_Init(u16 arr,u16 psc) { RCC->APB1ENR|=1<<0; //TIM2 时钟使能 RCC->APB2ENR|=1<<2; //使能PORTA时钟 RCC->APB2ENR|=1<<3; //使能PORTB时钟 RCC->APB2ENR|=1<<0; //AFIO时钟使能 AFIO->MAPR|=2<<8; //TIM2部分映射 GPIOA->CRL&=0XFFFFFF00; //PA0,PA1清除之前设置 GPIOA->CRL|=0X00000088; //PA0,PA1输入 GPIOA->ODR|=0<<1; //PA0,PA1下拉 GPIOB->CRH&=0xFFFF00FF; //PB10,PB11清除之前设置 GPIOB->CRH|=0x00008800; //PB10,PB11输入 GPIOB->ODR|=0<<11; //PB10,PB11下拉 TIM2->ARR=arr; //设定计数器自动重装值 TIM2->PSC=psc; //预分频器 //CH1 TIM2->CCMR1|=1<<0; //CC1S=01 选择输入端 IC1映射到TI1上 TIM2->CCMR1|=1<<4; //IC1F=0001 配置输入滤波器 以Fck_int采样,2个事件后有效 TIM2->CCMR1|=0<<2; //IC1PS=00 配置输入分频,不分频 TIM2->CCER|=0<<1; //CC1P=0 上升沿捕获 TIM2->CCER|=1<<0; //CC1E=1 允许捕获计数器的值到捕获寄存器中 //CH2 TIM2->CCMR1|=1<<8; //CC2S=01 选择输入端 IC1映射到TI1上 TIM2->CCMR1|=1<<12; //IC2F=0001 配置输入滤波器 以Fck_int采样,2个事件后有效 TIM2->CCMR1|=0<<10; //IC2PS=00 配置输入分频,不分频 TIM2->CCER|=0<<5; //CC2P=0 上升沿捕获 TIM2->CCER|=1<<4; //CC2E=1 允许捕获计数器的值到捕获寄存器中 //CH3 TIM2->CCMR2|=1<<0; //CC3S=01 选择输入端 IC1映射到TI1上 TIM2->CCMR2|=1<<4; //IC3F=0001 配置输入滤波器 以Fck_int采样,2个事件后有效 TIM2->CCMR2|=0<<2; //IC3PS=00 配置输入分频,不分频 TIM2->CCER|=0<<9; //CC3P=0 上升沿捕获 TIM2->CCER|=1<<8; //CC3E=1 允许捕获计数器的值到捕获寄存器中 //CH4 TIM2->CCMR2|=1<<8; //CC4S=01 选择输入端 IC1映射到TI1上 TIM2->CCMR2|=1<<12; //IC4F=0001 配置输入滤波器 以Fck_int采样,2个事件后有效 TIM2->CCMR2|=0<<10; //IC4PS=00 配置输入分频,不分频 TIM2->CCER|=0<<13; //CC4P=0 上升沿捕获 TIM2->CCER|=1<<12; //CC4E=1 允许捕获计数器的值到捕获寄存器中 //中断使能 TIM2->DIER|=1<<1; //允许捕获1中断 TIM2->DIER|=1<<2; //允许捕获2中断 TIM2->DIER|=1<<3; //允许捕获3中断 TIM2->DIER|=1<<4; //允许捕获4中断 TIM2->DIER|=1<<0; //允许更新中断 TIM2->CR1|=0x01; //使能定时器2 MY_NVIC_Init(2,0,TIM2_IRQn,2);//抢占2,子优先级0,组2 } 中断服务函数: //捕获状态 //[7]:0,没有成功的捕获;1,成功捕获到一次. //[6]:0,还没捕获到高电平;1,已经捕获到高电平了. //[5:0]:捕获高电平后溢出的次数 //CH1 u8 TIM2CH1_CAPTURE_STA=0; //输入捕获状态 u16 TIM2CH1_CAPTURE_Date2; //数据2 u16 TIM2CH1_CAPTURE_Date1; //数据1 //CH2 u8 TIM2CH2_CAPTURE_STA=0; //输入捕获状态 u16 TIM2CH2_CAPTURE_Date2; //数据2 u16 TIM2CH2_CAPTURE_Date1; //数据1 //CH3 u8 TIM2CH3_CAPTURE_STA=0; //输入捕获状态 u16 TIM2CH3_CAPTURE_Date2; //数据2 u16 TIM2CH3_CAPTURE_Date1; //数据1 //CH4 u8 TIM2CH4_CAPTURE_STA=0; //输入捕获状态 u16 TIM2CH4_CAPTURE_Date2; //数据2 u16 TIM2CH4_CAPTURE_Date1; //数据1 //定时器2中断服务程序 void TIM2_IRQHandler(void) { u16 tsr; tsr=TIM2->SR; //CH1中断处理 if((TIM2CH1_CAPTURE_STA&0X80)==0)//还未成功捕获 { if(tsr&0X01)//溢出 { if(TIM2CH1_CAPTURE_STA&0X40)//已经捕获到高电平了 { if((TIM2CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了 { TIM2CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次 TIM2CH1_CAPTURE_Date2=0XFFFF; }else TIM2CH1_CAPTURE_STA++; } } if(tsr&0x02)//捕获1发生捕获事件 { if(TIM2CH1_CAPTURE_STA&0X40) //捕获到一个下降沿 { TIM2CH1_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽 TIM2CH1_CAPTURE_Date2=TIM2->CCR1; //获取当前的捕获值. TIM2->CCER&=~(1<<1); //CC1P=0 设置为上升沿捕获 }else //还未开始,第一次捕获上升沿 { TIM2CH1_CAPTURE_Date2=0; TIM2CH1_CAPTURE_STA=0X40; //标记捕获到了上升沿 TIM2CH1_CAPTURE_Date1=TIM2->CCR1; TIM2->CCER|=1<<1; //CC1P=1 设置为下降沿捕获 } } } //CH2中断处理 if((TIM2CH2_CAPTURE_STA&0X80)==0)//还未成功捕获 { if(tsr&0X01)//溢出 { if(TIM2CH2_CAPTURE_STA&0X40)//已经捕获到高电平了 { if((TIM2CH2_CAPTURE_STA&0X3F)==0X3F)//高电平太长了 { TIM2CH2_CAPTURE_STA|=0X80;//标记成功捕获了一次 TIM2CH2_CAPTURE_Date2=0XFFFF; }else TIM2CH2_CAPTURE_STA++; } } if(tsr&0x04)//捕获1发生捕获事件 { if(TIM2CH2_CAPTURE_STA&0X40) //捕获到一个下降沿 { TIM2CH2_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽 TIM2CH2_CAPTURE_Date2=TIM2->CCR2; //获取当前的捕获值. TIM2->CCER&=~(1<<5); //CC1P=0 设置为上升沿捕获 }else //还未开始,第一次捕获上升沿 { TIM2CH2_CAPTURE_Date2=0; TIM2CH2_CAPTURE_STA=0X40; //标记捕获到了上升沿 TIM2CH2_CAPTURE_Date1=TIM2->CCR2; TIM2->CCER|=1<<5; //CC1P=1 设置为下降沿捕获 } } } //CH3中断处理 if((TIM2CH3_CAPTURE_STA&0X80)==0)//还未成功捕获 { if(tsr&0X01)//溢出 { if(TIM2CH3_CAPTURE_STA&0X40)//已经捕获到高电平了 { if((TIM2CH3_CAPTURE_STA&0X3F)==0X3F)//高电平太长了 { TIM2CH3_CAPTURE_STA|=0X80;//标记成功捕获了一次 TIM2CH3_CAPTURE_Date2=0XFFFF; }else TIM2CH3_CAPTURE_STA++; } } if(tsr&0x08)//捕获1发生捕获事件 { if(TIM2CH3_CAPTURE_STA&0X40) //捕获到一个下降沿 { TIM2CH3_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽 TIM2CH3_CAPTURE_Date2=TIM2->CCR3; //获取当前的捕获值. TIM2->CCER&=~(1<<9); //CC1P=0 设置为上升沿捕获 }else //还未开始,第一次捕获上升沿 { TIM2CH3_CAPTURE_Date2=0; TIM2CH3_CAPTURE_STA=0X40; //标记捕获到了上升沿 TIM2CH3_CAPTURE_Date1=TIM2->CCR3; TIM2->CCER|=1<<9; //CC1P=1 设置为下降沿捕获 } } } //CH4中断处理 if((TIM2CH4_CAPTURE_STA&0X80)==0)//还未成功捕获 { if(tsr&0X01)//溢出 { if(TIM2CH4_CAPTURE_STA&0X40)//已经捕获到高电平了 { if((TIM2CH4_CAPTURE_STA&0X3F)==0X3F)//高电平太长了 { TIM2CH4_CAPTURE_STA|=0X80;//标记成功捕获了一次 TIM2CH4_CAPTURE_Date2=0XFFFF; }else TIM2CH4_CAPTURE_STA++; } } if(tsr&0x10)//捕获1发生捕获事件 { if(TIM2CH4_CAPTURE_STA&0X40) //捕获到一个下降沿 { TIM2CH4_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽 TIM2CH4_CAPTURE_Date2=TIM2->CCR4; //获取当前的捕获值. TIM2->CCER&=~(1<<13); //CC1P=0 设置为上升沿捕获 }else //还未开始,第一次捕获上升沿 { TIM2CH4_CAPTURE_Date2=0; TIM2CH4_CAPTURE_STA=0X40; //标记捕获到了上升沿 TIM2CH4_CAPTURE_Date1=TIM2->CCR4; TIM2->CCER|=1<<13; //CC1P=1 设置为下降沿捕获 } } } TIM2->SR=0;//清除中断标志位 } 主函数(显示四路信号高点平的时间): int main(void) { u32 temp1=0,temp2=0,temp3=0,temp4=0; Stm32_Clock_Init(9); //初始化系统时钟 delay_init(72); //初始化延时 uart_init(72,115200); //串口初始化 TIM1_PWM_Init(1000-1,36-1); //不分频。PWM频率=72000/(899+1)=80Khz TIM2_Cap_Init(0XFFFF,72-1); //以2Mhz的频率计数 LED_Init(); TIM1_CH1_PWM_VAL=100; while(1) { if(TIM2CH1_CAPTURE_STA&0X80) //成功捕获到了一次高电平 { temp1=TIM2CH1_CAPTURE_STA&0X3F; temp1*=65536; //溢出时间总和 temp1+=TIM2CH1_CAPTURE_Date2; temp1-=TIM2CH1_CAPTURE_Date1; //得到总的高电平时间 printf("TIM2_CH1_HIGH:%d usrn",temp1); //打印总的高点平时间 TIM2CH1_CAPTURE_Date1=0; TIM2CH1_CAPTURE_STA=0; //开启下一次捕获 } if(TIM2CH2_CAPTURE_STA&0X80) //成功捕获到了一次高电平 { temp2=TIM2CH2_CAPTURE_STA&0X3F; temp2*=65536; //溢出时间总和 temp2+=TIM2CH2_CAPTURE_Date2; temp2-=TIM2CH2_CAPTURE_Date1; //得到总的高电平时间 printf("TIM2_CH2_HIGH:%d usrn",temp2); //打印总的高点平时间 TIM2CH2_CAPTURE_Date1=0; TIM2CH2_CAPTURE_STA=0; //开启下一次捕获 } if(TIM2CH3_CAPTURE_STA&0X80) //成功捕获到了一次高电平 { temp3=TIM2CH3_CAPTURE_STA&0X3F; temp3*=65536; //溢出时间总和 temp3+=TIM2CH3_CAPTURE_Date2; temp3-=TIM2CH3_CAPTURE_Date1; //得到总的高电平时间 printf("TIM2_CH3_HIGH:%d usrn",temp3); //打印总的高点平时间 TIM2CH3_CAPTURE_Date1=0; TIM2CH3_CAPTURE_STA=0; //开启下一次捕获 } if(TIM2CH4_CAPTURE_STA&0X80) //成功捕获到了一次高电平 { temp4=TIM2CH4_CAPTURE_STA&0X3F; temp4*=65536; //溢出时间总和 temp4+=TIM2CH4_CAPTURE_Date2; temp4-=TIM2CH4_CAPTURE_Date1; //得到总的高电平时间 printf("TIM2_CH4_HIGH:%d usrn",temp4); //打印总的高点平时间 TIM2CH4_CAPTURE_Date1=0; TIM2CH4_CAPTURE_STA=0; //开启下一次捕获 } printf("rn"); LED=!LED; delay_ms(10); } } |
|
|
|
只有小组成员才能发言,加入小组>>
3320 浏览 9 评论
3000 浏览 16 评论
3497 浏览 1 评论
9069 浏览 16 评论
4090 浏览 18 评论
1190浏览 3评论
612浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
603浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2339浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1899浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-27 17:45 , Processed in 1.211007 second(s), Total 78, Slave 59 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (威廉希尔官方网站 图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号