PCA(可编程计数器阵列)学习指南
适合嵌入式小白的完整学习指南
基于蓝桥杯 STC15F2K60S2 单片机
版本:V1.0 | 日期:2026-03-14
目录
1. PCA 是什么?
1.1 PCA 概述
PCA(Programmable Counter Array,可编程计数器阵列) 是 STC15 系列单片机内置的高级定时外设:
一个 16 位自由运行计数器(CH:CL),精度极高
三个独立 CCP 模块(CCP0/CCP1/CCP2),每个可独立配置
每个 CCP 模块支持四种模式:捕获、软件定时器、高速脉冲输出、PWM
时钟源可选:Fosc/12、Fosc/2、Timer0 溢出、外部 ECI 引脚
1.2 蓝桥杯中的 PCA
在蓝桥杯大模板中,PCA 主要用于超声波测距:
| 功能 | 使用的 PCA 部分 |
|---|---|
| 超声波计时 | CH:CL 16位计数器 + CR 控制 + CF 溢出标志 |
| CCP 模块 | 完全不需要配置(三个模块保持默认值即可) |
一句话理解:PCA 就是一个 16 位的"秒表",可以精确计时到 1μs(12MHz 晶振下),用来测量超声波从发射到接收的时间差,再换算成距离。
2. 硬件结构
2.1 时钟源选择
通过 CMOD 寄存器的 CPS1:CPS0 位选择:
| CPS1 | CPS0 | 时钟源 | 频率(12MHz晶振) |
|---|---|---|---|
| 0 | 0 | Fosc/12 ★ | 1 MHz(每 tick = 1μs) |
| 0 | 1 | Fosc/2 | 6 MHz |
| 1 | 0 | Timer0 溢出 | 由 Timer0 决定 |
| 1 | 1 | ECI(P1.2) | 外部信号 |
★ 超声波测距使用 Fosc/12,1μs 精度正好对应 1cm/58μs 的声速特性。
2.2 引脚分配
| 模块 | 引脚 | 备注 |
|---|---|---|
| CCP0 | P1.1 | 超声波 TX 发射 |
| CCP1 | P1.0 | 超声波 RX 接收 |
| CCP2 | P3.7 / P1.2 | 预留 |
| ECI | P1.2 | 外部时钟输入(少用) |
3. 关键寄存器
3.1 CMOD(PCA 工作模式寄存器)
地址:D9H,可位寻址
| 位 | 名称 | 功能 |
|---|---|---|
| 7 | CIDL | 1=空闲模式停止计数,0=继续计数 |
| 6 | WDTE | 看门狗定时器使能(通常为 0) |
| 5-4 | — | 保留 |
| 3 | CPS1 | 时钟源选择高位 |
| 2 | CPS0 | 时钟源选择低位 |
| 1 | — | 保留 |
| 0 | ECF | 1=允许 CF 溢出中断 |
超声波用法:CMOD = 0x00(Fosc/12,不开溢出中断)
3.2 CCON(PCA 控制寄存器)
地址:D8H,可位寻址
| 位 | 名称 | 功能 |
|---|---|---|
| 7 | CF | 计数器溢出标志,软件清零 |
| 6 | CR | 1=启动计数,0=停止计数 |
| 5-3 | — | 保留 |
| 2 | CCF2 | CCP2 中断标志,软件清零 |
| 1 | CCF1 | CCP1 中断标志,软件清零 |
| 0 | CCF0 | CCP0 中断标志,软件清零 |
3.3 CCAPMn(CCP 模块控制寄存器)
每个 CCP 模块有一个对应的 CCAPMn 寄存器,控制该模块的工作模式:
// 位定义(从高到低):— ECOM CAPP CAPN MAT TOG PWM ECCPn
CCAPM0 = 0x00; // CCP0 不使用(超声波场景下保持默认)
CCAPM1 = 0x00; // CCP1 不使用
CCAPM2 = 0x00; // CCP2 不使用
4. 四种工作模式
4.1 模式对比
| 模式 | 关键位 | 典型用途 |
|---|---|---|
| 捕获(Capture) | CAPP/CAPN=1 | 测脉冲宽度、频率 |
| 软件定时器 | ECOM=MAT=1 | 多路软件定时 |
| 高速脉冲输出 | ECOM=MAT=TOG=1 | 产生方波 |
| PWM 输出 | ECOM=PWM=1 | 电机调速、LED 调光 |
4.2 PWM 输出详解
// 设置 CCP0 为 8位 PWM,占空比 50%
CMOD = 0x00; // Fosc/12
CCAPM0 = 0x42; // ECOM=1, PWM=1
CCAP0L = 128; // 占空比 = (256-128)/256 = 50%
CCAP0H = 128; // 与 L 相同
CR = 1; // 启动计数
蓝桥杯模板中 PWM 调光是用 Timer1 中断手动实现的(软件 PWM),并非使用 PCA 硬件 PWM,这样更容易控制、不受 PCA 限制。
5. 实战:超声波测距
5.1 测距原理
- 单片机向 TX(P1.0) 发送 8 个 40kHz 方波脉冲
- 超声波模块发出声波
- 声波碰到障碍物后反射
- 模块收到回波,RX(P1.1) 输出高电平
- 用 PCA 计时:RX 高电平持续时间 = 声波往返时间 T(μs)
- 距离 = T × 声速 / 2 = T × 340/2/10000 cm = T × 0.017 cm
5.2 为什么用 PCA 而不用 Timer?
| 比较项 | Timer1 | PCA CH:CL |
|---|---|---|
| 位数 | 16 位 | 16 位 |
| 是否被占用 | ✗ 已做系统心跳(uwTick) | ✓ 空闲可用 |
| 精度 | 1μs(Fosc/12) | 1μs(Fosc/12) |
| 灵活性 | 有中断打断风险 | 可查询 CF 超时 |
结论:Timer1 已被系统调度占用,PCA 是最自然的替代选择。
6. 代码详解
6.1 超声波驱动(ultrasound.c)
#include "ultrasound.h"
sbit US_TX = P1^0; // 发射引脚
sbit US_RX = P1^1; // 接收引脚
// 发送一个 40kHz 周期(12.5μs 高 + 12.5μs 低)
void Ut_Send_Wave()
{
unsigned char i;
for (i = 0; i < 8; i++) // 发 8 个脉冲
{
US_TX = 1;
Delay12us(); // 高电平 12μs(40kHz 半周期)
US_TX = 0;
Delay12us(); // 低电平 12μs
}
}
6.2 完整测距函数
float Ut_Wave_Data()
{
unsigned int time;
// ① 配置 PCA
CMOD = 0x00; // Fosc/12 = 1MHz,不开溢出中断
CH = 0; CL = 0; // 清零计数器
CCON = 0x00; // CF=0, CR=0
// ② 发送超声波脉冲
Ut_Send_Wave();
// ③ 等待回波到来(RX 变高)
while (US_RX == 0);
// ④ 启动 PCA 计时
CR = 1;
// ⑤ 等待回波结束,同时检查是否超时
while (US_RX == 1 && CF == 0);
// ⑥ 停止计时
CR = 0;
// ⑦ 判断是否超时
if (CF == 1)
{
CF = 0;
return 0; // 超时,超出量程
}
// ⑧ 读取计时值
time = CH * 256 + CL; // 单位:μs
// ⑨ 换算距离
return time * 0.017f; // 单位:cm
}
6.3 逐步分析
| 步骤 | 代码 | 说明 |
|---|---|---|
| ① | CMOD = 0x00 |
时钟 = Fosc/12 = 1MHz |
| ② | CH=CL=0; CCON=0x00 |
清零计数器和标志 |
| ③ | Ut_Send_Wave() |
发 8×40kHz 脉冲 |
| ④ | while(US_RX==0) |
等回波高电平 |
| ⑤ | CR = 1 |
启动 PCA 计数 |
| ⑥ | while(US_RX==1 && CF==0) |
等回波结束或超时 |
| ⑦ | CR = 0 |
停止计数 |
| ⑧ | if(CF==1) return 0 |
CF=1 表示计数溢出,超出量程 |
| ⑨ | time = CH*256+CL |
读计数值(单位 μs) |
| ⑩ | return time * 0.017f |
距离 = 时间(μs) × 0.017(cm/μs) |
6.4 在主循环中使用
// main.c 任务调度
float distance;
void Task_Distance()
{
distance = Ut_Wave_Data();
// 如果 distance == 0,说明超出量程(> 约65cm)
}
// 在 task_t 数组中注册(120ms 执行一次)
{ Task_Distance, 120, 0 }
7. 常见问题
问题1:距离始终返回 0
原因:
- 超声波模块没有供电
- TX/RX 引脚接反(P1.0 接 TRIG,P1.1 接 ECHO)
- CF 标志没有清零,进入就超时
解决:
// 每次测距前确保 CF 清零
CF = 0;
CH = 0; CL = 0;
CCON = 0x00;
问题2:距离跳动很厉害
原因:PCA 计数和主程序读取之间有中断干扰
解决:
EA = 0; // 关中断
time = CH * 256 + CL;
EA = 1; // 开中断
问题3:精度不够(每次差 1~2cm)
原因:发脉冲函数的延时精度影响起始时刻
解决:
- 延时函数用精确计算的 NOP 延时
- 测距频率不要太高(模板中 120ms 一次,合理)
- 适当做多次平均滤波
问题4:PCA 和 Timer0 冲突?
PCA 与 Timer0 不冲突。冲突的是:
- P3.4(T0 外部计数引脚) 与键盘矩阵第4行(S9~S12)冲突
- PCA 的 ECI 引脚是 P1.2,与 CCP2 备用引脚共用
结论:超声波(PCA)+ 频率计(Timer0 计数模式)可以同时使用,无冲突。
问题5:计数值溢出怎么处理?
CH:CL 是 16 位,最大 65535。
65535 μs × 0.017 ≈ 111 cm 是测量上限。
// 实际项目中典型上限约 65cm(等待 RX 时间更长)
// 若 CF=1,说明超出量程
if (CF == 1)
{
CF = 0; // 必须手动清零!
return 0;
}
学习检查清单
完成以下任务,确保真正掌握 PCA:
- 能说出 PCA 中 CH:CL、CR、CF 分别代表什么
- 知道如何用 CMOD 选择时钟源
- 理解超声波测距的完整时序
- 能解释为什么用 Fosc/12 而不是 Fosc/2
- 知道 CF 标志需要软件清零
- 能写出完整的
Ut_Wave_Data()函数 - 理解距离公式
time × 0.017的来源 - 知道 PCA 模块与 Timer0/Timer1 各自的使用场景
公式速记
时钟频率 = Fosc ÷ 12 = 12MHz ÷ 12 = 1 MHz
计时精度 = 1 / 1MHz = 1 μs / tick
最大计时 = 65535 μs ≈ 65.5 ms
声速 = 340 m/s = 0.034 cm/μs
距离(cm) = T(μs) × 声速/2 = T × 0.034/2 = T × 0.017
文档版本:V1.0
最后更新:2026-03-14
适用于:蓝桥杯单片机竞赛 STC15F2K60S2