第15页 共43页
(3)指定操作的变量选择组合提高了程序的可读性。 (4)可使用与人的思维更相近的关键字和操作函数。 (5)程序的开发和调试时间大大缩短。
(6) C语言中的库文件提供了许多标准的例程。
(7)可实现模块化编程技术,从而可将己编制好的程序加入到新程序中。 (8) C语言可移植性好且非常普及。
目前,8051上的C语言的代码长度,已经做到了汇编水平的1.2~1.5倍。4K字节以上的程度,C语言的优势更能得到发挥。至于运行速度的问题,只要有好的仿真器,找出关键的代码,再进一步做一下人工优化,就可很容易达到美满。故在本系统中,单片机程序采用C语言编写,使用Keil C51编译软件来编程。
4.1.1编译软件Keil uVision2简介
Keil C51是美国Keil Software公司出品的51系列兼容单片机C语言软件开发系统, Keil C51软件提供丰富的库函数和功能强大的集成开发调试工具,全Windows界面。
Keil uVision2版本功能齐全,集编辑、编译、仿真于一体,支持汇编和C语言的程序设计,与汇编相比,C语言在功能上、结构性、可读性、可维护性上有明显的优势,因而易学易用界面友好,易学易用。Keil C51软件提供丰富的库函数和功能强大的集成开发调试工具,全Windows界面。另外重要的一点,只要看一下编译后生成的汇编代码,就能体会到Keil C51生成的目标代码效率非常之高,多数语句生成的汇编代码很紧凑,容易理解。在开发大型软件时更能体现高级语言的优势。Keil uVision2的运行界面如下图4.1所示。
第16页 共43页
图4.1 Keil uVision2的运行环境界
4.2软件设计
学习型遥控器的设计性能及实现与其软件设计编写有着密切的关系,在设计中采用内部定时器对信号高低电平计时的方法来采集数据并保存。
由于受到存储空间和代码长度的限值,硬件中的按键并没有完全充分使用。而是选择了其中的6个按键进行学习。
系统软件首先对定时器设置和初始化液晶显示。在主循环中检测按键,假如有学习按键按下时,则进入学习模式。此时要继续按下编号为3-6的某个按键,然后可以用红外遥控器对准接收头按下遥控器上需要学习的键,将学到的红外信号绑定到该编号键,并将学习到的红外解码数据存到EEPROM中。
在主循环中检测到编号3-6的按键,则进入发送模式。根据按键的编号找到相应EEPROM中的地址,读出红外数据,并将此数据调制经红外发射头发射出去。 软件流程图:
开始初始化设置有学习键按下 Y检测编号键N检测编号键N Y保存按键值从存储器读取数据检测学习红外信号 Y解码并保存到存储器N将数据进行红外调制发射返回返回
图4.2 主程序流程图
要实现学习型遥控器的软件设计,最关键的两个部分是学习功能和数据压缩。尽管
第17页 共43页
通信协议中有不同的帧格式,如帧头、系统码、操作码、同步码、帧间隔码、帧尾等,根据记录下降沿间的间隔时间来测量红外遥控信号的高低电平的脉宽值的原理,用户甚至不需要了解通信协议的具体内容,只需知道低电平(有红外发送载波)信号时长和高电平(无红外发送载波)信号时长就可以实现遥控命令的学习和存储。 4.2.1学习功能
在设计中采用内部定时器对信号高低电平计时的方法来采集数据并保存。由于红外接收信号直接接单片机的外部中断0端口,当输入信号产生低电平跳变时,系统启动内部定时器1,依次对输入高低电平脉冲宽度值计时。如果采集到编码信号位数大于设定值(程序中设定值)或者高电平信号时长大于一定值,即认为编码采集已经结束,学习子程序结束。
在设计中选择24 MHz晶振,一个机器周期是0.5us,计数器采用16 位计数器,可以记录的最大时间间隔为32.767ms;每次学习结束后,都将学习到的存储在单片机内部存储区的遥控命令数据压缩,并根据按键和LCD显示屏的显示数据统一编码再存入EEPROM。当在正常情况下时,则根据按键需要的遥控命令,从EEPROM中寻找到相关的遥控命令,对此命令进行解压后,再用软件模仿38 kHz载波信号发送编码信息。
学习功能部分程序设计:
/*************外部中断0*********************/
void inter0 (void) interrupt 0 using 1//下降沿一到,即进入中断服务程序 {
EX0=0;//外部中断1关闭 TH1 = 0; TL1 = 0;
//步骤一:定时器1对起始帧的负脉宽测量。 TR1=1;
//定时器1开始计数
PW_pt = 4; timeout = 0; length=0; RX_flag = 1; Head_flag = 0; End_flag=0;
while(ir_in == 0)//检测是否还是低电平,高电平到来才退出循环。 {
//清零
//数组指针
timeout++;
if (timeout>40000)//超过时间直接退出
第18页 共43页
}
{ }
//定时器1关闭计数
RX_flag = 0; break;
TR1=0;
PW_data[0] = TH1;//定时器1计数值,负脉宽计数值 PW_data[1] = TL1; TH1 = 0; TL1 = 0;
//步骤二:定时器1对起始帧的正脉宽测量。 TR1=1;
//定时器1开始计数
timeout = 0;
if((PW_data[0]<90)&&(PW_data[0]>55)) { } else { }
while(ir_in) //检测是否还是高电平,低电平到来才退出循环。 { } TR1=0;
//定时器1关闭计数
PW_data[2] = TH1;//定时器1计数值,正脉宽计数值 PW_data[3] = TL1; TH1 = 0; TL1 = 0;
//清零
Head_flag = 1;
Head_flag = 0;
timeout++;
if (timeout>20000)//超过时间直接退出 { }
RX_flag = 0; break;
//清零
第19页 共43页
for (u8_i=0; u8_i<26; u8_i++)//系统码------这里有26个脉冲,用掉104个存储单元
{
//步骤三:定时器1对数据帧的负脉宽测量。这里做成循环形式 TR1=1;
//定时器1开始计数
timeout = 0; RX_flag = 1;
while(ir_in == 0)//检测是否还是低电平,高电平到来才退出循环。 { } TR1=0;
//定时器1关闭计数
PW_data[PW_pt++] = TH1;//定时器1计数值,负脉宽计数值 PW_data[PW_pt++] = TL1; TH1 = 0; TL1 = 0;
//步骤四:定时器1对数据帧的正脉宽测量。 TR1=1;
//定时器1开始计数
timeout = 0; if(RX_flag) { }
RX_flag = 1;
while(ir_in) //检测是否还是高电平,低电平到来才退出循环。 {
timeout++;
if (timeout>10000)//超过时间直接退出 length++;
//清零
timeout++;
if (timeout>10000)//超过时间直接退出 { }
RX_flag = 0; break;