1、PIC单片机课程设计报告NORTH CHINA UNIVERSITY OF SCIENCE AND TECHNOLOGY单片机课程设计报告学生姓名: 李恒 学 号: 201214570408 学 院: 信息工程学院 专业班级: 12电信4班 指导教师: 崔博 2015年6月21日一、课程设计目的本课程设计是在PIC单片机原理及应用及单片机C语言编程课程的基础上,通过硬件设计、软件编程及仿真调试的实践,进一步掌握PIC单片机的应用方法,熟练掌握PICC C语言程序的编写与调试,是毕业设计前的一次重要实践,为今后走上工作岗位打下坚实的单片机应用基础。二、设计题目及要求2.1 设计题目:基于PIC1
2、6F877单片机的多功能电子时钟2.2 功能实现:实时时钟显示,时间可调;实时温度显示,并具有超限报警功能。输出显示采用LCD1602或LED七段数码显示器。2.3 设计要求:自己设计硬件和程序编程,画出完整的电路原理图,用Multisim或Proteus仿真,最终可实现脱机运行。三、设计原理说明3.1 时钟模块DS1307是美国DALLAS公司推出的I2C总线接口实时时钟芯片,它可独立于CPU 工作,不受CPU主晶振及其电容的影响,且计时准确,月累积误差一般小于10秒。芯片还具有主电源掉电情况下的时钟保护电路,DS1307的时钟靠后备电池维持工作,拒绝CPU对其读出和写入访问。同时还具有备用
3、电源自动切换控制电路,因而可在主电源掉电和其它一些恶劣环境场合中保证系统时钟的定时准确性。DS1307具有产生秒、分、时、日、月、年等功能,且具有闰年自动调整功能。同时,DS1307芯片内部还集成有一定容量、具有掉电保护特性的静态RAM,可用于保存一些关键数据。3.2 显示模块如用LCD1602作为显示设备,要求显示屏分两行显示,第一行显示日期(年-月-日)和星期,第二行显示时间(时:分:秒)和温度。显示秒由00一直加到59,分钟数也由00加到59,小时采用24小时制,由00加到23,如此循环显示。3.3 按键模块按键用来作为时间调整。要求至少设计4个按键,按键1作为设置键;按键2、按键3分别
4、为增加、减少键,在设置键按下时,此两个按键方有效;按键4是确定键,在设置键按下时方才有效。3.4 温度采集模块DS18B20是美国DALLAS公司推出的单总线数字温度传感器。DS18B20内部集成了温敏元件、数据转换芯片、存储器芯片和计算机接口芯片等多功能模块。其主要技术特点有:具有独特的单线接口方式;可以通过数据线供电,具有超低功耗工作方式;测温范围为-55125,测温精度为0.5;温度转换精度912位可编程,直接将温度转换值以二进制数码的方式串行输出。四、硬件原理图4.1电子时钟原理图4.2时钟模块4.3显示模块4.4按键模块4.5温度采集模块4.6电路仿真图4.6.1开机动画4.6.2实
5、时时钟显示,实时温度显示4.6.3时间可调,报警温度可调4.6.4闹钟功能4.6.5超限报警五、软件设计5.1流程图开始显示开机动画显示时间和温度执行修改时间、温度、闹钟命令按下扫描键盘无检测温度和闹钟报警/闹铃响超限/到时无5.2源程序5.2.1程序清单5.2.2主程序代码5.2.2.1主函数.c文件#include#includemain.huchar line1_data=Electronic Clock;uchar line2_data= Powered By Me ;void main(void)ADCON1=0x06; /基本配置PORTA,PORTE设定为数字端口TRISD=0;
6、 /PORTD设置为输出TRISB0=0; /PORTB0设置为输出RB0=0; /初始化蜂鸣器RB0(不响)TRISE0=0;TRISE1=0;TRISE2=0; _delay_ms(10); LCD_init();Init_ds1302();/初始化ds1302Key_init();/按键端口初始化LCD_write_str(0,0,line1_data);/在第0行显示字符串LCD_write_str(0,1,line2_data);/在第一行显示字符串_delay_ms(3000);LCD_clr();while(1) Display_Time();/显示时间Display_wendu
7、();/显示温度KeyScan();/按键扫描函数if(temp1warning_temp)RB0=1;_delay_ms(400);RB0=0;_delay_ms(400);elseRB0=0;if(hour=alarm_hour)&(min=alarm_min)RB0=1;_delay_ms(200);RB0=0;_delay_ms(200);elseRB0=0;5.2.2.2主函数.h文件#ifndef_MAIN_H_#define_MAIN_H_#define _STAT_BACKWARD_COMPATIBILITY /兼容PICC低版本 #include #includeall.h#
8、includelcd1602.h#includeds1302.h#includeds18b20.h#includekey.h_CONFIG(HS&WDTDIS&LVPDIS&PWRTEN);#endif5.2.3液晶显示屏LCD1602代码5.2.3.1液晶显示屏LCD1602.C文件#includelcd1602.h/*lcd清屏函数*/void LCD_clr()LCD1602_write_com(0x01);_delay_ms(5);/*光标定位函数。x为显示列,y为显示行*/void LocateXY(uchar x,uchar y)if(y=0)LCD1602_write_com(0
9、x80+x);elseLCD1602_write_com(0xC0+x);/*特定位置写字符*void LCD_write_str(uchar x,uchar y,uchar *s)LocateXY(x,y);while(*s)LCD1602_write_dat(*s);s+;/*特定位置显示数值*void LCD_write_data(uchar x,uchar y,uchar data)LocateXY(x,y);LCD1602_write_dat(data/10 +48);/将数值转换成其对应的ASIC码值,相当于以字符形式输出LCD1602_write_dat(data%10+ 48)
10、;/因为0的ASIC码值为48/*LCD1602初始化函数*void LCD_init(void)rs=0;rw=0;en=0;LCD1602_write_com(0x01); /清零LCD1602_write_com(0x38); /设置为16位两行字符,8位数据总线LCD1602_write_com(0x0c); /显示开关,关闭光标,没有闪烁LCD1602_write_com(0x06); /设置增量方式,显示移位方式/*读1602读忙状态*void LCD1602_check_busy(void) uint i=20000; dport_in; /端口设为输入 rs=0; rw=1;
11、en=1; while(0x80&dport)&(i0) i-; en=0; dport_out;/*向1602写数据*void LCD1602_write_dat(uchar lcd_dat) LCD1602_check_busy(); rs=1; rw=0; en=1; dport=lcd_dat; en=0;/*向1602写string数据*void LCD1602_write_string(const uchar *cy) while(*cy) LCD1602_check_busy(); rs=1; rw=0; en=1; dport=*cy; en=0; cy+; /*向1602写指
12、令*void LCD1602_write_com(uchar lcd_com) LCD1602_check_busy(); rs=0; rw=0; en=1;/下降沿把数据写进出 dport=lcd_com; en=0;5.2.3.1液晶显示屏LCD1602.H文件#ifndef _LCD1602_H_#define _LCD1602_H_#include#includeall.h#define dport PORTD /定义数据口#define dport_in TRISD=0xff /PD端口设为输入#define dport_out TRISD=0x00 /PD端口设为输出#define
13、 rs RE0 /并行数据/控制字选择 H:数据 L:控制字 串行片选信号#define rw RE1 /并行读/写选择 H:读 L:写 串行数据信号#define en RE2 /并行读写使能 H有效,下降沿锁定数据 串行时钟信号/* 函数定义 *void LCD1602_check_busy(void);/读1602读忙状态void LCD1602_write_dat(uchar lcd_dat);/向1602写数据*void LCD1602_write_string(const uchar *cy);/向1602写string数据void LCD1602_write_com(uchar
14、lcd_com);/向1602写指令void LocateXY(uchar x,uchar y);/光标定位函数。x为显示列,y为显示行void LCD_init(void);/LCD1602初始化函数*void LCD_write_data(uchar x,uchar y,uchar data);/特定位置显示数值*void LCD_write_str(uchar x,uchar y,uchar *s);/特定位置写字符*void LCD_clr();/lcd清屏函数*#endif5.2.4时钟芯片DS1302代码5.2.4.1时钟芯片DS1302.C文件#includeds1302.h/*
15、延时函数*void delay1()int i;for(i=100;i-;);/* 从ds1302读一个字节数据*/uchar read_byte(void)uchar i,dat=0;IO_IN;IO_CLR;for(i=0;i1; IO_OUT;SCK_CLR;return dat;/* 向ds1302写一个数据*/void write_byte(uchar dat)uchar i;for(i=0;i=1;SCK_CLR;/* 从ds1302的指定地址读一个字节数据*/uchar read_ds1302(uchar addr)char data,data1,data2;RST_CLR;SC
16、K_CLR;delay1();RST_SET;delay1();write_byte(addr);data=read_byte();SCK_CLR;RST_CLR;data1=data/16;data2=data%16;data=data2+data1*10;return data;/* 往ds1302的指定地址写一个字节数据*/void write_ds1302(uchar addr,uchar data)RST_CLR;SCK_CLR;delay1();RST_SET;delay1();write_byte(addr);SCK_CLR;write_byte(data);SCK_CLR;RS
17、T_CLR;/* 初始化ds1302函数*/void Init_ds1302()RST_CLR;SCK_CLR;RST_OUT;SCK_OUT;/* 写ds1302时间函数*/void Set_Time(char year,char month,char day,char hour,char min,char sec,char week)write_ds1302(0x8E,0x00); /根据写状态寄存器命令字,写入不保护指令 write_ds1302(0x80,sec/10*16+sec%10); /根据写秒寄存器命令字,写入秒的值write_ds1302(0x82,min/10*16+mi
18、n%10); /根据写分寄存器命令字,写入分的值write_ds1302(0x84,hour/10*16+hour%10); /根据写小时寄存器命令字,写入小时的值write_ds1302(0x86,day/10*16+day%10); /根据写日寄存器命令字,写入日的值write_ds1302(0x88,month/10*16+month%10); /根据写月寄存器命令字,写入月的值write_ds1302(0x8c,year/10*16+year%10); /根据写年寄存器命令字,写入年的值write_ds1302(0x8a,week/10*16+week%10);/根据写星期寄存器命令字
19、,写入星期的值write_ds1302(0x8e,0x80);/根据写状态寄存器命令字,写入写保护指令/* 读ds1302时间函数*/void Get_Time()write_ds1302(0x8e,0x00);/控制命令。wp=0,允许写操作write_ds1302(0x90,0xab);/涓流充电控制sec=read_ds1302(0x81); /读取秒min=read_ds1302(0x83); /读取分hour=read_ds1302(0x85);/读取时day = read_ds1302(0x87);/读取日month = read_ds1302(0x89);/读取月year = r
20、ead_ds1302(0x8D);/读取年week = read_ds1302(0x8B); /读取星期/* 显示时间函数*/void Display_Time()Get_Time();LCD_write_data(1,0,year); /显示年 LCD1602_write_dat(-);LCD_write_data(4,0,month); /显示月 LCD1602_write_dat(-);LCD_write_data(7,0,day); /显示日 Write_Week(10,0,week);/显示星期LCD_write_data(1,1,hour); /显示时 LCD1602_write_
21、dat(:);LCD_write_data(4,1,min); /显示分LCD1602_write_dat(:);LCD_write_data(7,1,sec);/显示秒/*星期显示函数*void Write_Week(int i,int j,uchar x)LCD_write_str(i,j,xinqix);5.2.4.1时钟芯片DS1302.H文件#ifndef _DS1302_H_#define _DS1302_H_#include#includeall.h#includelcd1602.h#define sclk RA1#define io RA0#define reset RA2#d
22、efine reset_IO TRISA2#define sclk_IO TRISA1#define io_IO TRISA0/* 复位脚*/#define RST_CLR reset=0#define RST_SET reset=1#define RST_IN reset_IO=1#define RST_OUT reset_IO=0/* 双向数据*/#define IO_CLR io=0#define IO_SET io=1#define IO_IN io_IO=1#define IO_OUT io_IO=0/* 时钟信号*/#define SCK_CLR sclk=0#define SCK
23、_SET sclk=1#define SCK_IN sclk_IO=1#define SCK_OUT sclk_IO=0int year,month,day,hour,min,sec,week;int alarm_hour=13,alarm_min=30;/定义闹铃时间和闹铃分钟uchar xinqi84= ,SUN,MON,TUS,WED,THU,FRI,SAT;void delay1();/延时函数void write_byte(uchar inbyte);/向ds1302写一个数据uchar read_byte(void);/从ds1302读一个字节数据void write_ds1302
24、(uchar addr,uchar data);/往ds1302的指定地址写一个字节数据uchar read_ds1302(uchar addr);/从ds1302的指定地址读一个字节数据void Write_Week(int i,int j,uchar x);/星期显示函数void Init_ds1302(void);/初始化ds1302函数*void Get_Time();/读ds1302时间函数*void Set_Time(char year,char month,char day,char hour,char min,char sec,char week);/写ds1302时间函数*v
25、oid Display_Time();/显示时间函数*#endif5.2.5温度传感器DS18B20代码5.2.5.1温度传感器DS18B20.C文件#includeds18b20.h/-/名称:复位DS18B20函数/-unsigned char Reset(void) unsigned char k;DQ_DIR=0;/设置RA4位输出口DQ=0; /拉低DQ总线开始复位 _delay_us(870); /保持DQ低大约870uS,符合不低于480US的要求 DQ_DIR=1; /设置RA4为输入口,以释放总线等电阻拉高总线 _delay_us(78); /大约78uS后,判断是否复位成功
26、if(DQ=0) /读出数据并保存 k=0; else k=1; _delay_us(400); /维持约400US,符合总读时隙不低于480US的要求 return k; /k=0为复位成功,k=1为复位失败/-/名称:写字节函数/-void WriteByte(unsigned char dat) unsigned char j; for(j=0;j1;/写入字节右移1位/-/名称:读字节函数/-unsigned char ReadByte(void) unsigned char j,buf=0; for(j=1;j1; /接收前,先将接收缓冲区右移DQ_DIR=0;/设置RA4位输出口
27、DQ=0; /拉低 NOP(); /维持2US,符合大于1US小于15US的规范 NOP(); /维持2US,符合大于1US小于15US的规范 DQ_DIR=1; /设置RA4为输入口,以释放总线等电阻拉高总线 准备读NOP(); /维持6US,符合大于1US小于15US的规范 NOP(); /维持6US,符合大于1US小于15US的规范NOP(); /维持6US,符合大于1US小于15US的规范 NOP(); /维持6US,符合大于1US小于15US的规范NOP(); /维持6US,符合大于1US小于15US的规范 NOP(); /维持6US,符合大于1US小于15US的规范if(DQ=1)
28、 buf|=0x80; /读出1位数据保存于buf中最高位_delay_us(52); /维持约52US,符合总读时隙不低于60US的要求 return buf; /退出的同时将接收缓冲区参数返回/-/名称:温度转换函数/-unsigned char Convert(void) if(Reset()=0) /如果复位成功 _delay_us(1000); /延时约1000US WriteByte(0xcc); / 跳过多器件识别 WriteByte(0x44); / 启动温度转换 return 1; elsereturn 0;/*读温度函数*void ReadFlash(void) unsig
29、ned char Lsb,Msb; if(Reset()=0) _delay_us(1000); /延时约1000US WriteByte(0xcc); / 跳过多器件识别 WriteByte(0xbe); / 读暂存器 Lsb=ReadByte(); / 低字节 Msb=ReadByte(); / 高字节 temp2=Lsb&0x0f; /LSB的低4位为小数部分temp1=(Lsb4)|(Msb99) temp1=99; if(temp215) temp2=0; buf=temp1; LCD_write_data(10,1,buf); /温度整数部分LCD1602_write_dat(.)
30、; /.buf=tabletemp2&0x0f;buf+=0x30;LCD1602_write_dat(buf); /温度小数部分LCD1602_write_dat(0xdf); /.LCD1602_write_dat(0x43); /C /*显示buf温度*void Display_temp(uchar buf) LCD_write_data(10,1,buf); /温度报警温度整数部分LCD1602_write_dat(.); /.LCD1602_write_dat(0x30); /温度小数部分0LCD1602_write_dat(0xdf); /.LCD1602_write_dat(0x
31、43); /C 5.2.5.1温度传感器DS18B20.H文件#ifndef _DS18B20_H_#define _DS18B20_H_#include#includeall.h#includelcd1602.h#defineDQ_DIR TRISC0 /控制DS18B20的DQ端#defineDQ RC0 /控制DS18B20的DQ端unsigned char temp1=0,temp2=0;/当前温度缓存unsigned char warning_temp=50;/设置报警温度const unsigned char table16=0,0,1,2,2,3,4,4,5,6,6,7,8,8,
32、9,9;void Display_wendu(void);/显示当前温度函数*void Display_temp(uchar buf);/显示buf温度函数*void ReadFlash(void) ;/读温度函数unsigned char Convert(void) ;/温度转换函数unsigned char ReadByte(void);/读字节函数void WriteByte(unsigned char dat);/写字节函数unsigned char Reset(void);/复位DS18B20函数#endif5.2.6独立按键代码5.2.6.1独立按键.C文件#includekey.h/*按键端口初始化*void Key_init()TRISB=0B00011110; /初始化RB4-RB1为输入方向 PORTB=0B11111110; /初始化RA5-RA0数值/*按键扫描函数*void KeyScan()uchar i=0;int warning_temp_buf=0;/报警温度缓存int alarm_hour_buf=0,alarm_min_buf=0;/闹钟时间缓存int year_buf,month_buf,day_buf,hour_buf,min_buf,sec_buf,week_buf;/设置时间缓存if(SET=0)while(!SET);