单片机原理及其应用C语言版.docx

上传人:b****6 文档编号:7277439 上传时间:2023-01-22 格式:DOCX 页数:29 大小:111.78KB
下载 相关 举报
单片机原理及其应用C语言版.docx_第1页
第1页 / 共29页
单片机原理及其应用C语言版.docx_第2页
第2页 / 共29页
单片机原理及其应用C语言版.docx_第3页
第3页 / 共29页
单片机原理及其应用C语言版.docx_第4页
第4页 / 共29页
单片机原理及其应用C语言版.docx_第5页
第5页 / 共29页
点击查看更多>>
下载资源
资源描述

单片机原理及其应用C语言版.docx

《单片机原理及其应用C语言版.docx》由会员分享,可在线阅读,更多相关《单片机原理及其应用C语言版.docx(29页珍藏版)》请在冰豆网上搜索。

单片机原理及其应用C语言版.docx

单片机原理及其应用C语言版

注意:

本课件为上课内容的一个补充,其中难免存在错误,请读者不吝赐教,如有问题请发送E-mail到zhaojian@。

本文根据教学的情况,随时进行修改和完善,所以欢迎同学随时注意本文档在课件中的更新情况。

单片机基础知识

单片机的外部结构:

1、DIP40双列直插;

2、P0,P1,P2,P3四个8位准双向I/O引脚;(作为I/O输入时,要先输出高电平)

3、电源VCC(PIN40)和地线GND(PIN20);

4、高电平复位RESET(PIN9);(10uF电容接VCC与RESET,即可实现上电复位)

5、内置振荡电路,外部只要接晶体至X1(PIN18)和X0(PIN19);(频率为主频的12倍)

6、程序配置EA(PIN31)接高电平VCC;(运行单片机内部ROM中的程序)

7、P3支持第二功能:

RXD、TXD、INT0、INT1、T0、T1

单片机内部I/O部件:

(所为学习单片机,实际上就是编程控制以下I/O部件,完成指定任务)

1、四个8位通用I/O端口,对应引脚P0、P1、P2和P3;

2、两个16位定时计数器;(TMOD,TCON,TL0,TH0,TL1,TH1)

3、一个串行通信接口;(SCON,SBUF)

4、一个中断控制器;(IE,IP)

针对AT89C52单片机,头文件AT89x52.h给出了SFR特殊功能寄存器所有端口的定义。

教科书的160页给出了针对MCS51系列单片机的C语言扩展变量类型。

C语言编程基础:

1、十六进制表示字节0x5a:

二进制为01011010B;0x6E为01101110。

2、如果将一个16位二进数赋给一个8位的字节变量,则自动截断为低8位,而丢掉高8位。

3、++var表示对变量var先增一;var—表示对变量后减一。

4、x|=0x0f;表示为x=x|0x0f;

5、TMOD=(TMOD&0xf0)|0x05;表示给变量TMOD的低四位赋值0x5,而不改变TMOD的高四位。

6、While

(1);表示无限执行该语句,即死循环。

语句后的分号表示空循环体,也就是{;}

 

第一章单片机最小应用系统:

单片机最小系统的硬件原理接线图:

1、接电源:

VCC(PIN40)、GND(PIN20)。

加接退耦电容0.1uF

2、接晶体:

X1(PIN18)、X2(PIN19)。

注意标出晶体频率(选用12MHz),还有辅助电容30pF

3、接复位:

RES(PIN9)。

接上电复位电路,以及手动复位电路,分析复位工作原理

4、接配置:

EA(PIN31)。

说明原因。

具体接法如下图所示:

第二章基本I/O口的应用

例一:

在P1.0口输出一方波。

具体程序如下:

#include/*该头文档描述单片机所有特殊功能寄存器的称名,程序中可直接使用,比喻'P1'*/

voidmain(void)//一个工程项目必须有一个main函数,并且只能有一个main函数

{

while

(1)//死循环,因为永远为真

{

P1_0=1;//P1.0口输出一个高电平

P1_0=0;//P1.0口输出一个低电平

}

}

例2:

在上题中,让P1.0口接上一个发光二极管,使发光二极管能闪烁。

考虑到要人眼能看到闪烁,所在输出电平以后加上延时程序就可以了。

具体程序如下:

#include

voiddelay(void)定义一个延时函数,如果函数在主函数之前定义则不用声明。

