有道是“一花独放不是春,万紫千红春满园”
我们不能只满足于 眼前所谓的 “够用、能用” 的少量知识,而不去深入学习探究,进而不慎封锁了自己的见识 和 更多 创新开发上的可能性。
曾经仅满足于学习了蓝桥杯单片机的三个外部晶振 定时器: 定时器0、1、2. 就认为完全够用了
直到有高人提点,方才认识到对官方手册的研究实属过于缺乏了,从而使得自我不能了解到CCP/PCA/PWM模块。
本文就学习研究下这个模块的基础以及驱动原理:
文末会安排代码硬件实验,使其实现外部中断、定时器、PWM输出等功能,并附上源码。
芯片型号:STC15F2K60S2
蓝桥杯单片机CT107D 开发实训平台
CCP代表的是Capture/Compare/PWM(捕捉/比较/脉宽调制)模块,它通常被用于控制PWM电机、PWM LED灯和其他需要精准控制的设备。
CCP意思是: Capture(捕获),Compare(比较),PWM(脉宽调制)。
PCA意思是: Programmable Counter Array(可编程计数器阵列)。
目录
PCA程序可编程计数器/定时器模块:
官方手册中有关CCP/PCA/PWM 的特殊功能寄存器:
特殊功能寄存器总表:(官方手册P783页):
PCA模块工作模式设定表(官方手册P789页):
官方手册例程代码学习(官方手册P801页开始查阅):
先再次介绍一下三个PCA引脚以及作用:
首先介绍特殊功能寄存器是否需要sfr:
1.1.PCA作外部中断使用代码:
1.2.PCA作外部中断使用下载演示:
2.1.PCA作16位定时器使用代码:
2.2.PCA作16位定时器使用下载演示:
3.1.PCA输出高速脉冲代码:
3.2.PCA输出高速脉冲下载演示:
4.1.CCP/PCA捕获模式测脉冲宽度: 手册第822页有该例程序.
PCA程序可编程计数器/定时器模块:
PCA定时器简介:
PCA模块是程序可编程计数器/定时器模块,用于在单片机中生成定时器/计数器信号。
PCA模块可以帮助用户通过适当的程序设置生成准确的计时器/计数器信号,并在开发各种应用时提供了很大的灵活性。
PCA模块包括三个独立的定时器/计数器,分别是PCA0,PCA1和PCA2。它们的主要区别在于它们的工作模式和功能不同。以下是三个模块的简要说明:
1. PCA0:
该模块是一个多功能模块,可以作为定时器或计数器来使用。它具有多种计时和模式选择功能,并且可以将其输出信号引导到可编程I/O管脚 上。
2. PCA1:
该模块仅可用作定时器,具有比PCA0更精确的计时器基准,并可输出具有可定制占空比的PWM信号。
3. PCA2:
该模块仅可用作计数器,具有与PCA0类似的计数器功能,可以出发中断。它还可以输出方波信号、比较器输出和计数器输入捕获信号。
官方手册中有关CCP/PCA/PWM 的特殊功能寄存器:
特殊功能寄存器总表:(官方手册P783页):
符号 | 描述 | 位地址及其符号 | 复位值 | ||||||||
地址 | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 | 00xx xx00 | ||
CCON | PCA控制寄存器 | D8H | CF | CR | --------------------------------------------- | CCF2 | CCF1 | CCF0 | 0xxx 0000 | ||
CMOD | PCA模式寄存器 | D9H | CIDL | ------------------------------------------- | CPS2 | CPS1 | CPS0 | ECF | x000 000 | ||
CCAPM0 | PCA 比较/捕获寄存器0、1、2 | DAH | ---------- | ECOM0 | CAPP0 | CAPN0 | MAY0 | TOG0 | PWM0 | ECCF0 | x000 0000 |
CCAPM1 | DBH | ---------- | ECOM1 | CAPP1 | CAPN1 | MAY1 | TOG1 | PWM1 | ECCF1 | x000 0000 | |
CCAPM2 | DCH | ---------- | ECOM2 | CAPP2 | CAPN2 | MAY2 | TOG2 | PWM2 | ECCF2 | 0000 0000 | |
CL | PCA的16位计数器 | E9H | -------------------------------------------------------------------------------------------------------------------------- | 0000 0000 | |||||||
CH | F9H | -------------------------------------------------------------------------------------------------------------------------- | 0000 0000 | ||||||||
PCA_PWM0 | PCA模块PWM寄存器 | F2H | EBS0_1 | EBS0_0 | ------------------------------------------------------------- | EPC0H | EPC0L | 00xx xx00 | |||
PCA_PWM1 | F3H | EBS1_1 | EBS1_0 | ------------------------------------------------------------- | EPC1H | EPC1L | 00xx xx00 | ||||
PCA_PWM2 | F4H | EBS2_1 | EBS2_0 | ------------------------------------------------------------- | EPC2H | EPC2L | 00xx xx00 | ||||
AUXRI P_SW1 | 配置的是串口一、CCP、SPI 的输出引脚映射 | A2H | S1_S1 | S1_S0 | CCP_S1 | CCP_S0 | SP1_S1 | SP1_S0 | ----------- | DPS | 0000 0000 |
CCAPnL(低位字节) | PCA的捕获/比较寄存器 | ------------------------------------------------------------------------------------------------------------------------------------ | 0000 0000 | ||||||||
CCAPnH(低位字节) | ------------------------------------------------------------------------------------------------------------------------------------ | 0000 0000 |
PCA控制寄存器 CCON:(官方手册P785页):
1. PCA计数器开关控制:
CCF0、CCF1和CCF2位控制PCA计数器是否开启。
2. PCA模块中断控制:
CF、CR和CCIE位控制PCA中断是否生效。
3. PCA定时器控制:
PCA模块可以通过CCON寄存器中的PCA计数器功能,实现定时器的功能。
4. PCA比较模式控制:
CCM0、CCM1和CCM2位控制PCA比较模式的选择和输出电平的控制。
PCA模式寄存器:CMOD (官方手册P784页):
CMOD寄存器作用是控制PCA的分频、工作模式以及中断允许的:
1. 选择计数器模式:
CMOD寄存器的最低位(bit 0)控制计数器模式,可以选择8位计数器模式或12位计数器模式。
2. 选择时钟源和分频系数:
CMOD寄存器的第2~4位(bit 2~4)控制时钟源和分频系数,可以选择系统时钟、LFOSC、HFOSC或外部时钟,并可以选择1、2、4、8、16或32的分频系数。
3. 使能中断:
CMOD寄存器的第5位(bit 5)可以使能PCA模块的中断功能,当计数器溢出时会触发中断。
PCA中断服务函数中断号为:
void PCA_ISR() interrupt7 using1
{
//中断服务内容
}
PCA 比较/捕获寄存器CCAPM0、CCAPM1、CCAPM2:(官方手册P785页):
CCAPM0:
CCAPM1:
CCAPM2:
PCA模块工作模式设定表(官方手册P789页):
PCA的16位计数器CL\CH:
CL和CH地址分别为E9H和F9H,复位值均为00H,用于保存PCA的装载值。
有点类似定时器的TL与TH,三个PCA模块共用一个计数器。
PCA模块PWM寄存器PCA_PWM0、PCA_PWM1、PCA_PWM2:
PCA_PWM0:
PCA_PWM1:
PCA_PWM2:
AUXR1寄存器配置的是串口一、CCP、SPI 的输出引脚映射:(手册第28页):
此处只细讲CCP,其余配置位就不贴图解释了,有兴趣自己查阅官方手册
其中第7 和 6位控制串口1的TXD/RXD的引脚映射,第3 和 2位控制SPI的引脚映射,
其中第5 和 4位控制的就是CCP的 ECI / CCP0 / CCP1 / CCP2 引脚映射:
ECI 是Capture Input引脚,用于捕获外部引脚的信号。
CCP0和CCP1 是Capture/Compare/PWM功能的引脚之一,它们可以用于PWM输出或者捕获外部引脚的信号。
CCP2 是另一个比较模块的引脚,可以被用于PWM输出或者比较外部引脚的信号。
PCA的捕获/比较寄存器——CCAPnL(低位字节)和CCAPnH(高位字节):
官方手册例程代码学习(官方手册P801页开始查阅):
先再次介绍一下三个PCA引脚以及作用:
ECI 是Capture Input引脚,用于捕获外部引脚的信号。
CCP0和CCP1 是Capture/Compare/PWM功能的引脚之一,它们可以用于PWM输出或者捕获外部引脚的信号。
CCP2 是另一个比较模块的引脚,可以被用于PWM输出或者比较外部引脚的信号。
单独的查阅每个特殊功能寄存器的作用与使用方法是很难学会使用的,在大致了解了几个寄存器以及作用后,
为了更具体深入了解如何通过配置这些寄存器来实现PCA的一些功能,
我们还需对官方例程进行一些学习:
本处贴出的学习代码都是我经过学习STC官方手册例程后,在蓝桥杯CT107D实训平台单片机上尝试实现的代码
首先介绍特殊功能寄存器是否需要sfr:
截图中左边是官方手册例程使用的是#include"reg52.h",右边是我们蓝桥杯开发板常用的 #include "stc15f2k60s2.h"头文件,
我们发现有关PCA/PWM 特殊功能寄存器的位定义都为我们写好了,因此使用#include "stc15f2k60s2.h"头文件就不需要手动sfr这些寄存器了。
1.1.PCA作外部中断使用代码:
手册第801页有该例程序
可直接新建工程复制下载使用:
/*
配置PCA为外部中断,
PCA引脚 P1^1 CCP0 接地后LED端口状态反转
*/
其中定时器0用来刷新复制LED的,不起其他作用。
/*
配置PCA为外部中断,
PCA引脚 P1^1 CCP0 接地后LED端口状态反转
*/
#include "stc15f2k60s2.h"
sbit buzz=P0^6;
#define CCP_S0 0x10 //P_SW1.4
#define CCP_S1 0x20 //P_SW1.5
unsigned char LED=0xff;
void inint_PCA_exit(); //初始化PCA为外部中断
void inint_port(unsigned char select); //74HC138\74hc573芯片片选 函数
void Timer0Init(void); //定时器0初始化
void main()
{
inint_port(5); //清除蜂鸣器
buzz=0;
inint_port(4); //清除LED
P0=0XFF;
Timer0Init(); //定时器0初始化
inint_PCA_exit(); //初始化PCA为外部中断
while(1)
{
}
}
//PCA中断服务函数
//using 1是指定中断服务程序使用1号寄存器组来保存寄存器的值。
void PCA_ISR() interrupt 7 using 1
{
CCF0=0; //清除中断标志
LED=~LED; //LED测试取反
}
//初始化PCA为外部中断
void inint_PCA_exit()
{
//映射引脚到(P1^2/ECI,P1^1/CCP0,P1^0/CCP1,P3^7/CCP2)
ACC=P_SW1;
ACC&=~(CCP_S0|CCP_S1); //CCP_S0=0 CCP_S1=0
P_SW1=ACC; //(P1^2/ECI,P1^1/CCP0,P1^0/CCP1,P3^7/CCP2)
/*
//映射引脚到(P3^4/ECI_2,P3^5/CCP0_2,P3^6/CCP1_2,P3^7/CCP2_2)
ACC=P_SW1;
ACC&=~(CCP_S0|CCP_S1);
ACC|=CCP_S0;
P_SW1=ACC; //(P3^4/ECI_2,P3^5/CCP0_2,P3^6/CCP1_2,P3^7/CCP2_2)
*/
/*
//映射引脚到(P2^4/ECI_3,P2^5/CCP0_3,P2^6/CCP1_3,P2^7/CCP2_3)
ACC=P_SW1;
ACC&=~(CCP_S0|CCP_S1);
ACC|=CCP_S1;
P_SW1=ACC; //(P2^4/ECI_3,P2^5/CCP0_3,P2^6/CCP1_3,P2^7/CCP2_3)
*/
CCON=0;
/*初始化PCA控制寄存器
PCA定时器停止
清除CF标志
清除模块中断标志
*/
CH=0;CL=0; //复位PCA寄存器
CMOD=0X00; //设置PCA时钟源、禁止PCA溢出中断
CCAPM0=0X11; //PCA模块0为下降沿触发
/*
CCAPM0=0X21; //PCA模块0为上升沿触发
CCAPM0=0X31; //PCA模块0为上升沿/下降沿触发
*/
CR=1; //PCA定时器开始工作
EA=1; //开启总中断
}
//74HC138\74hc573芯片片选 函数
void inint_port(unsigned char select)
{
switch(select)
{
case 4: P2=(P2&0X1F)|0X80;break; //LED
case 5: P2=(P2&0X1F)|0XA0;break; //蜂鸣器继电器
case 6: P2=(P2&0X1F)|0XC0;break; //数码管位选
case 7: P2=(P2&0X1F)|0XE0;break; //数码管段选
}
}
//初始化定时器0
void Timer0Init(void) //1毫秒@12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;EA=1;
}
//定时器0中断服务函数
void t0() interrupt 1
{
unsigned char i;
if(++i==8)
{i=0;inint_port(4);P0=LED;}
}
1.2.PCA作外部中断使用下载演示:
红线接GND模拟按键,开关LED:
2.1.PCA作16位定时器使用代码:
手册第805页有该例程序
/*
配置PCA为16位定时器
让LED端口以1S为周期闪烁
定时周期是10ms进一次中断
*/
/*
配置PCA为16位定时器
让LED端口以1S为周期闪烁
定时周期是10ms进一次中断
*/
#include "stc15f2k60s2.h"
sbit buzz=P0^6;
//假定芯片工作频率为18.432MHz 18432000L
//但蓝桥杯竞赛实训板是 12.000MHz 所以是 12000000L
//填 11.0592MHz 11059200L 应该也没问题
#define FOSC 12000000L
#define T100Hz (FOSC/12/100)
#define CCP_S0 0x10 //P_SW1.4
#define CCP_S1 0x20 //P_SW1.5
unsigned char LED=0xff;
unsigned char cnt=0;
unsigned int value;
void inint_PCA_TIMER(); //初始化PCA为16位定时器
void inint_port(unsigned char select); //74HC138\74hc573芯片片选 函数
void main()
{
inint_port(5); //清除蜂鸣器
buzz=0;
inint_port(4); //清除LED
P0=0XFF;
inint_PCA_TIMER(); //初始化PCA为16位定时器
while(1)
{
}
}
//PCA中断服务函数
//using 1是指定中断服务程序使用1号寄存器组来保存寄存器的值。
void PCA_ISR() interrupt 7 using 1
{
CCF0=0; //清除中断标志
CCAP0L=value;
CCAP0H=value>>8; //更新比较值
value+=T100Hz;
if(++cnt==100) //计数100次
{
cnt=0;
LED=~LED; //每一秒LED测试取反一次
inint_port(4);
P0=LED;
}
}
//初始化PCA为外部中断
void inint_PCA_TIMER()
{
//映射引脚到(P1^2/ECI,P1^1/CCP0,P1^0/CCP1,P3^7/CCP2)
ACC=P_SW1;
ACC&=~(CCP_S0|CCP_S1); //CCP_S0=0 CCP_S1=0
P_SW1=ACC; //(P1^2/ECI,P1^1/CCP0,P1^0/CCP1,P3^7/CCP2)
/*
//映射引脚到(P3^4/ECI_2,P3^5/CCP0_2,P3^6/CCP1_2,P3^7/CCP2_2)
ACC=P_SW1;
ACC&=~(CCP_S0|CCP_S1);
ACC|=CCP_S0;
P_SW1=ACC; //(P3^4/ECI_2,P3^5/CCP0_2,P3^6/CCP1_2,P3^7/CCP2_2)
*/
/*
//映射引脚到(P2^4/ECI_3,P2^5/CCP0_3,P2^6/CCP1_3,P2^7/CCP2_3)
ACC=P_SW1;
ACC&=~(CCP_S0|CCP_S1);
ACC|=CCP_S1;
P_SW1=ACC; //(P2^4/ECI_3,P2^5/CCP0_3,P2^6/CCP1_3,P2^7/CCP2_3)
*/
CCON=0;
/*初始化PCA控制寄存器
PCA定时器停止
清除CF标志
清除模块中断标志
*/
CH=0;CL=0; //复位PCA寄存器
CMOD=0X00; //设置PCA时钟源、禁止PCA溢出中断
value=T100Hz;
CCAP0L=value;
CCAP0H=value>>8; //初始化PCA模块0
value+=T100Hz;
CCAPM0=0X49; //PCA模块0为16位定时器模式
CR=1; //PCA定时器开始工作
EA=1; //开启总中断
}
//74HC138\74hc573芯片片选 函数
void inint_port(unsigned char select)
{
switch(select)
{
case 4: P2=(P2&0X1F)|0X80;break; //LED
case 5: P2=(P2&0X1F)|0XA0;break; //蜂鸣器继电器
case 6: P2=(P2&0X1F)|0XC0;break; //数码管位选
case 7: P2=(P2&0X1F)|0XE0;break; //数码管段选
}
}
2.2.PCA作16位定时器使用下载演示:
3.1.PCA输出高速脉冲代码:
手册第805页有该例程序,其中6、7、8位和9~16位PWM就不做演示了,自己查阅手册!
/*
配置PCA为输出高速脉冲
PCA模块0为16位定时器模式,同时反转CCP0(P1^1)口
*/
/*
配置PCA为输出高速脉冲
PCA模块0为16位定时器模式,同时反转CCP0(P1^1)口
*/
#include "stc15f2k60s2.h"
sbit buzz=P0^6;
//假定芯片工作频率为18.432MHz 18432000L
//但蓝桥杯竞赛实训板是 12.000MHz 所以是 12000000L
//填 11.0592MHz 11059200L 应该也没问题
#define FOSC 12000000L
#define T100Hz (FOSC/12/100)
#define CCP_S0 0x10 //P_SW1.4
#define CCP_S1 0x20 //P_SW1.5
unsigned char LED=0xff;
unsigned char cnt=0;
unsigned int value;
void inint_PCA_PWM1(); //初始化PCA输出高速脉冲
void inint_port(unsigned char select); //74HC138\74hc573芯片片选 函数
void main()
{
inint_port(5); //清除蜂鸣器
buzz=0;
inint_port(4); //清除LED
P0=0XFF;
inint_PCA_PWM1(); //初始化PCA输出高速脉冲
while(1)
{
}
}
//PCA中断服务函数
//using 1是指定中断服务程序使用1号寄存器组来保存寄存器的值。
void PCA_ISR() interrupt 7 using 1
{
CCF0=0; //清除中断标志
CCAP0L=value;
CCAP0H=value>>8; //更新比较值
value+=T100Hz;
}
//初始化PCA输出高速脉冲
void inint_PCA_PWM1()
{
//映射引脚到(P1^2/ECI,P1^1/CCP0,P1^0/CCP1,P3^7/CCP2)
ACC=P_SW1;
ACC&=~(CCP_S0|CCP_S1); //CCP_S0=0 CCP_S1=0
P_SW1=ACC; //(P1^2/ECI,P1^1/CCP0,P1^0/CCP1,P3^7/CCP2)
/*
//映射引脚到(P3^4/ECI_2,P3^5/CCP0_2,P3^6/CCP1_2,P3^7/CCP2_2)
ACC=P_SW1;
ACC&=~(CCP_S0|CCP_S1);
ACC|=CCP_S0;
P_SW1=ACC; //(P3^4/ECI_2,P3^5/CCP0_2,P3^6/CCP1_2,P3^7/CCP2_2)
*/
/*
//映射引脚到(P2^4/ECI_3,P2^5/CCP0_3,P2^6/CCP1_3,P2^7/CCP2_3)
ACC=P_SW1;
ACC&=~(CCP_S0|CCP_S1);
ACC|=CCP_S1;
P_SW1=ACC; //(P2^4/ECI_3,P2^5/CCP0_3,P2^6/CCP1_3,P2^7/CCP2_3)
*/
CCON=0;
/*初始化PCA控制寄存器
PCA定时器停止
清除CF标志
清除模块中断标志
*/
CH=0;CL=0; //复位PCA寄存器
CMOD=0X02; //设置PCA时钟源、禁止PCA溢出中断
value=T100Hz;
CCAP0L=value; //P1^1输出100KHz方波
CCAP0H=value>>8; //初始化PCA模块0
value+=T100Hz;
CCAPM0=0X4d; //PCA模块0为16位定时器模式,同时反转CCP0(P1^1)口
CR=1; //PCA定时器开始工作
EA=1; //开启总中断
}
//74HC138\74hc573芯片片选 函数
void inint_port(unsigned char select)
{
switch(select)
{
case 4: P2=(P2&0X1F)|0X80;break; //LED
case 5: P2=(P2&0X1F)|0XA0;break; //蜂鸣器继电器
case 6: P2=(P2&0X1F)|0XC0;break; //数码管位选
case 7: P2=(P2&0X1F)|0XE0;break; //数码管段选
}
}
3.2.PCA输出高速脉冲下载演示:
CCP/PCA捕获模式测脉冲宽度: 手册第822页有该例程序.
最后希望大家能从我的文章中初步了解学会了PCA\CCP\PWM的这三种功能,
觉得有用的话请给个三连支持