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

而且书上自带的代码写的很烂。 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

 #include "IR.h"
#ifndef __UART__
#define __UART__
void init_uart();
void send_hex(unsigned char);
void send_str(unsigned char *);
void send_code(IR_CODE);
#endif

uart.c

 #include <reg52.h>
#include "uart.h"
void init_uart()
{
//定时器1 溢出决定波特率
EA = ; //总中断开
TMOD |= <<; //定时器1 自动重装模式
TH1 = 0xfd; //当TL1中溢出时 TH1 的值自动重装进去
TL1 = 0xfd; //省去一个中断处理函数
TR1 = ; //开始计数
SM0 = ;
SM1 = ; //8bit UART 波特率可变
} void send_str(unsigned char *str)
{
while(*str)
{
SBUF = *str;
while(! TI);
TI = ;
str++;
}
} void send_hex(unsigned char hex)
{
SBUF = hex;
while(! TI);
TI = ;
} void send_code(IR_CODE ir_code)
{
unsigned char c;
unsigned char *p;
int i,j;
p = (unsigned char *)&ir_code;
send_str("custom:0x");
for(i=; i<; i++)
{
if( == i)
{
send_str(" code:0x");
}
for(j=; j>=; j--)
{
c = (*p>>(*(j))) & 0xf;
if(<=c && c<=)
{
send_hex('' + c);
}
else
{
send_hex('A' + c - 0xa);
}
}
p++;
}
send_str("\r\n");
}

EEPROM 相关

eeprom.h

 #include <reg52.h>
#ifndef __EEPROM__
#define __EEPROM__
/**
* STC90C52 结尾是 90C
* EEPROM 5K
* SRAM 215字节
* 每个扇区512字节 5K / 512 = 10 个扇区
* 扇区首地址 2000h 结束地址 33ffh
*/ /* FLASH 首地址 */
#define BASE_ADDR 0x2000 /* 特殊功能寄存器声明 */
sfr ISP_DATA = 0xe2;
sfr ISP_ADDRH = 0xe3;
sfr ISP_ADDRL = 0xe4;
sfr ISP_CMD = 0xe5;
sfr ISP_TRIG = 0xe6;
sfr ISP_CONTR = 0xe7; /* 定义命令字节 */
#define CMD_Read 0x01 //字节读数据命令
#define CMD_Prog 0x02 //字节编程数据命令
#define CMD_Erase 0x03 //扇区擦除数据命令
#define En_Wait_ISP 1<<7 | 1<<1 //设置等待时间 ,并使能ISP/IAP 11.0592 晶振 void lock_ISP();
void erase(unsigned int);
unsigned char read(unsigned int);
void prog(unsigned int, unsigned char); #endif

eeprom.c

 #include "eeprom.h"

 /* 执行完操作以后安全锁 */
void lock_ISP()
{
ISP_CONTR = ;
ISP_CMD = ;
ISP_TRIG = ;
ISP_ADDRH = 0xff;
ISP_ADDRL = 0xff;
} /* 擦除指定地址所在的整个扇区 */
void erase(unsigned int addr)
{
addr += BASE_ADDR; //发送地址
ISP_ADDRH = addr >> ;
ISP_ADDRL = addr; //发送解锁命令
ISP_CONTR = En_Wait_ISP; //发擦除命令
ISP_CMD = CMD_Erase; //发送触发命令
ISP_TRIG = 0x46;
ISP_TRIG = 0xB9; //最后锁定 ISP 仿误操作
lock_ISP();
} unsigned char read(unsigned int addr)
{
addr += BASE_ADDR; //发送地址
ISP_ADDRH = addr >> ;
ISP_ADDRL = addr; //发送解锁命令
ISP_CONTR = En_Wait_ISP; //发读命令
ISP_CMD = CMD_Read; //发送触发命令
ISP_TRIG = 0x46;
ISP_TRIG = 0xB9; //最后锁定 ISP 仿误操作
lock_ISP(); return ISP_DATA;
} void prog(unsigned int addr, unsigned char dat)
{
addr += BASE_ADDR; //发送要保存的数据
ISP_DATA = dat; //发送地址
ISP_ADDRH = addr >> ;
ISP_ADDRL = addr; //发送解锁命令
ISP_CONTR = En_Wait_ISP; //发编程命令
ISP_CMD = CMD_Prog; //发送触发命令
ISP_TRIG = 0x46;
ISP_TRIG = 0xB9; //最后锁定 ISP 仿误操作
lock_ISP();
}

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

