一 PCA介绍
1.1 PCA
可编程计数器阵列(PCA)提供增强的定时器功能,与标准8051计数器/定时器相比,它需要较少的CPU干预。PCA包含一个专用的16位计数器/定时器和5个16位捕捉/比较模块。每个捕捉/比较模块有其自己的I/O线(CEXn)。当被允许时,I/O线通过交叉开关连到端口I/O。
计数器/定时器由一个可配置的时基信号驱动,可以在四个输入源中选择时基信号:系统时钟12分频、系统时钟4分频、定时器0溢出或ECI线上的外部时钟信号。对PCA的配置和控制是通过系统控制器的特殊功能寄存器来实现的。PCA的基本原理框图下图。

1.2 捕捉/比较模块
每个模块都可被配置为独立工作,有四种工作方式:边沿触发捕捉、软件定时器、高速输出和脉宽调制器。每个模块在CIP-51系统控制器中都有属于自己的特殊功能寄存器(SFR)。这些寄存器用于配置模块的工作方式和与模块交换数据。
PCA0CPMn寄存器用于配置PCA捕捉/比较模块的工作方式,下表概述了模块工作在不同方式时该寄存器各位的设置情况。置‘1’ PCA0CPMn寄存器中的ECCFn位将允许模块的CCFn中断。
注意:要使单独的CCFn中断得到响应,必须先整体允许PCA0中断。通过将EA位(IE.7)和EPCA0位(EIE1.3)设置为逻辑1来整体允许PCA0中断。

1.3 边沿触发的捕捉方式
在该方式,CEXn引脚上出现的有效电平变化导致PCA捕捉PCA计数器/定时器的值并将其装入到对应模块的16位捕捉/比较寄存器(PCA0CPLn和PCA0CPHn)。
PCA0CPMn寄存器中的CAPNn位用于选择触发捕捉的电平变化类型:低电平到高电平(正沿)、高电平到低电平(负沿)或任何变化(正沿或负沿)。
当捕捉发生时,PCA0CN中的捕捉/比较标志(CCFn)被置为逻辑1并产生一个中断请求(如果CCF中断被允许)。
当CPU转向中断服务程序时,CCFn位不能被硬件自动清除,必须用软件清0。

二、红外编码
2.1数据格式
数据格式包括了引导码、用户码、数据码和数据码反码,编码总占32 位。数据反码是数据码反相后的编码,编码时可用于对数据的纠错。注意:第二段的用户码也可以在遥控应用电路中被设置成第一段用户码的反码。

2.2位定义
用户码或数据码中的每一个位可以是位‘1’,也可以是位‘0’。区分‘0’和‘1’是利用脉冲的时间间隔来区分,这种编码方式称为脉冲位置调制方式,英文简写PPM。

这里只介绍了红外的一些简单的知识,还有其他重复码等知识自行学习。
三、实现
3.1 准备
由于项目中的红外遥控器是定制的,首先用示波器观测下某一按键的波形图。

从图中可知,去除掉起始码,我们可以定义高电平短的是逻辑0,,高电平长点的是逻辑1。这样读到的32位数据为0111 0100 1000 1011 1000 1000 0111 0111
数据的分析:两两一组,一共四组。第一组是第二组的反码;第三组是第四组的反码。
3.2 软件实现

1 #define _9ms_val 0x40CC //9ms
2 #define _9ms_min 0x3900 //8.5ms
3 #define _9ms_max 0x4466 //9.5ms
4 #define _45ms_val 0x2066 //4.5ms
5 #define _45ms_min 0x1CCC //4ms
6 #define _45ms_max 0x2400 //5ms
7 #define _56ms_val 0x408 //0.56ms
8 #define _56ms_min 0x34F //0.46ms
9 #define _56ms_max 0x4C0 //0.66ms
10 #define _169ms_val 0xC2B //1.69ms
11 #define _169ms_min 0xACC //1.5ms
12 #define _169ms_max 0xC87 //1.74ms
13 #define _225ms_val 0x1033 //2.25ms
14 #define _225ms_min 0xE66 //2ms
15 #define _225ms_max 0x1200 //2.5ms
16 #define posedge 0x21 //正沿捕捉功能使能
17 #define engedge 0x11 //负沿捕捉功能使能

上面一部分的时间是这样算出来的,系统采用的时钟是22.1184MHz。在下面的初始化中,PCA使用的是系统时钟的十二分之一,即是1.8432MHz。拿9ms作说明,

得出 x=16588.8 ,转换成16进制数就是0x40CC。
最后两个宏参照图2得来,捕捉上升沿还是下降沿。

1 void init_pca(void) {
2 EIE1 |= 0x08; //允许PCA0的中断请求
3 PCA0MD = 0x1; //系统时钟的12分频 CF 中断
4 PCA0CN = 0x0;
5 PCA0CPM0 = 0x0;
6 PCA0CPM0 = engedge;
7 PCA0L = 0x0;
8 PCA0H = 0x0;
9 conut = 0x0;
10 state = 0x0;
11 rep_fg = 0;
12 }

初始化完成对变量的初始化,以及首先捕获下降沿。

