数码之家

 找回密码
 立即注册
搜索
查看: 183|回复: 5

[C51] 用CCP/PCA的16位捕获模式测脉冲宽度的程序,遇到点困难,请教下各位。

[复制链接]
发表于 6 天前 | 显示全部楼层 |阅读模式
本帖最后由 兔包公 于 2025-6-3 21:04 编辑

书从何起,废话开始,情况大概是这样,P5.4输出SYSclk/4的频率,直连CCP0/p1.1脚测脉冲宽度,宽度数据通过串口1发给手机。
通过本人几翻修改,测试下来,收到的数据反馈似乎对不上,实属不知如何下手。
几翻测试
①,串口部分,基本无问题。
②,p5.4输出,基本无问题,万表能测到6Mhz,刚好等于主时钟24M/4。




③,数据转10进制字符串,基本无问题。



④,从分析来看,6M这个频率下,按理PCA的16位计数器,应该不会溢出,但收到的数据大的离谱。




⑤,尝试在pca中断函数里,用一个变量去记录CCF0中断次数,再在主函数判断,只发送前10几个数据,还是不对,串口发送效果也不理想。



⑥,从手册来看,这频率应该是在能测量的范围内的,几翻无果后,怀疑频率太快了,于是又使用定时器0,P0.0产生500hz的,直连CCP0/p1.1去测,还是不对,心烦 。


一个奇怪的现象,我刚开始用万表去测p00脚的频率,一直没有数据,但当我用黑表笔去碰一下3.3v供电脚再拿开,500hz就出来了,甚至完全断开2表笔,只用红笔去测,竟然也显示500hz,但是如果你黑笔接地,红笔去测,又测不到频率,奇了怪了?什么问题,双向IO口驱动电流不够??万表问题??操作问题??鬼现象???




⑦,尝试别的写法,用u16 CCAP0_tmp去接收捕获寄存器的值,再处理串口发送。

             CCAP0_tmp = CCAP0H;       
                        CCAP0_tmp = (CCAP0_tmp << 8) + CCAP0L;
还是不对。


⑧,按我的理解,主时钟24M,周期1/24约等于41.667ns,每来个时钟,PCA的16位计数器+1,也就是每41.667ns计数器+1,6M的频率,周期1/6约等于166.667ns,那么计数器的值的,敞开误差不讲,应该约为4左右,那么捕获寄存器2次的值相减应该为4上下,再对。这么理解是否正确???



