END12345678 发表于 2009-3-17 23:42:07

深夜请教马老师捕捉输入

马老师及其他网友大哥:
    您好,我现在刚熟悉AVR单片机,以前一直用8951。调试AVR的捕捉功能时遇到很奇怪的问题。
本来我设置的是200MS改变一次PC口的第0位的电平(初始化是是高),并将此IO口接到ICP口,用ICP功能
捕捉脉冲高电平的宽度,并通过串口输出高电平脉宽的时间。
按道理串口是一直接到200的是16进制数0XC8,但是实际接到的数据如下:
C8 C8 4D 4D 4D 4D 4D 4D 4D 4D 4D C8 C8 C8 C8 C8 C8 C8 C8 4D 4D 4D 4D 4D 4D 4D 4D 4D
C8 C8 C8 C8 C8 C8 C8 C8 4E 4E 4E 4E 4E 4E 4E 4E 4E C8 C8 C8 C8 C8 C8 C8 C8 4D C8
C8 C8 C8 C8 4D 4D 4D 4D 4D 4D 4D 4D 4D C8 C8 C8 C8 C8 C8 C8 C8 4D 4D 4D 4D 4D 4D 4D

问题:为什么接到的数据中除了C8,还有4D,4E呢?

注明:我已经启动了输入捕获的噪音清除位。我实际用示波器查看PC口的第0位的电平变化,是标准的200MS。

测试程序如下:
#include <mega16.h>
#include <stdio.h>                                // 使用CVAVR的标准 Input/Output 函数
#include <delay.h>                                // 使用CVAVR的延时函数
#define ICP1      PIND.6      //脉冲输入由ICP1(Pind.6)输入
sfrw ICR1=0x26;               //补充定义16位寄存器ICR1地址为0X26(mega16.h中未定义)
unsigned char ov_counter;
unsigned int ICP_new,ICP_old;
unsigned long pulse_clocks;
unsigned int IR;


interrupt void timer1_ovf_isr(void)       // T/C1溢出中断
{
      ov_counter++;                              //记录溢出次数
}

interrupt void timer1_capt_isr(void)      // T/C1捕捉中断
{   
      unsigned char i=0;
      if (ICP1)         
      {                                                      //上升沿中断
                ICP_new = ICR1;                //记录上升沿开始时间
                TCCR1B = TCCR1B & 0xBF;                //设置T/C1为下降沿触发捕捉
                ov_counter = 0;                        //清零溢出计数器
      }
      else
      {                                                      //下降沿中断
                ICP_old = ICR1;                //记录下降沿时间
                TCCR1B = TCCR1B | 0x40;                //设置T/C1为上升沿触发捕捉
                pulse_clocks = (unsigned long)ICP_old - (unsigned long)ICP_new
+ (unsigned long)ov_counter * 0x10000 / 500;      //计算脉冲宽度ms
                IR=pulse_clocks;//只记录高脉冲维持的宽度
                if(i==99)i=0;
                putchar(pulse_clocks);
      }
}

void main(void)
{   
    unsigned char j;
    UCSRA=0x00;                // USART initialization
    UCSRB=0x18;                // Communication Parameters: 8 Data, 1 Stop, No Parity
    UCSRC=0x86;                // USART Receiver: On,USART Transmitter: On
    UBRRH=0x00;                // USART Mode: Asynchronous        ,USART Baud Rate: 9600
    UBRRL=0x19;
    //TCCR1B=0x42;                //初始化T/C1,1/8分频,上升沿触发捕捉
    TCCR1B=0xC2;                //初始化T/C1,1/8分频,上升沿触发捕捉,带滤波
    TIMSK=0x24;                //允许T/C1溢出和捕捉中断
///////////////////////////////////////////
    PORTC=0x01;                                // PC口的第0位输出"1",LED不亮
    DDRC=0x01;                                // 定义PC口的第0位为输出方式
////////////////////////////////////////////
#asm("sei")
    while (1)
    {
       PORTC.0 = ~PORTC.0;        // PC口第0位输出取反
       delay_ms(200);
    };
}

machao 发表于 2009-3-17 23:59:32

请下载“《AVR单片机嵌入式系统原理与应用实践》》---前2篇电子初稿(PDF合成版)”,参考第11章中的11.2这节,应该有帮助的。

你的捕捉中断写的不好,请仔细看书里的说明。

END12345678 发表于 2009-3-18 00:22:13