delay.h

 #ifndef __DELAY__
#define __DELAY__
void delay700us();
void delayms(int);
#endif

delay.c

 #include <intrins.h>
#include "delay.h"
void delayms(int ms) //@11.0592MHz
{
unsigned char i, j;
while(ms--)
{
_nop_();
i = ;
j = ;
do
{
while (--j);
}
while (--i);
}
} void delay700us() //@11.0592MHz
{
unsigned char i, j;
_nop_();
i = ;
j = ;
do
{
while (--j);
} while (--i);
}

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

IR.h

 #include <reg52.h>
#include "delay.h"
#ifndef __IR__
#define __IR__
//公用
typedef struct {
unsigned char custom_height;
unsigned char custom_lower;
unsigned char ir_code;
unsigned char re_ir_code;
} IR_CODE, *pIR_CODE; //接收
sbit IR = P3 ^ ;//红外线一体接收头 OUT void init_IR();
IR_CODE IR_recv(); //发射延时
#define m9 (65536-9000) //约9mS
#define m4_5 (65536-4500) //约4.5mS
#define m1_6 (65536-1630) //约1.65mS
#define m_65 (65536-580) //约0.65mS
#define m_56 (65536-560) //约0.56mS
#define m40 (65536-40000) //约40mS
#define m56 (65536-56000) //56mS
#define m2_25 (65536-2250) //约2.25mS sbit LaunchLED = P0 ^ ;//红外线发射LED 接PNP三极管基极, 经过试验不使用三极管时有效距离是40厘米, 可见 IO 下拉电流很小
void IR_launch(IR_CODE);
void IR_launch_time(bit,unsigned int);
void IR_launch_frame(unsigned char);
#endif

IR.c

 #include <string.h>
