stc89c52开发板遥控器解码 红外线发射 内置 eeprom 存储 串口显示编码

单片机,大概三年前,就买了一本 《爱上单片机》 最后就学会,用面包板了,编程书上基本没讲。 看原理图,看时序图,看数据手册, 都没讲。

而且书上自带的代码写的很烂。 1,缩近控制不好 2,命名混乱 3,做if 的时候 不变的常量放在左侧,这是很基本的约定 。。。

最后,还是什么也没有学会。

直到去年,开始学 ARM 了。 学完了 ARM 前面发的(s3c2440)以后, 在回头看单片机,发现单片机真是,简单的不得了!

但是也发现,单片机,不如 ARM 功能强大。速度也慢。很多控制器,没有,要用GPIO 来模拟,但是单片机的时序太精确的又不好做。定时器就那么几个。

ARM 上面,有linux 内核,里面的 Time 就很好用了。 HCLK 100M ,能达到 ns 级。

回顾结束了, 下面说这个开发板。 这个板子,真不错,各个IO 都用上了,外部中断,温度传感器,红外线一体接收头,按键,数码管,峰鸣器,LCD 接口,

流水灯了,USB 转口串口,都有了,体织还小。

下面的源码,用到的知识, UART 串口,发送字符串, 结构体定义数据类型,指针,定时器,中断,EEPROM 读取 擦除 写入 , 按键仿抖,知识点挺多的。

制作期间,遇到一些问题,有通过逻辑分析仪解决的,有从网上找源码修改的。 网上找的源码中,有的有不少错误,或是多余的代码,本人都对比数据手册,和

逻辑分析仪, 逐一,调试重写。保证代码是尽量最小。

为了能发射 红外线,把 LED 发光管,焊接在,峰鸣器的两端, 原因, 普通的 IO 口,拉力太小,而这款 stc89c52 不支持 推挽输出。

而峰鸣器这里,正好有个三极管来驱动。

题外话: 红外线LED 的耐压值 和 电流测试, 之前用 5V 直接点过,红色发光二极管,烧掉过,高亮度的白光二极管,试验过,烧不坏。

在网上找了几个人说,红外线的LED 最大可以接12V ,所以就试着接到5V上,结果,烧黑了。(网上的事,不可信)

峰鸣器这里三极管来驱动可以看到,发射极上并没有接限流电阻, 所以我这里用了一个,精密可调电位器,调到大约80欧。

接入。防止工作的时候烧坏。

测试输出 遥控器编码,到串口。 

下面是原理图

本编码,能识别标准的 NEC 码,经过测试,发现家中的 有线机顶盒是这种编码。 空调,电视机,并不适用,需要做修改。

标准的 NEC 码规范:

首次发送的是9ms的高电平脉冲,其后是4.5ms的低电平,接下来就是8bit的地址码(从低有效位开始发),而后是8bit的地址码的反码(主要是用于校验是否出错)。然后是8bit 的命令码(也是从低有效位开始发),而后也是8bit 的命令码的反码。其“0”为载波发射0.56ms,不发射0.565ms,其“1”为载波发射0.56ms,不发射1.69ms。

keil uvision4 工程

UART 串口相关:

uart.h

1 #include "IR.h"
2 #ifndef __UART__
3 #define __UART__
4 void init_uart();
5 void send_hex(unsigned char);
6 void send_str(unsigned char *);
7 void send_code(IR_CODE);
8 #endif