请教下大婶,错在哪里?该如何?
就这样,谢谢观看。
代码如下,基于sdcc语法编译。
…………………………………我是分割线…………………………
  1. #include "stc15.h"  
  2. #include <stdlib.h> //使用其__uitoa(unsigned int, char *, unsigned char)函数,u16转字符串
  3.                     //使用其__ultoa(unsigned long, char *, unsigned char)函数,u32转字符串         
  4.                  
  5. /*宏定义*/                           
  6. #define FOSC 24000000  //系统频率

  7. #define BAUD 115200    //串口波特率
  8. #define NONE_PARITY 0   //无校验
  9. #define ODD_PARITY 1   //奇校验
  10. #define EVEN_PARITY 2   //偶校验
  11. #define MARK_PARITY 3   //标记校验
  12. #define SPACE_PARITY 4   //空白校验
  13. #define PARITYBIT EVEN_PARITY   //取校验位为偶校验
  14. #define UART1_BUF_LENGTH 32 //串口缓冲数据长度

  15. #define CCP_S0 0x10   //P_SW1.4 //P_SW1第四位
  16. #define CCP_S1 0x20   //P_SW1.5 //P_SW1第五位

  17. //定时器0做16位自动重装, 中断频率为1000HZ,中断函数从P00取反输出500HZ方波信号.                                                                    
  18. #define        Timer0_Reload        (FOSC / 1000)                //Timer 0 中断频率, 1000次/秒

  19. /*变量声明*/
  20. __bit busy;  //串口忙碌标记
  21. u8 __idata RX1_Buffer[UART1_BUF_LENGTH];        //接收缓冲
  22. u8        RX1_Cnt;        //接收计数

  23. u8 cnt;   //存储PCA计时溢出次数
  24. u32 count0;   //记录上一次的捕获值
  25. u32 count1;   //记录本次的捕获值
  26. u32 length;   //存储信号的时间长度(count1 - count0)
  27. char length_str[32]=""; //信号时间长度转为字符串,用于串口发送


  28. /*函数声明*/
  29. void delay_ms(u16 ms);
  30. void Uart1_config(void);
  31. void SendData(u8 dat);
  32. void SendString(char *s);
  33. void PCA_config(void);
  34. void Timer0_config(void);

  35. /*主函数*/
  36. void main(void)
  37. {   
  38.     P0M0 =0;  //P0准双向
  39.     P0M1 =0;
  40.     P1M0 =0;
  41.     P1M1 =0;  //P1准双向
  42.     P5M0 =0;   
  43.     P5M1 =0;  //P5准双向
  44.     Uart1_config();
  45.     PCA_config();
  46.     Timer0_config();
  47.     EA = ENABLE;   //使能总中断
  48.    //CLK_DIV = 0x40;  //0100,0000 P5.4输出频率SYSclk
  49.    //CLK_DIV = 0x80;  //1000,0000 P5.4输出频率SYSclk/2
  50.     CLK_DIV = 0xC0;  //1100,0000 P5.4输出频率SYsclk/4
  51.     P00 = 0;         //引脚P00,先为0,低电平
  52.     TR0 =ENABLE ;  //定时器0开始运行
  53.     delay_ms(500);  //延时,等待P5.4输出频率稳定,
  54.     CR = ENABLE; //使能PCA开始工作  
  55.     SendString("STC15W4K56S4 PCA Uart Test !\r\n");
  56.     while(1){              
  57.            __ultoa(length, length_str, 10); //转为10进制字符串
  58.             SendString("length = ");
  59.             SendString(length_str);
  60.             SendString("\n");      
  61.             }
  62. }

  63. /*软件延时函数*/
  64. void  delay_ms(u16 ms)
  65. {
  66.      u16 i;
  67.          do{
  68.               i = FOSC / 13000;
  69.                   while(--i)        ;   
  70.      }while(--ms);
  71. }

  72. /*串口1配置函数*/
  73. void Uart1_config(void)
  74. {
  75.     #if (PARITYBIT == NONE_PARITY)  
  76.         SCON = 0x50;    //8位可变波特率
  77.     #elif (PARITYBIT == ODD_PARITY) || (PARITYBIT == EVEN_PARITY) || (PARITYBIT == MARK_PARITY)  
  78.         SCON = 0xda;    //9位可变波特率,校验位初始为1
  79.     #elif (PARITYBIT == SPACE_PARITY)  
  80.         SCON = 0xd2;    //9位可变波特率,校验位初始为0
  81.     #endif
  82.     T2L = (65536 - (FOSC/4/BAUD)) & 0x00FF; //设置波特率重装值,低8位
  83.     T2H = (65536 - (FOSC/4/BAUD))>>8;       //高8位
  84.     AUXR = 0x14;   //启动定时器2,1T模式
  85.     AUXR |= 0x01;   //选择定时器2为串口1的波特率发生器  
  86.     ES = ENABLE;   //使能串口1中断  
  87. }

  88. /*串口1中断函数*/
  89. void Uart1_int(void) __interrupt UART1_VECTOR
  90. {  
  91.     if (RI)  {  
  92.          RI = 0;  //清除RI位
  93.          RX1_Buffer[RX1_Cnt] = SBUF;
  94.          if(++RX1_Cnt >= UART1_BUF_LENGTH)        RX1_Cnt = 0;        //防溢出
  95.          }
  96.     if (TI)  {   
  97.         TI = 0;  //清除TI位   
  98.         busy = 0;  //清忙标志
  99.         }
  100. }
  101. /*发送一个字节*/
  102. void SendData(u8 dat)
  103. {  
  104.     while (busy);   
  105.     ACC = dat;  //获取校验位P (PSW.0)
  106.     if (P)        //根据P来设置校验位
  107.         {  
  108.         #if (PARITYBIT == ODD_PARITY)   
  109.         TB8 = 0;    //设置校验位为0
  110.         #elif (PARITYBIT == EVEN_PARITY)   
  111.         TB8 = 1;      //设置校验位为1
  112.         #endif  
  113.         }  
  114.     else  {  
  115.         #if (PARITYBIT == ODD_PARITY)   
  116.         TB8 = 1;    //设置校验位为1
  117.         #elif (PARITYBIT == EVEN_PARITY)   
  118.         TB8 = 0;   //设置校验位为0
  119.         #endif  
  120.          }  
  121.     busy = 1;
  122.     SBUF = ACC;    //写数据到UART数据寄存器
  123. }

  124. /*发送字符串*/
  125. void SendString(char *s)
  126. {  
  127.     while (*s)      //检测字符串结束标志  
  128.         {   
  129.         SendData(*s++);   //发送当前字符
  130.         }
  131. }

  132. /*PCA配置函数*/
  133. void PCA_config(void)
  134. {
  135.     //选择引脚
  136.     ACC = P_SW1;
  137.     ACC &= !(CCP_S0 | CCP_S1); //CCP_S0=0 CCP_S1=0  
  138.     P_SW1 = ACC; //(P1.2/ECI, P1.1/CCP0, P1.0/CCP1, P3.7/CCP2)
  139.    
  140. // ACC = P_SW1;
  141. // ACC &= !(CCP_S0 | CCP_S1); //CCP_S0=1 CCP_S1=0
  142. // ACC |= CCP_S0; //(P3.4/ECI_2, P3.5/CCP0_2, P3.6/CCP1_2, P3.7/CCP2_2)
  143. // P_SW1 = ACC;

  144. // ACC = P_SW1;
  145. // ACC &= !(CCP_S0 | CCP_S1); //CCP_S0=0 CCP_S1=1
  146. // ACC |= CCP_S1; //(P2.4/ECI_3, P2.5/CCP0_3, P2.6/CCP1_3, P2.7/CCP2_3)
  147. // P_SW1 = ACC;


  148.     CCON = 0;  //初始化PCA控制寄存器     
  149.                  //PCA定时器停止   
  150.                  //清除CF标志   
  151.                  //清除模块中断标志  
  152.     CL = 0; //PCA的16位计数器— 低8位CL和高8位CH
  153.     CH = 0;
  154.     CCAP0L = 0; //PCA捕捉/比较寄存器 — CCAPnL(低位字节)和CCAPnH(高位字节)
  155.     CCAP0H = 0;
  156.     CMOD = 0x09; //设置PCA时钟源为系统时钟,且使能PCA计时溢出中断  
  157.     CCAPM0= 0x21; //PCA模块0为16位捕获模式(上升沿捕获,      
  158.                        //可测从高电平开始的整个周期),且产生捕获中断
  159.   // CCAPM0= 0x11; //PCA模块0为16位捕获模式(下降沿捕获,        
  160.                        //可测从低电平开始的整个周期),且产生捕获中断
  161.   //CCAPM0= 0x31; //PCA模块0为16位捕获模式(上升沿/下降沿捕获,     
  162.                        //可测高电平或者低电平宽度),且产生捕获中断
  163.     cnt = 0;
  164.     count0 = 0;
  165.     count1 = 0;
  166.     length = 0;
  167. }

  168. /*PCA中断处理函数*/
  169. void PCA_int(void) __interrupt PCA_VECTOR
  170. {   
  171.     if (CF)  
  172.          {   
  173.             CF = 0;
  174.             cnt++;
  175.          }  
  176.     if (CCF0)
  177.          {  
  178.             CCF0 = 0;
  179.             count0 = count1;   //备份上一次的捕获值   
  180.            ((u8 *)&count1)[3] = CCAP0L;  //保存本次的捕获值   
  181.            ((u8 *)&count1)[2] = CCAP0H;
  182.            ((u8 *)&count1)[1] = cnt;
  183.            ((u8 *)&count1)[0] = 0;
  184.            length = count1 - count0;   //计算两次捕获的差值,即得到时间长度        
  185.          }
  186. }

  187. /*定期器0配置函数*/
  188. void Timer0_config(void)
  189. {
  190.         TR0 =DISABLE;        //停止计数
  191.         #if (Timer0_Reload < 64)        // 如果用户设置值不合适, 则不启动定时器
  192.                 #error "Timer0设置的中断过快!"
  193.         #elif ((Timer0_Reload/12) < 65536)        // 如果用户设置值不合适, 则不启动定时器
  194.                 ET0 = 1;        //允许中断
  195.         //        PT0 = 1;        //高优先级中断
  196.                 TMOD &= !0x03;
  197.                 TMOD |= 0;        //工作模式, 0: 16位自动重装, 1: 16位定时/计数, 2: 8位自动重装, 3: 16位自动重装, 不可屏蔽中断
  198.         //        TMOD |=  0x04;        //对外计数或分频
  199.                 TMOD &= !0x04;        //定时
  200.         //        INT_CLKO |=  0x01;        //输出时钟
  201.                 INT_CLKO &= !0x01;        //不输出时钟
  202.                 #if (Timer0_Reload < 65536)
  203.                         AUXR |=  0x80;        //1T mode
  204.                         TH0 = (u8)((65536 - Timer0_Reload) / 256);
  205.                         TL0 = (u8)((65536 - Timer0_Reload) % 256);
  206.                 #else
  207.                         AUXR &= !0x80;        //12T mode
  208.                         TH0 = (u8)((65536 - Timer0_Reload/12) / 256);
  209.                         TL0 = (u8)((65536 - Timer0_Reload/12) % 256);
  210.                 #endif
  211.                
  212.         #else
  213.                 #error "Timer0设置的中断过慢!"
  214.         #endif
  215. }

  216. /*定期器0中断处理函数*/
  217. void Timer0_int (void) __interrupt TIMER0_VECTOR
  218. {
  219.    P00 = !P00;
  220. }

