这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 国产MCU » 【STCAi8051U】使用PCA以下降沿方式捕捉外部脉冲

共1条 1/1 1 跳转至

【STCAi8051U】使用PCA以下降沿方式捕捉外部脉冲

专家
2026-03-08 15:02:35     打赏

折腾了几天,因为头文件的问题,加上各种调试,终于调通了使用PCA捕捉外部脉冲宽度的处理。以下是程序:

// 捕获外部脉冲宽度
// 工作主频 20MHz, PCA的计数周期=1/20微妙
// 测试时,需要短接P20 - P13,使定时器2输出的方波信号输出给PCA,作为PCA外部信号的输入信号
 
///*
// PCA 模块工作于捕获模式时,对模块的外部 CCP0/CCP1/CCP2 管脚的输入跳变进行采样。
// 当采样到有效跳变时,PCA 控制器立即将 PCA 计数器 CH 和 CL 中的计数值装载到模块的捕获寄存器中 CCAPnL 和 CCAPnH,
// 同时将 CCON 寄存器中相应的 CCFn 置 1。若 CCAPMn 中的 ECCFn 位被设置为 1,将产生中断。
// 由于所有 PCA 模块的中断入口地址是共享的,所以在中断服务程序中需要判断是哪一个模块产生了中断,并注意中断标志位需要软件清零。
// */
#include "../comm/Ai8051U.h" 
#include "intrins.h"
#include "stdio.h"
 
void Timer0_init(void);
 
#define FOSC 20000000UL  //定义主时钟
#define BRT (65536 - (FOSC / 115200) / 4)   //加 2 操作是为了让 Keil 编译器,自动实现四舍五入运算
#define Timer0_Reload   (FOSC / 2000)      //Timer 0 中断频率, 2000次/秒,周期0.5微妙,IO翻转后形成1000Hz脉冲波
 
#define  CF        0x80
#define  CR        0x40
#define  CCF0      0x01
 
unsigned char  cnt;     // 存储 PCA 计时溢出次数
unsigned long  count0;  // 记录上一次的捕获值
unsigned long  count1;  // 记录本次的捕获值
unsigned long  len1, len0;  // 存储信号的时间长度
 
unsigned long rec[100];   // 存储100组记录
unsigned int index=0;
 
unsigned long result = 0;
 
// 测试用输出引脚
// P2.0链接P1.3,P2.0输出的脉冲信号,由可编程计数器阵列PCA0由P1.3引脚捕获,作为对比,由P2.1输出
sbit TEST_OUT = P2^0;
sbit PCA0_OUT = P2^1;
 
 
bit busy1;
char wptr1;
char rptr1;
char buffer1[64];
char flag = 0;
 
// timer0初始化函数.
void Timer0_init(void) {
    TR0 = 0;    //停止计数
 
    ET0 = 1;    //允许中断
    TMOD &= ~0x03;    // 16位自动重装模式
 
    TMOD &= ~T0_CT;  // 定时器模式
    INTCLKO &= ~T0CLKO; //不输出时钟
 
    AUXR |= T0x12;  //1T mode
    TH0 = (unsigned char)((65536UL - Timer0_Reload) / 256);
    TL0 = (unsigned char)((65536UL - Timer0_Reload) % 256);
 
    TR0 = 1;    //开始运行
 
}
 
//========================================================================
// 函数: void timer0_int (void) interrupt TIMER0_VECTOR
// 描述:  timer0中断函数.
//========================================================================
void timer0_int (void) interrupt 1 {
    // P20电平翻转
    P20 = ~P20;
}
 
// 测试使用Timer2作为串口的波特率发生器
void Timer2Init(void) {
    T2L = BRT;
    T2H = BRT >> 8;
    //S1BRT = 1;  // S1BRT:串口1的波特率发生器选择位: 0-选择定时器1作为波特率发生器;1-选择定时器2作为波特率发生器(默认) 
    //T2x12 = 1;
    //T2R = 1;
    AUXR |= S1BRT | T2x12 | T2R;  
}
 
// 串口P3.6(RXD),P3.7(TXD)
////---------------------------------------------------------------
//void Uart1Isr() interrupt 4 {
//    if (TI) {
//        TI = 0;
//        busy1 = 0;
//    }
//    if (RI) {
//        RI = 0;
//        buffer1[wptr1++] = SBUF;
//        wptr1 &= 0x0f;
//    }
//}
 
void Uart1Init() {
    SCON = 0x50;  //
    wptr1 = 0x00;
    rptr1 = 0x00;
    busy1 = 0;
    
    P_SW1 |= 0x40;   // 选择P3.6,P3.7,因为擎天柱开发板上不提供P3.0,P3.1引脚
}
 
void Uart1Send(char dat) {
    //while (busy1);
  
    // 送出要发送的数据
    SBUF = dat;
  
    // 等待串口发送完成
    while (TI==0);
    TI=0;        //   清除标志
    
  //busy1 = 1;
//    // 送出要发送的数据
//    SBUF = dat;
}
 
void Uart1SendStr(char *p) {
    while (*p) {
        Uart1Send(*p++);
    }
}
 