{

unsignedinti,j;//定义两个变量用于循环控制

for(i=0;i<100;i++)

for(j=0;j<100;j++);

}

voidmain(void)

{

while

(1)

{

P1_0=1;

delay();//调用延时函数,具体的延时时间要看汇编语言才能确定

P1_0=0;

delay();

}

}

例3完成一个循环流水灯的程序,要求在同一时间里只能有一个灯亮,在P1口上接了8个发光二极管,当P1口输出低电平的时候发光二极管就会亮。

具体程序如下:

#include

voiddelay(void)

{

unsignedinti,j;

for(i=0;i<100;i++)

for(j=0;j<100;j++);

}

voidmain(void)

{

unsignedcharm;

while

(1)

{

P1=0xfe;//让第一个灯亮

delay();

for(m=0;m<7;m++)

{

P1=(P1<<1)|0x01;//将P1里的数全部向左移动一位,并保证移进P1的数为1

delay();

}

}

}

大家可以思考一下,如果这里改为同一时间里只有一个灯灭,程序应该怎么改?

例4让P1口输出八路倍频信号

具体程序如下:

#include

voidmain(void)

{

while

(1)

{

++P1;

}

}

例5从P1口输出倍频方波,要求P1.7变化得最快,P1.0变化得最慢。

具体程序如下:

#include

voidmain(void)

{

unsignedcharm,n;//定义两个中间变量完成交换过程

unsignedinti,j;

while

(1)

{

n=0;

++m;

n|=(m<<7)&0x80;//将第0位的值送至第7位

n|=(m<<5)&0x40;//将第1位的值送至第6位

n|=(m<<3)&0x20;//将第2位的值送至第5位

n|=(m<<1)&0x10;//将第3位的值送至第4位

n|=(m>>1)&0x08;//将第4位的值送至第3位

n|=(m>>3)&0x04;//将第5位的值送至第2位

n|=(m>>5)&0x02;//将第6位的值送至第1位

n|=(m>>7)&0x01;//将第7位的值送至第0位

P1=n;

for(i=0;i<1000;i++)

for(j=0;j<1000;j++);

}

}

注意:

一个字节的8位D7、D6至D0,分别输出到P3.7、P3.6至P3.0,比如P3=0x0f,则P3.7、P3.6、P3.5、P3.4四个引脚都输出低电平,而P3.3、P3.2、P3.1、P3.0四个引脚都输出高电平。

同样,输入一个端口P2,即是将P2.7、P2.6至P2.0,读入到一个字节的8位D7、D6至D0。

 

第三章显示驱动

数码管的接法和驱动原理

一支七段数码管实际由8个发光二极管构成,其中7个组形构成数字8的七段笔画,所以称为七段数码管,而余下的1个发光二极管作为小数点。

作为习惯,分别给8个发光二极管标上记号:

a,b,c,d,e,f,g,h。

对应8的顶上一画,按顺时针方向排,中间一画为g,小数点为h。

我们通常又将各二极与一个字节的8位对应,a(D0),b(D1),c(D2),d(D3),e(D4),f(D5),g(D6),h(D7),相应8个发光二极管正好与单片机一个端口Pn的8个引脚连接,这样单片机就可以通过引脚输出高低电平控制8个发光二极的亮与灭,从而显示各种数字和符号;对应字节,引脚接法为:

a(Pn.0),b(Pn.1),c(Pn.2),d(Pn.3),e(Pn.4),f(Pn.5),g(Pn.6),h(Pn.7)。

如果将8个发光二极管的负极(阴极)内接在一起,作为数码管的一个引脚,这种数码管则被称为共阴数码管,共同的引脚则称为共阴极,8个正极则为段极。

否则,如果是将正极(阳极)内接在一起引出的,则称为共阳数码管,共同的引脚则称为共阳极,8个负极则为段极。

以单支共阴数码管为例,可将段极接到某端口Pn,共阴极接GND,则可编写出对应十六进制码的七段码表字节数据如下图:

 

动态显示的电路连接如下图所示:

 

下面,我们编程在数码管上显示出“1234”。

程序如下:

#include

CodeunsignedcharSeg7Code[16]=/*用十六进数作为数组下标,可直接取得对应的七段编码字节*/

//0123456789AbCdEF

{0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};

voidmain(void)