#include "IR.h" void init_IR()
{
//接收
EA = ; //总中断开
EX0 = ; //IR 接收头使用外部中断0 来处理
IT0 = ; //下降沿触发 //发射
TMOD |= 0x01; //T0 16位工作方式
LaunchLED = ; //发射端口常态为高电平
} //发射
void IR_launch(IR_CODE ir_code)
{
IR_launch_time(, m9); //高电平9mS
IR_launch_time(, m4_5); //低电平4.5mS /*┈ 发送4帧数据┈*/
IR_launch_frame(ir_code.custom_height);
IR_launch_frame(ir_code.custom_lower);
IR_launch_frame(ir_code.ir_code);
IR_launch_frame(ir_code.re_ir_code); /*┈┈ 结束码 ┈┈*/
IR_launch_time(, m_65);
IR_launch_time(, m40);
} //发送 1 帧数据
void IR_launch_frame(unsigned char frame)
{
char i = ;
for(i=; i<; i++) //循环8次移位
{
IR_launch_time(, m_65); //高电平0.65ms
if(frame >>i & 0x1)
IR_launch_time(, m1_6); //发送最低位
else
IR_launch_time(, m_56);
}
} //38KHz脉冲发射 + 延时程序
void IR_launch_time(bit status,unsigned int t)
{
TH0 = t>>; //输入T0初始值
TL0 = t;
TF0 = ; //清0
TR0 = ; //启动定时器0
if( == status)
{
//BT=0时不发射38KHz脉冲只延时;BT=1发射38KHz脉冲且延时;
while(! TF0);
}
else
{
while()
{
/**
* 38KHz脉冲,占空比5:26
* 以下是逻辑分析仪测试结果
* 3:23us 识别正常
* 6:23us 识别正常
* 10:23us 识别失败
* 12:23us 识别失败
* 16:23us 识别失败
*/
LaunchLED = ;
if(TF0)break;
if(TF0)break;
LaunchLED = ;
if(TF0)break;
if(TF0)break;
if(TF0)break;
if(TF0)break;
if(TF0)break;
if(TF0)break;
if(TF0)break;
if(TF0)break;
if(TF0)break;
if(TF0)break;
}
}
TR0=; //关闭定时器0
TF0=; //标志位溢出则清0
LaunchLED =; //脉冲停止后,发射端口常态为高电平
} //接收
IR_CODE IR_recv()
{
/**
* 数据格式:
* 9ms低电平 4.5ms高电平 头部
* 定制高位 定制低位 数据码 数据反码
* 1: 560us低电平 1680us高电平 0.56ms 1.7ms
* 0: 560us低电平 560us高电平 0.56ms 0.56ms
*/
IR_CODE ir_code;
unsigned char i,k;
unsigned char *ir_char_p;
unsigned char ir_char;
ir_char_p = (unsigned char *)&ir_code; //栈分配 IR_CODE 竟然还要手动清0
memset(&ir_code, , ); delayms(); //9ms 内必须是低电平否则就不是头信息
if( == IR)
{
while(! IR);//等待4.5ms的高电平 //检测是否是 2.5ms 重码
delayms();
if( == IR)
{
//k 4位编码
for(k=; k<; k++)
{
ir_char = 0x0;
//i 每一个编码的 8bit
for(i=;i<;i++)
{
while(IR); //等待变为低电平时
while(! IR); //等待变为高电平后
delay700us(); //休眠700us 后读值
ir_char |= (char)IR << i;//先存低位
//使用下面指针操作就会失败出现不稳定
//*ir_char_p |= (char)IR << i;
}
*ir_char_p = ir_char;
ir_char_p++;
} //计算反码 code码是否正确
if(ir_code.ir_code != ~(ir_code.re_ir_code))
{
memset(&ir_code, , );
}
}
}
return ir_code;
}

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

main.c

 #include <reg52.h>