复制代码


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

打赏

参与人数 1家元 +30 收起 理由
jf201006 + 30 原創內容

查看全部打赏

发表于 6 天前 | 显示全部楼层
用手机玩单片机的都是大神
回复 支持 反对

使用道具 举报

发表于 6 天前 | 显示全部楼层
猪小呆 发表于 2025-6-3 21:10
用手机玩单片机的都是大神

其实我觉得意义不大,要干活还得是电脑,想便携就用笔记本...


二○二五年六月三日
回复 支持 反对

使用道具 举报

发表于 6 天前 | 显示全部楼层
猪小呆 发表于 2025-6-3 21:10
用手机玩单片机的都是大神

我也在想如何用手机玩单片机,可以不知道如何弄。楼主是高手!
回复 支持 反对

使用道具 举报

发表于 6 天前 | 显示全部楼层
lmn2005 发表于 2025-6-3 21:13
我也在想如何用手机玩单片机,可以不知道如何弄。楼主是高手!

楼主之前发过帖子,是自己写的手机APP,牛逼plus
回复 支持 反对

使用道具 举报

发表于 5 天前 | 显示全部楼层
不懂,跟看天书一样。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2025-6-9 04:18 , Processed in 0.265200 second(s), 8 queries , Redis On.

Powered by Discuz!

© 2006-2025 MyDigit.Net

快速回复 返回顶部 返回列表