1、 NORTH CHINA UNIVERSITY OF SCIENCE AND TECHNOLOGY 单片机课程设计报告 学生姓名: 王文帅 学 号: 14570405 学 院: 信息工程学院 专业班级: 12级电信四班 指引教师: 崔博 06 月 21 日 一、课程设计目 本课程设计是在《PIC单片机原理及应用》及《单片机C语言编程》课程基本上,通过硬件设计、软件编程及仿真调试实践,进一步掌握PIC单片机应用办法,纯熟
2、掌握PICC C语言程序编写与调试,是毕业设计前一次重要实践,为此后走上工作岗位打下坚实单片机应用基本。 二、设计题目及规定 2.1 设计题目: 基于PIC16F877单片机多功能电子时钟 2.2 功能实现: 实时时钟显示,时间可调;实时温度显示,并具备超限报警功能。输出显示采用LCD1602或LED七段数码显示屏。 2.3 设计规定: 自己设计硬件和程序编程,画出完整电路原理图,用Multisim或Proteus仿真,最后可实现脱机运营。 三、设计原理阐明 3.1 时钟模块 DS1307是美国DALLAS公司推出I2C总线接口实时时钟芯片,它可独立于CPU 工作,不受CP
3、U主晶振及其电容影响,且计时精确,月累积误差普通不大于10秒。芯片还具备主电源掉电状况下时钟保护电路,DS1307时钟靠后备电池维持工作,回绝CPU对其读出和写入访问。同步还具备备用电源自动切换控制电路,因而可在主电源掉电和其他某些恶劣环境场合中保证系统时钟定期精确性。 DS1307具备产生秒、分、时、日、月、年等功能,且具备闰年自动调节功能。同步,DS1307芯片内部还集成有一定容量、具备掉电保护特性静态RAM,可用于保存某些核心数据。 3.2 显示模块 如用LCD1602作为显示设备,规定显示屏分两行显示,第一行显示日期(年-月-日)和星期,第二行显示时间(时:分:秒)和温度。显示秒
4、由00始终加到59,分钟数也由00加到59,小时采用24小时制,由00加到23,如此循环显示。 3.3 按键模块 按键用来作为时间调节。规定至少设计4个按键,按键1作为设立键;按键2、按键3分别为增长、减少键,在设立键按下时,此两个按键方有效;按键4是拟定键,在设立键按下时方才有效。 3.4 温度采集模块 DS18B20是美国DALLAS公司推出单总线数字温度传感器。DS18B20内部集成了温敏元件、数据转换芯片、存储器芯片和计算机接口芯片等多功能模块。其重要技术特点有:具备独特单线接口方式;可以通过数据线供电,具备超低功耗工作方式;测温范畴为-55℃~125℃,测温精度为0.5℃;温
5、度转换精度9~12位可编程,直接将温度转换值以二进制数码方式串行输出。
四、硬件原理图
(含原理图、电路仿真图)
时间显示:
按下温度键temp后显示温度:
五、软件设计
(含流程图、带注释程序清单)
#include
6、Q_IN() TRISB5 = 1 //输入 #define DQ_OUT() TRISB5 =0 //输出 //DS1302引脚定义 #define IO RA1 #define SCLK RA0 #define RST RA2 //DS1302端口数据读写方向 #define SET_IO_RD() TRISA1 = 1 #define SET_IO_WR() TRISA1 = 0 #define LCD_CLS 0x01 #define LCD_HOME 0x02 #define LCD_SETMODE 0x04 #define LCD_SETVISIBLE
7、0x08 #define LCD_SHIFT 0x10 #define LCD_SETFUNCTION 0x20 #define LCD_SETCGADDR 0x40 #define LCD_SETDDADDR 0x80 #define PORT_LCD_DAT PORTD #define PORT_LCD_CTL PORTC #define TRIS_LCD_DAT TRISD #define TRIS_LCD_CTL TRISC #define RS RC5 #define RW RC6 #define E RC7 extern unsigned ch
8、ar DateTime[]; extern char WEEK[][4]= {"SUN","MON","TUS","WEN","THU","FRI","SAT"}; //0,1,2,3,4,5,6分别相应周日,周一至周六 //所读取日期时间(秒,分,时,日,月,星期,年) unsigned int temper; unsigned int settempe = 570 ; unsigned char DateTime [7]; unsigned char tmp[]="0000"; //向DS1302写入一种字节 unsigned char RB1_FLAG = 0; co
9、nst unsigned char tone[]={108,102,91,86,77,68,61,57};//发声频率常数 void delay18us(unsigned char x) //18us,每次加1,增长9us { unsigned char i; for(;x>0;x--) { for(i=0;i<1;i++); } } void delay1ms(unsigned int x) { unsigned char i; for(;x>0;x--) for(i=0;i<165;i++); } void write
10、byte_to_DS1302(unsigned char x) { unsigned char i;SET_IO_WR(); for(i=0x01;i !=0x00;i <<= 1) { if (x & i) IO = 1;else IO =0; SCLK = 0;SCLK =1;__delay_us(10); } } //从DS1302读取1字节 unsigned char get_byte_form_DS1302() { unsigned char i,dat =0x00;SET_IO_RD(); for(i =
11、0;i < 8;i++) { SCLK = 1;SCLK=0; if(IO) dat |= (0x01<>4)*10 + (dat&0x0F); } //从DS1302指定位置读数据 unsigned char read_data(unsigned char addr) { unsigned char dat; RST = 1; write_byte_to_DS1302(addr); dat = get_byte_form_D
12、S1302(); RST = 0; return dat; } //向DS1302指定地址写数据 void Write_DS1302(unsigned char addr,unsigned char dat) { RST = 1; write_byte_to_DS1302(addr); write_byte_to_DS1302(dat); RST = 0; } void port_init() { ADCON1 = 0X06; //RA设立为普通IO口 TRISA = 0X00; //端口A设立为输出 OPT
13、ION = 0X00; //端口B弱上拉使能 TRISB = 0xFF; PORTC = 0X00; TRISC = 0X00; //端口C设立为输出 PORTD = 0X00; TRISD = 0X00; //端口D设立为输出 speak=0; } void song(unsigned char x) { unsigned char i,j; for(i=0;i<60;i++) { for(j=0;j<3;j++) { speak = 1; delay18us(tone[x]);
14、 speak = 0; //翻转发声 delay18us(tone[x]); } } } void keyscan() { unsigned char a ,b ; DateTime[0]=0; Write_DS1302(0x8E,0x00); // Write_DS1302(0x80,0x80); // if(RB2==0) //小时+1 { DateTime[2] = DateTime[2]+1; //第三个元素小时+1 if(DateTime[2] == 24) DateTime[
15、2]= 0;//当变为24H时,小时位归零 a = DateTime[2]/10*16+DateTime[2]%10; //变成16进制数据 Write_DS1302(0x84,a); } if(RB3==0) { DateTime[1] = DateTime[1]+1; //第二个元素分钟+1 if(DateTime[1] == 60) DateTime[1]= 0;//当变为60min时,小时位归零 b = DateTime[1]/10*16+DateTime[1]%10; //变成16进制数据 Write_DS1302(0x82
16、b); } if(RB4==0) { song(3); while(RB4 == ON); Write_DS1302(0x80,0x00); // 调节完毕后启动时钟运营 Write_DS1302(0x8E,0x00); // 禁止写 RB1_FLAG = 0; // 将RB1按下后产生标志清除 } } //DS1302初始化 void init_DS1302() { unsigned char second_REG; second_REG = read_data(0x81); //读取秒寄存器当前值 /
17、/如果高位CH为1,则表达当前DS1302振荡器出于待机状态 //这时需要将CH(clock halt flag)位设为0,打开振荡器使时钟运营 if ((second_REG & 0x80) == 0x80) { Write_DS1302(0x8E,0x00) ; //写控制字节,取消写保护 Write_DS1302(0x80,second_REG & 0x7F) ; //0x80地址不变,设高位CH=0 Write_DS1302(0x8E,0x80) ; //加保护 } } //读取当前日期 voi
18、d getDateTime() { //Write_DS1302(0x8E,0x00);//容许写操作 unsigned char i,addr = 0x81; for (i=0;i<7;i++) { DateTime[i] = read_data(addr); addr +=2; } } //********LCD显示子函数***********************// //**********结束LCD子函数********************// void LCD_BUSY_WAIT()
19、//忙等待 { unsigned char status; TRIS_LCD_DAT = 0xFF; RS = 0;RW = 1; do { E =1;NOP(); status = PORT_LCD_DAT; E = 0; } while (status & 0x80); } void Write_LCD_Command(unsigned char cmd) //写LCD命令 { TRIS_LCD_DAT = 0x00; PORT_LCD_DAT = cmd; PORT_LCD_CTL = TRIS_LCD_CTL
20、 = 0x00; RS = 0; RW = 0; E = 1 ; NOP(); E = 0 ; LCD_BUSY_WAIT(); } void Write_LCD_Data(unsigned char dat) //写LCD寄存器数据 { TRIS_LCD_DAT = 0x00; PORT_LCD_DAT = dat; RS = 1; RW = 0; E = 1 ; NOP(); E = 0; LCD_BUSY_WAIT(); } void LCD_Initialize() { Write_LCD_Command(0x38); __de
21、lay_ms(2); Write_LCD_Command(0x01); __delay_ms(2); Write_LCD_Command(0x06); __delay_ms(2); Write_LCD_Command(0x0C); __delay_ms(2); } void LCD_ShowString(unsigned char r,unsigned char c,char *str) { unsigned char i = 0; if(r == 0) Write_LCD_Command(0x80 | c); else if(r == 1) Write_LCD_
22、Command(0xC0 | c); for (i = 0;i < 16 && str[i]!= '\0';i++) Write_LCD_Data(str[i]); } void display_str(unsigned char line,unsigned char location,unsigned char str)//line行,location列,str字符 { RS=0; RW=0; E=1; delay1ms(1); if (line==0) //line=0,第一行 PORT_LCD_DAT=0x80+location; if (l
23、ine==1) //line=1,第二行 PORT_LCD_DAT=0xC0+location; E=0; LCD_BUSY_WAIT() ; Write_LCD_Data(str); } void Set_LCD_POS(unsigned char r,unsigned char c) //设立显示起始位置 { if(r == 0) Write_LCD_Command(0x80 | c);else if(r == 1) Write_LCD_Command(0xC0 | c); } //********18B20子程序********// //涉及函
24、数:void Wait_18B20();// // void delay15us(unsigned int x) //x必要不不大于0 { do {x--;} while(x); } void reset() { unsigned char state_DQ=1; while(state_DQ) { DQ_OUT(); delay15us(64); //延时721us DQ_IN(); delay15us(5); //延时61us if (DQ) state_DQ=1; //若DS18B20无应答,
25、循环检查 else state_DQ=0; //检测到DS18B20应答低电平信号 delay15us(43); //延时490us } } void write_one_byte(unsigned char data) { unsigned char i,tmp; for(i=0;i<8;i++) { tmp=data&0x01; data>>=1; if (tmp) //tmp=1时 { DQ_OUT(); NOP();NOP(); DQ_IN(); delay15us(5
26、); //延时61us } else { DQ_OUT(); delay15us(5); //延时61us DQ_IN(); NOP();NOP(); } } } unsigned char read_one_byte() { unsigned char i,data; for(i=0;i<8;i++) { data>>=1; DQ_OUT(); NOP(); DQ_IN(); NOP();NOP(); if (DQ==1) data=data|0x80; del
27、ay15us(5); //延时61us } return(data); } unsigned int get_temperature() { unsigned char tmpA,tmpB; //定义读出RAM高低位变量 reset(); write_one_byte(0xcc); //写跳过ROM命令,由于总线上只有一种DS18B20 write_one_byte(0x44); //启动DS18B20温度转换命令 delay1ms(800); reset(); //如果再写命令必要再次复位 write_on
28、e_byte(0xcc); //写跳过ROM命令,由于总线上只有一种DS18B20 write_one_byte(0xbe); //读内部RAM中9字节内容命令 tmpA=read_one_byte(); //读内部RAM 低八位温度 tmpB=read_one_byte(); //读内部RAM 高八位温度 temper=tmpB*256+tmpA; return(temper); } void temp () { unsigned char tmpB,tmpA; unsigned int results; display_str
29、1,10,tmp[0]); //在指定位置显示百位/负号 display_str(1,11,tmp[1]); //在指定位置显示十位 display_str(1,12,tmp[2]); //在指定位置显示个位 display_str(1,13,'.'); //在指定位置显示小数点位 display_str(1,14,tmp[3]); //在指定位置显示十分位位 display_s
30、tr(1,15,0XDF); //在指定位置显示° display_str(1,16,'C'); //在指定位置显示C delay1ms(200); //tmp[21]... 22 23 25 results=get_temperature(); if(results>=0x1000) { results=results^0xFFFF; results++; tmpB=results/256; tmpA=(unsigned char)results; r
31、esults>>=4; tmp[0]=0x2D; //负号 } else { tmpB=results/256; //高八位 tmpA=(unsigned char)results; //低八位 results>>=4; tmp[0]=results/100+0x30; //百 } tmp[1]=results/10%10+0x30; //十 tmp[2]=results%10+0x30; //个 tmpA<<=4; tmpA>>=4; r
32、esults=625*tmpA; tmp[3]=results/1000+0x30; //if (results>40) //speak = 0; //song(0); } char LCD_disp_buff[17]; void pro_key_scan() { if(RB1 == 1) { getDateTime(); } if(RB1== 0) { song(0); while(RB1 == ON); __dela
33、y_ms(10); //if(RB1==0) //{ while(!RB1); RB1_FLAG = 1; //} } if (RB1_FLAG == 1) keyscan(); } void Screen() { sprintf(LCD_disp_buff,"%02d-%02d-%02d %3s", DateTime[6],DateTime[4],DateTime[3],WEEK[DateTime[5]-1]); LCD_ShowString(0,0,LCD_disp_buff);
34、 sprintf(LCD_disp_buff,"%02d:%02d:%02d ", DateTime[2],DateTime[1],DateTime[0]); LCD_ShowString(1,0,LCD_disp_buff); __delay_ms(100); } void compare() { if(temper > settempe ) //当温度超过35.7度,蜂鸣器便会报警。 { speak=1; song(1); } else { speak=0;
35、 } } void main () { //unsigned char i; TRISA0 = TRISA2 = 0; port_init(); LCD_Initialize(); init_DS1302; while(1) { pro_key_scan() ; Screen(); compare(); temp (); } } 六、设计总结 通过这次课程设计,使我更加夯实掌握了单片机方面知识,再设计过程中虽然遇到了某些问题,但通过一次又一次思考,一遍又一遍检查终于找出了因素所在,实践出真知,通过亲手制作,使咱们掌握知识不再是纸上谈兵。课程设计是一种重要教学环节,通过课程设计使咱们理解到某些实际与理论之间差别。通过课程设计不但可以巩固专业知识,为后来工作打下了坚实基本,而其还可以培养和纯熟使用资料,运用工具书能力,把咱们所学课本知识与实践结合起来,起到温故而知新作用。同步,设计让我感触很深。使我对抽象理论有了详细结识。感谢课程设计,她教会了我诸多! 设计成绩: 教师签名: 年 月 日