恩,谢谢,已经购买该书,一直在阅读中,非常好的书。这是我照着马老师以前的一个测试实验程序写的,老师原程序如下;
http://www.ouravr.com/bbs/bbs_content.jsp?bbs_sn=405567&bbs_page_no=1&search_mode=3&search_text=machao&bbs_id=9999
我现在自己改写的中断函数:
/*****************************************************************************
      ICP_old = ICR1;
      TCCR1B = TCCR1B ^ 0x40;   //改变触发方向
      pulse_clocks = (unsigned long)ICP_old - (unsigned long)ICP_new
+ (unsigned long)ov_counter * 0x10000 / 500;//计算脉冲宽度ms
      IR=pulse_clocks;//记录高低脉冲维持的宽度
      if(i==79)i=0;
      ICP_new=ICP_old ;
      putchar(pulse_clocks);//串口发送脉冲宽度
******************************************************************************/
这个函数测出来的脉冲宽度也不准确了。

machao 发表于 2009-3-18 03:03:50

谢谢你购买了我的书,但我还是要批评你,没有认真学习和体会里面的精华。

你的代码问题很多,我先说一个明显的你考虑有问题吗?

pulse_clocks = (unsigned long)ICP_old - (unsigned long)ICP_new + (unsigned long)ov_counter * 0x10000 / 500;

同时欢迎其他朋友一起分析找问题。

lljyes 发表于 2009-3-18 03:13:48

这么晚了,休息好,明天再想吧!

END12345678 发表于 2009-3-18 09:24:30

恩,接受批评,老师是说我这里数据定义有问题吗?
pulse_clocks = (unsigned long)ICP_old - (unsigned long)ICP_new + (unsigned long)ov_counter * 0x10000 / 500;
这个公式我是套用马老师以前写的一个函数里面的。原函数的地址在2搂已经贴了。
老师多多指教,感谢

END12345678 发表于 2009-3-18 09:31:27

另外我写这个程序主要是想把高脉冲维持的宽度,低脉冲维持的宽度都记录下来,
所以就有了我下面的写法:
*****************************************************************************
      ICP_old = ICR1;
      TCCR1B = TCCR1B ^ 0x40;   //改变触发方向
      pulse_clocks = (unsigned long)ICP_old - (unsigned long)ICP_new
+ (unsigned long)ov_counter * 0x10000 / 500;//计算脉冲宽度ms
      IR=pulse_clocks;//记录高低脉冲维持的宽度
      if(i==79)i=0;
      ICP_new=ICP_old ;
      putchar(pulse_clocks);//串口发送脉冲宽度   
******************************************************************************/
不过这个函数测出来的脉冲宽度也不准确。

END12345678 发表于 2009-3-18 10:09:02

这段程序是在CVAVR中实现的。在T/C1的捕捉中断中,先检查ICP1的实际状态,以确定是出现了
上升沿还是下降沿信号。如果中断是由上升沿触发的(ICP1为高电平),程序便开始一次脉冲
宽度的测量:记录下上升沿出现的时间,把T/C1的捕捉触发方式改为下降沿触发,并清空溢出
计数器。如果中断由下降沿触发(ICP1为低电平),表示到达脉冲的未端,程序记录下降沿出
现时间,计算出脉冲的宽度,再将T/C1的捕捉触发方式改为上升沿触发,以开始下一次的测量。
脉冲的实际宽度(毫秒格式)是根据T/C1的计数时钟个数来计算的。本例中T/C1的计数时钟是
系统时钟(4MHz)的8分频,即500KHz,相应的计数脉冲宽度为2us。因此计算出从上升沿和下降
沿之间总的计数脉冲个数,除以500个脉冲(为1ms)即得到以毫秒为单位的被测脉冲宽度了。

END12345678 发表于 2009-3-19 09:33:17

请教马老师了

END12345678 发表于 2009-3-19 11:49:14

pulse_clocks = (unsigned long)ICP_old - (unsigned long)ICP_new + (unsigned long)ov_counter * 0x10000 / 500;
掉了个大括号,改正如下:
pulse_clocks = ((unsigned long)ICP_old - (unsigned long)ICP_new + (unsigned long)ov_counter * 0x10000 )/ 500;
但是我觉得还是有问题

END12345678 发表于 2009-3-23 10:09:18

路过的朋友帮看看了谢谢

lhyavr 发表于 2009-3-27 13:04:42

我也是个AVR新手,正好要学这个方面应用,在其他AVR老师的指导下,我觉得你的程序 有几个潜在的问题,第一:输入捕获中断没有很好的中断嵌套。如果同时产生溢出中断和输入捕获中断,这样根据中断的优先级,程序会执行哪个中断呢?第二:你最后的那个计算我没有看明白。(XXXX*0x10000)/500,请问问什么前面要乘以一个16进制数后面就要除以个10进制数呢?

最好像马潮老师那样在主程序中判断前一次获得的数值和后一次是否相等。这样容错能力会更好吧。恩 以上会进一步用实践来检验!~~~

mfkqqw 发表于 2009-4-20 23:43:45

来听马老师讲课了!
页: [1]
查看完整版本: 深夜请教马老师捕捉输入