资源描述
1 课设所需软件简介
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中。
使用独立的Keil仿真器时,注意事项 :
* 仿真器标配11.0592MHz的晶振,但用户可以在仿真器上的晶振插孔中换插其他频率的晶振。
* 仿真器上的复位按钮只复位仿真芯片,不复位目标系统。
* 仿真芯片的31脚(/EA)已接至高电平,所以仿真时只能使用片内ROM,不能使用片外ROM;但仿真器外引插针中的31脚并不与仿真芯片的31脚相连,故该仿真器仍可插入到扩展有外部ROM(其CPU的/EA引脚接至低电平)的目标系统中使用。
优点:
1.Keil C51生成的目标代码效率非常之高,多数语句生成的汇编代码很紧凑,容易理解。在开发大型软件时更能体现高级语言的优势。
2.与汇编相比,C语言在功能上、结构性、可读性、可维护性上有明显的优势,因而易学易用。用过汇编语言后再使用C来开发,体会更加深刻。 Keil C51软件提供丰富的库函数和功能强大的集成开发调试工具,全Windows界面。
图1.1 KEIL操作界面
1.2 STC-ISP的简要介绍
STC-ISP 是一款单片机下载编程烧录软件,是针对STC系列单片机而设计的,可下载STC89系列、12C2052 系列和12C5410等系列的STC单片机,使用简便,现已被广泛使用。
操作说明如下:
(1) 打开STC-ISP,如下图界面,在MCU Type栏目下选中单片机,如STC89C52RC。
图1.2 ISP操作界面
(2) 根据您的9针数据线连接情况选中COM端口,波特率一般保持默认,如果遇到下载问题,可以适当下调一些。
(3) 先确认硬件连接正确,点击“打开文件”并在对话框内找到您要下载的HEX文件。
图1.3 选择HEX文件
(4)选择所要下载的文件,这样可以使您在每次编译KEIL时HEX代码能自动加载到STC-ISP,点击“Download/下载”。
(5) 手动按下电源开关便即可把可执行文件HEX写入到单片机内,如图是正在写入程序截图。
(6)程序写入完毕,目标板开始运行程序结果。
2 方案分析与确定
2.1 方案思路分析
由题目要求,根据设置的年份日的显示全年的年月公历、星期等信息。由于需要显示去年的日期以及星期等信息,普通的LED数码管显然已经不能胜任,为此,我们可以选择LCD1602液晶显示器完成课设要求。目前所具备的设备只有一块以STC89C52为内核的单片机,PC机。
课设要求显示日期等信息,我们可以充分利用单片机上面集成的DS1302时钟芯片。该芯片可以产生年份,月份,日期,星期,时,分,秒的数据,因此,完全可以完成实验的各项要求。
基于以上分析,我们可以利用DS1302产生的各种时钟数据,由CPU完成对数据的读取,然后通过CPU将数据写至LCD1602,以显示数据,达到可视化的效果。
对于课程设计的拓展部分,我们可以利用单片机开发板上面集成的DS18B20温度传感器来测试周围环境的温度,同样通过LCD1602来显示。此外可以通过测试按键是否闭合来改变各种时间数据,已达到校准的目的。
CPU
LCD1602
DS1302
DS18B20
图2.1 整体设计框架图
2.2 方案流程图
根据以上分析,以及使用各种芯片的操作流程,我们可以大致确定课程设计的软件设计方案方案,其大致流程如下:
重新设置时间,并写至进DS1302的寄存器
检测是否有时间校准按键按下
CPU将数据送至LCD1602显示
CPU读取DS18B20 ,DS1302的温度和时间数据
完成与DS18B20,LCD1602,DS1302相关的初始化设置
完成与DS18B20,LCD1602,DS1302相关的子函数的编程
相关引脚的特殊位定义
相关数字及字母数组设置
开始
图2.2 设计流程图
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的引脚功能图:
图3.1 DS1302引脚图
管脚描述:
X1 X2 32.768KHz 晶振管脚
GND 地
RST 复位脚
I/O 数据输入/输出引脚
SCLK 串行时钟
Vcc1,Vcc2 电源供电管脚
此外,拓展中用到了DS18B20温度传感器,其使用与DS1302比较类似,因此这里不再赘述。
3.2 DS1302的使用方法
使用DS1302时,要对其引脚和寄存器进行特殊设置,以实现所需功能。引脚设置后面会提及,这里不做说明,主要讲一下程序的编写。DS1302的的工作过程中包过读写一个字节数据等过程,下面做相应的解释。
/********************************
功 能:往DS1302写入数据
*********************************/
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;
}
/********************************
功 能:读取DS1302某地址的数据
*********************************/
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);
}
/***********************************
功 能:设置初始时间
************************************/
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;
}
/******************************
功 能:读取DS1302当前时间
*******************************/
void Get1302(uchar ucCurtime[])
{
uchar i;
uchar ucAddr = 0x81;
EA = 0;
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电源正极
第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.4 LCD1602的使用
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;
Lcd_rw = 1;
Lcd_en = 1;
Lcd_data = 0xff;
while (Lcd_data&0x80);
Lcd_en = 0;
/*LCD1602 命令字写入*/
}
void Lcd_command_write(uchar command)
{
Lcd_busy_wait();
Lcd_rs = 0;
Lcd_rw = 0;
Lcd_en = 0;
Lcd_data = command;
Lcd_en = 1;
Lcd_en = 0;
}
/*LCD1602 初始化*/
void Init_lcd()
{
Lcd_delay(20);
Lcd_command_write(0x38);
Lcd_delay(100);
Lcd_comman/*LCD1602 延时*/d_write(0x38);
Lcd_delay(50);
Lcd_command_write(0x38);
Lcd_delay(10);
Lcd_command_write(0x08);
Lcd_command_write(0x01);
Lcd_command_write(0x06);
Lcd_command_write(0x0c);
Lcd_data = 0xff; /*释放数据端口*/
}
void Lcd_char_write(uchar x_pos,y_pos,lcd_dat) /*LCD1602 字符写入*/
{
x_pos &= 0x0f; /* X位置范围 0~15 */
y_pos &= 0x01; /* Y位置范围 0~ 1 */
if(y_pos==1) x_pos += 0x40;
x_pos += 0x80;
Lcd_command_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; /*释放数据端口*/
}
4 整体电路设计和程序
由上面的分析可以得到实验的电路图如下:
图4.1 仿真电路图
设计源代码如下:
/****************************************************************
程序名称: LCD1602显示时间 显示当前温度
*****************************************************************/
/*头文件*/
#include <reg52.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
#define nop() _nop_()
sbit s1=P1^0;
sbit s2=P1^1;
sbit s3=P1^2;
sbit s4=P1^3;
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 code Weeks[][3]={{"SUN"},{"MON"},{"TUE"},{"WED"},{"THU"},{"FRI"},{"SAT"},{"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)
{
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;
}
/***************************************************************
函 数 名: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
返 回 值:无
****************************************************************/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;
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)
{
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;
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 忙等待*/
{
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_command_write(uchar command) /*LCD1602 命令字写入*/
{
lcd_busy_wait();
lcd_rs_port = 0;
lcd_rw_port = 0;
lcd_en_port = 0;
lcd_data_port = command;
lcd_en_port = 1;
lcd_en_port = 0;
}
void lcd_system_reset() /*LCD1602 初始化*/
{
lcd_delay(20);
lcd_command_write(0x38);
lcd_delay(100);
lcd_command_write(0x38);
lcd_delay(50);
lcd_command_write(0x38);
lcd_delay(10);
lcd_command_write(0x08);
lcd_command_write(0x01);
lcd_command_write(0x06);
lcd_command_write(0x0c);
lcd_data_port = 0xff; /*释放数据端口*/
}
void lcd_char_write(uchar x_pos,y_pos,lcd_dat) /*LCD1602 字符写入*/
{
x_pos &= 0x0f; /* X位置范围 0~15 */
y_pos &= 0x01; /* Y位置范围 0~ 1 */
if(y_pos==1) x_pos += 0x40;
x_pos += 0x80;
lcd_command_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]++;
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);
}
}
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)
{
展开阅读全文