uart.c

 1 #include <reg52.h>
 2 #include "uart.h"
 3 void init_uart()
 4 {
 5     //定时器1 溢出决定波特率
 6     EA  = 1;      //总中断开
 7     TMOD |= 1<<5; //定时器1 自动重装模式
 8     TH1 = 0xfd;   //当TL1中溢出时 TH1 的值自动重装进去
 9     TL1 = 0xfd;   //省去一个中断处理函数
10     TR1 = 1;      //开始计数
11     SM0 = 0;
12     SM1 = 1;      //8bit UART 波特率可变
13 }
14 
15 void send_str(unsigned char *str)
16 {
17     while(*str)
18     {
19         SBUF = *str;
20         while(! TI);
21         TI = 0;
22         str++;
23     }
24 }
25 
26 void send_hex(unsigned char hex)
27 {
28     SBUF = hex;
29     while(! TI);
30     TI = 0;
31 }
32 
33 void send_code(IR_CODE ir_code)
34 {
35     unsigned char c;
36     unsigned char *p;
37     int i,j;
38     p = (unsigned char *)&ir_code;
39     send_str("custom:0x");
40     for(i=0; i<4; i++)
41     {
42         if(2 == i)
43         {
44             send_str(" code:0x");
45         }
46         for(j=1; j>=0; j--)
47         {
48             c = (*p>>(4*(j))) & 0xf;
49             if(0<=c && c<=9)
50             {
51                 send_hex('0' + c);
52             }
53             else
54             {
55                 send_hex('A' + c - 0xa);
56             }
57         }
58         p++;
59     }
60     send_str("\r\n");
61 }

EEPROM 相关

eeprom.h

 1 #include <reg52.h>
 2 #ifndef __EEPROM__
 3 #define __EEPROM__
 4 /**
 5  * STC90C52 结尾是 90C 
 6  * EEPROM 5K
 7  * SRAM 215字节
 8  * 每个扇区512字节  5K / 512 = 10 个扇区
 9  * 扇区首地址 2000h 结束地址 33ffh
10  */
11 
12 /* FLASH 首地址 */
13 #define BASE_ADDR 0x2000
14 
15 /* 特殊功能寄存器声明 */
16 sfr ISP_DATA  = 0xe2;  
17 sfr ISP_ADDRH = 0xe3;    
18 sfr ISP_ADDRL = 0xe4;
19 sfr ISP_CMD   = 0xe5;
20 sfr ISP_TRIG  = 0xe6;     
21 sfr ISP_CONTR = 0xe7;
22 
23 /* 定义命令字节 */
24 #define CMD_Read    0x01 //字节读数据命令     
25 #define CMD_Prog    0x02 //字节编程数据命令     
26 #define CMD_Erase   0x03 //扇区擦除数据命令
27 #define En_Wait_ISP 1<<7 | 1<<1  //设置等待时间 ,并使能ISP/IAP 11.0592 晶振
28 
29 void lock_ISP();
30 void erase(unsigned int);
31 unsigned char read(unsigned int);
32 void prog(unsigned int, unsigned char);
33 
34 #endif

eeprom.c

 1 #include "eeprom.h"
 2 
 3 /* 执行完操作以后安全锁 */
 4 void lock_ISP()
 5 {
 6     ISP_CONTR = 0;
 7     ISP_CMD = 0;
 8     ISP_TRIG = 0;
 9     ISP_ADDRH = 0xff;
10     ISP_ADDRL = 0xff;
11 }
12 
13 /* 擦除指定地址所在的整个扇区  */
14 void erase(unsigned int addr)
15 {
16     addr += BASE_ADDR;
17     
18     //发送地址
19     ISP_ADDRH = addr >> 8;
20     ISP_ADDRL = addr;
21     
22     //发送解锁命令
23     ISP_CONTR = En_Wait_ISP;
24     
25     //发擦除命令
26     ISP_CMD = CMD_Erase;
27     
28     //发送触发命令
29     ISP_TRIG = 0x46;
30     ISP_TRIG = 0xB9;
31     
32     //最后锁定 ISP 仿误操作
33     lock_ISP();
34 }
35 
36 unsigned char read(unsigned int addr)
37 {
38     addr += BASE_ADDR;
39     
40     //发送地址
41     ISP_ADDRH = addr >> 8;
42     ISP_ADDRL = addr;
43     
44     //发送解锁命令
45     ISP_CONTR = En_Wait_ISP;
46     
47     //发读命令
48     ISP_CMD = CMD_Read;
49     
50     //发送触发命令
51     ISP_TRIG = 0x46;
52     ISP_TRIG = 0xB9;
53     
54     //最后锁定 ISP 仿误操作
55     lock_ISP();
56     
57     return ISP_DATA;
58 }
59 
60 void prog(unsigned int addr, unsigned char dat)
61 {
62     addr += BASE_ADDR;
63     
64     //发送要保存的数据
65     ISP_DATA = dat;
66     
67     //发送地址
68     ISP_ADDRH = addr >> 8;
69     ISP_ADDRL = addr;
70     
71     //发送解锁命令
72     ISP_CONTR = En_Wait_ISP;
73     
74     //发编程命令
75     ISP_CMD = CMD_Prog;
76     
77     //发送触发命令
78     ISP_TRIG = 0x46;
79     ISP_TRIG = 0xB9;
80     
81     //最后锁定 ISP 仿误操作
82     lock_ISP();
83 }

