职业技能训练之 电子技术课程设计报告
学 院 设计题目 班 级 姓 名 学 号 指导教师 时 间
电子与信息学院 基于51单片机设计的带有测温功能的电子时钟 XXX XXX XXX XXX 2012年06月25日
电子课程设计报告
一、 设计要求
二、 课程设计的方案、目的及意义
三、 硬件设计方案
四、 软件设计方案
五、 总结
六、 参考资料
目录
2
电子课程设计报告
一、 设计要求
用51单片机设计带温度显示的电子时钟,具体要求如下: 1、利用DS1302时钟芯片实现时钟功能模块。
2、时钟要求可以调节时间:年、月、日、时、分、秒。 3、利用LCD1602显示。
4、利用DS18B20芯片实现温度功能模块。 5、利用按键完成各项功能。
二、 课程设计方案、目的及意义
1、 总体方案:
用STCC51单片机作为CPU主控制器,DS1302时钟芯片提供准确时钟信号,DS18B20温度传感器采集温度信息,三个按键进行加减调整、功能切换作用,通过LCD1602对外多功能显示。 2、 具体方案:
CPU控制所有模块,通过循环反复从DS1302中读取时钟信息,传送至LCD1602显示,得到基本时钟功能。当分为59,秒为56时开始,每隔一秒LED灯点亮240毫秒,0分0秒时LED灯点亮700毫秒。从而实现整点光报时。
定时循环从DS18B20中读取温度信息,传送至LCD1602显示,得到基本温度计功能。当温度高于30度(包括30度)时,点亮红色LED灯,提醒当天为高温天气。低于0度时,点亮蓝色LED灯,提醒当天为冰冻天气。
键盘使用扫面方式,MENU键控制功能切换,完成时钟和温度间的转换。OK键控制时间调整与确定,UP、DOWN键调节时间,R、L 键选择调整对象。进入调整时,暂停DS1302数据读取,并将改变的时间数据写入DS1302,并送LCD1602显示,同时,启动LCD1602光标闪烁,确定调整对象,完成人机对话。退出调整时,停止写入数据,重新读取DS1302时钟信息。从而完善时钟功能。 3、 目的及意义
可作为产品生产,作为居家的时钟显示与温度计。
3
电子课程设计报告
三、硬件设计方案
1、原理图
2、PCB图
4
电子课程设计报告
3、各功能模块分析
(一)、主控制器:STC单片机C51
功能:程序存储器16K、RAM数字存储器1280、可直接通过串口下载程序,单一+5V电源供电,五个中断源的中断控制系统,片内振荡器和时钟产生电路,石英晶体和微调电容需要外接。
CPU:由运算和控制逻辑组成,同时还包括中断系统和部分外部特殊功能寄存器。
RAM:用以存放可以读写的数据,如运算的中间结果、最终结果以及欲显示的数据。
ROM:用以存放程序、一些原始数据和表格。
I/O口:四个8位并行I/O口,既可用作输入,也可用作输出。P0口做I/O口需接上拉电阻。
T/C:两个定时/记数器,既可以工作在定时模式,也可以工作在记数模式。
5
电子课程设计报告
(二)、LCD1602显示模块
介绍:工业字符型液晶,能够同时显示16x02即32个字符(16列2行)。 管脚信息:
显示模式设置:
显示开关及功能设置:
6
电子课程设计报告
数据指针设置:
写时序:
7
电子课程设计报告
注:R3调节显示屏对比度
(三)、DS1302时钟芯片
管脚信息:
VCC:Vcc1为后备电源,VCC2为主电源。在主电源关闭的情况下,也能保持时钟的连续运行。DS1302由Vcc1或Vcc2两者中的较大者供电。当Vcc2大于Vcc1+0.2V时,Vcc2给DS1302供电。当Vcc2小于Vcc1时,DS1302由Vcc1供电。
X1、X2:X1和X2是振荡源,外接32.768kHz晶振。
RST:RST是复位/片选线,通过把RST输入驱动置高电平来启动所有的数
8
电子课程设计报告
据传送。RST输入有两种功能:首先,RST接通控制逻辑,允许地址/命令序列送入移位寄存器;其次,RST提供终止单字节或多字节数据的传送手段。当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。如果在传送过程中RST置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。上电运行时,在Vcc>2.0V之前,RST必须保持低电平。只有在SCLK为低电平时,才能将RST置为高电平。I/O为串行数据输入输出端(双向),后面有详细说明。
SCLK:SCLK为时钟输入端。
日历寄存器:
注:
(1):小时寄存器的位7用于定义DS1302是运行于12小时模式还是24小时模式。当为高时,选择12小时模式。在12小时模式下,位5为低时为AM,高时为PM。在24小时模式下,位5是第二个10小时位。
(2)、秒寄存器的位7定义为时钟暂停标志。当该位置为1时,时钟振荡器停止,DS1302处于低功耗状态;该位置0是,时钟开始运行。
(3)、控制寄存器的位7是写保护位,其他7位均置为0。在任何的对时钟和RAM的写操作之前,WP位必须为0。当WP位1时,写保护位防止对任一寄存器的写操作。
控制字节:
注:
9
电子课程设计报告
控制字节的最高有效位(位7)必须是逻辑1,如果它为0,则不能把数据写入DS1302中,位6如果为0,则表示存取日历时钟数据,为1表示存取RAM数据;位5至位1指示操作单元的地址;最低有效位(位0)如为0表示要进行写操作,为1表示进行读操作,控制字节总是从最低位开始输出。
数据读写及时序:
在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。
(四)、DS18B20温度传感器
管脚信息: GND:接地 DQ: 数据I/O
10
电子课程设计报告
VDD:电源 存储器:
控制字节:
Skip ROM [CCh]:允许总线控制器不用提供 位ROM 编码就使用存储器操作命令,在单点总线情况下右以节省时间。 Convert T[44h]:开始温度转换。
Read Scratchpad[BEh]:读取暂存器和CRC 字节,知道9字节 时序: 读时间隙:
当从DS12B20读取数据时,主机生成读时间隙。当主机把数据线从逻辑高电平拉到逻辑低电平的时候,写时间隙开始。数据线必须保持至少1μs,从DS18B20 输出的数据在读时间隙的下降沿出现后15μs 内有效。因此,主机在读时间隙开始后必须停止把I/O 脚驱动为低电平15μs,以读取I/O 脚状态。在读时间隙的结尾,I/O 引脚将被外部上拉电阻拉到高电平。所有读时间隙必须最少60μs,包括两个读周期间至少1μs的恢复时间。
写时间隙:
当主机把数据线从逻辑高电平拉到逻辑低电平的时候,写时间隙开始。所有写时间隙必须最少持续60μs,包括两个写周期间至少1μs 的恢复时间。I/O 线电平变低后,DS18B20 在一个15μs 到60μs 的窗口内对I/O 线采样。如果线上是高电平,就是写1,如果线上是低电平,就是写0。
主机要生成一个写时间隙,必须把数据线拉到低电平然后释放,在写时间隙开始后的15μs 内允许数据线拉到高电平。
11
电子课程设计报告
主机要生成一个写0 时间隙,必须把数据线拉到低电平并保持60μs。
(六)、按键部分
原理:
键盘扫描,先将P3.6口置零,此时扫描S5~S8,如果P3.0~P3.3中有为0,则说明有对应键按下。然后将P3.7口置零,此时扫描S1~S4,如果P3.0~P3.3中有为0,则说明有对应键按下。
四、软件设计方案
部分程序代码 (一)主程序: #include #include\"lcd1602.h\"  //包含头文件,直接引用显示文件 #include\"ds18b20.h\" #include \"DS1302.h\" #define uchar unsigned char  #define uint unsigned int #define TIMER0_COUNT 0xEE11 sbit mode=P3^0;  sbit plus=P3^1;  sbit dec=P3^2; //设定修改位数  //加键  //减键 uchar count,s1num,timer0_tick,count=0; typedef struct{ 12 电子课程设计报告 char hour;                     char minute;                     char second;               }time; typedef struct{ uint year;                     char month;                     char day;               }date; time now={11,20,7};  //显示时间初始值 date today={12,6,16}; char code dayofmonth[]={31,28,31,30,31,30,31,31,30,31,30,31};  //设定月份数组,用以判定12个月的最大值 char code weekday[7][10]={\"Week1 \\\\\  //设定行星期显示数 uchar monthday(uchar year,uchar month) { if(month==2 && year%4==0)            //用以判定是否为润年,其2月有29天 return(29);    else return(28);    //非闰年时的该月份天数28 } void display_week()  //由年月日计算星期 { char days; 13 ,用以显示星期数 电子课程设计报告 days=(today.day+1+2*today.month+3*(today.month+1)/5+today.year+today.year/4-today.year/100+today.year/400)%7; display_string(&weekday[days][0]);    } static void timer0_initialize(void)   //timer0 initialize  { EA=0;        //设置不接受所有中断      timer0_tick=0; TR0=0;             //关闭Timer0 TMOD=0X01;       //设置Timer0为模式2,16位工作模式      TL0=(TIMER0_COUNT & 0X00FF);    //设置Timer0低八位数值      TH0=(TIMER0_COUNT >> 8); //设置Timer0高八位数值      PT1=1; //设置Timer0的优先级为最高      ET0=1;   //设置接受Timer0的中断      TR0=1;           //启动Timer0      EA=1;          //设置系统接受中断  } void write_time(uchar add,uchar number)   //写时间 { gotoxy(2,add);      } void write_riqi(uchar add,uchar number)   //写日期 { gotoxy(1,add);  } 14 display_data(number); display_data(number); 电子课程设计报告 void keyscan()    //按键扫描程序 { uchar mode_num; //设定mode_num,来判断是哪一位要修改,当 mode_num为零时,为非修改模式 if(mode==0)  { delay(5); //延时 //\"修改位\"的选择 if(mode==0)  //当外部按键mode没按下一次时,都使mode_num自加, 即mode_num表示为按键mode按下的次数 { mode_num++; while(!mode); TR0=0;                write_com(0x0f);            } if(mode_num==1) gotoxy(1,1); //当mode_num为1时,为年份改变位, 光标移到(1,2) if(mode_num==2) gotoxy(1,4); //当mode_num为2时,为月份改变位,光标移到(1,5) if(mode_num==3) gotoxy(1,7); //当mode_num为3时,为日期改 变位,光标移到(1,8) if(mode_num==4) gotoxy(2,1);    //当mode_num为4时,为小时改变位,光标移到(2,2) if(mode_num==5) gotoxy(2,4);   //当mode_num为5时,为分钟改变位,光标移到(2,5) if(mode_num==6) gotoxy(2,7); //当mode_num为6时,为秒数改 变位,光标移到(2,8) if(mode_num==7) { mode_num=0; //非修改模式时,将mode_num置零,有助 //当mode_num为7时,退出修改模式 15 电子课程设计报告 于判断是否为修改模式                            } if(mode_num!=0) { if(plus==0)    //当加键为零,即外部触发一次时,进行以下处理 { delay(5);  //延时 if(plus==0) //延时再次判断加键,防止抖动 { //为修改模式时,加减键的处理子程序 } write_com(0x0c); TR0=1; if(mode_num==1)  //当为年份改变时,年份自加一,并且显示修改后的日期 { today.year++; //因为年份主要是最后两位在改 write_riqi(1,today.year); 变,所以除以100来计算年份值,除以100求得的商值为十位 gotoxy(1,14); display_week(); //因为改变日期都会改变周数,所以要重新显示 } if(mode_num==2)   //以下处理同上 { today.month++; //特别注 if(today.month==13)  today.month=1; 意,月份不能超过12,当为13时,要将月份置1 16 电子课程设计报告 write_riqi(4,today.month); //除以10来计算月份值,除以 10求得的商值为十位                                               gotoxy(1,14);                          display_week(); } //以下处理同上 if(mode_num==3) { today.day++; if(today.day>monthday(today.year,today.month))  //特别要判断每个月份的最大值,不能超过此数,超过后要将天数置一                        today.day=1; write_riqi(7,today.day); //除以10来计算月份值,除以10求 得的商值为十位 gotoxy(1,14);                          display_week(); } //以下处理同上 if(mode_num==4) { now.hour++; if(now.hour==24) now.hour=0;   //小时不能超过24      write_time(1,now.hour); } //以下处理同上 if(mode_num==5) { 17 电子课程设计报告 now.minute++; if(now.minute==60) now.minute=0; write_time(4,now.minute); } //以下处理同上 if(mode_num==6) { now.second++; if(now.second==60) now.second=0; write_time(7,now.second); } } } if(dec==0)   //减键的处理和加键处理处理相反 { delay(5); if(dec==0) { if(mode_num==1) { today.year--; write_riqi(1,today.year); gotoxy(1,14);                          display_week(); } 18 电子课程设计报告 if(mode_num==2) { today.month--; if(today.month==0) today.month=12; //要注意月份的最小值为1,当减为零时,要讲月份置为12 write_riqi(4,today.month); //除以10来计算月份值,除以 10求得的商值为十位 gotoxy(1,14);                          display_week(); } if(mode_num==3) { today.day--; if(today.day==0)  //当天数减为0时,要重新赋值,为上一个月的最大值 today.day=monthday(today.year,today.month); write_riqi(7,today.day); //除以10来计算月份值,除以 10求得的商值为十位 gotoxy(1,14);                          display_week(); } if(mode_num==4) { now.hour--; if(now.hour<0) now.hour=23;  //当小时数小于0时,要重新赋 19 电子课程设计报告 值,置为23 write_time(1,now.hour); } if(mode_num==5) { now.minute--; if(now.minute<0) now.minute=59; //注意点同上 write_time(4,now.minute); } if(mode_num==6) { now.second--; if(now.second<0) now.second=59; //注意点同上 write_time(7,now.second); } } } }  } void display_temp()    //显示温度子程序   { uint wendu; //设置wendu变量来存放从18b20读取的温度 uchar A1,A2;  //A1用来存放温度值的十位,A2存放个位 20 电子课程设计报告 tmpchange();   //启动温度转换   wendu=tmp();  //读取温度值    A1=wendu/10;  //求A1值    A2=wendu%10;  //求A2值   gotoxy(2,10); display_data(A1); //显示A1值 display_string(\".\"); //用小数点分开个位和小数 write_date(int_to_char[A2]);  //向lcd写数据,并且注意要转换A2数据类型   } void timer0(void) interrupt 1 { TR0=0; TL0=(TIMER0_COUNT & 0X00FF);//设置Timer0低八位数值      TH0=(TIMER0_COUNT >> 8);//设置Timer0高八位数值      TR0=1; count++; if(count==30) display_temp(); //延时,当count=30时,显示温度,并延时      if(count==200)    //当count自加到200时,开始判断此时时、分、秒的值,并进行转换 { count=0; now.second++; if(now.second==60) //当秒达到60时,向分进位,且秒数置零 { now.second=0; now.minute++; if(now.minute==60)   //当分达到60时,向时进位,且分数置零 { now.minute=0; 21 电子课程设计报告 零 now.hour++; if(now.hour==24) //当时达到24时,向天进位,且时数置 { now.hour=0;   today.day++;  if(today.day>monthday(today.year,today.month)) //判断天数,当超过本月最大天数时,向月份进位,且天数置一   {        today.day=1;                                    today.month++;        if(today.month==13)   //判断月份,当月份超过12时,向年份进位,月份置一                                    {             today.month=1;                                         today.year++;                                    }       write_riqi(4,today.month); //在位置4处,向lcd写月份   }   write_riqi(7,today.day); //在位置7处,向lcd写天数   gotoxy(1,14);                               display_week(); } } write_time(4,now.minute); //在位置4处,向lcd写分数 } write_time(1,now.hour);   //在位置1处,向lcd写时数 22 电子课程设计报告 } } write_time(7,now.second); //在位置7处,向lcd写秒数 void main() { SYSTEMTIME CurrentTime; LcdInitiate(); //初始化lcd timer0_initialize();  //初始化内部定时器 Initial_DS1302();  //初始化DS1302 DateToStr(&CurrentTime);  //从DS1302读取日期  TimeToStr(&CurrentTime);  //从DS1302读取时间  gotoxy(1,1); display_string(CurrentTime.DateString);  //显示日期 gotoxy(1,10); display_week();   //显示周几      gotoxy(2,1); display_string(CurrentTime.TimeString);  //显示时间      gotoxy(2,10); display_string(\"00.0C\");      while(1) keyscan();  } (二)DS1302时钟模块 #include  23 //显示温度 电子课程设计报告 #ifndef _REAL_TIMER_DS1302_2003_7_21_ #define _REAL_TIMER_DS1302_2003_7_21_ sbit  DS1302_CLK = P1^6;              //实时时钟时钟线引脚  sbit  DS1302_IO  = P1^7;              //实时时钟数据线引脚  sbit  DS1302_RST = P1^5;              //实时时钟复位线引脚 sbit  ACC0 = ACC^0; sbit  ACC7 = ACC^7; typedef struct __SYSTEMTIME__ { unsigned char Second; unsigned char Minute; unsigned char Hour; unsigned char Week; unsigned char Day; unsigned char Month; unsigned char  Year; unsigned char DateString[9]; unsigned char TimeString[9]; //定义的时间类型 }SYSTEMTIME; #define AM(X) X #define PM(X) (X+12)             #define DS1302_SECOND 0x80 0x80放的的秒数 #define DS1302_MINUTE #define DS1302_HOUR  #define DS1302_WEEK  #define DS1302_DAY // 转成24小时制 //时钟芯片的寄存器位置,存放时间,如 0x82 0x84  0x8A 0x86 24 电子课程设计报告 #define DS1302_MONTH #define DS1302_YEAR  #define DS1302_RAM(X) 0x88 0x8C (0xC0+(X)*2) //用于计算 DS1302_RAM 地 址的宏  RAM命令控制字地址:0xC0-0xFD void DS1302InputByte(unsigned char d)  //实时时钟写入一字节(内部函数) { unsigned char i;     ACC = d;     for(i=8; i>0; i--)     { DS1302_IO = ACC0;            //将IO线上的数据存放在ACC中 DS1302_CLK = 1; //特别注意,DS1302写一数据时, CLK要产生一负脉冲 DS1302_CLK = 0; //每次移位一位 ACC = ACC >> 1; }  } unsigned char DS1302OutputByte(void)  //实时时钟读取一字节(内部函数) { unsigned char i;     for(i=8; i>0; i--)     { ACC = ACC >>1; //将前一个下降沿读出的数据右 移一位从而读出该次读出的数放入ACC7         ACC7 = DS1302_IO; DS1302_CLK = 1; //SCLK下降沿读出DS1302的数 DS1302_CLK = 0;      } 25 电子课程设计报告 return(ACC);  } void Write1302(unsigned char ucAddr, unsigned char ucDa) //其中Addr为 DS1302地址, ucData为要写的数据 { DS1302_RST = 0;  沿,CLK为低电平     DS1302_CLK = 0;     DS1302_RST = 1; DS1302InputByte(ucAddr);        // 地址,命令      DS1302InputByte(ucDa);            DS1302_CLK = 1;     DS1302_RST = 0; } unsigned char Read1302(unsigned char ucAddr) { unsigned char ucData;     DS1302_RST = 0;     DS1302_CLK = 0;     DS1302_RST = 1; DS1302InputByte(ucAddr|0x01);        // 地址,命令 ucData = DS1302OutputByte();         // 读1字节数据    DS1302_CLK = 1; DS1302_RST = 0;     return(ucData); } //读取DS1302某地址的数据 // 写1字节数据 //写数据时,要注意RST产生一上升 26 电子课程设计报告 void DS1302_SetProtect(bit flag)        //是否写保护 { if(flag) Write1302(0x8E,0x80);  //0x8E 控制字节地址,bit7=WP WP=1 禁止数据 写入DS1302   } void DS1302_SetTime(unsigned char Address, unsigned char Value)        // 设置时间函数 { DS1302_SetProtect(0); Write1302(Address, ((Value/10)<<4 | (Value%10))); //读写数据时要特别注意数else Write1302(0x8E,0x00);  //WP=0  允许写入DS1302 据的处理,要写的数据要进行拼凑,将Value数据的十位取出,放入高四位,Value的个位放在低四位上 } void DS1302_GetTime(SYSTEMTIME *Time) { unsigned char ReadValue; //用来传递读取1302的数据 ReadValue = Read1302(DS1302_SECOND);//读取秒数,存放在ReadValue变量中 Time->Second = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); // 将 ReadValue数据的第4、5、6位取出,实际上就是秒数的十位,但是以二进制的形式表示,所以要进行移位,向右移四位,在乘以10,在加上ReadValue变量中秒数的,将十六进制的数化成十进制数表示,并放在Time->Second中 ReadValue = Read1302(DS1302_MINUTE); Time->Minute = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);//同上 ReadValue = Read1302(DS1302_HOUR); 27 电子课程设计报告 } Time->Hour = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);  //同上 ReadValue = Read1302(DS1302_DAY); Time->Day = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); ReadValue = Read1302(DS1302_WEEK); Time->Week = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); //同上 ReadValue = Read1302(DS1302_MONTH); Time->Month = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); //同上 ReadValue = Read1302(DS1302_YEAR); Time->Year = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F); //同上 //同上 void DateToStr(SYSTEMTIME *Time) 序 { //年份转换成可供lcd显示的字段子程 Time->DateString[0] = Time->Year/10 + '0'; //Time->DateString[0]中存放的就 是要显示年的十位 Time->DateString[1] = Time->Year%10 + '0'; //Time->DateString[1]中存放的就 是要显示年的个位 Time->DateString[2] = '-'; //年和月份之间用-分开 Time->DateString[3] = Time->Month/10 + '0'; //Time->DateString[3]中存放的 就是要显示月份的十位 Time->DateString[4] = Time->Month%10 + '0'; //Time->DateString[4]中存放的 就是要显示月份的个位 Time->DateString[5] = '-'; Time->DateString[6] = Time->Day/10 + '0';  //Time->DateString[6]中存放的就 是要显示日的十位 Time->DateString[7] = Time->Day%10 + '0';  //Time->DateString[7]中存放的 就是要显示日的个位 Time->DateString[8] = '\\0'; //记住,结束时要添加'\\0',以作为 28 电子课程设计报告 结束标志 } void TimeToStr(SYSTEMTIME *Time) {          } void Initial_DS1302(void) { unsigned char Second=Read1302(DS1302_SECOND); if(Second&0x80)             //BIT7=CH  CH=0振荡器允许工作,CH=1, //DS1302的初始化程序 Time->TimeString[0] = Time->Hour/10 + '0'; Time->TimeString[1] = Time->Hour%10 + '0'; Time->TimeString[2] = ':'; Time->TimeString[3] = Time->Minute/10 + '0'; Time->TimeString[4] = Time->Minute%10 + '0'; Time->TimeString[5] = ':'; Time->TimeString[6] = Time->Second/10 + '0'; Time->TimeString[7] = Time->Second%10 + '0'; Time->TimeString[8] = '\\0'; //时间转换成可供lcd显示的字段子程序 振荡器停止     }  #endif (三)DS18B20温度模块 #include  29 DS1302_SetTime(DS1302_SECOND,0); 电子课程设计报告 #include  uint temp;             // 温度变量 sbit DS=P1^0;           //定义数据传输接口 void delay1(uchar count) //延时 { while(count>0) count--; } void reset(void)       //送初值和初始命令 {   DS=0;   delay1(100);   DS=1;   delay1(4);   delay1(200); } bit read_bit(void)       //读一比特,特别注意对于18b20,要读数据时,一定要产生DS正脉冲,然后才传送数据 { bit temp;     DS=0;     _nop_();     DS=1;     _nop_();     temp=DS;     delay1(200);     return temp; 30 电子课程设计报告 } uchar read_byte(void)   //读一字节的数据 { uchar i,byte=0;      bit j; for(i=0;i<8;i++)      { byte=_cror_(byte ,1); //将byte数据向右循环一位,每次都取出最高位          j=read_bit(); //设定j为读取进来的一比特数 if(j==0) byte=byte|0x00;  //当j=0时,即读取一字节数完毕,取出次字节数 else   byte=byte|0x80;      } return byte; } void write_byte(uchar command)   //写一字节到18b20 {   uchar i; for(i=0;i<8;i++)  //用i来设定一字节的数据     { if((command & 0x01)==0)  脉冲         {            DS=0;             delay1(8);            DS=1;           _nop_(); 31 //每次取出最高位 //返回字节数 //取出最低位为零时,DS要产生一个负脉冲 电子课程设计报告 }         else            {              DS=0;              _nop_();             DS=1;              delay1(8);           } command=_cror_(command,1);           }  } void tmpchange(void)  //启动18b20 {   reset(); write_byte(0xcc);  //直接向18b20发送温度变换命令         write_byte(0x44);  //启动18b20进行温度转换 } uint tmp()               //获取温度 {   float tt;   uchar a,b;   reset(); write_byte(0xcc); //直接向18b20发送温度变换命令   write_byte(0xbe); //读取温度寄存器的温度值   a=read_byte();//读低八位   b=read_byte();//读高八位   temp=b; //否则产生一上升沿 32 电子课程设计报告 temp<<=8;    //因为18b20处理接受到的数据要进行处理,先将高八位左移4位 temp=temp|a; //在和低八位相或,整合取得的数据   tt=temp*0.0625;   temp=tt*10+0.5;   return temp; } (四)LCD1602显示模块 #include //读写控制信号线 sbit lcden=P2^2;   //使能信号线 sbit BF=P0^7;     //忙碌标志位,将BF位定义为P0.7引脚 #define lcd_mode 0x38 #define display_cursor 0x0f #define undisplay_cursor 0x0c char code int_to_char[]={'0','1','2','3','4','5','6','7','8','9'}; //******************************************************** void delay(uint x) {    } //********************************************************    unsigned char BusyTest(void) 33 //延时程序 uint a,b; for(a=x;a>0;a--) for(b=10;b>0;b--); 电子课程设计报告 { bit result; rs=0;       //根据规定,RS为低电平,RW为高电平时,可以读状态 rw=1; lcden=1;        //E=1,才允许读写     _nop_();   //空操作     _nop_();     _nop_(); _nop_();   //空操作四个机器周期,给硬件反应时间     result=BF;  //将忙碌标志电平赋给result lcden=0; return result;   } /***************************************************** 函数功能:将模式设置指令或显示地址写入液晶模块 入口参数:dictate ***************************************************/ void WriteInstruction (unsigned char dictate) { while(BusyTest()==1); //如果忙就等待 rs=0;                   //根据规定,RS和R/W同时为低电平时,可以 写入指令 rw=0; lcden=0;                    //E置低电平(根据表8-6,写指令时,E为高 脉冲, // 就是让E从0到1发生正跳变,所以应先 置\"0\" _nop_(); _nop_();                //空操作两个机器周期,给硬件反应时间 34 电子课程设计报告 P0=dictate;             //将数据送入P0口,即写入指令或地址  _nop_();  _nop_();  _nop_(); _nop_();                //空操作四个机器周期,给硬件反应时间  lcden=1;                    //E置高电平  _nop_();  _nop_();  _nop_(); _nop_();               //空操作四个机器周期,给硬件反应时间  lcden=0;                  //当E由高电平跳变成低电平时,液晶模块开 始执行命令  } void write_com(uchar com) //写命令 {         } void WriteAddress(unsigned char x)  { WriteInstruction(x|0x80); //显示位置的确定方法规定为\"80H+地址码x\"  } //******************************************************** 35 P0=com; rs=0; //写命令,记住时序问题 //rs要为低电平 //使lcd使能端产生一个正脉冲 lcden=0; delay(10); lcden=1; delay(10); lcden=0; 电子课程设计报告 void write_date(unsigned char y) //写数据 { { while(BusyTest()==1); rs=1;           //RS为高电平,RW为低电平时,可以写入数据   rw=0; lcden=0;            //E置低电平(根据表8-6,写指令时,E为高脉冲, P0=y;           //将数据送入P0口,即将数据写入液晶模块 _nop_();   _nop_(); _nop_(); _nop_();       //空操作四个机器周期,给硬件反应时间 lcden=1;             //E置高电平   _nop_();   _nop_();   _nop_(); _nop_();          //空操作四个机器周期,给硬件反应时间 lcden=0;              //当E由高电平跳变成低电平时,液晶模块开始执行 命令  } } //******************************************************** void LcdInitiate(void) { delay(15);             //延时15ms,首次写指令时应给LCD一段较长的反应时间 WriteInstruction(0x38);  //显示模式设置:16×2显示,5×7点阵,8位数据接口 delay(5);   //延时5ms  WriteInstruction(0x38); 36 电子课程设计报告 } delay(5); WriteInstruction(0x38); delay(5); WriteInstruction(0x0d);  //显示模式设置:显示开,有光标,光标闪烁 delay(5); WriteInstruction(0x0c);  //显示模式设置:光标右移,字符不移 delay(5); WriteInstruction(0x01);  //清屏幕指令,将以前的显示内容清除 delay(5); //******************************************************** void display_string(char *p)   //写字符串 { while(*p) { write_date(*p); delay(20); p++; }  } //******************************************************** void gotoxy(uchar x,uchar y) //设置移动坐标  { if(x==1) write_com(0x80+y); else write_com(0xc0+y); 37 电子课程设计报告 } //********************************************************  void display_data(char number)  //显示数据 { uchar x,y; x=number/10;   //将取得的数据转换成十进制     y=number%10; write_date(int_to_char[x]);     write_date(int_to_char[y]);  } (五)键盘扫描部分 char fun,flag,flag2,adjust,wei,adt; sbit rr=P3^7; sbit ll=P3^6; sbit anjian=P1^6; sbit right=P3^3; sbit left=P3^2; sbit up=P3^1; sbit down=P3^0; sbit menu=P3^3; sbit ok=P3^2; void keyscan()                {  uchar i; ll=0; //按键功能标志位        //按键灯光提醒 //调整时间按键程序 38 电子课程设计报告 if(fun==1)                 //功能1,时钟情况下 { if(ok==0)                  //判断OK键摁下 {   delay_MS(5);           //去抖动  if(ok==0)   { } } ll=1; rr=0; while(!ok);      //松手监测 anjian=0;       //提示灯点亮 delay_MS(240); anjian=1; flag++;         //按键计数 ds1302_write_byte(write_wp,0x00);  //DS1302暂停ds1302_write_byte(write_sec,0x80); ds1302_write_byte(write_wp,0x80); if(flag==2)      //调整时间完毕,退出调整 {  wei=0;  adt=0; ds1302_write_time();  flag=0; } adjust=1; //允许调整时间标志位 lcd1602_write_command(0x0d); lcd1602_write_command(0x80+0x03); 39 电子课程设计报告 if(adjust==1) { if(right==0)               //判断左移按键 {                       } …… delay_MS(5); if(right==0) {                   } while(!right); anjian=0; delay_MS(240); anjian=1; wei++;            //移动位置标志 adt=1; if(wei==1) {   } …… if(wei==8)         //循环移动 {    } wei=1; lcd1602_write_command(0x0d); lcd1602_write_command(0x80+0x03); lcd1602_write_command(0x0d);  //显示光标 lcd1602_write_command(0x80+0x03); if(adt==1) 40 电子课程设计报告 {  if(up==0)                //时间加1                                                              …… {                                   ll=1; } delay_MS(5); if(up==0) {  while(!up);  anjian=0;  delay_MS(240);  anjian=1;  if(wei==1)  {   time1[1]=time1[1]+1; if(time1[1]==80)   {    time1[1]=0; } ds1302_write_time();  //写入调整后的时间   ds1302_read_time();   //显示调整后的时间  for(i=1;i<8;i++)       TempData[i]=time1[i];   change();   display(); lcd1602_write_command(0x0d);   lcd1602_write_command(0x80+0x03); } 41 电子课程设计报告 } } void keyscan1()             //主功能键的扫描 {  ll=0;  if(menu==0)  {   delay_MS(5);    if(menu==0)    {     while(!menu);     anjian=0;     delay_MS(240);     anjian=1;     fun++;     if(fun==1)     {      flag=1;      flag2=1;      EA=0;      shizhong();      delay_MS(1000);      flag=0; }     if(fun==2)     { flag=1; 42 电子课程设计报告 } } } } flag2=1; wendu(); delay_MS(1000); EA=1; flag2=0; fun=0; ll=1; 五、总结 通过课程设计,锻炼了进行课题制作的能力,从画原理图,元器件的选用,电路的设计,阅读芯片资料,程序的编写,调试中,进一步掌握单片机技术,提高编程能力。并且激发学习的兴趣,理论与实际相结合,为进一步自主研究打下基础。 在整个过程中,PCB图的绘画以及程序的编写这两方面需很大的耐心。同时,要求仔细研究,严格按照时序图编写。另外,在程序编写上,如何将温度和时钟通过一键进行功能的切换也是一个难点。在做到此项功能之前,必须要求各个功能模块直接划分清晰,程序流程及逻辑合理。要求各个模块之间不会产生干扰。完成后,可以提高程序结构度,熟练标志位的正确使用,进一步提高逻辑能力。 对于综合性的程序,使用多个头文件的方式,能有效提高编程时的清晰度,逻辑性。也便于迅速准确查找问题所在。因此,又可提高头文件的使用能力,熟悉头文件的功能及意义。 我在编写此课题的程序时,先将各个功能模块划分清楚,确定各个头文件。然后更具芯片说明书,先将各个芯片基本功能的子程序写完。其中包括初始化, 43 电子课程设计报告 读写字节,写命令等程序。然后将各个模块功能进行整合。 先将DS1302读出的时间数据,转换为字符,送LCD1602显示,从而完成基本时钟功能。程序完成后,烧入芯片,测试程序,如果能按照预设格式显示出时间,并且时间走动,说明时钟和显示模块正常。如果未能达到预设效果,检查程序,反复调试,直至达到效果,方可继续。建议检查DS1302读写时序部分的程序,LCD1602初始化部分程序。 然后开始编写按键功能部分,按要求完成各个功能。注意各个标志位的使用,功能的切换,按键调时后,LCD1602的及时显示,按键之间的互相屏蔽。可通过标志位允许后续按键的功能,调时时,写入后,立即读取显示,即可完成人机对话。 接着,写DS18B20部分的程序,用定时器中断,定时控制温度读取。读取温度转换成字符,送LCD1602显示。实现温度功能。此处应注意中断的开启和关闭。不然会影响温度读取。我在读取子程序中先关闭中断,退出子程序前重新开启中断。 再来完成时钟和温度功能的拓展部分,整点报时和高温报警。原理很简单,关键在于,报警监测程序加在哪里。一开始我加在了读取时间和温度的程序部分,然后发现如果这样,那么在LCD1602显示该数据之前就产生了报警,从而产生了时差,所以,我将报警程序加在了显示时进行判断。 紧接着就改完成两个功能间切换的程序,通过一个fun标志位,切换功能模块。设置两个标志允许位。当切换至时钟时,关闭温度允许,切换后,则反之。 最后添加开机画面,直接LCD1602显示。按下功能键后进入程序。完成所有部分。 整个程序部分并不困难,有一定的单片机基础的话,很容易的能写完所有的程序。难点在于完成代码后的调试,查错,需要很强的耐心。 七、 参考资料 1、DS1302芯片说明书(中文版) 2、DS18B20芯片说明书(中文版) 3、LCD1602芯片说明书(中文版) 44 电子课程设计报告 4、郭天祥《十天学会单片机》 45              
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- 91gzw.com 版权所有 湘ICP备2023023988号-2
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务
