C51单片机课程设计报告.docx
《C51单片机课程设计报告.docx》由会员分享,可在线阅读,更多相关《C51单片机课程设计报告.docx(30页珍藏版)》请在冰豆网上搜索。
C51单片机课程设计报告
内容提要
单片微型计算机简称单片机,是典型的嵌入式微控制器,常用英文字母的缩写MCU表示单片机,它最早是被用在工业控制领域。
单片机由芯片内仅有CPU的专用处理器发展而来。
最早的设计理念是通过将大量外围设备和CPU集成在一个芯片中,使计算机系统更小,更容易集成进复杂的而对体积要求严格的控制设备当中。
单片机又称单片微控制器,它不是完成某一个逻辑功能的芯片,而是把一个计算机系统集成到一个芯片上。
相当于一个微型的计算机,和计算机相比,单片机只缺少了I/O设备。
概括的讲:
一块芯片就成了一台计算机。
它的体积小、质量轻、价格便宜、为学习、应用和开发提供了便利条件。
同时,学习使用单片机是了解计算机原理与结构的最佳选择。
单片机是靠程序运行的,并且可以修改。
通过不同的程序实现不同的功能,尤其是特殊的独特的一些功能,这是别的器件需要费很大力气才能做到的,有些则是花大力气也很难做到的。
一个不是很复杂的功能要是用美国50年代开发的74系列,或者60年代的CD4000系列这些纯硬件来搞定的话,电路一定是一块大PCB板!
但是如果要是用美国70年代成功投放市场的系列单片机,结果就会有天壤之别!
只因为单片机的通过你编写的程序可以实现高智能,高效率,以及高可靠性!
目前单片机渗透到我们生活的各个领域,几乎很难找到哪个领域没有单片机的踪迹。
导弹的导航装置,飞机上各种仪表的控制,计算机的网络通讯与数据传输,工业自动化过程的实时控制和数据处理,广泛使用的各种智能IC卡,民用豪华轿车的安全保障系统,录像机、摄像机、全自动洗衣机的控制,以及程控玩具、电子宠物等等,这些都离不开单片机。
更不用说自动控制领域的机器人、智能仪表、医疗器械以及各种智能机械了。
所以说对于单片机课程的学习对于我们电子信息工程专业的学生来说至关重要,然而学习单片机课程设计是其一个极为重要的实践环节,无论是硬件扩展、接口应用还是编程方法、程序调试,都离不开该环节。
单片机课程设计过程中,学生通过查阅资料、接口设计、程序设计、安装调试等环节,完成一个涉及MCS-51单片机多种资源应用并具有综合功能的小系统目标板的设计与编程应用。
通过该环节,学生不但能够将课堂上学到的理论知识与实际应用结合起来,而且能够对电子电路、电子元器件等方面的知识进一步加深认识,同时在软件编程、排错调试、焊接技术、相关仪器设备的使用技能等方面得到全面的锻炼和提高。
一、课程设计目的:
通过课程设计,使学生巩固和加深对单片机基本知识的理解,能够将课堂上学到的理论知识与实际应用结合起来,而且能够对电子电路、电子元器件等方面的知识进一步加深认识,同时在软件编程、排错调试、焊接技术、相关仪器设备的使用技能等方面得到全面的锻炼和提高。
学会查寻资料、方案设计、方案比较,以及单元电路设计计算等环节,进一步提高学生综合运用所学知识的能力,提高分析解决实际问题的能力。
锻炼分析、解决电子电路问题的实际本领,通过此综合训练,为以后毕业设计打下一定的基础。
二、已知技术参数和条件
(1)STC89C51单片机
(2)LED七段数码显示器及8个LED小彩灯
(3)74HC595串口转并口芯片
(4)电位器
(5)按键
(6)PCF8591AD-DA数模转换芯片
(7)电阻箱,5V电源,电阻若干,导线若干
(8)KEIL软件
(9)MCS-51单片机官方烧写程序软件STC_ISP_V488.exe
三、设计任务和要求
1.通过单片机的I/O口直接控制8盏LED小灯,实现7种彩灯灯光效果(左移、右移、中间向两边展开、两边向中间合拢等,不含全部点亮,全部熄灭);
2.用按键来切换控制器的灯光模式,能显示当前的运行模式号;
3.彩灯效果的移动速度基于AD输入量的大小(控制器通过PCF8591对0~5V信号进行采样,根据模拟信号的大小而改变彩灯移动速度);彩灯控制器还要显示当前模拟输入信号的大小(数码管显示);
4.彩灯效果中的延时采用定时中断实现。
按键、数码管直接用C51的I/O驱动
扩展:
可以通过串口通讯芯片595来控制彩灯效果的切换,将AD采样结果显示的数码管上。
四、需求分析
根据本次课程设计题目分析及其题目简化,需要做的是通过电位器来控制8盏彩灯的扫描显示速度,并且显示此时AD芯片的采样值,并且通过一个按键来切换不同的彩灯的7种显示模式。
于是乎,可以将系统分为两个部分:
其一,模拟采样部分;其二,彩灯显示部分。
最后得到以下的设计方案:
通过一个电位器来得到一个可以改变的模拟值;
采用PCF8591采样电位器所产生的模拟值,并将其转化为数字信号;
MCU通过I2C总线读取PCF8951内部ROM中所缓存的数值;
编写数码管显示函数,通过单片机的I/O口驱动数码管显示PCF8591所采样的值。
(数码管高四位显示PCF8591所采样的数值,低四位显示当前PCF8591所采取的精确电压值,精确到小数点后三位);
编写LED彩灯的显示,利用定时器0来控制每一盏小灯的显示延迟时间,利用定时器1和pcf8591所采样的数字信号来共同确定LED彩灯的扫描速度,即通过调节电位器可以改变LED灯的扫描速度(通过MCU的I/O驱动74HC595芯片实现串口转并口来驱动8个LED小彩灯的显示);
采用外部中断0来切换LED彩灯的显示模式。
五、硬件电路图设计及描述
注:
电路图原图另有PDF文件
1、数码管显示电路设计
课程设计中所用的数码管为共阴极数码管,当给其引脚加入高电平时,数码管中对应的那一段就亮。
下图为外部显示图,其内部结构如下:
共阴极数码管内部结构
2、LED显示电路设计
本次课程设计中要用发光二极管显示状态。
由下图知发光二极管阳极经过电阻接高电平,当其阴极为低电平时,二极管就发光显示所处状态。
通过串口转并口芯片74HC595的并口输出来改变LED灯的显示状态。
3、AD转换电路设计
此次课程设计的AD转换芯片采用的是PCF8591芯片,芯片的9、10管脚是I2C串口协议传输引脚,其与MCU的P2^0、P2^1接口连接。
A0、A1、A2为地址线,此处接地,地址为I2C传输数据的地址为000。
四路模拟输入接口为AIN0、AIN1、AIN2、AIN3,此处只选择一路模拟输入-------AIN0。
模拟输入信号通过一个电位器的改变而提供。
4、时钟电路设计
时钟电路对单片机是不可缺的,单片机的每个功能都要以时钟电路为基础工作。
单片机内部自带一个时钟电路,外部接入定时控制元件即可构成一个稳定的自激振荡器。
其中机器周期共有12个振荡脉冲周期,因此,机器周期是时钟周期的12倍。
本课程设计中时钟电路中使用的晶体是12MHz,则时钟周期为(1/12)us,机器周期为1us。
实验图如下:
5、外部中断信号电路设计
外部中断信号的是由一个简单的不带锁的按键开关提供的一个脉冲信号。
六、软件设计思想及流程
(1)使用单片机资源的情况:
设计时使用单片机资源的情况如下:
AT89S51单片机的P0、P1口作为数码管数据线接口,其中,P0口接是段选,P1接位选,P2^0、P2^1接AD转换芯片PCF8591的I2C串口接口,P3^4、P3^5、P3^6口接74HC595的串口输入接口,P3^2外接外部中断按键。
单片机定时器0、定时器1均匀方式1来控制定时器运行。
(2)软件系统的各个模块功能:
本设计的软件系统主要采用以下基本模块来实现:
主程序、中断服务程序、串口传输程序。
主程序用于对于各个程序模块的运行及控制,以及各个模式程序的初始化。
特别是定时中断的初始值。
在主程序中还主要的对pcf8591进行了操作,采样,量化,显示。
有三个中断服务程序,分别是定时器0中断、定时器1中断、外部中断,通过外部中断来实现各个LED灯的显示模式的切换。
定时器中断0用于控制LED灯的显示延时。
定时器1用于操作LED灯的不同模式的显示。
串口传输程序只要有两种串口传输函数,分别I2C串口传输函数和595串口转并口的串口传输函数。
在串口函数里面详细的对串口进行了操作,里面有I2C串口的初始化,终止,数据传输,应答,595串口的传输。
(3)设计的软件流程框图:
(4)软件调试过程
使用keil软件在做写程序之前选择createHEXFi:
表示生成可烧写进单片机的HEX文件。
在编写程序结束之后点击编译,生成HEX文件。
再生成HEX文件之后打开STC单片机官方烧写软件进行程序烧写:
选择单片机型号;
选择生成的HEX文件;
选择单片机连接的COM口;
选择传输的波特率;
断电烧写。
七、实验效果
在完成烧写程序完成之后单片机即可正常执行程序。
执行效果如下:
电位器1即模拟信号的输入,通过旋转电位器1可改变数码管的显示电位器1的采样值,与调节LED的扫描显示速度。
通过按键2可改变LED灯的显示模式。
不同的实验效果图还有如下:
八、源程序代码
/************main.c***************/
#include
#include
#include
#definePCF85910x90//PCF8591地址
unsignedcharLED[9]={0x01,0x01,0x01,0x01,0x7F,0x81,0x81,0x01,0xAA};//LED的显示模式
unsignedcharLED_kind=1,a=0x80,b=0x01;
unsignedintj,mun;
//此表为LED的字模,共阴数码管0-9-
unsignedcharcodeDisp_Tab[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f
0x6f};
unsignedlongxdataLedOut[8];
unsignedintD[3],n=1;
/********************DAC变换,转化函数*********************/
bitDACconversion(unsignedcharsla,unsignedcharc,unsignedcharVal)
{
Start_I2c();//启动总线
SendByte(sla);//发送器件地址
if(ack==0)return(0);
SendByte(c);//发送控制字节
if(ack==0)return(0);
SendByte(Val);//发送DAC的数值
if(ack==0)return(0);
Stop_I2c();//结束总线
return
(1);
}
/***********************ADC发送字节[命令]数据函数***************************/
bitISendByte(unsignedcharsla,unsignedcharc)
{
Start_I2c();//启动总线
SendByte(sla);//发送器件地址
if(ack==0)return(0);
SendByte(c);//发送数据
if(ack==0)return(0);
Stop_I2c();//结束总线
return
(1);
}
/**************************ADC读字节数据函数**********************/
unsignedcharIRcvByte(unsignedcharsla)
{unsignedcharc;
Start_I2c();//启动总线
SendByte(sla+1);//发送器件地址+1使写入地址为0x91为读地址10010001
if(ack==0)return(0);
c=RcvByte();//读取数据0
Ack_I2c
(1);//发送非就答位
Stop_I2c();//结束总线
return(c);
}
//****************************************************/
main()
{chari;
TMOD=0x11;//设置计数模式为方式2
TH0=(65536-500)/256;
TL0=(65536-500)%256;
TH1=(65536-50)/256;
TL1=(65536-50)%256;
IE=0x8B;//开定时器0、定时器1
TR0=1;
TR1=1;
IT0=1;
while
(1)
{
ISendByte(PCF8591,0x40);//使用通道0进行采样
D[0]=IRcvByte(PCF8591);//从pcf8591读取模数转换的数值
D[1]=D[0]*5/0.255;//将模数转换的数值转换成电压值
D[2]=D[0]*2/25.5;//用于控制LED的速度
/***********将AD的值送到LED数码管显示*************/
LedOut[0]=Disp_Tab[D[0]%10000/1000];
LedOut[1]=Disp_Tab[D[0]%1000/100];
LedOut[2]=Disp_Tab[D[0]%100/10];
LedOut[3]=Disp_Tab[D[0]%10];
/***********转换后的电压值送到LED数码管显示************/
LedOut[4]=Disp_Tab[D[1]%10000/1000]|0x80;
LedOut[5]=Disp_Tab[D[1]%1000/100];
LedOut[6]=Disp_Tab[D[1]%100/10];
LedOut[7]=Disp_Tab[D[1]%10];
for(i=0;i<8;i++)
{P0=LedOut[i];//段选择
switch(i)//使用switch位选择
{
case0:
P1=0xFE;break;
case1:
P1=0xFD;break;
case2:
P1=0xFB;break;
case3:
P1=0xF7;break;
case4:
P1=0xEF;break;
case5:
P1=0xDF;break;
case6:
P1=0xBF;break;
case7:
P1=0x7F;break;
}
//for(j=0;j<90;j++){;}//扫描间隔时间
n=1;
while(n);
P1=0xFF;//清除余辉相映
}
}
}
voidTimer0(void)interrupt1//3定时器1的中断号1定时器0的中断号0外部中断12外部中断24串口中断
{
TH0=(65536-1000)/256;
TL0=(65536-1000)%256;
n=0;
}
voidTimer2(void)interrupt0
{
if(LED_kind<=7)
LED_kind++;
else
LED_kind=1;
}
voidTimer1(void)interrupt3//3定时器1的中断号1定时器0的中断号0外部中断12外部中断24串口中断
{
unsignedcharc=1,d=0;
j++;
TH1=(65536-40000)/256;
TL1=(65536-40000)%256;
if(j>D[2])
{
switch(LED_kind)
{
case1:
HC595SendData(~(LED[0]++));break;
case2:
LED[1]=_cror_(LED[1],1);mun=~LED[1];HC595SendData(mun);break;
case3:
LED[2]=_crol_(LED[2],1);
mun=~LED[2];
HC595SendData(mun);
break;
case4:
{
mun=~LED[3];
if(LED[3]<=0x80)
{
HC595SendData(mun);
LED[3]=LED[3]<<1;
LED[3]=LED[3]|0x01;
}
else
{
LED[3]=0x01;
HC595SendData(mun);
}
}
break;
case5:
{
HC595SendData(LED[4]);
LED[4]=LED[4]>>1;
if(LED[4]==0)
{
HC595SendData(LED[4]);
LED[4]=0x7F;
}
}
break;
case6:
{
if(a!
=0)
{
d=a^LED[7];
HC595SendData(~d);
a=a>>1;
LED[7]=LED[7]<<1;
}
else
{
a=0x80;
LED[7]=0x01;
d=a|LED[7];
HC595SendData(~d);
}
}break;
case7:
{
LED[8]=~LED[8];
HC595SendData(LED[8]);
}break;
}
j=0;
}
}
/***************************I2C.c************************/
#include
#include
#include
#defineucharunsignedchar
#defineuintunsignedint
#defineNOP()_nop_()/*定义空指令*/
#define_Nop()_nop_()/*定义空指令*/
sbitSCL=P2^1;//I2C时钟
sbitSDA=P2^0;//I2C数据
bitack;/*应答标志位*/
sbitMOSIO=P3^4;//HC595数据线
sbitR_CLK=P3^5;//输出时钟
sbitS_CLK=P3^6;//输入时钟
/*******************************************************************
起动总线函数
函数原型:
voidStart_I2c();
功能:
启动I2C总线,即发送I2C起始条件.
********************************************************************/
voidStart_I2c()
{
SDA=1;/*发送起始条件的数据信号*/
_Nop();
SCL=1;
_Nop();/*起始条件建立时间大于4.7us,延时*/
_Nop();
_Nop();
_Nop();
_Nop();
SDA=0;/*发送起始信号*/
_Nop();/*起始条件锁定时间大于4μs*/
_Nop();
_Nop();
_Nop();
_Nop();
SCL=0;/*钳住I2C总线,准备发送或接收数据*/
_Nop();
_Nop();
}
/*******************************************************************
结束总线函数
函数原型:
voidStop_I2c();
功能:
结束I2C总线,即发送I2C结束条件.
********************************************************************/
voidStop_I2c()
{
SDA=0;/*发送结束条件的数据信号*/
_Nop();/*发送结束条件的时钟信号*/
SCL=1;/*结束条件建立时间大于4μs*/
_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
SDA=1;/*发送I2C总线结束信号*/
_Nop();
_Nop();
_Nop();
_Nop();
}
/*******************************************************************
字节数据发送函数
函数原型:
voidSendByte(UCHARc);
功能:
将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
此状态位进行操作.(不应答或非应答都使ack=0)
发送数据正常,ack=1;ack=0表示被控器无应答或损坏。
********************************************************************/
voidSendByte(unsignedcharc)
{
unsignedcharBitCnt;
for(BitCnt=0;BitCnt<8;BitCnt++)/*要传送的数据长度为8位*/
{
if((c<elseSDA=0;
_Nop();
SCL=1;/*置时钟线为高,通知被控器开始接收数据位*/
_Nop();
_Nop();/*保证时钟高电平周期大于4μs*/
_Nop();
_Nop();
_Nop();
SCL=0;
}
_Nop();
_Nop();
SDA=1;/*8位发送完后释放数据线,准备接收应答位*/
_Nop();
_Nop();
SCL=1;
_Nop();
_Nop();
_Nop();
if(SDA==1)ack=0;
elseack=1;/*判断是否接收到应答信号*/
SCL=0;
_Nop();
_Nop();
}
/*******************************************************************
字节数据接收函数
函数原型:
UC