延时: 新版本的 STC 下载工具上,有个延时计算器,精度不错。以下代码来源于它。

delay.h

1 #ifndef __DELAY__
2 #define __DELAY__
3 void delay700us();
4 void delayms(int);
5 #endif

delay.c

 1 #include <intrins.h>
 2 #include "delay.h"
 3 void delayms(int ms) //@11.0592MHz
 4 {
 5     unsigned char i, j;
 6     while(ms--)
 7     {
 8         _nop_();
 9         i = 2;
10         j = 199;
11         do
12         {
13             while (--j);
14         } 
15         while (--i);
16     }
17 }
18 
19 void delay700us() //@11.0592MHz
20 {
21     unsigned char i, j;
22     _nop_();
23     i = 2;
24     j = 61;
25     do
26     {
27         while (--j);
28     } while (--i);
29 }

重头戏 红外编码 与 解码,这2个写在一起了,使用的时候,可以很方便的分离开。

IR.h

 1 #include <reg52.h>
 2 #include "delay.h"
 3 #ifndef __IR__
 4 #define __IR__
 5 //公用
 6 typedef struct {
 7     unsigned char custom_height;
 8     unsigned char custom_lower;
 9     unsigned char ir_code;
10     unsigned char re_ir_code;
11 } IR_CODE, *pIR_CODE;
12 
13 //接收
14 sbit IR     = P3 ^ 2;//红外线一体接收头 OUT 
15 
16 void init_IR();
17 IR_CODE IR_recv();
18 
19 //发射延时
20 #define m9   (65536-9000)        //约9mS
21 #define m4_5 (65536-4500)        //约4.5mS
22 #define m1_6 (65536-1630)        //约1.65mS
23 #define m_65 (65536-580)         //约0.65mS
24 #define m_56 (65536-560)         //约0.56mS
25 #define m40  (65536-40000)       //约40mS
26 #define m56  (65536-56000)       //56mS
27 #define m2_25 (65536-2250)        //约2.25mS
28 
29 sbit LaunchLED = P0 ^ 4;//红外线发射LED 接PNP三极管基极, 经过试验不使用三极管时有效距离是40厘米, 可见 IO 下拉电流很小
30 void IR_launch(IR_CODE);
31 void IR_launch_time(bit,unsigned int);
32 void IR_launch_frame(unsigned char);
33 #endif

