PIC16F877A CCP输入捕捉问题

2019-03-25 19:47发布

大伙,帮我看看程序错在哪里?
CCP输入捕捉的

void interrupt CCP1INT (void)
{
  if(CCP1IF==1)
    {
      CCP1IF=0;  
      if(LastCaptureData==0)  // 捕捉到第一个上升沿
        {
          LastCaptureData=CCPR1H;
          LastCaptureData=(LastCaptureData<<8)+CCPR1L;
        }
      else // 捕捉到第二个上升沿
        {
          NewCaptureData=CCPR1H;
          NewCaptureData=(NewCaptureData<<8)+CCPR1L;
          G_Period=(NewCaptureData+LastCaptureData)/2;
          Rec_Temp1=1000000/G_Period;
          Rec_Temp =Rec_Temp1;
          LastCaptureData=0;         
        }
    }
}

该程序的“G_Period=(NewCaptureData+LastCaptureData)/2;”改为“G_Period=NewCaptureData-LastCaptureData;”就正常,为什么?不是两次上升沿捕捉的是两个周期的吗? 此帖出自小平头技术问答
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
12条回答
haian_bch
1楼-- · 2019-03-26 22:30
引用 6 楼 cfanlwn 的回复:
CCPR1H和CCPR1L没有清零啊,保留原来的值嘛
第二次上升沿CCPR1H和CCPR1L被读取给NewCaptureData后,NewCaptureData累计的是两次的值


看了书才知道其实是TMR1H和TMR1L没有清零,就算CCPR1H和CCPR1L清零也还是一样,NewCaptureData累计的是两次的值,NewCaptureData-LastCaptureData才是周期。这是测周法,测频法呢?是不是设置定时器1为同步计数方式,然后读取这段时间的脉冲数量?怎么知道时间的长短呢?不解。。。
socvince
2楼-- · 2019-03-27 03:19
首先给你看下源代码:

第10章  利用CCP模块设计频率计

