第2-16讲:可编程计数器阵列PCA
-
- 学习目的
- 掌握STC8A8K64D4系列PCA可编程计数器阵列的原理。
- 掌握4个PCA外设相关寄存器配置及程序设计。
- PCA概述
PCA全称是可编程计数器阵列(Programable Counter Array),其中P表示可以编程控制、C表示计数器、A表示阵列,即有多路通道。他和我们前面学习的定时/计数器类似,对于初学者,可以把他理解为一个功能更加强大的定时/计数器。
PCA可以根据不同的应用需求进行编程,具有较高的灵活性和可靠性,因此他在工业自动化、仪器仪表等领域得到广泛的应用。
STC8A8K64D4 系列单片机内部集成了 4 组可编程计数器阵列模块,可用于软件定时器、外部脉冲捕获、高速脉冲输出和 PWM 脉宽调制输出,其结构图如下所示。
图1:PCA结构图
PCA 外设包含4路PCA模块,这4路PCA模块使用同一个可配置时钟源的16 位计数器, 并且他们使用的模式寄存器CCON和控制寄存器CMOD也是同一个,我们在使用时应注意这一点,因为,对他们的配置会同时对4个PCA模块有效。
除了共有的配置,每个PCA模块还有一个独立的配置寄存器“PCA 模块模式控制寄存器(CCAPMn,n=0~4)”,用于配置PCA模块的比较、捕获、高速脉冲输出和 PWM 脉宽调制输出,其中PCA模块工作于PWM模式时,还有一个寄存器“PCA 模块 PWM 模式控制寄存器(PCA_PWMn)”用于配置PWM的位宽。
PCA 模块的工作模式有4种:软件定时器、外部脉冲捕获、高速脉冲输出和 PWM 脉宽调制输出。PCA 模块工作于捕获、高速脉冲输出和 PWM时,需要通过“P_SW1”寄存器配置关联的引脚,用于连接输入的脉冲、输出高速脉冲和PWM。工作于软件定时器模式时,是没有关联引脚的。
-
- STC8A8K64D4的PCA应用步骤
- 软件定时器
- STC8A8K64D4的PCA应用步骤
PCA 模块工作于软件定时器时的应用流程如下图所示。
图2:PCA工作于软件定时器模式时的应用流程
-
-
-
- 配置PCA时钟源
-
-
PCA时钟源由“PCA 模式寄存器(CMOD)”中的CPS[2:0]位配置,此外,还需配置CIDL位用于确定计数器在空闲模式下是否计数,如下所示。
PCA 模式寄存器(CMOD):
- CIDL:空闲模式下是否停止 PCA 计数。
- 0:空闲模式下 PCA 继续计数。
- 1:空闲模式下 PCA 停止计数。
- CPS[2:0]:PCA 计数脉冲源选择位,可配置项如下表所示。
表1:PCA时钟源
CPS[2:0] | PCA 的输入时钟源 |
000 | 系统时钟/12 |
001 | 系统时钟/2 |
010 | 定时器 0 的溢出脉冲 |
011 | ECI 脚的外部输入时钟 |
100 | 系统时钟 |
101 | 系统时钟/4 |
110 | 系统时钟/6 |
111 | 系统时钟/8 |
-
-
-
- 设置定时时间
-
-
PCA应用于软件定时器时和定时器类似,都是通过对时钟的计数达到定时的目的,所不同的是定时器设置了计数初值后,就不用软件干预了,每次溢出后硬件都会自动重装计数初值,而PCA软件定时器则需要软件干预。PCA软件定时器没有自动重装的机制,每次匹配中断后,都需要修改匹配值(在当前匹配值的基础上加上定时时间对应的计数值)。
使用PCA软件定时器时,“PCA 计数器寄存器(CL,CH)”的值通常设置为0,然后根据定时时间、PCA时钟源计算出比较值。PCA启动计数后,计数器的值向上递增,当计数器的值和比较值匹配时产生匹配中断,因此,对于PCA软件定时器来说,需要计算的是比较值。
- 比较值计算示例如下(定时时间10ms):
系统时钟:24MHz。
PCA时钟源:系统时钟/12 = 2Mhz,即一个时钟的时间是:1/2000000秒 = 0.5us。
比较值 = 10000us/0.5us = 20000,对应的16进制为0x4E20。因此,比较寄存器初次赋值为(CCAP0L = 0x20; CCAP0H = 0x4E;),之后,在PCA中断服务函数中,每次加上这个数值后再对比较寄存器赋值。
-
-
-
- 中断配置
-
-
中断配置包括中断的开启/关闭和中断优先级的配置。
PCA计数器溢出中断PCA的4个模块各有一个匹配/捕获中断,中断的开启和关闭由中断使能寄存器IE的位1(ET0)和位3(ET1)控制,如下图所示。
- ECF:PCA 计数器溢出中断允许位。
- 0:禁止 PCA 计数器溢出中断。
- 1:使能 PCA 计数器溢出中断。
- ECCFn:允许 PCA 模块 n 的匹配/捕获中断
- 0:禁止 PCA 模块 n 的匹配/捕获中断。
- 1:使能PCA 模块 n 的匹配/捕获中断。
PCA的中断优先级由IP和IPH寄存器位7的组合配置,如下图所示。
- PPCAH,PPCA:CCP/PCA/PWM中断优先级控制位
- 00: CCP/PCA/PWM 中断优先级为 0 级(最低级)。
- 01: CCP/PCA/PWM 中断优先级为 1 级(较低级)。
- 10: CCP/PCA/PWM 中断优先级为 2 级(较高级)。
- 11: CCP/PCA/PWM 中断优先级为 3 级(最高级)。
- 中断优先级配置示例:配置PCA中断优先级为最高优先级3
IP |= 0x80; //中断优先级配置为最高优先级:PPCAH = 1,PPCA = 1
IPH |= 0x80;
- 注意事项:PCA开启中断的情况下,还需要开启总中断“EA=1”,中断才能起作用。通常,我们会在主函数“main()”中开启总中断,这是因为我们开发的程序中可能会使用到多个中断,在主函数中开启一次即可。
-
-
- 启动和停止PCA计数器
-
-
PCA是通过置位“PCA 控制寄存器(CCON)”中的“CR”位启动PCA计数器、通过清零“CR”位停止PCA计数器的,如下所示。
PCA 控制寄存器(CCON):
- CR:PCA 计数器允许控制位。
- 0:停止 PCA 计数。
- 1:启动 PCA 计数。
-
- 输出高速脉冲
-
PCA工作于输出高速脉冲模式时和软件定时器类似,区别是高速脉冲模式需要配置PCA模块的功能引脚,用于输出脉冲。
图3:PCA工作于输出高速脉冲模式时的应用流程
PCA的各个模块工作于外部脉冲捕获、高速脉冲输出和 PWM 脉宽调制输出模式时,需要配置功能引脚,工作于软件定时器模式时无需配置。
PCA功能引脚由“外设端口切换控制寄存器 1(P_SW1)”中的CCP_S[1:0]位配置,如下所示。
外设端口切换控制寄存器 1(P_SW1):
应用流程图中的“设置定时时间”决定了脉冲的频率/周期,定时时间计算比较值的方式和软件定时器一样。比较值写入捕获值/比较值寄存器并启动PCA计数后,当 PCA 计数器的计数值与PCA模块捕获值/比较值寄存器的值相匹配时,PCA 模块关联的引脚输出将发生翻转。需要注意的是:一个周期需要2倍的定时时间(输出方波),如下图所示。
图4:输出脉冲的周期
-
-
- 捕获测量脉冲宽度
-
PCA 模块工作于捕获模式时,对模块的关联引脚输入跳变进行采样。当采样到有效跳变时,PCA 控制器立即将 PCA 计数器 CH 和 CL 中的计数值装载到模块的捕获寄存器中 CCAPnL 和 CCAPnH,同时将 CCON 寄存器中相应的 CCFn 置 1。若 CCAPMn 中的 ECCFn 位被设置为 1,将产生中断。
中断服务程序中,读出PCA计数值并保存,以便于计算两次上升沿或两次下降沿之间计数值的差,由这个差值即可计算出两次上升沿或两次下降沿之间的时间间隔。PCA工作于脉宽测量模式时的应用流程如下所示。
图5:PCA工作于脉宽测量模式时的应用流程
捕获脉宽需要注意以下两个方面:
- PCA工作于脉宽测量模式时只能设置为捕获上升沿或者捕获下降沿,不能设置同时捕获上升沿和下降沿。
- PCA计数器溢出。
- 示例:测量一个周期为10ms的方波,PCA配置为上升沿捕获、系统时钟24MHz,PCA时钟源为系统时钟/12(即一个计数值对应的时间是:0.5us),示意图如下所示。
图6:输出脉冲的周期
第一个上升沿到来后,PCA进入中断,在中断服务函数中读出PCA 计数器的值,存储到变量“cnt_value_last”。第二个上升沿到来后,同样在中断服务函数中读出PCA 计数器的值,存储到变量“cnt_value_current”,求出变量“cnt_value_current”和“cnt_value_last”的差值,再乘以一个计数值对应的时间即为测量的脉宽。
在计算两次上升沿之间的计数值的时候需要考虑PCA计数器的溢出问题,通常,我们会开启PCA的溢出中断用来跟踪PCA计数器的溢出情况,详细的处理方式见实验程序。
-
-
- 输出PWM
-
PCA 模块工作于PWM模式时,可配置为6/7/8/10 位 PWM。 PCA计数器启动后, PCA计数器的值向上递增,当PCA计数器的值小于设置的比较值时,配置的功能引脚输出低电平;当PCA计数器的值大于等于设置的比较值时,配置的功能引脚输出高电平;当PCA计数器溢出后,比较值自动重装,由此,实现无需软件干预的PWM输出。
图7:PCA工作于PWM模式时的应用流程
PCA工作于PWM模式时的时钟源和功能引脚配置和其他模式一样,本节中我们重点关注PWM位数、频率(周期)和占空的配置。
PWM的位数由“PCA 模块 PWM 模式控制寄存器(PCA_PWMn)”中的CPS[2:0]位配置,如下图所示。
PCA 模块 PWM 模式控制寄存器(PCA_PWMn):
PWM可配置为6、7、8或10 位,PWM配置的位数不同,他的重载值和比较值的组成也是不一样的,如下图所示。
- PWM频率(周期)
PCA工作于PWM模式时,PWM的频率由PWM配置的位数和时钟源决定,另外注意PCA的4个通道共用同一个时钟源,因此,PCA的各个模块工作于PWM模式时,不能单独设置各个模块的PWM频率。
PWM频率计算公式如下,其中n是PWM的位数,可取的值为6、7、8、10:
- 注:下列情况PWM输出固定电平:
- 当EPCnH=0,XCCAP0H=0及CCAPnH=00H时, PWM固定输出高。
- 当EPCnH=1,XCCAP0H=3及CCAPnH=FFH时, PWM固定输出低。
- PWM占空比
PWM频率确定后,PWM的位数、比较值和重装值的位数也就确定了,同时,一个PWM周期包含多少个计数也确定了,因此,设置比较值和重装值即可设置一个PWM周期内高电平的时间,即PWM的占空比。(PCA计数器的初值通常在初始化时设置为0,计数器启动后向上计数,当PCA计数器的值小于设置的比较值时,配置的功能引脚输出低电平;当PCA计数器的值大于等于设置的比较值时,配置的功能引脚输出高电平)。
- 示例:6位PWM,占空比:50%
PWM为6位时,一个周期包含的计数值为64,如果要占空比为50%,把比较值和重装值设置为64的一半即可,即32,对应的16进制为0x20。
//PWM占空比为50%
CCAP0L = 0x20; //PCA模块0的PWM比较值
CCAP0H = 0x20; //PCA模块0的PWM重装值
- 示例:6位PWM,占空比:20%
//PWM占空比为25%
CCAP0L = 0x10; //PCA模块0的PWM比较值
CCAP0H = 0x10; //PCA模块0的PWM重装值
-
- 软件设计
- 软件定时器实验
- 软件设计
- 注:本节的实验是在“实验2-1-3:流水灯(自编驱动文件方式)”的基础上修改,本节对应的实验源码是:“实验2-16-1:PCA软件定时器实验”。
-
-
- 实验内容
-
-
使用PCA模块0实现200ms的软件定时器,定时时间到了之后翻转指示灯D1的状态,即驱动指示灯D1以200ms的间隔闪烁。PCA配置如下:
- 系统时钟:24MHz。
- PCA时钟源:系统时钟/12 = 2Mhz。
- 中断:关闭计数器溢出中断,使能PCA模块0的匹配/捕获中断,中断优先级设置为2(较高级)。
-
-
- 代码编写
-
-
- 新建一个名称为“pca.c”的文件及其头文件“pca.h”并保存到工程的“Source”文件夹,并将“pca.c”加入到Keil工程中的“SOURCE”组。
- 引用头文件
因为在“main.c”文件中使用了“pca.c”文件中的函数,所以需要引用下面的头文件“pca.h”。
代码清单:引用头文件
- //引用pca的头文件
- #include "pca.h"
- PCA初始化和启动
配置PCA时钟源为系统时钟的1/12,本例中使用的系统时钟是24MHz,即PCA时钟源为2MHz。PCA计数值初值设置为0,之后将定时10ms的比较值写入比较寄存器并开启匹配/捕获中断。因为PCA软件定时器是通过修改比较值实现的,所以,我们定义一个变量“counter_val”用于存储比较器,每次写入比较值后,“counter_val”的值增加10ms对应的计数值,以备下一次修改比较值时使用。
配置完成后,启动PCA计数器,当计数器的计数值等于比较值时,触发匹配/比较中断,代码清单如下。
代码清单:初始化PCA
- /**************************************************************************************
- * 描 述 : PCA初始化,初始化PCA模块0用于软件定时器,定时时间10ms
- * 参 数 : 无
- * 返回值 : 无
- **************************************************************************************/
- void pca_init(void)
- {
- CCON = 0x00; //CF、CR、CCF1、CCF0~CCF3位均清零
- /*--------------------PCA模式寄存器CMOD配置---------------------------
- 位7 位6 位5 位4 位3~位1 位0
- CIDL x x x CPS[2~0] BCF
- 0 x x x 000 0
- CIDL=0:空闲模式下仍然计数
- CPS[2~0]=000:PCA时钟源选择:系统时钟/12
- BCF=0:关闭PCA计数器溢出中断
- -----------------------------END------------------------------------*/
- CMOD = 0x00;
- CL = 0x00; //PCA计数器赋初值
- CH = 0x00; //PCA计数器赋初值
- IP &= ~0x02; //中断优先级配置为2(较高优先级)
- IPH |= 0x02;
- /*------------------PCA模块0模式控制寄存器CCAPM0配置------------------
- 位7 位6 位5 位4 位3 位2 位1 位0
- x ECOM0 CCAPP0 CCAPN0 MAT0 TOG0 PWM0 ECCF0
- x 1 0 0 1 0 0 1
- ECOM0=1:允许PCA模块0的比较功能
- CCAPP0=0:关闭PCA模块0的上升沿捕获
- CCAPN0=0:关闭PCA模块0的下降沿捕获
- MAT0=1:允许PCA模块0的匹配功能
- TOG0=0:关闭PCA模块0的高速脉冲输出功能
- PWM0=0:关闭PCA模块0的脉宽调制输出功能
- ECCF0=1:允许PCA模块0的匹配/捕获中断
- -----------------------------END------------------------------------*/
- CCAPM0 = 0x49;
- //写入比较值,注意:必须先写CCAP0L,再写CCAP0H
- CCAP0L = (u8)CLK_SOURCE_2MHZ_10MS; //PCA比较值寄存器赋初值
- CCAP0H = (u8)(CLK_SOURCE_2MHZ_10MS>>8); //PCA比较值寄存器赋初值
- counter_val = CLK_SOURCE_2MHZ_10MS * 2; //下一次写入比较寄存器的数值
- CR = 1; //启动PCA计数器阵列计数
- }
- PCA中断服务函数
因为PCA配置的定时时间是10ms,为了达到200ms的定时,我们定义一个变量“count”来记录Timer0的溢出次数,当“count”的值等于20的时候,表示达到200ms,此时,翻转指示灯D1的状态,从而实现驱动D1以200ms间隔闪烁的目的。
代码清单:PCA中断服务函数
- /**************************************************************************************
- * 描 述 : PCA中断服务程序
- * 参 数 : 无
- * 返回值 : 无
- **************************************************************************************/
- void pca_isr (void) interrupt 7
- {
- CCF0 = 0; //清PCA模块0匹配/捕获中断标志
- //写入比较值,注意:必须先写CCAP0L,再写CCAP0H
- CCAP0L = (u8)counter_val; //PCA比较值寄存器赋值
- CCAP0H = (u8)(counter_val >> 8);
- counter_val += CLK_SOURCE_2MHZ_10MS; //因为计数器的计数值不会清零,所以通过修改比较值达到定时的目的
- cnt++; //每10ms执行一次cnt加一的操作
- if(cnt == 20) //cnt加到20后,时间为200ms
- {
- led_toggle(LED_1); //翻转用户指示灯D1状态
- cnt = 0;
- }
- }
- 主函数
主函数中初始化指示灯和PCA并开启总中断,PCA计数启动后,即会按照配置的时间触发中断,代码清单如下。
代码清单:主函数
- /**************************************************************************************
- * 描 述 : 主函数
- * 参 数 : 无
- * 返回值 : int类型
- **************************************************************************************/
- int main(void)
- {
- P2M1 &= 0x3F; P2M0 &= 0x3F; //设置P2.6~P2.7为准双向口(指示灯D1和D2)
- pca_init(); //初始化PCA
- EA = 1; //使能总中断
- while(1)
- {
- }
- }
-
-
- 硬件连接
-
-
本实验需要使用LED指示灯D1,按照下图所示短接指示灯和按键的跳线帽。
图8:跳线帽短接
-
-
-
- 实验步骤
-
-
- 解压“…\第3部分:配套例程源码”目录下的压缩文件“实验2-16-1:PCA软件定时器实验”,将解压后得到的文件夹拷贝到合适的目录,如“D\STC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程出现问题)。
- 双击“…\pca_timer\project”目录下的工程文件“pca_timer.uvproj”。
- 点击编译按钮编译工程,编译成功后生成的HEX文件“pca_timer.hex”位于工程的“…\pca_timer\Project\Object”目录下。
- 打开STC-ISP软件下载程序,下载使用内部IRC时钟,IRC频率选择:24MHz。
- 程序运行后,可以观察到用户指示灯D1以200mS间隔闪烁。
-
- 输出高速脉冲实验
-
- 注:本节的实验是在“实验2-16-1:PCA软件定时器实验”的基础上修改,本节对应的实验源码是:“实验2-16-2:PCA输出高速脉冲实验”。
-
-
- 实验内容
-
-
使用PCA模块0输出100KHz的高速脉冲, PCA配置如下:
- PCA模块0功能引脚:P1.7。
- 系统时钟:24MHz。
- PCA时钟源:系统时钟= 24Mhz。
- 中断:关闭计数器溢出中断,使能PCA模块0的匹配/捕获中断,中断优先级设置为2(较高级)。
- 脉冲频率100KHz,对应的比较值为:24000000L/2/100000 = 120。
-
-
- 代码编写
-
-
- 引用头文件
因为在“main.c”文件中使用了“pca.c”文件中的函数,所以需要引用下面的头文件“pca.h”。
代码清单:引用头文件
- //引用pca的头文件
- #include " pca.h"
- PCA初始化
本例中输出的脉冲频率较高,因此PCA时钟源配置为系统时钟24MHz,PCA模块0的功能引脚为P1.7,即通过引脚P1.7输出高速脉冲,代码清单如下。
代码清单:PCA初始化
- /***************************************************************************
- * 描 述 : PCA初始化
- * 参 数 : 无
- * 返回值 : 无
- **************************************************************************/
- void pca_init(void)
- {
- CCON = 0x00; //CF、CR、CCF1、CCF0位均清零
- P_SW1 &= ~0x30; //PCA模块0功能引脚选择P1.7
- /*--------------------PCA模式寄存器CMOD配置---------------------------
- 位7 位6 位5 位4 位3~位1 位0
- CIDL x x x CPS[2~0] BCF
- 0 x x x 100 0
- CIDL=0:空闲模式下仍然计数
- CPS[2~0]=100:PCA时钟源选择:系统时钟
- BCF=0:关闭PCA计数器溢出中断
- -----------------------------END------------------------------------*/
- CMOD = 0x08;
- CL = 0x00; //PCA计数器赋初值
- CH = 0x00; //PCA计数器赋初值
- IP &= ~0x02; //中断优先级配置为2(较高优先级)
- IPH |= 0x02;
- /*------------------PCA模块0模式控制寄存器CCAPM0配置------------------
- 位7 位6 位5 位4 位3 位2 位1 位0
- x ECOM0 CCAPP0 CCAPN0 MAT0 TOG0 PWM0 ECCF0
- x 1 0 0 1 1 0 1
- ECOM0=1:运行PCA模块0的比较功能
- CCAPP0=0:关闭PCA模块0的上升沿捕获
- CCAPN0=0:关闭PCA模块0的下降沿捕获
- MAT0=1:允许PCA模块0的匹配功能
- TOG0=1:开启PCA模块0的高速脉冲输出功能
- PWM0=0:关闭PCA模块0的脉宽调制输出功能
- ECCF0=1:允许PCA模块0的匹配/捕获中断
- -----------------------------END------------------------------------*/
- CCAPM0 = 0x4D;
- //PCA模块0初始化部分
- counter_val = T100KHZ; //counter_val赋值
- //写入比较值,注意:必须先写CCAP0L,再写CCAP0H
- CCAP0L = (u8)counter_val; //PCA比较值寄存器赋初值
- CCAP0H = (u8)(counter_val >> 8); //PCA比较值寄存器赋初值
- counter_val = T100KHZ * 2; //下一次写入比较寄存器的数值
- CR = 1; //启动PCA计数器阵列计数
- }
- PCA中断服务函数
PCA工作于输出高速脉冲模式时,输出引脚的翻转是由硬件自动完成的,因此,中断服务函数中处理比较值即可,代码清单如下。
代码清单:PCA中断服务函数
- /***************************************************************************
- * 描 述 : PCA中断服务程序
- * 参 数 : 无
- * 返回值 : 无
- **************************************************************************/
- void pca_isr (void) interrupt 7
- {
- CCF0 = 0; //将PCA计数器阵列溢出标志位软件清零
- //写入比较值,注意:必须先写CCAP0L,再写CCAP0H
- CCAP0L = (u8)counter_val; //PCA比较值寄存器赋值
- CCAP0H = (u8)(counter_val >> 8);
- counter_val += T100KHZ; //因为计数器的计数值不会清零,所以通过修改比较值达到定时的目的
- }
- 主函数
主函数中初始化指示灯和PCA并开启总中断,PCA计数启动后,即会按照配置的时间触发中断,代码清单如下。
代码清单:主函数
- /***************************************************************************
- * 描 述 : 主函数
- * 参 数 : 无
- * 返回值 : int类型
- **************************************************************************/
- int main(void)
- {
- P1M1 &= 0x7F; P1M0 &= 0x7F; //设置P1.7为准双向口
- pca_init(); //PCA初始化
- EA = 1; //使能总中断
- while(1)
- {
- }
- }
-
-
- 硬件连接
-
-
本实验通过P1.7输出100KHz高速脉冲,P1.7引脚的位置如下图所示。
图9:硬件连接
-
-
-
- 实验步骤
-
-
- 解压“…\第3部分:配套例程源码”目录下的压缩文件“实验2-16-2:PCA输出高速脉冲实验”,将解压后得到的文件夹拷贝到合适的目录,如“D\STC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程出现问题)。
- 双击“…\pca_pulse\project”目录下的工程文件“pca_pulse.uvproj”。
- 点击编译按钮编译工程,编译成功后生成的HEX文件“pca_pulse.hex”位于工程的“…\pca_pulse\Project\Object”目录下。
- 打开STC-ISP软件下载程序,下载使用内部IRC时钟,IRC频率选择:24MHz。
- 程序运行后,用示波器测量P1.7,可以观察P1.7输出100KHz的方波。
-
- 测量脉冲宽度实验
-
- 注:本节的实验是在“实验2-10-1:Timer0定时驱动LED闪烁”的基础上修改,本节对应的实验源码是:“实验2-16-3:PCA测量脉冲宽度实验”。
-
-
- 实验内容
-
-
为了方便实验,本例使用Timer0定时200ms,Timer0中断服务函数中翻转P2.6的状态,即通过P2.6输出方波周期为400ms的方波。
使用PCA模块0捕获P2.6输出的方波, PCA配置如下:
- PCA模块0功能引脚:P1.7。
- 系统时钟:24MHz。
- PCA时钟源:系统时钟= 24Mhz。
- 上升沿捕获。
- 中断:开启计数器溢出中断和PCA模块0的匹配/捕获中断,中断优先级设置为2(较高级)。
每次测量后,通过串口输出测量得到的计数值和计数值对应的时间(单位毫秒)。
- 说明:关于Timer0部分的程序,读者可以阅读《第2-10讲:定时器和计数器》,这里不再赘述。
-
-
- 代码编写
-
-
- 新建一个名称为“pca.c”的文件及其头文件“pca.h”并保存到工程的“Source”文件夹,并将“pca.c”加入到Keil工程中的“SOURCE”组。
- 引用头文件
因为在“main.c”文件中使用了“pca.c”文件中的函数,所以需要引用下面的头文件“pca.h”。
代码清单:引用头文件
- //引用pca的头文件
- #include "pca.h"
- PCA初始化
本例配置PCA模块0功能引脚为P1.7,测量由Timer0产生的周期为400ms的方波,PCA模块0的时钟源为系统时钟的1/12,上升沿捕获,代码清单如下。
代码清单:PCA初始化和启动
- /***************************************************************************
- * 描 述 : PCA初始化
- * 入 参 : 无
- * 返回值 : 无
- **************************************************************************/
- void pca_init(void)
- {
- CCON = 0x00; //CF、CR、CCF1、CCF0位均清零
- P_SW1 &= ~0x30; //PCA模块0功能引脚选择P1.7
- /*--------------------PCA模式寄存器CMOD配置---------------------------
- 位7 位6 位5 位4 位3~位1 位0
- CIDL x x x CPS[2~0] BCF
- 0 x x x 100 0
- CIDL=0:空闲模式下仍然计数
- CPS[2~0]=000:PCA时钟源选择:系统时钟/12
- BCF=1:开启PCA计数器溢出中断
- -----------------------------END------------------------------------*/
- CMOD = 0x01;
- CL = 0x00; //PCA计数器赋初值
- CH = 0x00; //PCA计数器赋初值
- IP &= ~0x02; //中断优先级配置为2(较高优先级)
- IPH |= 0x02;
- /*------------------PCA模块0模式控制寄存器CCAPM0配置------------------
- 位7 位6 位5 位4 位3 位2 位1 位0
- x ECOM0 CCAPP0 CCAPN0 MAT0 TOG0 PWM0 ECCF0
- x 0 1 0 0 0 0 1
- ECOM0=0:关闭PCA模块0的比较功能
- CCAPP0=1:开启PCA模块0的上升沿捕获
- CCAPN0=0:关闭PCA模块0的下降沿捕获
- MAT0=0:关闭PCA模块0的匹配功能
- TOG0=0:关闭PCA模块0的高速脉冲输出功能
- PWM0=0:关闭PCA模块0的脉宽调制输出功能
- ECCF0=1:允许PCA模块0的匹配/捕获中断
- -----------------------------END------------------------------------*/
- CCAPM0 = 0x21;
- CCAP0L = 0x00; //PCA比较值寄存器赋初值
- CCAP0H = 0x00; //PCA比较值寄存器赋初值
- CR = 1; //启动PCA计数器阵列计数
- }
- 测量脉宽
为了便于计算脉宽,我们定义了如下3个变量分别用于保存两次捕获时读取的PCA计数值和他们的差值。这里,我们定义的变量是32位的,这是为了处理PCA计数器的溢出。PCA计数器是16位的,当其溢出后会从0开始计数,因此,溢出后将变量“ticks_current”的值加上0x00010000,再减去上次捕获时保存的计数值即可得到正确的差值。
代码清单:PCA初始化和启动
- volatile u32 tick_diff; //存储两次捕获之间的计数值的差值
- volatile u32 ticks_last; //记录上一次的捕获值
- volatile u32 ticks_current;//记录本次的捕获值
P1.7上出现上升沿或者PCA计数器溢出会触发PCA中断,PCA中断服务程序中读取PCA计数值,并计算差值,之后将测量完成标志“flag”置位,代码清单如下。
代码清单:PCA中断服务程序
- /***************************************************************************
- * 描 述 : PCA中断服务程序
- * 参 数 : 无
- * 返回值 : 无
- **************************************************************************/
- void pca_isr (void) interrupt 7
- {
- if(CF) //PCA计数器溢出
- {
- CF = 0; //清零PCA计数器溢出标志位
- ticks_current += 0x00010000; //当前计数值变量增加一个溢出的计数值
- }
- if(CCF0) //是PCA模块0匹配/捕获中断标志
- {
- CCF0 = 0; //清零PCA模块0匹配/捕获中断标志位
- ((u8 *)&ticks_current)[3] = CCAP0L; //16位计数值保存到ticks_current的低16位
- ((u8 *)&ticks_current)[2] = CCAP0H;
- tick_diff = ticks_current - ticks_last; //length保存的即为捕获的脉冲宽度量化值
- ticks_last = ticks_current & 0x0000FFFF; //备份上一次的捕获值,备份时去掉溢出部分
- ticks_current = 0; //当前计数值变量清零
- flag = 1; //测量完成标志置位
- }
- }
- 串口打印测量结果
测量完成后,通过串口打印出计数差值极其对应的时间,代码清单如下。
代码清单:打印测量结果
- /***************************************************************************
- * 描 述 : 串口打印计数值和脉宽
- * 参 数 : 无
- * 返回值 : 无
- **************************************************************************/
- void print_pw(void)
- {
- float time_ms;
- if(flag) //测量完成,打印测量结果
- {
- printf("counter value: %lu \r\n",length);
- //PCA时钟源使用的是:系统时钟(24MHz)/12 = 2MHz,脉宽对应的时间(秒)=length/2000000
- //这里打印的是毫秒,所以除以2000
- time_ms = (float)length / 2000;
- printf("pulse width: %.2f ms\r\n",time_ms);
- flag = 0;
- }
- }
- 主函数
主函数中配置引脚并调用Timer0和PCA初始化函数,完成对Timer0和PCA的初始化和启动。之后在主循环里面调用测量结果打印函数,该函数中会检查测量完成标志,如测量完成标志置位,测打印测量结果,代码清单如下。
代码清单:主函数
- /***************************************************************************
- * 描 述 : 主函数
- * 参 数 : 无
- * 返回值 : int类型
- **************************************************************************/
- int main(void)
- {
- P1M1 &= 0x7F; P1M0 &= 0x7F; //设置P1.7为准双向口
- P2M1 &= 0x3F; P2M0 &= 0x3F; //设置P2.6~P2.7为准双向口
- P3M1 &= 0xFE; P3M0 &= 0xFE; //设置P3.0为准双向口(串口1的RxD)
- P3M1 &= 0xFD; P3M0 |= 0x02; //设置P3.1为推挽输出(串口1的TxD)
- uart1_init(); //串口1初始化
- timer0_init(); //定时器0初始化
- pca_init();
- timer0_start(); //启动定时器0
- EA = 1; //使能总中断
- while(1)
- {
- print_pw(); //查询测量是否完成,完成后串口打印测量结果
- }
- }
-
-
- 硬件连接
-
-
本实验需要用拔掉指示灯D1的跳线帽,然后用杜邦线将P26连接到P17,如下图所示。
图10:硬件连接
-
-
-
- 实验步骤
-
-
- 解压“…\第3部分:配套例程源码”目录下的压缩文件“实验2-16-3:PCA测量脉冲宽度实验”,将解压后得到的文件夹拷贝到合适的目录,如“D\STC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程出现问题)。
- 双击“…\pca_capture\project”目录下的工程文件“pca_capture.uvproj”。
- 点击编译按钮编译工程,编译成功后生成的HEX文件“pca_capture.hex”位于工程的“…\pca_capture\Project\Object”目录下。
- 打开STC-ISP软件下载程序,下载使用内部IRC时钟,IRC频率选择:24MHz。
- 程序运行后,可以在串口调试助手接收窗口观察到脉宽测量结果,如下图所示。
图11:串口输出的测量的计数值和对应的时间
-
-
- 输出PWM实验
-
- 注:本节的实验是在“实验2-16-3:测量脉冲宽度实验”的基础上修改,本节对应的实验源码是:“实验2-16-4:PCA输出PWM实验”。
-
-
- 实验内容
-
-
配置PCA模块0工作于PWM模式, PCA配置如下:
- 系统时钟:24MHz。
- PCA时钟源:系统时钟= 24Mhz。
- 功能引脚:P1.7。
- 中断:不开启中断。
- PWM位数:6位。
- PWM频率:375KHz。
- PWM占空比:25%。
-
-
- 代码编写
-
-
- 新建一个名称为“pca.c”的文件及其头文件“pca.h”并保存到工程的“Source”文件夹,并将“pca.c”加入到Keil工程中的“SOURCE”组。
- 引用头文件
因为在“pca.c”文件中使用了“pca.c”文件中的函数,所以需要引用下面的头文件“pca.h”。
代码清单:引用头文件
- //引用pca的头文件
- #include " pca.h"
- PCA初始化
本例中,我们配置PCA模块0工作于PWM模式,输出频率为375KHz、占空比为25%的波形,代码清单如下。
代码清单:PCA初始化
- /***************************************************************************
- * 描 述 : PCA初始化
- * 入 参 : 无
- * 返回值 : 无
- **************************************************************************/
- void pca_init(void)
- {
- CCON = 0x00; //CF、CR、CCF1、CCF0位均清零
- P_SW1 &= ~0x30; //PCA模块0功能引脚选择P1.7
- /*--------------------PCA模式寄存器CMOD配置---------------------------
- 位7 位6 位5 位4 位3~位1 位0
- CIDL x x x CPS[2~0] BCF
- 0 x x x 100 0
- CIDL=0:空闲模式下仍然计数
- CPS[2~0]=100:PCA时钟源选择:系统时钟
- BCF=0:关闭PCA计数器溢出中断
- -----------------------------END------------------------------------*/
- CMOD = 0x08;
- CL = 0x00; //PCA计数器赋初值
- CH = 0x00; //PCA计数器赋初值
- /*------------------PCA模块0模式控制寄存器CCAPM0配置------------------
- 位7 位6 位5 位4 位3 位2 位1 位0
- x ECOM0 CCAPP0 CCAPN0 MAT0 TOG0 PWM0 ECCF0
- x 1 0 0 0 0 1 0
- ECOM0=1:允许PCA模块0的比较功能
- CCAPP0=0:关闭PCA模块0的上升沿捕获
- CCAPN0=0:关闭PCA模块0的下降沿捕获
- MAT0=0:关闭PCA模块0的匹配功能
- TOG0=0:关闭PCA模块0的高速脉冲输出功能
- PWM0=1:开启PCA模块0的脉宽调制输出功能
- ECCF0=0:关闭PCA模块0的匹配/捕获中断
- -----------------------------END------------------------------------*/
- CCAPM0 = 0x42;
- //PCA模块0工作于6位PWM,由此可以计算出PWM频率=24Mhz/64 = 375KHz
- PCA_PWM0 &= 0xBF;
- PCA_PWM0 |= 0x80;
- //PWM占空比为50%
- //CCAP0L = 0x20; //PCA模块0的PWM比较值
- //CCAP0H = 0x20; //PCA模块0的PWM重装值
- //PWM占空比为25%
- CCAP0L = 0x10; //PCA模块0的PWM比较值
- CCAP0H = 0x10; //PCA模块0的PWM重装值
- CR = 1; //启动PCA计数器阵列计数
- }
- 主函数
主函数中初始化PCA及其功能引脚,PCA计数启动后,PWM输出完全由硬件完成,无需软件干预,代码清单如下。
代码清单:主函数
- /**************************************************************************************
- * 描 述 : 主函数
- * 参 数 : 无
- * 返回值 : int类型
- **************************************************************************************/
- int main(void)
- {
- P1M1 &= 0x7F; P1M0 &= 0x7F; //设置P1.7为准双向口
- pca_init(); //初始化并启动PCA计数
- while(1)
- {
- }
- }
-
-
- 硬件连接
-
-
本实验通过P1.7输出频率为375KHz、占空比为25%的PWM波形,P1.7引脚的位置如下图所示。
图12:硬件连接
-
-
-
- 实验步骤
-
-
- 解压“…\第3部分:配套例程源码”目录下的压缩文件“实验2-16-4:PCA输出PWM实验”,将解压后得到的文件夹拷贝到合适的目录,如“D\STC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程出现问题)。
- 双击“…\pca_pwm\project”目录下的工程文件“pca_pwm.uvproj”。
- 点击编译按钮编译工程,编译成功后生成的HEX文件“pca_pwm.hex”位于工程的“…\pca_pwm\Project\Object”目录下。
- 打开STC-ISP软件下载程序,下载使用内部IRC时钟,IRC频率选择:24MHz。
- 程序运行后,用示波器测量P1.7,可以观察P1.7输出频率为375KHz、占空比为25%的PWM波形。