IR.c

  1 #include <string.h>
  2 #include "IR.h"
  3 
  4 void init_IR()
  5 {
  6     //接收
  7     EA  = 1;  //总中断开
  8     EX0 = 1;  //IR 接收头使用外部中断0 来处理
  9     IT0 = 1;  //下降沿触发
 10     
 11     //发射
 12     TMOD |= 0x01; //T0 16位工作方式
 13     LaunchLED = 1;      //发射端口常态为高电平
 14 }
 15 
 16 //发射
 17 void IR_launch(IR_CODE ir_code)
 18 {
 19     IR_launch_time(1, m9);   //高电平9mS
 20     IR_launch_time(0, m4_5); //低电平4.5mS
 21     
 22     /*┈ 发送4帧数据┈*/
 23     IR_launch_frame(ir_code.custom_height);
 24     IR_launch_frame(ir_code.custom_lower);
 25     IR_launch_frame(ir_code.ir_code);
 26     IR_launch_frame(ir_code.re_ir_code);                                                                             
 27     
 28     /*┈┈ 结束码 ┈┈*/
 29     IR_launch_time(1, m_65);
 30     IR_launch_time(0, m40);
 31 }
 32 
 33 //发送 1 帧数据
 34 void IR_launch_frame(unsigned char frame)
 35 { 
 36     char i = 0;
 37     for(i=0; i<8; i++)  //循环8次移位
 38     {     
 39         IR_launch_time(1, m_65);  //高电平0.65ms
 40         if(frame >>i & 0x1)
 41             IR_launch_time(0, m1_6); //发送最低位
 42         else
 43             IR_launch_time(0, m_56);
 44      }
 45 }
 46 
 47 //38KHz脉冲发射 + 延时程序
 48 void IR_launch_time(bit status,unsigned int t)
 49 {
 50   TH0 = t>>8; //输入T0初始值
 51   TL0 = t;
 52   TF0 = 0;    //清0
 53   TR0 = 1;    //启动定时器0
 54   if(0 == status)
 55   {
 56     //BT=0时不发射38KHz脉冲只延时;BT=1发射38KHz脉冲且延时;
 57     while(! TF0);  
 58   }
 59   else
 60   {      
 61       while(1)
 62       {
 63           /**
 64            * 38KHz脉冲,占空比5:26
 65            * 以下是逻辑分析仪测试结果
 66            * 3:23us  识别正常
 67            * 6:23us  识别正常
 68            * 10:23us 识别失败
 69            * 12:23us 识别失败
 70            * 16:23us 识别失败
 71            */
 72           LaunchLED = 0;
 73           if(TF0)break;
 74           if(TF0)break;
 75           LaunchLED = 1;
 76           if(TF0)break;
 77           if(TF0)break;
 78           if(TF0)break;
 79           if(TF0)break;
 80           if(TF0)break;
 81           if(TF0)break;
 82           if(TF0)break;
 83           if(TF0)break;
 84           if(TF0)break;
 85           if(TF0)break;
 86          }
 87     }     
 88     TR0=0; //关闭定时器0
 89     TF0=0; //标志位溢出则清0
 90     LaunchLED =1; //脉冲停止后,发射端口常态为高电平
 91 }
 92 
 93 //接收
 94 IR_CODE IR_recv()
 95 {
 96     /**
 97      * 数据格式:
 98      * 9ms低电平 4.5ms高电平 头部
 99      * 定制高位  定制低位  数据码  数据反码
100      * 1: 560us低电平 1680us高电平  0.56ms 1.7ms 
101      * 0: 560us低电平 560us高电平   0.56ms 0.56ms
102      */
103     IR_CODE ir_code;
104     unsigned char i,k;
105     unsigned char *ir_char_p;
106     unsigned char ir_char;
107     ir_char_p = (unsigned char *)&ir_code;
108     
109     //栈分配 IR_CODE 竟然还要手动清0
110     memset(&ir_code, 0, 4);
111     
112     delayms(5); //9ms 内必须是低电平否则就不是头信息
113     if(0 == IR)
114     {
115         while(! IR);//等待4.5ms的高电平
116         
117         //检测是否是 2.5ms 重码 
118         delayms(3);
119         if(1 == IR)
120         {
121             //k 4位编码
122             for(k=0; k<4; k++)
123             {
124                 ir_char = 0x0;
125                 //i 每一个编码的 8bit
126                 for(i=0;i<8;i++)
127                 {
128                     while(IR);     //等待变为低电平时
129                     while(! IR);   //等待变为高电平后
130                     delay700us();  //休眠700us 后读值
131                     ir_char |= (char)IR << i;//先存低位
132                     //使用下面指针操作就会失败出现不稳定
133                     //*ir_char_p |= (char)IR << i;
134                 }
135                 *ir_char_p = ir_char;
136                 ir_char_p++;
137             }
138             
139             //计算反码 code码是否正确
140             if(ir_code.ir_code != ~(ir_code.re_ir_code))
141             {
142                 memset(&ir_code, 0, 4);
143             }
144         }
145     }
146     return ir_code;
147 }

