打印
[STM8]

【转】STM8输入捕获

[复制链接]
779|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
联通移不动|  楼主 | 2016-11-2 23:36 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

最近在用STM8的过程中需要用到一个频率检测的功能,还好STM8S207的定时器中自带有输入捕获功能,之前还想着用定时器计数方式来实现的,但既然人家提供了该功能,那就试试吧,由于硬件里面接的是PC1引脚就只看了Timer1,其他的定时器应该也是类似的,看了资料之后发现STM8的输入捕获其实与STC12C5A60S2中的PCA捕获模式很类似,但是看资料没有后者清晰易懂。。。

在捕获模式中,基本上只用到了读进程,在STM8中有一个影子寄存器,但对于我们来说是看不到的,我们仅操作预装载寄存器即可。而且需要注意的是无论是计数器还是捕获/比较寄存器都是先读/写高8位,后读/写低8位数据。

在文档中给出了一个输入捕获模式的流程


按着这个流程来就可以完成我们的输入捕获

文档中首先提到将TIM1_CCMR1寄存器的CC1S位写01,将端口配置为输入,但在TIM1_CCMR1的寄存器中有说明CC1S位的更改需在通道关闭时(TIM1_CCER1寄存器的CC1E=0)才可写入,

因此在配置中先将TIM1_CCER1寄存器的CC1E位写0,然后将TIM1_CCMR1的CC1S位写01,


[cpp] view plain copy


  • TIM1_CCER1 &= (unsigned char)~0x01;//清零TIM1_CCER1中的CC1E位,之后才可配置TIM1_CCMR1  
  •     TIM1_CCMR1 = 0x01;//配置TIM1_CCMR1中的CC1S位为1,CC1通道配置为输入,IC1映射到TI1FP1上  
  •                     //无滤波器、无预分频器(捕获输入口上检测到的每一个边沿都触发一次捕获)  


TIM1_CCMR1寄存器有两种功能,分别对应捕获模式和比较模式,只需要捕获模式即可


滤波器是用来避免频率波动的直接写0即可,无滤波器,分频器我们也写00不用分频器,当然也可以使用分频器,提高准确率。

接着是设置触发方式,我们选择上升沿触发



[cpp] view plain copy


  • TIM1_CCER1 &= (unsigned char)~0x02;//上升沿或者高电平触发  
[url=][/url]



沙发
联通移不动|  楼主 | 2016-11-2 23:38 | 只看该作者
最后使能捕获功能,设置TIM1_CCER1寄存器的CC1E位=1,由于我们采用中断方式因此也将TIM1_IER寄存器的CC1IE位置1,允许中断请求。

完整的初始化代码如下


[cpp] view plain copy


  • void signal_capture_Init(void)  
  • {  
  •     TIM1_CNTRH = 0x00;//清零计数器高8位  
  •     TIM1_CNTRL = 0x00;//清零计数器低8位  
  •     TIM1_PSCRH = 0x00;//计数器时钟分频高8位  
  •     TIM1_PSCRL = 0x10;//计数器时钟分频低8位16分频  
  •     TIM1_CCER1 &= (unsigned char)~0x01;//清零TIM1_CCER1中的CC1E位,之后才可配置TIM1_CCMR1  
  •     TIM1_CCMR1 = 0x01;//配置TIM1_CCMR1中的CC1S位为1,CC1通道配置为输入,IC1映射到TI1FP1上  
  •                     //无滤波器、无预分频器(捕获输入口上检测到的每一个边沿都触发一次捕获)  
  •     TIM1_CCER1 &= (unsigned char)~0x02;//上升沿或者高电平触发  
  •   
  •     TIM1_IER |= 0x02;//CC1IE=1,使能捕获/比较1中断  
  •     TIM1_CCER1 |= 0x01;//捕获使能  
  •     TIM1_CR1 |= 0x01;//使能定时/计数器  
  • }  


当发生一个输入捕获时,计数器的值被传送到TIM1_CCR1寄存器中,计时器的时钟源在程序中我们设置为16分频


分频过后计数器的频率为1MHz,这里采用分频主要是避免计数器溢出,这样同时也降低了精度,同时设置计数器的初值为0,计数器默认计数方式是向上计数,计到最大值后又从0开始计数,

中断处理代码如下


[cpp] view plain copy


  • @far @interrupt void signal_capture_irq (void)  
  • {  
  •       
  •     if(TIM1_SR1&0x02)  
  •     {  
  •         TIM1_SR1 &= (unsigned char)~0x02;//清除CC1IF标志  
  •         if(vsync_cap_data_old == 0x00)  
  •         {//第一次捕获中断来临  
  •             vsync_cap_data_old = TIM1_CCR1H;//先读取高8位数据  
  •             vsync_cap_data_old = (unsigned int)(vsync_cap_data_old<<8) + TIM1_CCR1L;//再读取低8位数据  
  •         }  
  •         else  
  •         {  
  •             //第二次捕获中断来临  
  •             vsync_cap_data_new = TIM1_CCR1H;//先读取高8位数据  
  •             vsync_cap_data_new = (unsigned int)(vsync_cap_data_new<<8) + TIM1_CCR1L;//再读取低8位数据  
  •             TIM1_IER &= (unsigned char)~0x02;//禁止通道1捕获/比较中断  
  •             TIM1_CR1 &= (unsigned char)~0x01;//停止计数器  
  •             if(vsync_cap_data_new > vsync_cap_data_old)  
  •                 vsync_period = (vsync_cap_data_new - vsync_cap_data_old);  
  •             else  
  •                 vsync_period = 0xFFFF + vsync_cap_data_new - vsync_cap_data_old;  
  •             vsync_cap_data_old = 0x00;  
  •             isCaptureOver = 1;  
  •         }  
  •     }  
  • }  


我们捕获两次中断计算时间差,

[cpp] view plain copy


  • if(isCaptureOver)  
  •     {  
  •         //如果捕获完成则对数据进行处理  
  •         cmd_puts("period:");  
  •         cmd_hex((unsigned char)(vsync_period>>8));  
  •         cmd_hex((unsigned char)vsync_period);  
  •         TIM1_CNTRH = 0x00;//清零计数器高8位  
  •         TIM1_CNTRL = 0x00;//清零计数器低8位  
  •         TIM1_IER |= 0x02;//CC1IE=1,使能捕获/比较1中断  
  •         TIM1_CR1 |= 0x01;//使能定时/计数器  
  •         isCaptureOver = 0;  
  •     }  

这里只从串口输出了周期,结果如下


可以看到周期在一个范围内波动我们取一个值0x79ED来计算,它所对应的频率f=1000000/0x79ED=32.0379Hz还是比较接近我们的实际输入频率30Hz,误差是大了些,可以通过代码继续改进


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

67

主题

127

帖子

0

粉丝