本帖最后由 糖悦之果飞 于 2016-3-23 09:04 编辑
51单片机小车,通过蓝牙发送温度数据,tigerboard作为显示器接受并且显示温度,小车遥控通过普通红外遥控器均可。
一 实验目的 1 本实验通过利用STC89C52单片机来解码红外遥控器的编码,然后通过L298n电机驱动电路控制小车的行驶。 2 通过18b20温度传感器将采集到的温度发给单片机,经过单片机的处理再通过蓝牙模块发给tigerboard安卓客户端,通过安卓串口app把温度显示出来。 3 掌握使用java语言和eclipse软件编写安卓串口app程序。 4 学会制作pcb原理图,自己手工腐蚀电路板。学会对代码的修改分析,优化代码的结构。 二 硬件电路与程序设计 1 系统设计原理框图 如图1所示为系统设计的原理框图,红外接收器接收到红外遥控信号,然后经过51单片机对编码信号进行解码。通过读取不同的键码来控制小车电机的动作。同时51单片机把18b20的温度经过处理,通过串口发送给蓝牙模块。蓝牙模块把采集到的温度发送给tigerboard安卓客户端。 图1 系统框图
2 红外遥控发射的原理 当我们按下遥控器后,遥控器发出一帧数据。这一帧数据由前导码、用户码高 8位、用户码低8位、数据码、数据反码组成。其中前导码为9ms高电平接着4.5ms低电平,标志数据帧的开始;用户码(共16位)为红外接收器识别遥控器的身份的标志,不同的遥控器一般用户码不同,以防止不同电器设备之间遥控码的干扰;数据码为红外接收器识别遥控器上不同的按键的标志,对同一遥控器来说,按不同的键所发出的二进制编码具有相同的用户码,不同的数据码,如图2所示.数据反码用于信息正确接收校验。
file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ksohtml\wps79.tmp.jpg 图2发射端数据的编码
2.1 16位用户码,8位数据码和8位数据反码中的数据位定义 以脉宽 0.56ms间隔0.565ms周期1.125ms表示二进制“0”,以脉宽0.56ms间隔1.69ms周期2.25ms表示二进制“1”,其波形分别如图3(a)和(b)所示。
file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ksohtml\wps7A.tmp.jpg 图3 3 红外遥控接收原理 解码就是将HX1838输出的脉冲还原为二进制的“0”和“1”,得到二进制“0”,“1”序列,进而分析所含的用户码和数据码。当HX1838在遥控有效距离内,接收到红外遥控脉冲信号后,由内部转换成电压信号并经放大、长时控制、干扰抑制、带通滤波并整形后输出遥控代码脉冲.脉冲的形式为遥控器发射脉冲的倒像.图4表明了遥控发射码与HX1838解码输出码之间的波形关系.
单片机接收端与发射端的信号是倒像关系,发射端引导码是 9ms高电平,2.25ms低电平,在单片机接收端就变成了9ms低电平和2.25ms高电平,发射端发射位0,则单片机接收端为高电平。
file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ksohtml\wps7B.tmp.jpg 遥控器发射码 遥控器接收码 图4 3.L298n电机驱动原理 该驱动电路可以驱动2路驱动电机,使能端ENA,ENB为高电平有效。控制方式如表所示
表 1 L298n 电机驱动原理
file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ksohtml\wps7C.tmp.jpg
4. 18b20温度采集 file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ksohtml\wps7D.tmp.jpg本系统采用半导体温度传感器作为敏感元件。传感器我们采用了DS18B20单总线可编程温度传感器,来实现对温度的采集和转换,直接输出数字量,可以直接和单片机进行通讯,大大简化了电路的复杂度。DS18B20应用广泛,性能可以满足题目的设计要求。DS18B20的测温电路如图5所示
图5 DS18B20测温电路
(1) DSI8B20的测温功能的实现: 其测温电路的实现是依靠单片机软件的编程上。 当DSI8B20接收到温度转换命令后,开始启动转换。转换完成后的温度值就以16位带符号扩展的二进制补码形式存储在高速暂存存储器的0,1字节。单片机可通过单线接口读到该数据,读取时低位在前,高位在后,数据格式以0.062 5℃/LSB形式表示。温度值格式如表2.2.1所示,其中“S”为标志位,对应的温度计算:当符号位S=0时,直接将二进制位转换为十进制;当S=1时,先将补码变换为原码,再计算十进制值。DSI8B20完成温度转换后,就把测得的温度值与 TH做比较,若T>TH或T<TL,则将该器件内的告警标志置位,并对主机发出的告警搜索命令做出响应。 表 2 DS18b20温度值格式表
file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ksohtml\wps7E.tmp.jpg
(2)、DSl820工作过程中的协议 初始化 -> RoM操作命令 -> 存储器操作命令-> 处理数据 ① 初始化 单总线上的所有处理 均从初始化开始 ② ROM操作命令 总线主机检测到DSl820的存在便可以发出ROM操作命令之一这些命令如表3所示
表3 ROM操作命令表 file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ksohtml\wps7F.tmp.jpg
③存储器操作命令如表 4所示
表4存储器操作命令表 file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ksohtml\wps80.tmp.jpg 3)温度转换算法及分析 由于DS18B20转换后的代码并不是实际的温度值,所以要进行计算转换。温度高字节(MS Byte)高5位是用来保存温度的正负(标志为S的bit11~bit15),高字节(MS Byte)低3位和低字节来保存温度值(bit0 ~ bit10)。其中低字节(LS Byte)的低4位来保存温度的小数位(bit0 ~ bit 3)。由于本程序采用的是0.0625的精度,小数部分的值,可以用后四位代表的实际数值乘以0.0625,得到真正的数值,数值可能带几个小数位,所以采取小数舍入,保留一位小数即可。也就说,本系统的温度精确到了0.1度。 算法核心:首先程序判断温度是否是零下,如果是,则DS18B20保存的是温度的补码值,需要对其低8位(LS Byte)取反加一变成原码。处理过后把DS18B20的温度Copy到单片机的RAM中,里面已经是温度值的Hex码了,然后转换Hex码到BCD码,分别把小数位,个位,十位的BCD码存入RAM中。 4.1 读取DS18B20温度模块子程序 每次对DA18B20操作时多要按造DS18B20工作过程中的协议进行。 初始化-> RoM操作命令-> 存储器操作 命令 -> 处理数据 程序流程图如图6所示。
file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ksohtml\wps81.tmp.jpg 图6读取DS18B20温度子程序流程图 4..2 数据处理子程序 由于 DS18B20转换后的代码并不是实际的温度值,所以要进行数据处理。由于本程序采用的是0.0625的精度,小数部分的值,可以用后四位代表的实际数值乘以0.0625,得到真正的数值,数值可能带几个小数位,所以采取四舍五入,保留一位小数即可。也就说,本系统的温度精确到了0.1度。
三 硬件电路原理图 file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ksohtml\wps82.tmp.jpg
file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ksohtml\wps83.tmp.jpgfile:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ksohtml\wps84.tmp.jpg 单片机原理图
外接收电路 18b20 温度模块
file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ksohtml\wps85.tmp.jpg L298n 电机驱动电路
file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ksohtml\wps86.tmp.jpg
蓝牙模块收发电路
四 程序设计 下位机源程序代码如下 #include<reg52.h> #include<stdio.h> #include <wendu.h> sbit IN1=P1^0; sbit IN2=P1^1; sbit IN3=P1^2; sbit IN4=P1^3; sbit ENA=P0^0; sbit ENB=P0^1;
char code Tab[16]="0123456789ABCDEF";
char data TimeNum[]=" "; char data Test1[]=" ";
unsigned char irtime;//红外用全局变量
bit irpro_ok,irok; unsigned char IRcord[4]; //处理后的红外码 unsigned char irdata[33]; //33个高低电平的时间数据
void Ir_work(void); void Ircordpro(void); void ShowString (unsigned char line,char *ptr); void go(); void back(); void left(); void right(); void Fleft(); void Fright(); void stop() ; void zidingyi(); void delay(unsigned int i);
//===============================定时器0中断函数=================== void tim0_isr (void) interrupt 1 using 1//定时器0中断服务函数 { irtime++; //用于计数2个下降沿之间的时间 }
//==============================外部中断0函数======================= void ex0_isr (void) interrupt 0 using 0//外部中断0服务函数 { static unsigned char i; //接收红外信号处理 static bit startflag; //是否开始处理标志位
if(startflag) { if(irtime<63&&irtime>=33) i=0; //判断是否是引导码
irdata=irtime;//存储每个电平的持续时间,用于以后判断是0还是1 irtime=0; i++; if(i==33) { irok=1; i=0; } } else {irtime=0;startflag=1;} } //==============================定时器0初始化===================== void TIMEinit(void) { TMOD=0x02;//定时器0工作方式2,TH0是重装值,TL0是初值 TH0=0x00; //重载值 TL0=0x00; //初始化值 ET0=1; //开中断 TR0=1; } //=======================外部中断初始化=========================== void EX0init(void) { IT0 = 1; //指定外部中断0下降沿触发,INT0 (P3.2) EX0 = 1; //使能外部中断 EA = 1; //开总中断 } /******************************************************************/ /* 红外键值处理 */ /******************************************************************/ void Ir_work(void) {
TimeNum[5] = Tab[IRcord[0]/16]; //处理客户码并显示 TimeNum[6] = Tab[IRcord[0]%16]; TimeNum[8] = Tab[IRcord[1]/16]; //处理客户码并显示 TimeNum[9] = Tab[IRcord[1]%16]; TimeNum[11] = Tab[IRcord[2]/16]; //处理数据码并显示 TimeNum[12] = Tab[IRcord[2]%16]; TimeNum[14] = Tab[IRcord[3]/16]; //处理数据反码并显示 TimeNum[15] = Tab[IRcord[3]%16];
irpro_ok=0; //处理完成后清除标志位 }
//================================红外解码函数======================= void Ircordpro(void) { unsigned char i, j, k; unsigned char cord,value;
k=1; for(i=0;i<4;i++) //处理4个字节 { for(j=1;j<=8;j++) //处理1个字节8位 { cord=irdata[k]; if(cord>7) { value=value|0x80; } else { value=value; } if(j<8) { value=value>>1; } k++; } IRcord=value; value=0; } irpro_ok=1;//处理完毕标志位置1 }
//===========================主函数======================== void main(void) {
ENA=1; ENB=1; EX0init(); //初始化外部中断 TIMEinit();//初始化定时器 P2=0xff; while(1)//主循环 { if(irok) //如果接收好了进行红外处理 { Ircordpro(); irok=0; } if(irpro_ok) //如果处理好后进行工作处理,如按对应的按键后显示对应的数字等// { Ir_work(); if(TimeNum[11]==Tab[0]&&TimeNum[12]==Tab[12]){wendu();zidingyi();} //0c anjian1 if(TimeNum[11]==Tab[0]&&TimeNum[12]==Tab[8]) left(); //如果按下“4”键,左转 if(TimeNum[11]==Tab[1]&&TimeNum[12]==Tab[8]) go(); //如果按下“2”键,前进 if(TimeNum[11]==Tab[5]&&TimeNum[12]==Tab[10]) right(); //如果按下“6”键,右转 if(TimeNum[11]==Tab[1]&&TimeNum[12]==Tab[12]) stop(); //如果按下“5”键,停止 if(TimeNum[11]==Tab[4]&&TimeNum[12]==Tab[2]) Fleft(); //如果按下“7”键,后左转 if(TimeNum[11]==Tab[5]&&TimeNum[12]==Tab[2]) {back();} //如果按下“ch”键,后退 if(TimeNum[11]==Tab[4]&&TimeNum[12]==Tab[10]) Fright(); //如果按下“9”键,后右转 } } } void go() { //ENA=1; //ENB=1; IN1=0; IN2=1; IN3=1; IN4=0; } //==向后================================== void back() { //ENA=1; //ENB=1; IN1=1; IN2=0; IN3=0; IN4=1; } //==左转向前====================================== void left() {// ENA=1; //ENB=1; IN1=0; IN2=1; IN3=0; IN4=0; } //==右转向前====================================== void right() {// ENA=1; //ENB=1; IN1=0; IN2=0; IN3=1; IN4=0; } //==左转向后====================================== void Fleft() { // ENA=1; // ENB=1; IN1=0; IN2=0; IN3=0; IN4=1; } //==右转向后====================================== void Fright() {// ENA=1; // ENB=1; IN1=1; IN2=0; IN3=0; IN4=0; } //==停止程序================================== void stop() {// ENA=1; // ENB=1; IN1=0; IN2=0; IN3=0; IN4=0; } void zidingyi() { ENA=1; ENB=1; EX0init(); //初始化外部中断 TIMEinit();//初始化定时器 P2=0xff; } /* * DS18B20 */
#include <reg52.h> #include <intrins.h>
typedef unsigned char uint8; typedef unsigned int uint16; typedef char int8; typedef int int16;
sbit DQ = P2^2; //温度输入口
#define nops(); {_nop_(); _nop_(); _nop_(); _nop_();} //定义空指令
void delay(uint16 n) { while (n--); }
void delay_ms(uint16 n) { uint8 m=120;
while (n--) while (m--); }
/* * 18B20复位函数 */ void DS18b20_reset(void) { bit flag=1;
while (flag) { while (flag) { DQ = 1; delay(1); DQ = 0; delay(50); // 550us DQ = 1; delay(6); // 66us flag = DQ; } delay(45); //延时500us flag = ~DQ; } DQ=1; }
/* * 18B20写1个字节函数 * 向1-WIRE总线上写一个字节 */ void write_byte(uint8 val) { uint8 i;
for (i=0; i<8; i++) { DQ = 1; _nop_(); DQ = 0; nops(); //4us DQ = val & 0x01; //最低位移出 delay(6); //66us val >>= 1; //右移一位 } DQ = 1; delay(1); }
/* * 18B20读1个字节函数 * 从1-WIRE总线上读取一个字节 */ uint8 read_byte(void) { uint8 i, value=0;
for (i=0; i<8; i++) { DQ=1; _nop_(); value >>= 1; DQ = 0; nops(); //4us DQ = 1; nops(); //4us if (DQ) value|=0x80; delay(6); //66us } DQ=1;
return(value); }
/* * 启动温度转换 */ void start_temp_sensor(void) { DS18b20_reset(); write_byte(0xCC); // 发Skip ROM命令 write_byte(0x44); // 发转换命令 }
/* * 读出温度 */ int16 read_temp(void) { uint8 temp_data[2]; // 读出温度暂放 int16 temp;
DS18b20_reset(); // 复位 write_byte(0xCC); // 发Skip ROM命令 write_byte(0xBE); // 发读命令 temp_data[0]=read_byte(); //温度低8位 temp_data[1]=read_byte(); //温度高8位
temp = temp_data[1]; temp <<= 8; temp |= temp_data[0]; temp >>= 4;
return temp; }
void uart_init(void) { TMOD = 0x21; // 定时器1工作在方式2(自动重装) SCON = 0x50; // 10位uart,允许串行接受
TH1 = 0xFD; TL1 = 0xFD;
TR1 = 1; }
void UART_Send_Byte(uint8 dat) { SBUF = dat; while (TI == 0); TI = 0; }
/** * 将数据转换成ASC码并通过UART发送出去 */ void UART_Send_Dat(uint8 dat) { UART_Send_Byte(dat/10%10 + '0'); UART_Send_Byte(dat%10 + '0'); }
void wendu() { unsigned char a; int16 ans;
uart_init();
a=100; while (a) { start_temp_sensor();
delay_ms (200); // 延时0.2秒
ans=read_temp();
if (ans < 0) { UART_Send_Byte('-'); ans = -ans; }
UART_Send_Byte(' '); UART_Send_Dat( ans); UART_Send_Byte('\r'); // UART_Send_Byte('\n');
a--; } } 五 app设计 (1) 温度显示区域。可以实时显示当前温度 (2) 连接按钮 可以选择要连接的蓝牙模块 (3)保存按钮 可以把采集到的温度保存到某个文件夹下面 (5)清空按钮可以清空温度显示 ( 6)send 按钮可以给蓝牙模块发送一些指令
file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ksohtml\wps87.tmp.jpg
六 实验结果 可以遥控小车前后左右行驶并且把温度发送到tigerboard安卓客户端,完成了实验要求。 七 总结与体会 接口实验课程实验历时大半个学期,通过自己编写、运行程序,不仅可以巩固了以前所学过的知识,而且学到了很多在书本上所没有学到过的知识。以前对于编程工具的使用还处于一知半解的状态上,但是经过一段实践,对于怎么去排错、查错,怎么去看每一步的运行结果,怎么去了解每个寄存器的内容以确保程序的正确性上都有了很大程度的提高。 通过这次接口实验使我懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的,只有把所学的理论知识与实践相结合起来,从理论中得出结论,才能真正提高自己的实际动手能力和独立思考的能力。在设计的过程中遇到问题,可以说得是困难重重,这毕竟第一次做的,难免会遇到过各种各样的问题,同时在设计的过程中发现了自己的不足之处,对以前所学过的知识理解得不够深刻,掌握得不够牢固。这次接口实验终于顺利完成了,在设计中遇到了很多编程问题,最后在自己的思考以及和同学的讨论中,终于迎刃而解。 八 参考文献 [1] 郭天祥编著。51单片机C语言教程。电子工业出版社 [2] 张洪润等.单片机应用设计200例.北京:北京航空航天大学出版社,2006. [3] 李刚编著 疯狂安卓讲义。电子工业出版社. [4]洪维恩编著。JAVA完全自学手册。中国铁道出版社.
附带小车程序以及蓝牙app代码
|