主操作函数,在这个里面,先是从 EEPROM 中读到了,设置的值。

main.c 

  1 #include <reg52.h>
  2 #include <intrins.h>
  3 #include <string.h>
  4 #include "uart.h"
  5 #include "delay.h"
  6 #include "IR.h"
  7 #include "eeprom.h"
  8 
  9 IR_CODE read_ir_code(unsigned char);
 10 void save_block_ir_code();
 11 void save_ir_code(IR_CODE ir_code,unsigned int addr);
 12 
 13 //全局接收红外线信号存放
 14 IR_CODE global_ir_code;
 15 
 16 //从eeprom 中读取 按键值
 17 IR_CODE k3_ir_code;
 18 IR_CODE k4_ir_code;
 19 IR_CODE k5_ir_code;
 20 IR_CODE k6_ir_code;
 21 
 22 //3个按键
 23 sbit K3 = P3 ^ 5;
 24 sbit K4 = P3 ^ 4;
 25 sbit K5 = P3 ^ 3;
 26 
 27 #ifdef DEBUG
 28 //main 中 EX1 = 1;
 29 //外部中断1 按下时发送红外线信号
 30 void infrared_led_int1() interrupt 2
 31 {
 32     EX1 = 0;
 33     IR_launch(global_ir_code);
 34     EX1 = 1;
 35 }
 36 #endif
 37 
 38 void main()
 39 {
 40     //总中断开关
 41     EA  = 1;
 42     init_uart();
 43     init_IR();
 44     
 45     //全局接收红外线清0
 46     memset(&global_ir_code, 0, 4);
 47     
 48     //从eeprom 中读取 按键值
 49     k3_ir_code = read_ir_code(0);
 50     k4_ir_code = read_ir_code(4);
 51     k5_ir_code = read_ir_code(8);
 52     
 53     while(1)
 54     {
 55         //按键按下
 56         if(0 == K3)
 57         {
 58             //去抖动后
 59             delayms(30);
 60             if(0 == K3)
 61             {
 62                 //关中断 中断打开时,时钟可能不准
 63                 EA  = 0;
 64                 
 65                 //如果有全局接收 则存入 eeprom
 66                 if(global_ir_code.ir_code)
 67                 {
 68                     k3_ir_code = global_ir_code;
 69                     save_block_ir_code();
 70                     memset(&global_ir_code, 0, 4);
 71                     
 72                     //闪灯2次表示 存入成功
 73                     P1 = 0x0;
 74                     delayms(300);
 75                     P1 = 0xff;                    
 76                     delayms(300);
 77                     P1 = 0x0;
 78                     delayms(300);
 79                     P1 = 0xff;
 80                 }
 81                 else
 82                 {
 83                     IR_launch(k3_ir_code);
 84                     
 85                     //闪灯1次表示 发射成功
 86                     P1 = 0x0;
 87                     delayms(300);
 88                     P1 = 0xff;    
 89                 }
 90                 //开中断
 91                 EA  = 1;
 92             }
 93         }
 94         //按键按下
 95         if(0 == K4)
 96         {
 97             //去抖动后
 98             delayms(30);
 99             if(0 == K4)
100             {
101                 //关中断 中断打开时,时钟可能不准
102                 EA  = 0;
103                 
104                 //如果有全局接收 则存入 eeprom
105                 if(global_ir_code.ir_code)
106                 {
107                     k4_ir_code = global_ir_code;
108                     save_block_ir_code();
109                     memset(&global_ir_code, 0, 4);
110                     
111                     //闪灯2次表示 存入成功
112                     P1 = 0x0;
113                     delayms(300);
114                     P1 = 0xff;                    
115                     delayms(300);
116                     P1 = 0x0;
117                     delayms(300);
118                     P1 = 0xff;
119                 }
120                 else
121                 {
122                     IR_launch(k4_ir_code);
123                     
124                     //闪灯1次表示 发射成功
125                     P1 = 0x0;
126                     delayms(300);
127                     P1 = 0xff;    
128                 }
129                 //开中断
130                 EA  = 1;
131             }
132         }
133         //按键按下
134         if(0 == K5)
135         {
136             //去抖动后
137             delayms(30);
138             if(0 == K5)
139             {
140                 //关中断 中断打开时,时钟可能不准
141                 EA  = 0;
142                 
143                 //如果有全局接收 则存入 eeprom
144                 if(global_ir_code.ir_code)
145                 {
146                     k5_ir_code = global_ir_code;
147                     save_block_ir_code();
148                     memset(&global_ir_code, 0, 4);
149                     
150                     //闪灯2次表示 存入成功
151                     P1 = 0x0;
152                     delayms(300);
153                     P1 = 0xff;                    
154                     delayms(300);
155                     P1 = 0x0;
156                     delayms(300);
157                     P1 = 0xff;
158                 }
159                 else
160                 {
161                     IR_launch(k5_ir_code);
162                     
163                     //闪灯1次表示 发射成功
164                     P1 = 0x0;
165                     delayms(300);
166                     P1 = 0xff;    
167                 }
168                 //开中断
169                 EA  = 1;
170             }
171         }
172     }
173 }
174 
175 void IR_int() interrupt 0
176 {
177     IR_CODE ir_code;
178     EX0 = 0;//处理过程中 关中断
179     ir_code = IR_recv();
180     if(ir_code.ir_code)
181     {
182         //点亮P1 显示收到了编码
183         P1 = 0x0;
184         delayms(300);
185         P1 = 0xff;
186         
187         //发送到串口
188         send_code(ir_code);
189         
190         //给全局编码赋值
191         global_ir_code = ir_code;
192     }
193     EX0 = 1;//处理结束后 开中断
194 }
195 
196 //读取 eeprom 中的值
197 IR_CODE read_ir_code(unsigned char addr)
198 {
199     IR_CODE ir_code;
200     ir_code.custom_height = read(addr);
201     ir_code.custom_lower  = read(addr+1);
202     ir_code.ir_code       = read(addr+2);
203     ir_code.re_ir_code    = read(addr+3);
204     return ir_code;
205 }
206 
207 //保存所有编码
208 void save_block_ir_code()
209 {
210     erase();
211     save_ir_code(k3_ir_code, 0);
212     save_ir_code(k4_ir_code, 4);
213     save_ir_code(k5_ir_code, 8);
214 }
215 
216 //保存一个编码
217 void save_ir_code(IR_CODE ir_code,unsigned int addr)
218 {
219     prog(addr,   ir_code.custom_height);
220     prog(addr+1, ir_code.custom_lower);
221     prog(addr+2, ir_code.ir_code);
222     prog(addr+3, ir_code.re_ir_code);
223 }

最后是使用说明:

先打开串口,工具,推荐用  putty ,开机后,对着红外线一体化接收头,按遥控器, 这里putty 上就会显示出来,编码的十六进制。 你可以把它保存到电脑上,以后遥控器丢了,也不怕。

设置 3个铵键的编码方法是: 开机,对着接收头,按遥控器, 这里除了串口上有输出外,流水灯会全部亮一下。 这时,按下 K3 K4 K5 中的其中一个, 那么这个编码就被保存

到了 单片机内部的 EEPROM 中,之后, 流水灯会闪二下。 说明设置完成。 三个按键,都是如此设置。 

特别说明, 此编码,解码,仅用于 标准NFC 码规范, 不是所有遥控器都能识别。 后期,博主会更新加入识别其它编码的功能。 

原文地址:https://www.cnblogs.com/ningci/p/5425771.html