1.1 Keil uVision4的简要介绍
2009年2月发布Keil μVision4,Keil μVision4引入灵活的窗口管理系统,使开发人员能够使用多台监视器,并提供了视觉上的表面对窗口位置的完全控制的任何地方。新的用户界面可以更好地利用屏幕空间和更有效地组织多个窗口,提供一个整洁,高效的环境来开发应用程序。新版本支持更多最新的ARM芯片,还添加了一些其他新功能。
2011年3月ARM公司发布最新集成开发环境RealView MDK开发工具中集成了最新版本的Keil uVision4,其编译器、调试工具实现与ARM器件的最完美匹配。
Keil C51开发系统基本知识Keil C51开发系统基本知识 1. 系统概述
Keil C51软件提供丰富的库函数和功能强大的集成开发调试工具,全Windows界面。另外重要的一点,只要看一下编译后生成的汇编代码,就能体会到Keil C51生成的目标代码效率非常之高,多数语句生成的汇编代码很紧凑,容易理解。在开发大型软件时更能体现高级语言的优势。下面详细介绍Keil C51开发系统各部分功能和使用。 2. Keil C51单片机软件开发系统的整体结构
C51工具包的整体结构,uVision与Ishell分别是C51 for Windows和for Dos的集成开发环境(IDE),可以完成编辑、编译、连接、调试、仿真等整个开发流程。开发人员可用IDE本身或其它编辑器编辑C或汇编源文件。然后分别由C51及C51编译器编译生成目标文件(.OBJ)。目标文件可由LIB51创建生成库文件,也可以与库文件一起经L51连接定位生成绝对目标文件(.ABS)。ABS文件由OH51转换成标准的Hex文件,以供调试器dScope51或tScope51使用进行源代码级调试,也可由仿真器使用直接对目标板进行调试,也可以直接写入程序存贮器如EPROM中。
1 / 33
使用独立的Keil仿真器时,注意事项:
* 仿真器标配11.0592MHz的晶振,但用户可以在仿真器上的晶振插孔中换插其他频率的晶振。
* 仿真器上的复位按钮只复位仿真芯片,不复位目标系统。
* 仿真芯片的31脚(/EA)已接至高电平,所以仿真时只能使用片内ROM,不能使用片外ROM;但仿真器外引插针中的31脚并不与仿真芯片的31脚相连,故该仿真器仍可插入到扩展有外部ROM(其CPU的/EA引脚接至低电平)的目标系统中使用。 优点:
1.Keil C51生成的目标代码效率非常之高,多数语句生成的汇编代码很紧凑,容易理解。在开发大型软件时更能体现高级语言的优势。
2.与汇编相比,C语言在功能上、结构性、可读性、可维护性上有明显的优势,因而易学易用。用过汇编语言后再使用C来开发,体会更加深刻。Keil C51软件提供丰富的库函数和功能强大的集成开发调试工具,全Windows界面。
图1.1 KEIL操作界面
2 / 33
1.2 STC-ISP的简要介绍
STC-ISP 是一款单片机下载编程烧录软件,是针对STC系列单片机而设计的,可下载STC89系列、12C2052 系列和12C5410等系列的STC单片机,使用简便,现已被广泛使用。
操作说明如下:
(1) 打开STC-ISP,如下图界面,在MCU Type栏目下选中单片机,如STC89C52RC。
图1.2ISP操作界面
(2) 根据您的9针数据线连接情况选中端口,波特率一般保持默认,如果遇到下载问题,可以适当下调一些。
3 / 33
(3) 先确认硬件连接正确,点击“打开文件”并在对话框内找到您要下载的HEX文件。
图1.3 选择HEX文件
(4)选择所要下载的文件,这样可以使您在每次编译KEIL时HEX代码能自动加载到STC-ISP,点击“Download/下载”。
(5) 手动按下电源开关便即可把可执行文件HEX写入到单片机内,如图是正在写入程序截图。
(6)程序写入完毕,目标板开始运行程序结果。
4 / 33
2 方案分析与确定 2.1 方案思路分析 由题目要求,根据设置的年份日的显示全年的年月公历、星期等信息。由于需要显示去年的日期以及星期等信息,普通的LED数码管显然已经不能胜任,为此,我们可以选择LCD1602液晶显示器完成课设要求。目前所具备的设备只有一块以STC89C52为内核的单片机,PC机。 课设要求显示日期等信息,我们可以充分利用单片机上面集成的DS1302时钟芯片。该芯片可以产生年份,月份,日期,星期,时,分,秒的数据,因此,完全可以完成实验的各项要求。 基于以上分析,我们可以利用DS1302产生的各种时钟数据,由CPU完成对数据的读取,然后通过CPU将数据写至LCD1602,以显示数据,达到可视化的效果。 对于课程设计的拓展部分,我们可以利用单片机开发板上面集成的DS18B20温度传感器来测试周围环境的温度,同样通过LCD1602来显示。此外可以通过测试按键是否闭合来改变各种时间数据,已达到校准的目的。 DS1302 CPU LCD1602 DS18B20 图2.1 整体设计框架图 5 / 33
2.2 方案流程图 根据以上分析,以及使用各种芯片的操作流程,我们可以大致确定课程设计的软件设计方案方案,其大致流程如下:
开始 相关引脚的特殊位定义 相关数字及字母数组设置 完成与DS18B20,LCD1602,DS1302相关的子函数的编程 完成与DS18B20,LCD1602,DS1302相关的初始化设置 CPU读取DS18B20 ,DS1302的温度和时间数据 CPU将数据送至LCD1602显示 检测是否有时间校准按键按下 重新设置时间,并写至进DS1302的寄存器 图2.2设计流程图
6 / 33
3 单元芯片电路的设置及总体设计
3.1 DS1302芯片引脚功能
DS1302 是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周日、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.5V~5.5V。采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。DS1302内部有一个31×8的用于临时性存放数据的RAM寄存器。DS1302是DS1202的升级产品,与DS1202兼容,但增加了主电源/后备电源双电源引脚,同时提供了对后备电源进行涓细电流充电的能力。
DS1302的引脚排列,其中Vcc1为后备电源,VCC2为主电源。在主电源关闭的情况下,也能保持时钟的连续运行。DS1302由Vcc1或Vcc2两者中的较大者供电。当Vcc2大于Vcc1+0.2V时,Vcc2给DS1302供电。当Vcc2小于Vcc1时,DS1302由Vcc1供电。X1和X2是振荡源,外接32.768kHz晶振。RST是复位/片选线,通过把RST输入驱动置高电平来启动所有的数据传送。RST输入有两种功能:首先,RST接通控制逻辑,允许地址/命令序列送入移位寄存器;其次,RST提供终止单字节或多字节数据的传送手段。当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。如果在传送过程中RST置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。上电运行时,在Vcc>2.0V之前,RST必须保持低电平。只有在SCLK为低电平时,才能将RST置为高电平。I/O为串行数据输入输出端(双向),后面有详细说明。SCLK为时钟输入端。下图为DS1302的引脚功能图:
7 / 33
图3.1 DS1302引脚图
管脚描述:
X1 X2 32.768KHz 晶振管脚 GND 地 RST 复位脚 I/O 数据输入/输出引脚 SCLK 串行时钟 Vcc1,Vcc2 电源供电管脚
此外,拓展中用到了DS18B20温度传感器,其使用与DS1302比较类似,因此这里不再赘述。
8 / 33
3.2 DS1302的使用方法
使用DS1302时,要对其引脚和寄存器进行特殊设置,以实现所需功能。引脚设置后面会提及,这里不做说明,主要讲一下程序的编写。DS1302的的工作过程中包过读写一个字节数据等过程,下面做相应的解释。 /******************************** 功能:往DS1302写入数据
*********************************/ void W1302(ucharucAddr, ucharucDa) {
T_RST = 0; T_CLK = 0; T_RST = 1;
RTInputByte(ucAddr); /* 地址,命令 */ RTInputByte(ucDa); /* 写1Byte数据*/ T_CLK = 1; T_RST = 0; }
/******************************** 功能:读取DS1302某地址的数据 *********************************/ uchar R1302(ucharucAddr) {
ucharucData; T_RST = 0; T_CLK = 0; T_RST = 1;
RTInputByte(ucAddr); /* 地址,命令 */ ucData = RTOutputByte(); /* 读1Byte数据 */ T_CLK = 1;
9 / 33
T_RST = 0; return(ucData); }
/*********************************** 功能:设置初始时间
************************************/ void Set1302(uchar *pClock) { uchari;
ucharucAddr = 0x80;
EA = 0;
W1302(0x8e,0x00); /* 控制命令,WP=0,写操作?*/ for(i =7; i>0; i--) {
W1302(ucAddr,*pClock); /* 秒分时日月星期年 */ pClock++; ucAddr +=2; }
W1302(0x8e,0x80); /* 控制命令,WP=1,写保护?*/ }
/****************************** 功能:读取DS1302当前时间 *******************************/ void Get1302(ucharucCurtime[]) { uchari;
ucharucAddr = 0x81;
EA = 0;
10 / 33
EA = 1;
for (i=0; i<7; i++) {
ucCurtime[i] = R1302(ucAddr);/*格式为: 秒分时日月星期年 */ ucAddr += 2; } }
EA = 1;
3.3 LCD1602引脚功能
1602液晶也叫1602字符型液晶它是一种专门用来显示字母、数字、符号等的点阵型液晶模块它有若干个5X7或者5X11等点阵字符位组成,每个点阵字符位都可以显示一个字符。每位之间有一个点距的间隔每行之间也有间隔起到了字符间距和行间距的作用,正因为如此所以他不能显示图形 n1602LCD是指显示的内容为16X2,即可以显示两行,每行16个字符液晶模块(显示字符和数字)。
图3.2 LCD1602引脚图
1602采用标准的16脚接口,其中: 第1脚:VSS为电源地 第2脚:VDD接5V电源正极
11 / 33
第3脚:V0为液晶显示器对比度调整端,接正电源时对比度最弱,接地电源时对比度最高(对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器调整对比度)。
第4脚:RS为寄存器选择,高电平1时选择数据寄存器、低电平0时选择指令寄存器。
第5脚:RW为读写信号线,高电平(1)时进行读操作,低电平(0)时进行写操作。
第6脚:E(或EN)端为使能(enable)端。 第7~14脚:D0~D7为8位双向数据端。
第15~16脚:空脚或背灯电源。15脚背光正极,16脚背光负极。
3.4LCD1602的使用
LCD1602的使用与前面的DS1302的使用有类似的地方,包括引脚的设置和对应的编程,引脚的设置后面会提及到,这里只涉及对应的编程。
/*LCD1602 延时*/ void Lcd_delay(uchar ms) {
uchar j; while(ms--){
for(j=0;j<250;j++) {;} } }
/*LCD1602 忙等待*/
void Lcd_busy_wait(){ Lcd_rs = 0;
12 / 33
Lcd_rw = 1; Lcd_en = 1; Lcd_data = 0xff; while (Lcd_data&0x80); Lcd_en = 0;
/*LCD1602 命令字写入*/ }
void Lcd_mand_write(uchar mand) {
Lcd_busy_wait(); Lcd_rs = 0; Lcd_rw = 0; Lcd_en = 0; Lcd_data = mand; Lcd_en = 1; Lcd_en = 0; }
/*LCD1602 初始化*/
void Init_lcd() {
Lcd_delay(20); Lcd_mand_write(0x38); Lcd_delay(100);
Lcd_man/*LCD1602 延时*/d_write(0x38); Lcd_delay(50); Lcd_mand_write(0x38); Lcd_delay(10);
13 / 33
Lcd_mand_write(0x08); Lcd_mand_write(0x01); Lcd_mand_write(0x06); Lcd_mand_write(0x0c); Lcd_data = 0xff; /*释放数据端口*/ }
void Lcd_char_write(uchar x_pos,y_pos,lcd_dat) /*LCD1602 字符写入*/ {
x_pos &= 0x0f; /* X位置X围 0~15 */ y_pos &= 0x01; /* Y位置X围 0~ 1 */ if(y_pos==1) x_pos += 0x40; x_pos += 0x80; Lcd_mand_write(x_pos); Lcd_busy_wait(); Lcd_rs = 1; Lcd_rw = 0; Lcd_en = 0; Lcd_data = lcd_dat; Lcd_en = 1; Lcd_en = 0;
Lcd_data = 0xff; /*释放数据端口*/ }
14 / 33
4 整体电路设计和程序
由上面的分析可以得到实验的电路图如下:
图4.1 仿真电路图
设计源代码如下:
/****************************************************************
程序名称: LCD1602显示时间显示当前温度
*****************************************************************/
15 / 33
/*头文件*/
#include sbit DQ =P3^5; //定义DS18B20通信端口 sbit lcd_rs_port = P1^7; /*定义LCD控制端口*/ sbit lcd_rw_port = P1^6; sbit lcd_en_port = P2^4; #define lcd_data_port P0 sbit led_latch_port = P2^5; /*发光二极管寄存器LE引脚*/ sbit sled_latch_port = P3^6; /*数码管寄存器LE引脚*/ sbit T_CLK = P1^4; /*实时时钟时钟线引脚 */ sbit T_IO = P1^5; /*实时时钟数据线引脚 */ sbit T_RST = P2^2; /*实时时钟复位线引脚 */ sbit ACC0 = ACC^0; sbit ACC7 = ACC^7; uchar code mun_to_char[] = {\"0123456789ABCDEF\定义数字跟ASCII码的关系*/ uchar data time_data_buff[7]={0x50,0x59,0x23,0x31,0x12,0x04,0x12};/*格式为: 秒分时日月星期年 */ uchar data lcd1602_line1[]={\" 2000/00/00 000 \uchar data lcd1602_line2[]={\" 00:00:00 00.0\uchar 16 / 33 code Weeks[][3]={{\"SUN\\"SUN\ /****************************************************************函数名:RTInputByte() 功能:实时时钟写入一字节 说明:往DS1302写入1Byte数据 (内部函数) 入口参数:d 写入的数据 返回值:无 ****************************************************************/ void RTInputByte(uchar d) { uchar i; ACC = d; for(i=8; i>0; i--) { T_IO = ACC0; /*相当于汇编中的 RRC */ T_CLK = 1; T_CLK = 0; ACC = ACC >> 1; } } /****************************************************************函数名:RTOutputByte() 功能:实时时钟读取一字节 说明:从DS1302读取1Byte数据 (内部函数) 入口参数:无 返回值:ACC ****************************************************************/ uchar RTOutputByte(void) 17 / 33 { uchar i; for(i=8; i>0; i--) { ACC = ACC >>1; /*相当于汇编中的 RRC */ ACC7 = T_IO; T_CLK = 1; T_CLK = 0; } return(ACC); } /****************************************************************函数名:W1302() 功能:往DS1302写入数据 说明:先写地址,后写命令/数据 (内部函数) 调用:RTInputByte() , RTOutputByte() 入口参数:ucAddr: DS1302地址, ucData: 要写的数据 返回值:无 ****************************************************************/void W1302(uchar ucAddr, uchar ucDa) { T_RST = 0; T_CLK = 0; T_RST = 1; RTInputByte(ucAddr); /* 地址,命令 */ RTInputByte(ucDa); /* 写1Byte数据*/ T_CLK = 1; T_RST = 0; } 18 / 33 /*************************************************************** 函数名:R1302() 功能:读取DS1302某地址的数据 说明:先写地址,后读命令/数据 (内部函数) 调用:RTInputByte() , RTOutputByte() 入口参数:ucAddr: DS1302地址 返回值:ucData :读取的数据 ****************************************************************/uchar R1302(uchar ucAddr) { uchar ucData; T_RST = 0; T_CLK = 0; T_RST = 1; RTInputByte(ucAddr); /* 地址,命令 */ ucData = RTOutputByte(); /* 读1Byte数据 */ T_CLK = 1; T_RST = 0; return(ucData); } /**************************************************************** 函数名:Set1302() 功能:设置初始时间 说明:先写地址,后读命令/数据(寄存器多字节方式) 调用:W1302() 入口参数:pClock: 设置时钟数据地址格式为: 秒分时日月星期年 7Byte (BCD码)1B 1B 1B 1B 1B 1B 1B 返回值:无 ****************************************************************/ 19 / 33 void Set1302(uchar *pClock) { uchar i; uchar ucAddr = 0x80; EA = 0; W1302(0x8e,0x00); /* 控制命令,WP=0,写操作?*/ for(i =7; i>0; i--) { W1302(ucAddr,*pClock); /* 秒分时日月星期年 */ pClock++; ucAddr +=2; } W1302(0x8e,0x80); /* 控制命令,WP=1,写保护?*/ EA = 1; } /**************************************************************** 函数名:Get1302() 功能:读取DS1302当前时间 说明: 调用:R1302() 入口参数:ucCurtime: 保存当前时间地址。当前时间格式为: 秒分时日月星期年 7Byte (BCD码) 1B 1B 1B 1B 1B 1B 1B 返回值:无 ****************************************************************/void Get1302(uchar ucCurtime[]) { uchar i; uchar ucAddr = 0x81; 20 / 33 EA = 0; for (i=0; i<7; i++) { ucCurtime[i] = R1302(ucAddr);/*格式为: 秒分时日月星期年 */ ucAddr += 2; } EA = 1; } //DS18B20延时函数 void delay(unsigned int i) { while(i--); } //DS18B20初始化函数 void Init_DS18B20(void) { unsigned char x=0; DQ = 1; //DQ复位 delay(8); //稍做延时 DQ = 0; //单片机将DQ拉低 delay(80); //精确延时大于 480us DQ = 1; //拉高总线 delay(14); x=DQ; //稍做延时后如果x=0则初始化成功 x=1则初始化失败 delay(20); } //DS18B20读一个字节 uchar ReadOneChar(void) 21 / 33 { unsigned char i=0; unsigned char dat = 0; for (i=8;i>0;i--){ DQ = 0; // 给脉冲信号 dat>>=1; DQ = 1; // 给脉冲信号 if(DQ) dat|=0x80; delay(4); } return(dat); } //DS18B20写一个字节 void WriteOneChar(unsigned char dat) { unsigned char i=0; for (i=8; i>0; i--){ DQ = 0; DQ = dat&0x01; delay(5); DQ = 1; dat>>=1; } } //DS18B20读取温度 uint ReadTemperature(void) { unsigned char a=0; 22 / 33 unsigned char b=0; unsigned int t=0; float tt=0; Init_DS18B20(); WriteOneChar(0xCC); // 跳过读序号列号的操作 WriteOneChar(0x44); // 启动温度转换 Init_DS18B20(); WriteOneChar(0xCC); //跳过读序号列号的操作 WriteOneChar(0xBE); //读取温度寄存器等(共可读9个寄存器)前两个就是温度 a=ReadOneChar(); b=ReadOneChar(); t=b; t<<=8; t=t|a; tt=t*0.0625; //将温度的高位与低位合并 t= tt*10+0.5; //对结果进行4舍5入 return(t); } //--------------------------------------------- void lcd_delay(uchar ms) /*LCD1602 延时*/ { uchar j; while(ms--){ for(j=0;j<250;j++) {;} } } void lcd_busy_wait() /*LCD1602 忙等待*/ 23 / 33 { lcd_rs_port = 0; lcd_rw_port = 1; lcd_en_port = 1; lcd_data_port = 0xff; while (lcd_data_port&0x80); lcd_en_port = 0; } void lcd_mand_write(uchar mand) /*LCD1602 命令字写入*/ { lcd_busy_wait(); lcd_rs_port = 0; lcd_rw_port = 0; lcd_en_port = 0; lcd_data_port = mand; lcd_en_port = 1; lcd_en_port = 0; } void lcd_system_reset() /*LCD1602 初始化*/ { lcd_delay(20); lcd_mand_write(0x38); lcd_delay(100); lcd_mand_write(0x38); lcd_delay(50); lcd_mand_write(0x38); lcd_delay(10); lcd_mand_write(0x08); 24 / 33 lcd_mand_write(0x01); lcd_mand_write(0x06); lcd_mand_write(0x0c); lcd_data_port = 0xff; /*释放数据端口*/ } void lcd_char_write(uchar x_pos,y_pos,lcd_dat) /*LCD1602 字符写入*/ { x_pos &= 0x0f; /* X位置X围 0~15 */ y_pos &= 0x01; /* Y位置X围 0~ 1 */ if(y_pos==1) x_pos += 0x40; x_pos += 0x80; lcd_mand_write(x_pos); lcd_busy_wait(); lcd_rs_port = 1; lcd_rw_port = 0; lcd_en_port = 0; lcd_data_port = lcd_dat; lcd_en_port = 1; lcd_en_port = 0; lcd_data_port = 0xff; /*释放数据端口*/ } void keyscan() { if(s1==0) { lcd_delay(10); if(s1==0) { time_data_buff[1]++; 25 / 33 if(time_data_buff [1]==0x5a) { time_data_buff[1]=0x00; } while(!s1); if(time_data_buff[1]==0x0a) { time_data_buff [1]=0x10; } if(time_data_buff[1]==0x1a) { time_data_buff[1]=0x20; } if(time_data_buff[1]==0x2a) { time_data_buff[1]=0x30; } if(time_data_buff[1]==0x3a) { time_data_buff[1]=0x40; } if(time_data_buff[1]==0x4a) { time_data_buff[1]=0x50; } Set1302(time_data_buff); } } 26 / 33 if(s2==0) { lcd_delay(10); if(s2==0) { time_data_buff[2]++; if(time_data_buff[2]==0x24) { time_data_buff[2]=0x00; } if(time_data_buff[2]==0x0a) { time_data_buff[2]=0x10; } if(time_data_buff[2]==0x1a) { time_data_buff[2]=0x20; } while(!s2); Set1302(time_data_buff); } } if(s3==0) { lcd_delay(10); if(s3==0) { time_data_buff[5]++; if(time_data_buff[5]==0x07) 27 / 33 { time_data_buff[5]=0x00; } } while(!s3); Set1302(time_data_buff); } if(s4==0) { lcd_delay(10); if(s4==0) { time_data_buff[3]++; if(time_data_buff[3]==0x32) { time_data_buff[3]=0x01; } if(time_data_buff[3]==0x0a) { time_data_buff[3]=0x10; } if(time_data_buff[3]==0x1a) { time_data_buff[3]=0x20; } if(time_data_buff[3]==0x2a) { time_data_buff[3]=0x30; } 28 / 33 } while(!s4); Set1302(time_data_buff); } } void main() { uchar i; uint temp; //温度值 lcd_system_reset(); /*LCD1602 初始化*/ lcd_data_port = 0xff; /*释放P0端口*/ led_latch_port = 0; sled_latch_port = 0; Set1302(time_data_buff); /*设置时间*/ while(1){ Get1302(time_data_buff); /*读取当前时间*/ temp = ReadTemperature(); /*读取当前温度值*/ /*刷新显示*/ lcd1602_line1[3] = mun_to_char[time_data_buff[6]/0x10]; lcd1602_line1[4] = mun_to_char[time_data_buff[6]%0x10]; /*年*/ lcd1602_line1[6] = mun_to_char[time_data_buff[4]/0x10]; lcd1602_line1[7] = mun_to_char[time_data_buff[4]%0x10]; /*月*/ lcd1602_line1[9] = mun_to_char[time_data_buff[3]/0x10]; lcd1602_line1[10] = mun_to_char[time_data_buff[3]%0x10]; /*日*/ for(i=0;i<3;i++) lcd1602_line1[i+12]=Weeks[time_data_buff[5]&0x07][i]; /*星期*/ lcd1602_line2[2] = mun_to_char[time_data_buff[2]/0x10]; lcd1602_line2[3] = mun_to_char[time_data_buff[2]%0x10]; /*时*/ lcd1602_line2[5] = mun_to_char[time_data_buff[1]/0x10]; 29 / 33 lcd1602_line2[6] = mun_to_char[time_data_buff[1]%0x10]; /*分*/ lcd1602_line2[8] = mun_to_char[time_data_buff[0]/0x10]; lcd1602_line2[9] = mun_to_char[time_data_buff[0]%0x10]; /*秒*/ //------ lcd1602_line2[12] = mun_to_char[temp/100]; lcd1602_line2[13] = mun_to_char[temp%100/10]; lcd1602_line2[15] = mun_to_char[temp%10]; /*温度*/ //------ for(i=0;i<16;i++) lcd_char_write(i,0,lcd1602_line1[i]); for(i=0;i<16;i++) lcd_char_write(i,1,lcd1602_line2[i]); keyscan(); } } 30 / 33 5 设计结果 编程完成后,在KEIL中调试程序,开始时出现了很多错误,有的是变量没有定义,有的是数组名出了错,有的是函数调用时方法不对,同多运行结果的提示,我对上面的错误进行了一一排查,直到编译成功。 在编译工作完成后,本以为就此方案设计就此成功了,没想到后面的调试依旧是一波三折。首先,将程序烧到板上时,LCD1602不能显示结果,经过排查,发现是接触不良,重新将其接好后,再次烧录程序。终于显示了结果,可是现实的结果并不是自己设定的日期,后来才发现在设置初值时,将顺序搞乱了,重新整理后,上面显示正确的结果。 完成了任务后,我开始了设计拓展部分。首先,仔细了解了DS18B20的引脚功能以及相关函数,便开始将并入设计好的部分。接下来我编写了时间校准功能的程序,这一次遇到了很大的麻烦。由于DS1302读出的数据是BCD码,而改变马只码值时想用十进制数字,因此两者没有统一,而且重新设置初值时必须送BCD码进去,编写起来十分困难。后来想到了if语句可以帮助我加以判断数值,可以免去十进制与BCD码之间的转换部分,十分简单。下面是我的实验结果。 图5.1 课设结果实物 31 / 33 6 心得体会 从六月十号六月十六号,前后一个星期的时间里,我把时间都投入到了单片机课设上面,时间有点仓促,课设也有一定的难度,但最后在老师和同学们的共同帮助下,终于完成了。回顾此次课设,自己有不小的收获。 首先,这次课设我们都要用到KEIL编译软件,虽然很早就接触到了这种软件,但只是会一点皮毛。这个课设要用到很多新的功能,运用起来有一定的困难,不过最后通过查询一些资料,能较好地掌握这些知识。主要的困难在后面的芯片程序设计部分。 为了完成此次课设,我再次翻阅了所学的理论知识,对题目有了一定的理解后,开始相关的设计。由于硬件的连接已经完成,单片机板已经完整,整个课设的难处在于程序设计部分。 首先,对于DS1302时钟芯片,其初始化设置以及CPU读取时间数据,均需要熟悉相应的引脚功能和运用相应的函数,学会运用这些函数耗费了我不少时间,为此我专门复习了一下以前学习的C语言知识。掌握了DS1302的使用方法后,我开始学习LCD的使用方法,与前面一样,我复习了以前的知识,熟悉了其引脚功能,为后面的程序编写积累了知识。另外在做拓展部分是,还掌握了DS18B20的使用方法,此次课程设计着实让我学到了很多硬件和软件方面的知识。 通过此次课设,我对单片机的C语言编程有了初步的掌握,熟悉了几种芯片的使用方法,同时提高了动手操作的能力,理论和实践都有了提高。课设做完后,也发现了自己的一些不足,平时很少自己动手设计程序,以至于用的时候有很多困难,在以后的时间里,我会多设计一些电路,提高自己的动手实践能力。 在此还要感谢载客射中帮助了我的老师和同学。 参考文献 32 / 33 [1] 李广弟兄朱月秀冷祖祁,单片机基础(第3版). 航航天大学 [2] 华成英童诗白,模拟电子技术基础(第四版). 高等教育 [3] 康华光电子技术基础数字部分(第五版). 高等教育 [4] 赵建领薛园园,零基础学单片机C语言程序设计. 机械工业 [5] 李群芳X士军. 单片机课程设计指导. 电子工业 [6] 郭天祥. 51单片机C语言教程. 电子工业 33 / 33 因篇幅问题不能全部显示,请点此查看更多更全内容