{

unsignedinti;

while

(1)

{

P2|=0x0f;//消隐,让数码管开始处于不亮的状态

P0=LedCode[1];//将“1”的代码送出

P2&=0xfe;//选中第一个数码管

for(i=0;i<1000;i++);

P2|=0x0f;

P0=LedCode[2];

P2&=0xfd;

for(i=0;i<1000;i++);

P2|=0x0f;

P0=LedCode[3];

P2&=0xfb;

for(i=0;i<1000;i++);

P2|=0x0f;

P0=LedCode[4];

P2&=0xf7;

for(i=0;i<1000;i++);

}

}

关于DRIVER

编写DRIVER的目的是让程序能适应更多的场合,让我们的使用更加方便,大家可以把一些自己编过的有用的程序做成DRIVER便于自己以后的使用。

下面介绍显示的驱动程序:

首先,定义一个头文档,描述可用函数,如下:

#ifndef_LedDriver_H_//防止重复引用该文档,如果没有定义过符号_KEY_H_,则编译下面语句

#define_LedDriver_H_//只要引用过一次,即#include,则定义符号_KEY_H_

voidLedPrint(unsignedcharDat)//数据缓冲区间,完成移位功能

voidLedWork(void)//送数到显示数码管

#endif

 

然后,定义函数体文档LedDriver.C,如下:

#include

#include“LedDriver.h”

codeunsignedcharLedCode[16]=//Code是表示这个数组的存储空间

{0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};

unsignedcharDisBuf[4];

voidLedPrint(unsignedcharDat)

{

DisBuf[0]=DisBuf[1];//每次用后一个数冲掉前一个数,便于扩展显示位数

DisBuf[1]=DisBuf[2];

DisBuf[2]=DisBuf[3];

DisBuf[3]=Dat;

}

voidLedWork(void)

{

staticunsignedchari=0;/*static表示静态变量,指变量的赋值只在第一次定义的时候赋*/

unsignedintm;

P2|=0x0f;

P0=LedCode[DisBuf[i]];

switch(i)//选择数据送到哪个管子

{

case0:

P2_0=0;break;

case1:

P2_1=0;break;

case2:

P2_2=0;break;

case3:

P2_3=0;break;

}

if(++i>=4)i=0;//判断四位数是否都已经送完

for(m=0;m<1000;m++);//延时

}

这样DRIVER的程序就编好了,我们以后用的时候直接调用函数就可以了。

主程序可以编写如下:

#include

#include“LedDriver.h”

voidmain(void)

{

LedPrint

(1);//调用函数,把想显示的数据送如缓存

LedPrint

(2);

LedPrint(3);

LedPrint(4);

while

(1)

{

LedWork();

}

}

下面介绍一个例子供大家参考。

显示“12345678”

P1端口接8联共阴数码管SLED8的段极:

P1.7接段h,…,P1.0接段a

P2端口接8联共阴数码管SLED8的段极:

P2.7接左边的共阴极,…,P2.0接右边的共阴极

方案说明:

晶振频率fosc=12MHz,数码管采用动态刷新方式显示,在1ms定时断服务程序中实现

#include

unsignedcharDisBuf[8];/*全局显示缓冲区,DisBuf[0]对应右SLED,DisBuf[7]对应左SLED*/

voidDisplayBrush(void)