// 可编程计数器模块中断
void PCA_Isr() interrupt 7 {
    char buf[64]={'\0'};
  
    // 判断是否发生了溢出(无论是否检测到脉冲沿时,发生了溢出中断)
    if (CCON & CF) {
        CCON &= ~CF;      // 清除中断标志
        cnt++;            // PCA 计时溢出次数 +1
    }
    
    // 是否发生捕获中断,指检测到脉冲沿了
    if (CCON & CCF0) {
        // 输出测试引脚电平翻转
        PCA0_OUT = ~PCA0_OUT;
        
        // 软件清除中断标志 
        CCON &= ~CCF0;
      
        // 保存当前计数
        len0=len1;
        len1 = ((cnt*256 + CCAP0H) * 256 + CCAP0L);
      
        if (flag == 0) {
            if (index<100) {
                //Uart1Send('>');
                rec[index++] = len1-len0;
            } else {
                //Uart1Send('/');
                flag = 1;
            }
          
        }
      
        //sprintf(buf, "(%d,%d,%d) %ld - %ld = %ld \r\n", cnt, CCAP0H, CCAP0L, len1, len0, len1-len0);
        //Uart1SendStr(buf);
    
    }
}
 
void main(void) {
    int i = 0;
    char buf[64]={'\0'};
  
    //EAXFR = 1;  // 允许访问扩展的特殊寄存器, XFR
    P_SW2 |= EAXFR;
    
    //(32 位模式请使用这句,注释下一句 )
    //  P_SW2 |= 0x80;  // 允许访问扩展的特殊寄存器, XFR
    //(8 位模式请使用这句,注释上一句 )
    WTST = 0; // 设置取程序代码等待时间,
    // 赋值为 0 表示不等待,程序以最快速度运行
    CKCON = 0; // 设置访问片内的 xdata 速度,
    // 赋值为 0 表示用最快速度访问,不增加额外的等待时间
    P0M0 = 0x00;    P0M1 = 0x00;    P1M0 = 0x00;    P1M1 = 0x00;
    P2M0 = 0x00;    P2M1 = 0x00;    P3M0 = 0x00;    P3M1 = 0x00;
    P4M0 = 0x00;    P4M1 = 0x00;    P5M0 = 0x00;    P5M1 = 0x00;
 
    // 初始化定时器0,定时周期1000Hz
    Timer0_init();
 
    // 初始化定时器2,给串口1做波特率发生器用
    Timer2Init();
    
    // 初始化串口1
    Uart1Init();
    
    // 用户变量初始化
    cnt = 0;  
    count0 = 0;
    count1 = 0;
    len1 = 0; len0=0;
 
    Uart1SendStr("Start main ......\r\n");
    
    CCON = 0x00;
    // CMOD设置:
    // B7 = 0              :空闲模式下 PCA 继续计数
    // B6 - B5(CCP_S) = 00 :ECI-P1.2 、 CCP0 - P1.3、 CCP1 - P1.4、CCP2 - P1.1
    // B4 - B1(CPS) = 0100 :PCA 时钟为系统时钟
    // B0= 1                :使能 PCA 计数器溢出中断
    CMOD = 0x09;  // PCA 时钟为系统时钟 , 使能 PCA 计数器溢出中断
    
    // 计数器清零
    CL = 0x00;
    CH = 0x00;
    
    // B6(ECOM0)  = 0 :禁止PCA0的比较功能
    // B5(CCAPP0) = 0 :禁止PCA0上升沿捕捉
    // B4(CCAPN0) = 1 :允许PCA0下降沿捕捉
    // B3(MAT0)   = 0 :禁止PCA0匹配
    // B2(TOG0)   = 0 :禁止PCA0高速脉冲输出
    // B1(PWM0)   = 0 :禁止PCA0输出PWM
    // B0(EXXF0)  = 1 :允许PCA0匹配/捕获中断
    CCAPM0 = 0x11;  // 16 位下降沿捕获
    //CCAPM0 = 0x21;  // 16 位上升沿捕获)
    //CCAPM0 = 0x31;  // 16 位边沿捕获捕获模式(上升沿、下降沿均捕获)
    
    CCAP0L = 0x00;
    CCAP0H = 0x00;
    CCON |= CR;  // 启动 PCA 计时器
    
//    // 允许串口中断
//    ES = 1;
    
    EA = 1;
    while (1) {
        if (flag == 1) {
            //Uart1Send('=');
            for (i=0; i<100; i++) {
                sprintf(buf, "\r\n[%d] = %ld", i, rec[i]);
                Uart1SendStr(buf);
            }
            index = 0;
            flag = 0;
        }
 
    }
}


最初还想着使用串口中断方式输出调试信息,结果发现中断之间的影响导致测试信号异常。于是不用串口中断,使用UartSend函数输出串口信息,然后发现在中断处理函数PCA_Isr()中使用UartSend函数会使定时器0输出的方波信号失真,正常占空比为50%的方波变成下面的样子(黄色的信号是定时器0产生的测量用信号,给PCA输入用的;粉色的是PCA中断产生的信号,也就是捕捉下降沿时产生的测试信号。粉色信号是正常的):

图片1.png

同时获得的测量数据(就是相邻换色信号下降沿之间的PCA计数值):

图片4.png

数据偏差还是挺大的。这应该是终端中使用了while导致的。于是改为在PCA的中断中收集数据,在主程序的while中输出,才获得正常信号。

图片2.png

获取到的PCA计数值:

图片3.png

PCA计数值总体来说偏差不大。但是还有一个比较奇怪的地方,就是46、47组数据,严重偏离。开始以为是随机出现的。后来通过不断复位程序测量发现,这是个固定规律的结果,连数据值都完全一样。这下就不知道是什么原因了。不过这不影响对PCA测量外部脉冲宽度的影响。更何况PCA的输入信号是单片机自身的TIMER0产生的。以后再试试完全用外部信号实验。









关键词: 懒猫的学习笔记     Ai8051U     PCA    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]