#include <intrins.h>
#include <string.h>
#include "uart.h"
#include "delay.h"
#include "IR.h"
#include "eeprom.h" IR_CODE read_ir_code(unsigned char);
void save_block_ir_code();
void save_ir_code(IR_CODE ir_code,unsigned int addr); //全局接收红外线信号存放
IR_CODE global_ir_code; //从eeprom 中读取 按键值
IR_CODE k3_ir_code;
IR_CODE k4_ir_code;
IR_CODE k5_ir_code;
IR_CODE k6_ir_code; //3个按键
sbit K3 = P3 ^ ;
sbit K4 = P3 ^ ;
sbit K5 = P3 ^ ; #ifdef DEBUG
//main 中 EX1 = 1;
//外部中断1 按下时发送红外线信号
void infrared_led_int1() interrupt
{
EX1 = ;
IR_launch(global_ir_code);
EX1 = ;
}
#endif void main()
{
//总中断开关
EA = ;
init_uart();
init_IR(); //全局接收红外线清0
memset(&global_ir_code, , ); //从eeprom 中读取 按键值
k3_ir_code = read_ir_code();
k4_ir_code = read_ir_code();
k5_ir_code = read_ir_code(); while()
{
//按键按下
if( == K3)
{
//去抖动后
delayms();
if( == K3)
{
//关中断 中断打开时,时钟可能不准
EA = ; //如果有全局接收 则存入 eeprom
if(global_ir_code.ir_code)
{
k3_ir_code = global_ir_code;
save_block_ir_code();
memset(&global_ir_code, , ); //闪灯2次表示 存入成功
P1 = 0x0;
delayms();
P1 = 0xff;
delayms();
P1 = 0x0;
delayms();
P1 = 0xff;
}
else
{
IR_launch(k3_ir_code); //闪灯1次表示 发射成功
P1 = 0x0;
delayms();
P1 = 0xff;
}
//开中断
EA = ;
}
}
//按键按下
if( == K4)
{
//去抖动后
delayms();
if( == K4)
{
//关中断 中断打开时,时钟可能不准
EA = ; //如果有全局接收 则存入 eeprom
if(global_ir_code.ir_code)
{
k4_ir_code = global_ir_code;
save_block_ir_code();
memset(&global_ir_code, , ); //闪灯2次表示 存入成功
P1 = 0x0;
delayms();
P1 = 0xff;
delayms();
P1 = 0x0;
delayms();
P1 = 0xff;
}
else
{
IR_launch(k4_ir_code); //闪灯1次表示 发射成功
P1 = 0x0;
delayms();
P1 = 0xff;
}
//开中断
EA = ;
}
}
//按键按下
if( == K5)
{
//去抖动后
delayms();
if( == K5)
{
//关中断 中断打开时,时钟可能不准
EA = ; //如果有全局接收 则存入 eeprom
if(global_ir_code.ir_code)
{
k5_ir_code = global_ir_code;
save_block_ir_code();
memset(&global_ir_code, , ); //闪灯2次表示 存入成功
P1 = 0x0;
delayms();
P1 = 0xff;
delayms();
P1 = 0x0;
delayms();
P1 = 0xff;
}
else
{
IR_launch(k5_ir_code); //闪灯1次表示 发射成功
P1 = 0x0;
delayms();
P1 = 0xff;
}
//开中断
EA = ;
}
}
}
} void IR_int() interrupt
{
IR_CODE ir_code;
EX0 = ;//处理过程中 关中断
ir_code = IR_recv();
if(ir_code.ir_code)
{
//点亮P1 显示收到了编码
P1 = 0x0;
delayms();
P1 = 0xff; //发送到串口
send_code(ir_code); //给全局编码赋值
global_ir_code = ir_code;
}
EX0 = ;//处理结束后 开中断
} //读取 eeprom 中的值
IR_CODE read_ir_code(unsigned char addr)
{
IR_CODE ir_code;
ir_code.custom_height = read(addr);
ir_code.custom_lower = read(addr+);
ir_code.ir_code = read(addr+);
ir_code.re_ir_code = read(addr+);
return ir_code;
} //保存所有编码
void save_block_ir_code()
{
erase();
save_ir_code(k3_ir_code, );
save_ir_code(k4_ir_code, );
save_ir_code(k5_ir_code, );
} //保存一个编码
void save_ir_code(IR_CODE ir_code,unsigned int addr)
{
prog(addr, ir_code.custom_height);
prog(addr+, ir_code.custom_lower);
prog(addr+, ir_code.ir_code);
prog(addr+, ir_code.re_ir_code);
}

最后是使用说明:

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

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

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

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