{codeunsignedcharcathode[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//阴极控制码

codeunsignedcharSeg7Code[16]=//用十六进数作为数组下标,可直接取得对应的七段编码字节

{0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};

staticunsignedchari=0;//(0≤i≤7)循环刷新显示,由于是静态变量,此赋值只做一次。

P2=0xff;//显示消隐,以免下一段码值显示在前一支SLED

P1=Seg7Code[DisBuf[i]];//从显示缓冲区取出原始数据,查表变为七段码后送出显示

P2=cathode[i];//将对应阴极置低,显示

if(++i>=8)i=0;//指向下一个数码管和相应数据

}

voidTimer0IntRoute(void)interrupt1

{

TL0=-1000;//由于TL0只有8bits,所以将(-1000)低8位赋给TL0

TH0=(-1000)>>8;//取(-1000)的高8位赋给TH0,重新定时1ms

DisplayBrush();

}

voidTimer0Init(void)

{

TMOD=(TMOD&0xf0)|0x01;//初始化,定时器T0,工作方式1

TL0=-1000;//定时1ms

TH0=(-1000)>>8;

TR0=1;//允许T0开始计数

ET0=1;//允许T0计数溢出时产生中断请求

}

voidDisplay(unsignedcharindex,unsignedchardataValue)

{

DisBuf[index]=dataValue;

}

voidmain(void)

{

unsignedchari;

for(i=0;i<8;i++){Display(i,8-i);区qhkode[DisBuf[i]];//;f7,0xfd,0xfb,0xfe};__________________________________________________________________________________}//DisBuf[0]为右,DisBuf[0]为左

Timer0Init();

EA=1;//允许CPU响应中断请求

While

(1);

}

第四章键盘驱动

单片机I/O口作为输入的前提是必须首先输出一个高电平。

 

charKbhit(void)

{

P1.0P1_0=1;//使P1.0作为输出口使用

if(P1_0==1)return(0);

elsereturn

(1);//如果有键按下则返回1

}

一般来说,按键的时候会有抖动,我们可以用加延时的办法来去除抖动。

即:

P1_0=1;

if(P1_0==1)return(0);

else

{

延时20ms;

if(P1_0==1)return(0);//表示前面的按键为键抖动,不是真的按下了

elsereturn

(1);//确实是按下了键,返回1,表示有键按下

}

一般来说我们在一个单片机系统中都不可能只有一个按键,因此我们介绍下面这个行列式4X4按键。

由P1端口的高4位和低4位构成4X4的矩阵键盘,本程序只认为单键操作为合法,同时按多键时无效。

 

判断有无键按下的程序:

charKbhit(void)//定义一个返回值为char型的函数

{

P1=0xf0;//使键盘的列为高电平,作输入,行为低电平,作输出

if(P1==0xf0)return(0);//判断是否有键按下

elsereturn

(1);

}

下面我们来写取键值的程序:

unsignedcharGetKeyCode(void)

{

unsignedcharkeycode;//定义一个键值变量用于判断键值

P1=0x0f;

keycode=P1;

P1=0xf0;

keycode|=P1;//将组合以后的键值存储

switch(keycode)根据键值变量判断到底是哪个键按下了

{

case0xee:

return(0);break;

case0xde:

return

(1);break;

case0xbe:

return

(2);break;

case0x7e:

return(3);break;

case0xed:

return(4);break;

case0xdd:

return(5);break;

case0xbd:

return(6);break;

case0x7d:

return(7);break;

case0xeb:

return(8);break;

case0xdb:

return(9);break;

case0xbb:

return(10);break;

case0x7b:

return(11);break;

case0xe7:

return(12);break;

case0xd7:

return(13);break;

case0xb7:

return(14);break;

case0x77:

return(15);break;

}

}

下面是键盘的Driver程序:

首先我们还是来写KeyDriver.h这个程序:

#ifndef_KeyDriver_h_

#define_KeyDriver_h_

charKhbit(void);

charGetch(void);

#endif

注意:

我们写的头文件必须存在当前工程所在的文件夹里,而定义函数的C文件放在任意地方都可以,我们只需要去选择它的目录,添加到我的工程中就可以了。

下面我们来写KeyDriver.c文件:

#include

#include"KeyDriver.h"

charKbhit(void)

{

P1=0xf0;

if(P1==0xf0)return(0);

elsereturn

(1);

}

unsignedcharGetKeyCode(void)

{

unsignedcharkeycode;

P1=0x0f;

keycode=P1;

P1=0xf0;

keycode|=P1;

switch(keycode)

{

case0xee:

return(0);

case0xde:

return

(1);

case0xbe:

return

(2);

case0x7e:

return(3);

case0xed:

return(4);

case0xdd:

return(5);

case0xbd:

return(6);

case0x7d:

return(7);

case0xeb:

return(8);

case0xdb:

return(9);

case0xbb:

return(10);

case0x7b:

return(11);

case0xe7:

return(12);

case0xd7:

return(13);

case0xb7:

return(14);

case0x77:

return(15);

}

}

最后我们来做一个按键显示程序:

#include

#include"LedDriver.h"//包含显示驱动里的送数入显存和显示函数

#include"KeyDriver.h"//包含键盘里的判断有键按下和判键值函数

voidmain(void)

{

unsignedchark;

for(k=1;k<5;k++)/*将1234四个数送入显存,使程序在没有按键的时候显示1234四个数字在数码管上*/

{

LedPrint(k);

}

while

(1)

{

LedWork();//调显示函数,使显存里的数显示在数码管上

if(Kbhit()==1)//判断有无键按下

LedPrint(GetKeyCode());//有键按下的时候将键值送到显存

}

}

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 表格模板 > 合同协议

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1