1 #ifdef eclipse
2 void PCA0_ISR(void) /* 可编程计数器阵列0 */
3 #else
4 void PCA0_ISR (void) interrupt EPCA0_VECTOR /* 可编程计数器阵列0 */
5 #endif
6 {
7 EA = 0;
8 switch (state) {
9 case 0x0: //开始第一个下降沿
10 PCA0L = PCA0H = 0x0;
11 CR = 1; //允许PCA计数器
12 PCA0CPM0 = posedge; //设置捕获高电平
13 conut = 0x0;
14 state = 0x1;
15 break;
16 /*----------------------------------------------------------------------------*/
17 case 0x1: //低电平宽度 9ms
18 CR = 0;
19 TIM_L_VAL.c[1] = PCA0CPL0;
20 TIM_L_VAL.c[0] = PCA0CPH0;
21 PCA0L = PCA0H = 0x0;
22 PCA0CPM0 = engedge; //设置捕获低电平
23 if ((TIM_L_VAL.i > _9ms_min) && (TIM_L_VAL.i < _9ms_max)) { //9ms比较
24 CR = 1;
25 state = 0x2;
26 } else
27 state = 0x0; //不是9ms脉冲
28 break;
29 /*----------------------------------------------------------------------------*/
30 case 0x2: //高电平宽度 4.5ms 2.25
31 TIM_H_VAL.c[1] = PCA0CPL0;
32 TIM_H_VAL.c[0] = PCA0CPH0;
33 CR = 0;
34 PCA0L = PCA0H = 0x0;
35 conut = 0x0;
36 if ((TIM_H_VAL.i > _45ms_min) && (TIM_H_VAL.i < _45ms_max)) //4.5ms比较
37 {
38 PCA0CPM0 = posedge; //设置捕获高电平
39 CR = 1;
40 state = 0x3;
41 } else {
42 if ((TIM_H_VAL.i > _225ms_min) && (TIM_H_VAL.i < _225ms_max)) //2.25ms比较
43 {
44 PCA0CPM0 = posedge; //设置捕获高电平
45 CR = 1;
46 rep_fg = 1; //重发标志
47 state = 0x3;
48 } else //干扰信号,重新开始
49 {
50 PCA0CPM0 = engedge; //设置捕获低电平
51 state = 0x0;
52 }
53 }
54 break;
55 /*----------------------------------------------------------------------------*/
56 case 0x3: //低电平宽度 0.56ms
57 TIM_L_VAL.c[1] = PCA0CPL0;
58 TIM_L_VAL.c[0] = PCA0CPH0;
59 CR = 0;
60 PCA0L = PCA0H = 0x0;
61 PCA0CPM0 = engedge;
62 if (conut == 32) {
63 if ((key.c[0] == ~key.c[1]) && (key.c[2] == ~key.c[3])
64 && (key.c[3] == use_code)) {
65 key_code = key.c[1];
66 key_bit = 1;
67 isr_send_signal(DIS_Handle);
68 }
69 conut = 0x0;
70 state = 0x0;
71 } else {
72 if ((TIM_L_VAL.i > _56ms_min) && (TIM_L_VAL.i < _56ms_max)) //0.56ms比较
73 {
74 if (rep_fg) {
75 state = 0x0; //重发码
76 rep_fg = 0;
77 //isr_send_signal(DIS_Handle);
78 } else {
79 CR = 1;
80 state = 0x4;
81 }
82 } else
83 state = 0x0;
84 }
85 break;
86 /*----------------------------------------------------------------------------*/
87 case 0x4: //高电平宽度
88 TIM_H_VAL.c[1] = PCA0CPL0;
89 TIM_H_VAL.c[0] = PCA0CPH0;
90 CR = 0;
91 PCA0L = PCA0H = 0x0;
92 key.l = key.l >> 1;
93 if ((TIM_H_VAL.i > _169ms_min) && (TIM_H_VAL.i < _169ms_max))//1.69ms比较
94 key.l |= 0x80000000;
95 if ((TIM_H_VAL.i > _56ms_min) && (TIM_H_VAL.i < _56ms_max)) //0.56ms比较
96 key.l &= ~0x80000000;
97 CR = 1;
98 PCA0CPM0 = posedge; //设置捕获高电平
99 state = 0x3;
100 conut++;
101 break;
102 /*----------------------------------------------------------------------------*/
103 default:
104 CR = 0;
105 PCA0L = PCA0H = 0x0;
106 PCA0CPM0 = engedge;
107 state = 0x0;
108 break;
109 /*----------------------------------------------------------------------------*/
110 }
111 CCF0 = 0;
112 EA = 1;
113 }

状态0产生后,开始计数,并设置捕获上升沿。捕获到上升沿,进入到状态1,分析是否是9ms。
状态3和状态4是核心部分,接收32位有效数据,并且判断是否有效。当为有效按键时,会发送一个信号,显示任务会等待这个信号。
代码的第92行到第96行,接收位的处理,先接收到的位存储在字节的最低位。
总的来说,根据波形合理的设置捕获方式,判断起始码,接收32位数据。