嵌入式课程设计88078.docx
《嵌入式课程设计88078.docx》由会员分享,可在线阅读,更多相关《嵌入式课程设计88078.docx(20页珍藏版)》请在冰豆网上搜索。
![嵌入式课程设计88078.docx](https://file1.bdocx.com/fileroot1/2023-1/27/e277572c-a279-460e-b8e7-6b2bd2e83e0a/e277572c-a279-460e-b8e7-6b2bd2e83e0a1.gif)
嵌入式课程设计88078
嵌入式课程设计
学院:
计算机与通信工程学院专业:
物联网工程班级:
物联1501
姓名:
王强学号:
41501602实验日期:
2017年12月25日
实验名称:
嵌入式课程设计
实验目的:
以STC89开发板为硬件平台,开发温度采集、动态数码管显示、按键响应、与PC串口通讯的综合程序,实现以下功能:
1)PC上的串口调试助手通过串口给STC89开发板发送“GetTemp”命令。
2)STC89开发板从串口接收到“GetTemp”命令后启动温度传感器DS18B20的测温程序获取当前温度,测试完成时将所测得温度数据显示在动态数码管上。
(动态数码管在温度获取之前应该显示“FFFFFFFF”,只有在获取温度后才显示温度值)
3)动态数码管显示出温度数据后,请通过按键触发STC89开发板通过串口回送步骤2所测的温度数据给PC上串口调试助手,同时恢复动态数码管显示为“FFFFFFFF”。
为保证每个同学的实验都独立完成,要求回送的数据包含自己的学号,即如果你的学号是20150809,当前温度值是19.6摄氏度,那么在PC上的串口调试助手应该显示:
20150809:
19.6°C。
硬件电路说明:
1)STC89处理器管脚和晶振电路
2)独立按键
独立按键一共5个,分别连接在单片机的P3.0到P3.4口。
去抖动的方式,我们采用软件延时的方法。
过程如下:
先设置IO口为高电平(一般上电默认就为高),读取IO口电平确认是否有按键按下,如有IO电平为低电平后,延时几个ms,再读取该IO电平,如果任然为低电平,说明对应按键按下,执行相应按键的程序。
3)DS18B20温度传感器部分
DS18B20内部的低温度系数振荡器是一个振荡频率随温度变化很小的振荡器,为计数器1提供一频率稳定的计数脉冲。
高温度系数振荡器是一个振荡频率对温度很敏感的振荡器,为计数器2提供一个频率随温度变化的计数脉冲。
初始时,温度寄存器被预置成-55℃,每当计数器1从预置数开始减计数到0时,温度寄存器中寄存的温度值就增加1℃,这个过程重复进行,直到计数器2计数到0时便停止。
初始时,计数器1预置的是与-55℃相对应的一个预置值。
以后计数器1每一个循环的预置数都由斜率累加器提供。
为了补偿振荡器温度特性的非线性性,斜率累加器提供的预置数也随温度相应变化。
计数器1的预置数也就是在给定温度处使温度寄存器寄存值增加1℃计数器所需要的计数个数。
DS18B20内部的比较器以四舍五入的量化方式确定温度寄存器的最低有效位。
在计数器2停止计数后,比较器将计数器1中的计数剩余值转换为温度值后与0.25℃进行比较,若低于0.25℃,温度寄存器的最低位就置0;若高于0.25℃,最低位就置1;若高于0.75℃时,温度寄存器的最低位就进位然后置0。
这样,经过比较后所得的温度寄存器的值就是最终读取的温度值了,其最后位代表0.5℃,四舍五入最大量化误差为±1/2LSB,即0.25℃。
温度寄存器中的温度值以9位数据格式表示,最高位为符号位,其余8位以二进制补码形式表示温度值。
测温结束时,这9位数据转存到暂存存储器的前两个字节中,符号位占用第一字节,8位温度数据占据第二字节。
DS18B20测量温度时使用特有的温度测量技术。
DS18B20内部的低温度系数振荡器能产生稳定的频率信号;同样的,高温度系数振荡器则将被测温度转换成频率信号。
当计数门打开时,DS18B20进行计数,计数门开通时间由高温度系数振荡器决定。
芯片内部还有斜率累加器,可对频率的非线性度加以补偿。
测量结果存入温度寄存器中。
一般情况下的温度值应该为9位,但因符号位扩展成高8位,所以最后以16位补码形式读出。
电路图及管脚如下:
4)串口通信
串行通信是将数据字节分成一位一位的形式在一条传输线上逐个地传送,此时只需要一条数据线,外加一条公共信号地线和若干控制信号线。
因为一次只能传送一位,所以对于一个字节的数据,至少要分S位才能传送完毕。
串行通信的必要过程是:
发送时,要把并行数据变成串行数据发送到线路上去,接收时,要把串行信号再变成并行数据,这样才能被计算机及其他设备处理。
在串行通信中,收、发双方对发送或接收数据的速率要有约定。
通过编程可对单片机串行口设定为4种工作方式,其中方式0和方式2的波特率是固定的,而方式1和方式3的波特率是可变的,由定时器T1的溢出率来决定。
串行口的4种工作方式对应三种波特率。
由于输入的移位时钟的来源不同,所以各种方式的波特率计算公式也不相同,以下是4种方式波特率的计算公式。
开发板的电路图如下图
5)动态数码管显示
数码管的显示原理是靠点亮内部的发光二极管来发光,下面就来我们讲解一个数码管是如何亮起来的。
数码管内部电路如下图所示,从右图可看出,一位数码管的引脚是10个,显示一个8字需要7个小段,另外还有一个小数点,所以其内部一共有8个小的发光二极管,最后还有一个公共端,生产商为了封装统一,单位数码管都封装10个引脚,其中第3和第8引脚是连接在一起的。
而它们的公共端又可分为共阳极和共阴极,中间图为共阴极内部原理图,右图为共阳极内部原理图。
上图展出了常用的两种数码管的引脚排列和内部结构。
总所周知,点亮发光二极管就是要给予它足够大的正向压降。
所以点亮数码管其实也就是给它内部相应的发光二极管正向压降。
如上图左(一共a、b、c、d、e、f、g、DP八段),如果要显示“1”则要点亮b、c两段LED;显示“A”则点亮a、b、c、e、f、g这六段LED;
动态显示是多个数码管,交替显示,利用人的视觉暂停作用使人看到多个数码管同时显示的效果。
完整程序代码:
主程序文件main.c:
#include"temp.h"//引用temp.h头文件,包括一些有关温度传感器的函数
#include"reg51.h"//引用reg51.h头文件,说明引脚地址
#defineGPIO_DIGP0//将P0端口定义为GPIO_DIG
#defineGPIO_LEDP2//将P2端口定义为GPIO_LED
sbitLSA=P2^2;//位选,P2^2定义为LSA
sbitLSB=P2^3;//同上位选定义变量
sbitLSC=P2^4;//同上位选定义变量
sbitK3=P3^2;//位选,P3^2定义为K3按键
unsignedintdisp[8]={0x71,0x71,0x71,0x71,0x71,0x71,0x71,0x71};//显示FFFFFFFF
unsignedcharcodeDIG_CODE[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
//0、1、2、3、4、5、6、7、8、9的显示码
unsignedcharcheck1[7]={'0','0','0','0','0','0','0'};//用来存放串口通信传送字符
unsignedcharcheck2[7]={'G','e','t','T','e','m','p'};//用来比较串口通信传送字符
unsignedcharmessage[17]={'4','1','5','0','1','6','0','2',':
','0','0','0','0','0','0','0','0'};
//显示学号为41501602:
unsignedcharDisplayData[8];//用来存放要显示的8位数的值
unsignedcharKeyValue=0;//是否按下独立按键的标志位
voidDigDisplay();//动态数码管显示函数
voidUsartConfiguration();//串口设置函数
voidLcdDisplay(int);//数码管显示读取到的温度
voidIntConfiguration();//设置外部中断
voidDelay(unsignedintn);//延时函数
voidTimer0Configuration();//定时器初始设置
voidCheckMessage(charm);//逐字检查串口通信字符是否正确
unsignedintflag=0;//数码管显示标志位
unsignedintj=0;//用于CheckMessage逐字检查发送字符
unsignedcharNum=0;//动态数码管位选
/*******************************************************************************
主函数模块
*******************************************************************************/
voidmain()//主函数
{
unsignedinti;
IntConfiguration();//初始化外部中断设置
UsartConfiguration();//初始化串口设置
Timer0Configuration();//初始化定时器设置
while
(1)
{
if(flag==1)//当flag为1时数码管直接显示当前温度
{
LcdDisplay(Ds18b20ReadTemp());//调用温度传感器函数并在数码管显示温度
}
if(flag==0)//当flag为0时数码管显示FFFFFFFF
{
for(i=0;i<8;i++)//循环给disp[]数组赋值
{
disp[i]=0x71;//disp[i]赋值
}
}
}
}
/*******************************************************************************
独立按键模块
*******************************************************************************/
voidIntConfiguration()//设置外部中断
{
//设置INT0
IT0=1;//跳变沿触发方式(下降沿)
EX0=1;//打开INT0的中断允许。
//设置INT1
IT1=1;//跳变沿触发方式为下降沿
EX1=1;//打开INT1的中断允许
EA=1;//打开总中断
}
voidDelay(unsignedintn)//延时50us误差0us
{
unsignedchara,b;
for(;n>0;n--)
{
for(b=1;b>0;b--)
for(a=22;a>0;a--);
}
}
voidInt0()interrupt0//外部中断0的中断函数K3按键中断
{
unsignedintk=0;
Delay
(1);//延时消抖
if(K3==0)//如果K3按键被按下的话,动态数码管就显示FFFFFFFF
{
KeyValue=1;
flag=0;
for(k=0;k<17;k++)//逐字将message[]字符数组中字符送入SBUF
{
SBUF=message[k];//将接收到的数据放入到发送寄存器
while(!
TI);//等待发送数据完成
TI=0;//清除发送完成标志位
}
if(k==17)//如果message数组发送完毕就归零准备下一次
{
k=0;
}
}
else
KeyValue=0;//K3按键没被按下的时候就一直保持当前状态,显示当前温度
}
/*******************************************************************************
串口通信模块
*******************************************************************************/
voidUsartConfiguration()//串口设置
{
SCON=0X50;//设置为工作方式1
TMOD=0X20;//设置计数器工作方式2,也就是自动重装模式
PCON=0X80;//波特率加倍
TH1=0XF3;//计数器初始值设置,注意波特率是4800的
TL1=0XF3;
ES=1;//打开接收中断
EA=1;//打开总中断
TR1=1;//打开计数器
}
voidUsart()interrupt4//串口中断
{
unsignedcharreceiveData;
receiveData=SBUF;//出去接收到的数据
RI=0;//清除接收中断标志位
CheckMessage(receiveData);//判断发送字节是否正确
}
voidCheckMessage(charm)//逐字检查串口通信字符是否正确
{
check1[j]=m;//将串口接收到的字符一位一位的赋给check1[]
if(check1[j]!
=check2[j])//按位逐字检查发送字符是否符合要求
{
flag=-1;//不符合直接退出
}
if(j==6)//符合时改变显示标志位,数码管显示当前温度
{
flag=1;
}
j++;//按位检查
}
/*******************************************************************************
动态数码管模块
*******************************************************************************/
voidDigDisplay()interrupt1//动态数码管显示
{
//定时器在工作方式二会自动重装初,所以不用在赋值。
TH0=0X9c;//给定时器赋初值,定时1ms
TL0=0X00;
GPIO_DIG=0;//消隐
switch(Num)//位选,选择点亮的数码管,
{
case(7):
LSA=0;LSB=0;LSC=0;break;//显示第7位
case(6):
LSA=1;LSB=0;LSC=0;break;//显示第6位
case(5):
LSA=0;LSB=1;LSC=0;break;//显示第5位
case(4):
LSA=1;LSB=1;LSC=0;break;//显示第4位
case(3):
LSA=0;LSB=0;LSC=1;break;//显示第3位
case
(2):
LSA=1;LSB=0;LSC=1;break;//显示第2位
case
(1):
LSA=0;LSB=1;LSC=1;break;//显示第1位
case(0):
LSA=1;LSB=1;LSC=1;break;//显示第0位
}
GPIO_DIG=disp[Num];//发送段码
Num++;//动态数码管从0到7循环点亮
if(Num>7)
Num=0;
}
voidTimer0Configuration()//定时器初始设置
{
TMOD=0X20;//选择为定时器模式,工作方式2,仅用TRX打开启动。
TH0=0X9C;//给定时器赋初值,定时100us
TL0=0X9C;
ET0=1;//打开定时器0中断允许
EA=1;//打开总中断
TR0=1;//打开定时器
}
/*******************************************************************************
温度传感器DS18B20模块
*******************************************************************************/
voidLcdDisplay(inttemp)//数码管显示读取到的温度
{
unsignedchardatas[]={0,0,0,0,0};//定义数组
floattp;
if(temp<0)//当温度值为负数
{
disp[2]=0x40;//如果温度值为负数的时候,disp[2]数码管显示负号-
//因为读取的温度是实际温度的补码,所以减1,再取反求出原码
temp=temp-1;
temp=~temp;
tp=temp;
temp=tp*0.0625*100+0.5;
//留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点
//后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就
//算由?
.5,还是在小数点后面。
}
else
{
disp[2]=0;//如果温度不是负数,就置零
tp=temp;//因为数据处理有小数点所以将温度赋给一个浮点型变量
//如果温度是正的那么,那么正数的原码就是补码它本身
temp=tp*0.0625*100+0.5;
//留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点
//后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就
//算加上0.5,还是在小数点后面。
}
message[9]=(temp/10000)+'0';//具体温度的百位
message[10]=(temp%10000/1000)+'0';//具体温度的十位
message[11]=(temp%1000/100)+'0';//具体温度的个位
message[12]='.';//小数点字符
message[13]=(temp%100/10)+'0';//具体温度的十分位
message[14]=(temp%10)+'0';//具体温度的千分位
message[15]='o';//message[15]和[16]字符串代表°C摄氏度符号
message[16]='C';
disp[0]=0;//disp[0]和disp[1]直接置零
disp[1]=0;
disp[3]=DIG_CODE[temp/10000];//数码管显示具体温度的百位
disp[4]=DIG_CODE[temp%10000/1000];//数码管显示具体温度的十位
disp[5]=DIG_CODE[temp%1000/100]|0x80;//数码管显示具体温度的个位
//共阴接法,数码管为高电平驱动,且小数点的段接在IO的bit7,所以用|0x80来点亮小数点
disp[6]=DIG_CODE[temp%100/10];//数码管显示具体温度的十分位
disp[7]=DIG_CODE[temp%10];//数码管显示具体温度的千分位
}
温度传感器temp.c:
#include"temp.h"
/*******************************************************************************
*函数名:
Delay1ms
*函数功能:
延时函数
*输入:
无
*输出:
无
*******************************************************************************/
voidDelay1ms(unsignedinty)
{
unsignedintx;
for(y;y>0;y--)
for(x=110;x>0;x--);
}
/*******************************************************************************
*函数名:
Ds18b20Init
*函数功能:
初始化
*输入:
无
*输出:
初始化成功返回1,失败返回0
*******************************************************************************/
unsignedcharDs18b20Init()
{
unsignedinti;
DSPORT=0;//将总线拉低480us~960us
i=70;
while(i--);//延时642us
DSPORT=1;//然后拉高总线,如果DS18B20做出反应会将在15us~60us后总线拉低
i=0;
while(DSPORT)//等待DS18B20拉低总线
{
i++;
if(i>5000)//等待>5MS
return0;//初始化失败
}
return1;//初始化成功
}
/*******************************************************************************
*函数名:
Ds18b20WriteByte
*函数功能:
向18B20写入一个字节
*输入:
com
*输出:
无
*******************************************************************************/
voidDs18b20WriteByte(unsignedchardat)
{
unsigned