PIC单片机课程设计报告.docx
《PIC单片机课程设计报告.docx》由会员分享,可在线阅读,更多相关《PIC单片机课程设计报告.docx(23页珍藏版)》请在冰点文库上搜索。
PIC单片机课程设计报告
NORTHCHINAUNIVERSITYOFSCIENCEANDTECHNOLOGY
单片机课程设计报告
学生姓名:
王文帅
学号:
201214570405
学院:
信息工程学院
专业班级:
12级电信四班
指导教师:
崔博
2015年06月21日
一、课程设计目的
本课程设计是在《PIC单片机原理及应用》及《单片机C语言编程》课程的基础上,通过硬件设计、软件编程及仿真调试的实践,进一步掌握PIC单片机的应用方法,熟练掌握PICCC语言程序的编写与调试,是毕业设计前的一次重要实践,为今后走上工作岗位打下坚实的单片机应用基础。
二、设计题目及要求
2.1设计题目:
基于PIC16F877单片机的多功能电子时钟
2.2功能实现:
实时时钟显示,时间可调;实时温度显示,并具有超限报警功能。
输出显示采用LCD1602或LED七段数码显示器。
2.3设计要求:
自己设计硬件和程序编程,画出完整的电路原理图,用Multisim或Proteus仿真,最终可实现脱机运行。
三、设计原理说明
3.1时钟模块
DS1307是美国DALLAS公司推出的I2C总线接口实时时钟芯片,它可独立于CPU工作,不受CPU主晶振及其电容的影响,且计时准确,月累积误差一般小于10秒。
芯片还具有主电源掉电情况下的时钟保护电路,DS1307的时钟靠后备电池维持工作,拒绝CPU对其读出和写入访问。
同时还具有备用电源自动切换控制电路,因而可在主电源掉电和其它一些恶劣环境场合中保证系统时钟的定时准确性。
DS1307具有产生秒、分、时、日、月、年等功能,且具有闰年自动调整功能。
同时,DS1307芯片内部还集成有一定容量、具有掉电保护特性的静态RAM,可用于保存一些关键数据。
3.2显示模块
如用LCD1602作为显示设备,要求显示屏分两行显示,第一行显示日期(年-月-日)和星期,第二行显示时间(时:
分:
秒)和温度。
显示秒由00一直加到59,分钟数也由00加到59,小时采用24小时制,由00加到23,如此循环显示。
3.3按键模块
按键用来作为时间调整。
要求至少设计4个按键,按键1作为设置键;按键2、按键3分别为增加、减少键,在设置键按下时,此两个按键方有效;按键4是确定键,在设置键按下时方才有效。
3.4温度采集模块
DS18B20是美国DALLAS公司推出的单总线数字温度传感器。
DS18B20内部集成了温敏元件、数据转换芯片、存储器芯片和计算机接口芯片等多功能模块。
其主要技术特点有:
具有独特的单线接口方式;可以通过数据线供电,具有超低功耗工作方式;测温范围为-55℃~125℃,测温精度为0.5℃;温度转换精度9~12位可编程,直接将温度转换值以二进制数码的方式串行输出。
四、硬件原理图
(含原理图、电路仿真图)
时间显示:
按下温度键temp后显示温度:
五、软件设计
(含流程图、带注释的程序清单)
#include
#include
#include
#define_XTAL_FREQ1000000UL
#definespeakRC3
#defineON0
#defineOFF1
#defineDQRB5
//设置数据方向
#defineDQ_IN()TRISB5=1//输入
#defineDQ_OUT()TRISB5=0//输出
//DS1302引脚定义
#defineIORA1
#defineSCLKRA0
#defineRSTRA2
//DS1302端口数据读写方向
#defineSET_IO_RD()TRISA1=1
#defineSET_IO_WR()TRISA1=0
#defineLCD_CLS0x01
#defineLCD_HOME0x02
#defineLCD_SETMODE0x04
#defineLCD_SETVISIBLE0x08
#defineLCD_SHIFT0x10
#defineLCD_SETFUNCTION0x20
#defineLCD_SETCGADDR0x40
#defineLCD_SETDDADDR0x80
#definePORT_LCD_DATPORTD
#definePORT_LCD_CTLPORTC
#defineTRIS_LCD_DATTRISD
#defineTRIS_LCD_CTLTRISC
#defineRSRC5
#defineRWRC6
#defineERC7
externunsignedcharDateTime[];
externcharWEEK[][4]={"SUN","MON","TUS","WEN","THU","FRI","SAT"};
//0,1,2,3,4,5,6分别对应周日,周一至周六
//所读取的日期时间(秒,分,时,日,月,星期,年)
unsignedinttemper;
unsignedintsettempe=570;
unsignedcharDateTime[7];
unsignedchartmp[]="0000";
//向DS1302写入一个字节
unsignedcharRB1_FLAG=0;
constunsignedchartone[]={108,102,91,86,77,68,61,57};//发声的频率常数
voiddelay18us(unsignedcharx)//18us,每次加1,增加9us
{
unsignedchari;
for(;x>0;x--)
{
for(i=0;i<1;i++);
}
}
voiddelay1ms(unsignedintx)
{
unsignedchari;
for(;x>0;x--)
for(i=0;i<165;i++);
}
voidwrite_byte_to_DS1302(unsignedcharx)
{
unsignedchari;SET_IO_WR();
for(i=0x01;i!
=0x00;i<<=1)
{
if(x&i)IO=1;elseIO=0;
SCLK=0;SCLK=1;__delay_us(10);
}
}
//从DS1302读取1字节
unsignedcharget_byte_form_DS1302()
{
unsignedchari,dat=0x00;SET_IO_RD();
for(i=0;i<8;i++)
{
SCLK=1;SCLK=0;
if(IO)dat|=(0x01<
}
return(dat>>4)*10+(dat&0x0F);
}
//从DS1302指定位置读数据
unsignedcharread_data(unsignedcharaddr)
{
unsignedchardat;
RST=1;
write_byte_to_DS1302(addr);
dat=get_byte_form_DS1302();
RST=0;
returndat;
}
//向DS1302指定地址写数据
voidWrite_DS1302(unsignedcharaddr,unsignedchardat)
{
RST=1;
write_byte_to_DS1302(addr);
write_byte_to_DS1302(dat);
RST=0;
}
voidport_init()
{
ADCON1=0X06;//RA设置为普通IO口
TRISA=0X00;//端口A设置为输出
OPTION=0X00;//端口B弱上拉使能
TRISB=0xFF;
PORTC=0X00;
TRISC=0X00;//端口C设置为输出
PORTD=0X00;
TRISD=0X00;//端口D设置为输出
speak=0;
}
voidsong(unsignedcharx)
{
unsignedchari,j;
for(i=0;i<60;i++)
{
for(j=0;j<3;j++)
{
speak=1;
delay18us(tone[x]);
speak=0;//翻转发声
delay18us(tone[x]);
}
}
}
voidkeyscan()
{
unsignedchara,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[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,b);
}
if(RB4==0)
{
song(3);
while(RB4==ON);
Write_DS1302(0x80,0x00);//调整完毕后启动时钟运行
Write_DS1302(0x8E,0x00);//禁止写
RB1_FLAG=0;//将RB1按下后产生的标志清除
}
}
//DS1302初始化
voidinit_DS1302()
{
unsignedcharsecond_REG;
second_REG=read_data(0x81);//读取秒寄存器当前值
//如果高位CH为1,则表示当前DS1302振荡器出于待机状态
//这时需要将CH(clockhaltflag)位设为0,打开振荡器使时钟运行
if((second_REG&0x80)==0x80)
{
Write_DS1302(0x8E,0x00);//写控制字节,取消写保护
Write_DS1302(0x80,second_REG&0x7F);//0x80地址不变,设高位CH=0
Write_DS1302(0x8E,0x80);//加保护
}
}
//读取当前日期
voidgetDateTime()
{
//Write_DS1302(0x8E,0x00);//允许写操作
unsignedchari,addr=0x81;
for(i=0;i<7;i++)
{
DateTime[i]=read_data(addr);
addr+=2;
}
}
//********LCD显示子函数***********************//
//**********结束LCD子函数********************//
voidLCD_BUSY_WAIT()//忙等待
{
unsignedcharstatus;
TRIS_LCD_DAT=0xFF;
RS=0;RW=1;
do
{
E=1;NOP();
status=PORT_LCD_DAT;
E=0;
}
while(status&0x80);
}
voidWrite_LCD_Command(unsignedcharcmd)//写LCD命令
{
TRIS_LCD_DAT=0x00;
PORT_LCD_DAT=cmd;
PORT_LCD_CTL=TRIS_LCD_CTL=0x00;
RS=0;
RW=0;
E=1;
NOP();
E=0;
LCD_BUSY_WAIT();
}
voidWrite_LCD_Data(unsignedchardat)//写LCD寄存器数据
{
TRIS_LCD_DAT=0x00;
PORT_LCD_DAT=dat;
RS=1;
RW=0;
E=1;
NOP();
E=0;
LCD_BUSY_WAIT();
}
voidLCD_Initialize()
{
Write_LCD_Command(0x38);
__delay_ms
(2);
Write_LCD_Command(0x01);
__delay_ms
(2);
Write_LCD_Command(0x06);
__delay_ms
(2);
Write_LCD_Command(0x0C);
__delay_ms
(2);
}
voidLCD_ShowString(unsignedcharr,unsignedcharc,char*str)
{
unsignedchari=0;
if(r==0)Write_LCD_Command(0x80|c);
else
if(r==1)Write_LCD_Command(0xC0|c);
for(i=0;i<16&&str[i]!
='\0';i++)
Write_LCD_Data(str[i]);
}
voiddisplay_str(unsignedcharline,unsignedcharlocation,unsignedcharstr)//line行,location列,str字符
{
RS=0;
RW=0;
E=1;
delay1ms
(1);
if(line==0)//line=0,第一行
PORT_LCD_DAT=0x80+location;
if(line==1)//line=1,第二行
PORT_LCD_DAT=0xC0+location;
E=0;
LCD_BUSY_WAIT();
Write_LCD_Data(str);
}
voidSet_LCD_POS(unsignedcharr,unsignedcharc)//设置显示起始位置
{
if(r==0)Write_LCD_Command(0x80|c);else
if(r==1)Write_LCD_Command(0xC0|c);
}
//********18B20子程序********//
//包括函数:
voidWait_18B20();//
//
voiddelay15us(unsignedintx)//x必须大于0
{
do
{x--;}
while(x);
}
voidreset()
{
unsignedcharstate_DQ=1;
while(state_DQ)
{
DQ_OUT();
delay15us(64);//延时721us
DQ_IN();
delay15us(5);//延时61us
if(DQ)
state_DQ=1;//若DS18B20无应答,循环检查
else
state_DQ=0;//检测到DS18B20应答低电平信号
delay15us(43);//延时490us
}
}
voidwrite_one_byte(unsignedchardata)
{
unsignedchari,tmp;
for(i=0;i<8;i++)
{
tmp=data&0x01;
data>>=1;
if(tmp)//tmp=1时
{
DQ_OUT();
NOP();NOP();
DQ_IN();
delay15us(5);//延时61us
}
else
{
DQ_OUT();
delay15us(5);//延时61us
DQ_IN();
NOP();NOP();
}
}
}
unsignedcharread_one_byte()
{
unsignedchari,data;
for(i=0;i<8;i++)
{
data>>=1;
DQ_OUT();
NOP();
DQ_IN();
NOP();NOP();
if(DQ==1)
data=data|0x80;
delay15us(5);//延时61us
}
return(data);
}
unsignedintget_temperature()
{
unsignedchartmpA,tmpB;//定义读出RAM的高低位变量
reset();
write_one_byte(0xcc);//写跳过ROM命令,因为总线上只有一个DS18B20
write_one_byte(0x44);//启动DS18B20温度转换命令
delay1ms(800);
reset();//如果再写命令必须再次复位
write_one_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);
}
voidtemp()
{
unsignedchartmpB,tmpA;
unsignedintresults;
display_str(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_str(1,15,0XDF);//在指定位置显示°
display_str(1,16,'C');//在指定位置显示C
delay1ms(200);//tmp[21]...222325
results=get_temperature();
if(results>=0x1000)
{
results=results^0xFFFF;
results++;
tmpB=results/256;
tmpA=(unsignedchar)results;
results>>=4;
tmp[0]=0x2D;//负号
}
else
{
tmpB=results/256;//高八位
tmpA=(unsignedchar)results;//低八位
results>>=4;
tmp[0]=results/100+0x30;//百
}
tmp[1]=results/10%10+0x30;//十
tmp[2]=results%10+0x30;//个
tmpA<<=4;
tmpA>>=4;
results=625*tmpA;
tmp[3]=results/1000+0x30;
//if(results>40)
//speak=0;
//song(0);
}
charLCD_disp_buff[17];
voidpro_key_scan()
{
if(RB1==1)
{
getDateTime();
}
if(RB1==0)
{
song(0);
while(RB1==ON);
__delay_ms(10);
//if(RB1==0)
//{
while(!
RB1);
RB1_FLAG=1;
//}
}
if(RB1_FLAG==1)keyscan();
}
voidScreen()
{
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);
sprintf(LCD_disp_buff,"%02d:
%02d:
%02d",
DateTime[2],DateTime[1],DateTime[0]);
LCD_ShowString(1,0,LCD_disp_buff);
__delay_ms(100);
}
voidcompare()
{
if(temper>settempe)//当温度超过35.7度,蜂鸣器便会报警。
{
speak=1;
song
(1);
}
else
{
speak=0;
}
}
voidmain()
{
//unsignedchari;
TRISA0=TRISA2=0;
port_init();
LCD_Initialize();
init_DS1302;
while
(1)
{
pro_key_scan();
Screen();
compare();
temp();
}
}
六、设计总结
通过这次课程设计,使我更加扎实的掌握了单片机