10.5  程序设计
10.5.4  程序清单
  1. #include               
  2. #include               
  3. #include               
  4. //本程序利用CCP1模块实现一个“简易数字频率计”的功能
  5. const  char        table[11]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0XD8,0x80,0x90,0xFF};
  6. //不带小数点的显示段码表
  7. const  char        table0[11]={0X40,0X79,0X24,0X30,0X19,0X12,0X02,0X78,0X00,0X10,0xFF};
  8. //带小数点的显示段码表
  9. bank3        int        cp1z[11];                        //定义一个数组,用于存放各次的捕捉值
  10. union        cp1
  11. {int                y1;
  12.         unsigned  char        cp1e[2];
  13. }cp1u;                                                        //定义一个共用体
  14. unsigned        char        COUNTW,COUNT;        //测量脉冲个数寄存器
  15. unsigned        char        COUNTER,data,k;
  16. unsigned        char        FLAG @ 0XEF;
  17. #define FLAGIT(adr,bit)  ((unsigned)(&adr)*8+(bit))        //绝对寻址位操作指令
  18. static        bit FLAG1  @ FLAGIT(FLAG,0);
  19. static        bit FLAG2  @ FLAGIT(FLAG,1);
  20. static        bit FLAG3  @ FLAGIT(FLAG,2);
  21. unsigned        char        s[4];                                //定义一个显示缓冲数组
  22. int                T5 ,uo;
  23. double        RE5;
  24. double        puad5;
  25. //spi方式显示初始化子程序
  26. void SPIINIT()
  27. {
  28.         PIR1=0;
  29.         SSPCON=0x30;       
  30.         SSPSTAT=0xC0;
  31. //设置SPI的控制方式,允许SSP方式,并且时钟下降沿发送,与"74HC595,当其
  32. //SCLk从低到高跳变时,串行输入寄存器"的特点相对应
  33.         TRISC=0xD7;                                //SDO引脚为输出,SCK引脚为输出
  34.         TRISA5=0;                                        //RA5引脚设置为输出,以输出显示锁存信号
  35.         FLAG1=0        ;
  36.         FLAG2=0        ;
  37.         FLAG3=0        ;
  38.         COUNTER=0X01;
  39. }
  40. //CCP模块工作于捕捉方式初始化子程序
  41. void        ccpint( )
  42. {
  43.         CCP1CON=0X05;                                //首先设置CCP1捕捉每个脉冲的上升沿
  44.         T1CON=0X00;                                //关闭TMR1震荡器
  45.         PEIE=1;                                                //外围中断允许(此时总中断关闭)
  46.         CCP1IE=1;                                        //允许CCP1中断
  47.         TRISC2=1;                                        //设置RC2为输入
  48. }
  49. //系统其它部分初始化子程序
  50. void        initial( )
  51. {
  52.         COUNT=0X0B;                                //为保证测试精度,测试5个脉冲的参数后
  53.         //求平均值,每个脉冲都要捕捉其上升、下降沿,
  54.         //故需要有11次中断
  55.         TRISB1=0;
  56.         TRISB2=0;
  57.         TRISB4=1;
  58.         TRISB5=1;                                        //设置与键盘有关的各口的输入、输出方式
  59.         RB1=0;
  60.         RB2=0;                                                //建立键盘扫描的初始条件       
  61. }
  62. //SPI传送数据子程序
  63. void                  SPILED(data)
  64. {
  65.         SSPBUF=data;                                //启动发送
  66.         do        {
  67.                   ;
  68.         }while(SSPIF==0);
  69.         SSPIF=0;
  70. }
  71. //显示子程序,显示4位数
  72. void        display( )
  73. {
  74.         RA5=0;                                                //准备锁存
  75.         for(COUNTW=0;COUNTW<4;COUNTW++){
  76.                 data=s[COUNTW];
  77.                 data=data&0x0F;
  78.                 if(COUNTW==k)        data=table0[data];//第二位需要显示小数点
  79.                 else        data=table[data];
  80.                 SPILED(data);                        //发送显示段码
  81.         }
  82.         for(COUNTW=0;COUNTW<4;COUNTW++){
  83.                 data=0xFF;
  84.                 SPILED(data);                        //连续发送4个DARK,使显示好看一些
  85.         }
  86.         RA5=1;                                                //最后给一个锁存信号,代表显示任务完成
  87. }
  88. //键盘扫描子程序
  89. void        keyscan( )
  90. {
  91.         if((RB4==0)||(RB5==0))        FLAG1=1        ;//若有键按下,则建立标志FLAG1
  92.         else        FLAG1=0        ;                                //若无键按下,则清除标志FLAG1
  93. }
  94. //键服务子程序
  95. void                keyserve( )
  96. {
  97.         PORTB=0XFD        ;
  98.         if(RB5==0)        data=0X01;
  99.         if(RB4==0)        data=0X03;
  100.         PORTB=0XFB;
  101.         if(RB5==0)        data=0X02;
  102.         if(RB4==0)        data=0X04;                //以上确定是哪个键按下
  103.         PORTB=0X00;                                //恢复PORTB的值
  104.         if(data==0x01)        {
  105.                 COUNTER=COUNTER+1;        //若按下S9键,则COUNTER加1
  106.                 if(COUNTER>4)        COUNTER=0x01;//若COUNTER超过4,则又从1计起
  107.         }
  108.         if(data==0x02)        {
  109.                 COUNTER=COUNTER-1;        //若按下S11键,则COUNTER减1
  110.         if(COUNTER<1)        COUNTER=0x04;//若COUNTER小于1,则又循环从4计起
  111.         }
  112.         if(data==0x03)        FLAG2=1        ;                //若按下S10键,则建立标志FLAG2
  113.         if(data==0x04)        FLAG2=0        ;                //若按下S12键,则清除标志FLAG2
  114. }
  115. //中断服务程序
  116. void         interrupt        cp1int(void)
  117. {
  118.         CCP1IF=0;                                        //清除中断标志
  119.         cp1u.cp1e[0]=CCPR1L;
  120.         cp1u.cp1e[1]=CCPR1H;
  121.         cp1z[data]=cp1u.y1;                        //存储1次捕捉值
  122.         CCP1CON=CCP1CON^0X01;        //把CCP1模块改变成捕捉相反的脉冲沿
  123.         data++;
  124.         COUNT--;
  125. }
  126. //周期处理子程序
  127. void          PERIOD( )
  128. {
  129.         T5=cp1z[10]-cp1z[0];                        //求得5个周期的值
  130.         RE5=(double)T5;                                //强制转换成双精度数
  131.         RE5=RE5/5;                                        //求得平均周期,单位为μs
  132. }
  133. //频率处理子程序
  134. void          FREQUENCY( )
  135. {
  136.         PERIOD( );                                        //先求周期
  137.         RE5=1000000/RE5;                        //周期值求倒数,再乘以1 000 000,得频率,
  138.         //单位为HZ
  139. }
  140. //脉宽处理子程序
  141. void                PULSE( )
  142. {
  143.         int        pu;
  144.         for(data=0,puad5=0;data<=9;data++)        {
  145.                 pu=cp1z[data+1]-cp1z[data];
  146.                 puad5=(double)pu+puad5;
  147.                 data=data+2;
  148.         }                                                        //求得5个脉宽的和值
  149.         RE5=puad5/5;                                //求得平均脉宽
  150. }
  151. //占空比处理子程序
  152. void                OCCUPATIONAL( )
  153. {
  154.         PULSE( );                                        //先求脉宽
  155.         puad5=RE5;                                        //暂存脉宽值
  156.         PERIOD();                                        //再求周期
  157.         RE5=puad5/RE5;                                //求得占空比
  158. }                       
  159. //主程序
  160. main( )
  161. {
  162.         SPIINIT( );                                        //SPI方式显示初始化
  163.         while(1) {
  164.                 ccpint();                                        //CCP模块工作于捕捉方式初始化
  165.                 initial();                                        //系统其它部分初始化
  166.                 if(FLAG2==0)        {
  167.                         s[0]=COUNTER;                //第一个存储COUNTER的值
  168.                         s[1]=0X0A;
  169.                         s[2]=0X0A;
  170.                         s[3]=0X0A;                        //后面的LED将显示"DARK"
  171.                 }
  172.                 display( );                                //调用显示子程序
  173.                 keyscan();                                //键盘扫描               
  174.                 data=0x00;                                //存储数组指针赋初值
  175.                 TMR1H=0;
  176.                 TMR1L=0;                                //定时器1清0
  177.                 CCP1IF=0;                                //清除CCP1的中断标志,以免中断一打开就进入
  178.         //中断
  179.                 ei( );                                        //中断允许
  180.                 TMR1ON=1;                                //定时器1开
  181.                 while(1){
  182.                         if(COUNT==0)break;
  183.                 }                                                //等待中断次数结束
  184.                 di();                                        //禁止中断
  185.                 TMR1ON=0;                                //关闭定时器
  186.                 keyscan();                                //键盘扫描       
  187.                 if(FLAG1==1)        keyserve()        ;        //若确实有键按下,则调用键服务程序                       
  188.                 if(FLAG2==0)        continue;        //如果没有按下确定键,则终止此次循环,
  189.         //继续进行测量
  190.         //如果按下了确定键,则进行下面的数值转换和显示工作
  191.         if(COUNTER==0x01) FREQUENCY();        //COUNTER=1,则需要进行频率处理
  192.         if(COUNTER==0x02) PERIOD();                        //COUNTER=2,则需要进行周期处理
  193.         if(COUNTER==0x03) OCCUPATIONAL();//COUNTER=3,则需要进行占空比处理
  194.         if(COUNTER==0x04) PULSE();                        //COUNTER=4,则需要进行脉宽处理
  195.                 k=5;
  196.                 if(RE5<1){
  197.                 RE5=RE5*1000;                        //若RE5<1,则乘以1 000,保证小数点的精度       
  198.                 k=0x00;
  199.         }
  200.         else        if(RE5<10){
  201.                 RE5=RE5*1000;                        //若RE5<10,则乘以1 000,保证小数点的精度       
  202.                 k=0x00;
  203.         }
  204.         else        if(RE5<100){
  205.                 RE5=RE5*100;                        //若RE5<100,则乘以100,保证小数点的精度       
  206.                 k=0x01;
  207.         }
  208.         else        if(RE5<1000){
  209.                 RE5=RE5*10;                        //若RE5<1000,则乘以10,保证小数点的精度       
  210.                 k=0x02;
  211.         }
  212.         else        RE5=RE5        ;
  213.                 uo=(int)RE5;
  214.                 sprintf(s,"%4d",uo);                //把需要显示的数据转换成4位ASII码,且放入数
  215.         //组S中
  216.                 display();
  217.         }
  218. }
复制代码
17584681
3楼-- · 2019-03-27 07:31
楼主可以好好看看上述代码的注释。

另外的说的周期与频率的问题,一般的PIC单片机书籍中都详细的说明。
楼主可以看看这个网页:
http://www.mcustudio.com/Article/2008/223.html

详细介绍了PIC单片机利用CCP实现周期、频率、脉宽、占空比等参数的测量原理。
ZONGTONG
4楼-- · 2019-03-27 10:38
另外楼主需特别注意一点:2次中断的时间间隔必须大于1次中断服务的执行时间; 否则如果在中断服务程序执行时又发生CCP中断,就不能正常工作。
chenye171
5楼-- · 2019-03-27 14:50
 精彩回答 2  元偷偷看……
zzxdzhw
6楼-- · 2019-03-27 17:37
学习了

一周热门 更多>