stc89c52开发板遥控器解码 红外线发射 内置 eeprom 存储 串口显示编码的更多相关文章

  1. STC8H开发(十二): I2C驱动AT24C08,AT24C32系列EEPROM存储

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  2. Python开发【第五篇】内置函数

    abs() 函数返回数字的绝对值 __author__ = "Tang" a = -30 all() 函数用于判断给定的可迭代参数iterable中的所有元素是否都为True,如果 ...

  3. 解剖Nginx·模块开发篇(5)解读内置非默认模块 ngx_http_stub_status_module

    1 Background ngx_http_stub_status_module 是一个 Nginx 的内置 HTTP 模块,该模块可以提供 Nginx 的状态信息.默认情况下这个模块是不被编译进来的 ...

  4. [SAP ABAP开发技术总结]预定义(内置)数据类型

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  5. Arduino各开发板

    参考来源:https://www.arduino.cn/thread-42417-1-1.html 查了好久,发现除了奈何等等几位大神总结过arduino各板子之间的性能.差异,没有很新的分析文章,在 ...

  6. 【STM32-V5】STM32F407开发板开源, 丰富软件资源, 强劲硬件配置, 配套500实例, 10套手册带视频教程2019-12-12

    淘宝购买地址:购买地址链接 从2013年5月份发布至今,开发板硬件更新过6个版本,软件资料更新过85次.当前标准库最新版本V8.8,HAL库最新版本V1.1 安富莱微信公共平台,欢迎大家关注(打造高质 ...

  7. 主机+虚拟机ubuntu+mini2440开发板互相ping通

    折腾这么久,终于将主机,虚拟机和开发板三者之间能够相互ping通,虽然还没有实现我要的功能,不管怎么说先将步骤简单的概括下,用交叉网线将开发板与主机相连,开发板与主机的ip要设置在同一网段内,在配置u ...

  8. 基于uFUN开发板的心率计(一)DMA方式获取传感器数据

    前言 从3月8号收到板子,到今天算起来,uFUN到手也有两周的时间了,最近利用下班后的时间,做了个心率计,从单片机程序到上位机开发,到现在为止完成的差不多了,实现很简单,uFUN开发板外加一个Puls ...

  9. 意法半导体STM32MP157A MPU加持,米尔科技首款ST Linux开发板MYD-YA157C评测

    ST公司去年推出了MPU系列芯片,MPU系列不同于以往产品,它既包含有ARM公司Cortex M 单片机核心,也包含有ARM公司Cortex A 应用处理器核心,以期将STM32单片机产品优势扩展到更 ...

随机推荐

  1. http://www.cnblogs.com/dumuqiao/p/3654702.html?utm_source=tuicool&utm_medium=referral

    http://www.cnblogs.com/dumuqiao/p/3654702.html?utm_source=tuicool&utm_medium=referral

  2. ORACLE十进制与十六进制的转换

    十进制与十六进制的转换 十进制-->十六进制 select to_char(100,'XX') from dual; 十六进制-->十进制select to_number('7D','XX ...

  3. sed 例子

    sed 应用示例 给1.1.1.1后面增加2.2.2.2: sed -i 's/Server=1.1.1.1/&,2.2.2.2/g' zabbix_agentd.conf

  4. ThreadLocal 多线程并发,数据隔离

    ThreadLocal:  创建一个线程本地变量. 本质:在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本. 优点:既实现多线程并发,游兼顾数据的安全性. 区别:Synchro ...

  5. Linux就这个范儿 第11章 独霸网络的蜘蛛神功

    Linux就这个范儿 第11章  独霸网络的蜘蛛神功  第11章 应用层 (Application):网络服务与最终用户的一个接口.协议有:HTTP FTP TFTP SMTP SNMP DNS表示层 ...

  6. php 扩展dll

    一.准备工作: 注:php5.2没有vc9,php5.3.php5.4没有vc6.呵呵.PHP5.5开始,不支持xp和win2003了,更是vc11了.--------------->所以,扩展 ...

  7. SoapUI接口测试·第一个HTTP Request接口请求和断言

    一.新建SOAP项目 [File]-[New SOAP Project],在[Project Name]输入{工程名},点击[OK].   二.新建TestSuite 选中项目,右键选择[New Te ...

  8. Nginx负载均衡和LVS负载均衡的比较分析

    LVS和Nginx都可以用作多机负载的方案,它们各有优缺,在生产环境中需要好好分析实际情况并加以利用. 首先提醒,做技术切不可人云亦云,我云即你云:同时也不可太趋向保守,过于相信旧有方式而等别人来帮你 ...

  9. NPOI 导入,导出EXCEL

    代码: public static class NPOIExcelHelper { /// <summary> /// DataTable导出到Excel文件 /// </summa ...

  10. Spring Boot 5 SpringSecurity身份验证

    对于没有访问权限的用户需要转到登录表单页面.要实现访问控制的方法多种多样,可以通过Aop.拦截器实现,也可以通过框架实现(如:Apache Shiro.Spring Security). pom.xm ...