因为51单片机中的定时器数量较少,多个外设同时使用时可能会出现定时器不够用的情况,此时可以使用单片机中的PCA模块作为定时器来使用。
STC15单片机中有3路可编程计数器阵列,PCA(Programmable Counter Array)是其中之一。它具有多个定时器/计数器和捕获/比较单元。PCA模块可以用于实现定时器、计数器、PWM(脉宽调制)和捕获等功能。本篇介绍PCA用作定时器的简单写法:
一:看数据手册来编程:
通过数据手册可以看到PCA其实是和定时器的使用方式大同小异,也是需要打开中断,声明中断函数等等。可以看到它的中断号是7。
既然和定时器使用方式差不多,那就是需要初始化pca,写好中断函数就可以了。
二:PCA初始化函数
1.定时器初始化要配置TMOD,那PCA也需要配置它的模式寄存器CMOD。
一般的使用将CIDL配置为0应该就可以,B4-B6应该是无效位,也将其配置为0就行,B1-B3是选择时钟源输入的模式,也就是选择是否将系统时钟进行分频后作为PCA的时钟源。
系统时钟频率通常指的是内部IRC(Internal RC)时钟的频率。内部IRC时钟是单片机内部集成的一个振荡器,它提供了单片机运行所需的时钟信号。 STC15F2K60S2单片机的内部IRC时钟频率为11.0592MHz。这是单片机默认的系统时钟源,可以通过STC-ISP烧录软件初始为12MHZ。可以通过配置寄存器来选择内部IRC时钟作为系统时钟源。
所以当IRC时钟频率调为12MHZ,将PCA定时器的时钟源设置为 系统时钟/12 时,系统时钟频率为12MHz,则PCA定时器的时钟源频率为12MHz / 12 = 1MHz。所以B3B2B1=000。
最后B0的ECF是PCA计数溢出中断使能位,置1代表允许CCON中的CF位的中断(我的理解是CF是个中断标志,通过CF的状态来决定是否进入中断,所以允许CF位的中断才能在计数溢出时进入中断函数)。所以CMOD我们配置好了,CMOD |= 0x01;
|= 的作用是把要置1的置1,不改变其它位。&= 是把要清零的位清零,不改变其它位。作用刚好相反,或置1 与清零。
2.定时器初始化要配置TCON,PCA也需要配置它的控制寄存器CCON。
前面配置CMOD时说过CF是计数溢出标志位,即在ECF=1允许CF中断时,若PCA计数溢出则CF被置1,进入中断函数。注意:进入中断后要手动将CF清零,否则就不能进入下一次中断(我尝试过不行)。CR置零,CR就是类似于定时器中的TR位,用来打开或关闭计数器。其余三位CCF也清零即可。所以CCON=0x00;
3.定时器中的装载值的TH和TL对应于PCA中的CH(8位)和CL(8位)。
假设系统时钟频率为f_sys,并且将PCA定时器的时钟源设置为f_sys/12。每个PCA时钟周期的时间可以计算为1 / (f_sys / 12) 秒。因此,CH和CL加起来的最大计时值为 65535 * (1 / (f_sys / 12)) 秒。前面配置CMOD时我们将系统频率配置为了12MHZ,选择了 系统频率/12 作为PCA的时钟源,代入公式计算得到每个PCA时钟周期的时间是1us,CH加上CL是16位二进制,所以最大可计到65535us产生一次溢出。可以通过对CH和CL赋不同的初始值来得到间隔多久产生一次中断。
4.打开总中断EA=1,打开PCA计数器CR=1
这个和定时器初始化是一样的操作。
到这里pca初始化函数就写完了。
void pca_init()
{
CMOD|=0x01;//PCA计数脉冲选择系统时钟/12作为时钟源
CCON=0x00;//将溢出位cf(溢出标志位)置零cr(启停位)置零停止计数
CH=0xd8; //8位
CL=0xef; //8位
EA=1; //打开总中断
CR=1; //打开PCA计数器
}
二:中断函数
这个和定时器的不可重装模式是一样的,需要在中断函数里手动重装CH和CL值,并且需要把CF清零。中断号为7。
void pca_interrupt() interrupt 7
{
CF=0; //计数器溢出后被置1,需要软件清零 才能进入下一次中断
CH=0xd8; //8位
CL=0xef; //8位
}
三:例程
以蓝桥杯STC15F2K60S2单片机为例,使用PCA作为定时器实现LED以1秒间隔闪烁。
#include <stc15f2k60s2.h>
#define PWM_PERIOD 100 //CHCL为0xd8ef,换算为十进制是55535,所以每次溢出是10000us,当溢出100次时刚好是1秒
void Device(unsigned char p2dat,unsigned char p0dat)
{
P0 = p0dat;
P2 = (P2&0x1f)|p2dat;
P2 = P2&0x1f;
}
void pca_init()
{
CMOD|=0x01;//PCA技术脉冲选择为系统时钟不分频(B3为1),ECF位=1,允许寄存器CCON中CF位的中断 所以CMOD=0x09
CCON=0x00;//将溢出位cf(溢出标志位)置零cr(启停位)置零停止计数
CH=0xd8; //8位
CL=0xef; //8位
EA=1; //打开总中断
CR=1; //打开PCA计数器
}
unsigned char i;
void pca_interrupt() interrupt 7
{
static unsigned int pwm_count = 0;
CF=0; //计数器溢出后被置1,需要软件清零 才能进入下一次中断
CH=0xd8; //8位
CL=0xef; //8位
pwm_count++;
if (pwm_count >= PWM_PERIOD)
{
pwm_count = 0;
if(i==0)Device(0x80,0x00);
else Device(0x80,0xff);
i = (i+1)%2;
}
}
void main() {
pca_init();
Device(0x80,0xff);//初始化关闭LED
while (1) {
// 主循环中可以进行其他操作
}
}