第八章 单片机8051内部资源C语言编程.docx

上传人:b****8 文档编号:29213107 上传时间:2023-07-21 格式:DOCX 页数:46 大小:350.80KB
下载 相关 举报
第八章 单片机8051内部资源C语言编程.docx_第1页
第1页 / 共46页
第八章 单片机8051内部资源C语言编程.docx_第2页
第2页 / 共46页
第八章 单片机8051内部资源C语言编程.docx_第3页
第3页 / 共46页
第八章 单片机8051内部资源C语言编程.docx_第4页
第4页 / 共46页
第八章 单片机8051内部资源C语言编程.docx_第5页
第5页 / 共46页
点击查看更多>>
下载资源
资源描述

第八章 单片机8051内部资源C语言编程.docx

《第八章 单片机8051内部资源C语言编程.docx》由会员分享,可在线阅读,更多相关《第八章 单片机8051内部资源C语言编程.docx(46页珍藏版)》请在冰豆网上搜索。

第八章 单片机8051内部资源C语言编程.docx

第八章单片机8051内部资源C语言编程

第八章8051内部资源C语言编程

一、I/O口编程

例1:

用按键控制发光二极管。

并口是用的最多的资源,下面以P1口为例,见电路图,P1口低4位接了4个按钮,高4位接了4个指示灯,要求按下相应的按钮,对应的指示灯亮。

P10对应P14,依次类推。

指示灯是端口输出高电平亮。

源程序如下:

#include

voidmain()

{

unsignedchardatax,i;

while

(1)

{

P1=P1|0x0f;//给低4位置1,高4位不变

x=P1&0x0f;//读低4位

x=~x;//低4位取反

P1=x<<4;//左移4位

for(i=0;i<255;i++);//延时

}

}

(端口编程)

注意上面几种常见的用法。

 

二、中断的C语言编程

编号

中断源

入口地址

0

外部中断0

0003H

1

定时器/计数器0

000BH

2

外部中断1

0013H

3

定时器/计数器1

001BH

4

串行口中断

0023H

C51编译器支持在C源程序中直接开发中断程序。

前面已经讲过,中断服务程序是通过按规定语法格式定义的一个函数。

(中断有关内容)

中断服务程序的函数定义的语法格式如下:

Void函数名(void)interruptm[usingn]{中断程序代码;}

m为MCS-51中断源编号,见表

usingn选项用于实现工作寄存器组的切换,n是中断服务子程序中选用的工作寄存器组号(0-3)。

例2:

设AT89C52的时钟频率为12MHz,利用定时中断在其P1.0引脚输出周期为4ms,占空比为1:

1的方波。

确定定时器工作方式和计算定时器初值。

选用定时器T0工作方式1,每个机器周期为1μs,翻转一次电平需要2ms,则

计数次数n=2000/1=2000,

初值x=65536-2000=63536=F830H

参考程序如下:

#include

sbitP10=P1^0;//定义位

voidclock_initial()reentrantusing0//在中断中调用,定义为重入函数

{TR0=0;

TH0=0XF8;//装载计数初值

TL0=0X30;

TR0=1;}

main()

{

TMOD=0x01;//定时器T0方式1工作

P10=0;//初始值为低电平

TF0=0;//清除中断标志位

clock_initial();

ET0=1;

EA=1;

do{}while

(1);//死循环,等价于汇编语言的SJMP$

}

voidclk_int(void)interrupt1using0

{

P10=!

P10;//逻辑变量

clock_initial();

}

(延时中断)

例3:

图示是利用优先权解码芯片74LS148,在单片机8031的一个外部中断INT1上扩展多个中断源的原理电路图。

图中是以开关闭合来模拟中断请求信号。

当有任一中断源产生中断请求,能给8031的INT1引脚送一个有效中断信号,由P1的低3位可得对应中断源的中断号。

74LS148是8线-3线优先编码器,

为输入端,

为编码输出端,

为扩展端。

真值表见右图。

只要有输入的中断请求,

为低电平,申请中断。

同时根据P10,P11,P12的值,可以判断出是那一个中断源提出的中断申请。

在中断服务程序中仅设置标志,并保存I/O口输入状态。

参考程序如下:

#include

unsignedcharstatus;

bitflag=0;

voidservice_int1()interrupt2using2//INT1中断服务程序,使用第3组工作寄存器

{flag=1;//设置标志

status=P1&0x07;//存输入口状态}

voidmain(void)

{IP=0x04;//置INT1为高优先级中断

IE=0x84;//INT1开中断,CPU开中断

for(;;)//无限循环

{if(flag)//有中断

{switch(status)//根据中断源分支

{case0:

{中断1程序;}break;//处理IN0

case1:

{中断2程序;}break;//处理IN1

case2:

{中断3程序;}break;//处理IN2

case3:

{中断4程序;}break;//处理IN3

case4:

{中断5程序;}break;//处理IN4

case5:

{中断6程序;}break;//处理IN5

case6:

{中断7程序;}break;//处理IN6

default:

{中断8程序;}//处理IN8

}

flag=0;//处理完成清标志

}}}

三、定时器/计数器的c语言编程

定时器/计数器的有关内容:

定时器的编程,要选择定时器/计数器的工作方式,如果要定时,需要计算初值,溢出时相应中断标志置1。

下面用例子就进行说明。

例4:

将中断中的例2采用查询的方法实现。

采用AT89C51单片机。

参考程序如下:

#include

main()

{

TMOD=0x01;//置工作方式,

for(;;)//无限循环

{

TR0=0;

TF0=0;

TH0=0xf8;

TL0=0x30;

TR0=1;//开定时器T0

do{}while(!

TF0);//循环等待,TF0为1时退出

P1_0=!

P1_0;//AT89C51的头文件中已经定义

}

}

计数器在生产线等场合应用的很多,下面看以下定时器的编程。

思路:

计数器初始化,如果计数总值小于65535,直接取出TL0、TH0中的数据即可,如果大于65535,则要设置一个存储单元进行软件计数,本程序用软件计数。

每中断一次,计数值增加65535。

例5、由P3.4输入脉冲信号,用定时器T0进行计数,并不断输出计数值。

#include

typedefunsignedcharuchar;

#defineuintunsignedint;

ucharclow,chigh;

unsignedlongcvalue=0;//用全局变量传递数据

voidc_initial()

{

TCON=0x00;//将中断标志和开关全置0

TH0=0x00;

TL0=0x00;

TR0=1;//开定时器T0

}

uintc_module()//从TL0、TH0中取数据并计数

{unsignedlongdatax;

do

{

chigh=TH0;//读取数据

clow=TL0;

}while(chigh!

=TH0);//(item1)

x=cvalue*65536+chigh*256+clow;//计数总值

returnx;

}

voidmain()

{

TMOD=0x05;//设T0为16位(方式1)计数状态,

c_initial();//初始化

ET0=1;//开中断

EA=1;

do{c_module();

(显示程序和其他数据处理程序)

}while

(1);

}

voidc_int(void)interrupt1using1//中断程序,计溢出中断的次数

{

cvalue=cvalue+1;

}

(定时中断1)

item1;在THO、TL0中取数时,有一个时间差,先取TH0,然后在取TL0,在取TL0前,TL0可能进位。

因此取完TL0后返回来再取TH0判断是否变化,取值过程中若有进位,则重新取值。

四、串行口的C语言编程

(串行通信有关内容)

串行口通信,采用T1定时器作为波特率发生器,对不同的工作方式,波特率的设置是不同的,数据的位数也不同。

下面举几个例子:

例6:

在单片机中,printf的默认输出是串行口,用串行口输出字符“helloworld”。

#include

#include

voidmain(void)

{

SCON=0x50;//串行通信方式1,10位

TMOD=0x20;//定时器T1方式2

TH1=243;//置初值

TL1=243;

TR1=1;//开定时器

TI=1;//用串行口软件仿真输出时,必须这样设置

while

(1){

printf("HelloWorld\n");

}}

以上程序很简单,但在keil软件上进行仿真时很有用。

注意,和原例有修改,原例是用Monitor-51可以在目标硬件上调试程序。

例7:

要求每按一下按键(P10口),从串行口发出一个字符,该程序主要用于串行通信是否正常工作的调试。

和计算机通信时,由于各种原因,有时经常调试不通,可以用最简单的程序进行调试,调好后再调试应用程序。

下面是一段调试单片机与微机用VB编程通信的一段调试程序。

波特率9600B/S,24MHz的晶振频率,定时器初值的确定,当SMOD=1时,舍入误差较小(作业已做过)。

取x=243=0xf3.

#include

#defineucharunsignedchar;

#defineuintunsignedint;

voidmain()

{

uchardatax=5;//待发送的数

TMOD=0x20;//定时器T1方式2

TL1=0xf3;//置初值

TH1=0xf3;

SCON=0xd8;//串行通信方式3

PCON=0x80;//SMOD=1

TR1=0;

while

(1)

{while(P1_0!

=0);//当按键未按下时等待

while(P1_0==0);//当按键未放开时等待

TR1=1;

SBUF=x;

while(TI==0);

TI=0;

TR1=0;}

}

(串行通信)

例8:

点对点的串行异步通信

1、通信双方的硬件连接

2、程序流程图及编程

点对点通信双方基本等同,只是人为规定一个为发送,一个为接收。

要求两机串行口的波特率相同,因而发送和接收方串行口的初始化相同。

可编制含有初

 

始化函数、发送函数接收函数的程序,在主函数中根据程序的发送、接收设置TR,采用条件判别决定使用发送函数还是接收函数。

这样点对点通信的双方都可运行此程序,只需在程序运行之前人为设置选择TR,一个令TR=0,一个令

TR=1,然后分别编译,在两机上分别装入,同时运行。

A机开始发送时,先送一个AA信号,B机接收到AA信号后,回答一个BB信号,表示准备好可以接受。

然后A机发送,B机接收,A机边发送边求校验和,B机边接收边求校验和,发送完后发校验和,B机接收校验和进行校对。

不正确要求重发。

本例晶振频率为11.0592MHz,波特率为1200。

初值为:

下面程序是将发送和接收都合在一个程序中,可以分别装入两个单片机系统中,由语句“#defineTR1”确定发送或接收,TR=0发送,TR=1为接收。

3、参考源程序

#include

#defineucharunsignedchar

#defineTR1//发送与接收差别值TR=0发送,TR=1为接收

ucharidatabuf[16];

ucharpf;//求和

voidinit(void)//串行口初始化

{TMOD=0x20;//设T/C1为定时方式2

TH1=0xe8;//设定波特率

TL1=0xe8;

PCON=0x00;

TR1=1;//启动T/C1

SCON=0x50;//串行口工作在方式1

}

voidsend(ucharidata*d)//发送字符,参数用指针说明

{uchari;//下面是两机联络

do{

SBUF=0xaa;//发送联络信号

while(TI==0);//等待发送结束

TI=0;

for(i=1;i<0xff;i++);//注意发送完以后要等待接收,否则出错

}while((SBUF^0xbb)!

=0);//B机未准备好,继续联络

RI=0;//接收后RI=1,软件清0

do{//以下为发送数据,每发送一个都要求和。

pf=0;//清校验和

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

{SBUF=d[i];//发送一个数据

pf+=d[i];//求校验和

while(TI==0);TI=0;//等待发送结束

}

SBUF=pf;//发送校验和

while(TI==0);TI=0;//等待发送结束

while(RI==0);

RI=0;//等待B机回答

}while(SBUF!

=0);//回答出错,则重发

}

voidreceive(ucharidata*d)//接收字符

{uchari;

do{while(RI==0);

RI=0;//等待接收

}while((SBUF^0xaa)!

=0)//判A机请求否

SBUF=0xbb;//发应答信号

while(TI==0);TI=0;//等待发送结束

while

(1)

{pf=0;//清校验和

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

{while(RI==0);RI=0;

d[i]=SBUF;//接收一个数据

pf+=d[i];}//求校验和

while(RI==0);RI=0;//接收A机校验和

if((SBUF^pf)==0)//接收的校验和与自加得比较

{SBUF=0x00;//校验和相同,回答0X00

while(TI==0);TI=0;

break;}//校验和相同发"00"

else

{SBUF=0xff;//出错发"FF",重新接收

while(TI==0);TI=0;}

}

}

voidmain(void)

{init();

if(TR==0)

{send(buf);//参数为数组名,指针

}

else

{receive(buf);

}

}

(串行通信1)

将上面的程序发送与接收分开,接收程序如下:

#include

#defineucharunsignedchar

ucharidatabuf[16];

ucharpf;//求和

voidinit(void)//串行口初始化

{TMOD=0x20;//设T/C1为定时方式2

TH1=0xe8;//设定波特率

TL1=0xe8;

PCON=0x00;

TR1=1;//启动T/C1

SCON=0x50;//串行口工作在方式1

}

voidreceive(ucharidata*d)//接收字符

{uchari;

do{while(RI==0);

RI=0;//等待接收

}while((SBUF^0xaa)!

=0)//判A机请求否

SBUF=0xbb;//发应答信号

while(TI==0);TI=0;//等待发送结束

while

(1)

{pf=0;//清校验和

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

{while(RI==0);RI=0;

d[i]=SBUF;//接收一个数据

pf+=d[i];}//求校验和

while(RI==0);RI=0;//接收A机校验和

if((SBUF^pf)==0)//接收的校验和与自加得比较

{SBUF=0x00;//校验和相同,回答0X00

while(TI==0);TI=0;

break;}//校验和相同发"00"

else

{SBUF=0xff;//出错发"FF",重新接收

while(TI==0);TI=0;}

}

}

voidmain(void)

{init();

receive(buf);

}

发送程序如下:

#include

#defineucharunsignedchar

ucharidatabuf[16];

ucharpf;//求和

voidinit(void)//串行口初始化

{TMOD=0x20;//设T/C1为定时方式2

TH1=0xe8;//设定波特率

TL1=0xe8;

PCON=0x00;

TR1=1;//启动T/C1

SCON=0x50;//串行口工作在方式1

}

voidsend(ucharidata*d)//发送字符,参数用指针说明

{uchari;//下面是两机联络

do{

SBUF=0xaa;//发送联络信号

while(TI==0);//等待发送结束

TI=0;

for(i=1;i<0xff;i++);//注意发送完以后要等待接收,否则出错

}while((SBUF^0xbb)!

=0);//B机未准备好,继续联络

RI=0;//接收后RI=1,软件清0

do{//以下为发送数据,每发送一个都要求和。

pf=0;//清校验和

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

{SBUF=d[i];//发送一个数据

pf+=d[i];//求校验和

while(TI==0);TI=0;//等待发送结束

}

SBUF=pf;//发送校验和

while(TI==0);TI=0;//等待发送结束

while(RI==0);

RI=0;//等待B机回答

}while(SBUF!

=0);//回答出错,则重发

voidmain(void)

{init();

send(buf);//参数为数组名,指针

}

五、综合编程举例

例9、定时器程序,用到定时器、定时器中断、端口按键控制并用串行口输出,是一个片内资源的综合应用实例,程序是由本人编写,并用keil软件仿真通过,但不是所有的功能全部调试过,难免还有错误。

从思路上,不是完美的,但通过亲自编程,体会了该程序编写中的难点,以便更好的给大家介绍。

晶振按24MHz,用定时器0方式1,计数次数为25000/0.5=50000,初值为65535-50000=15535=3CAFH。

该程序有分辨大、小月、2月,24-12显示功能,有时间调整功能。

参考程序:

#include

#include

#include

#defineucharunsignedchar

#defineuintunsignedint

sbitan1=P1^0;//调整时间功能转换

sbitan2=P1^1;//加1

sbitan3=P1^2;//减1

sbitan4=P1^3;//12-24显示转换

sbitan5=P1^4;//显示年月日与时分秒转换

sbitan6=P1^5;//待定

uchardataclow,chigh,year=4,month=12,day=31,hour=23,minute=59,second=0,m,x;

uintdatan=0;

voidc_initial()reentrantusing1

{

TCON=0x00;

TH0=0x3c;

TL0=0xaf;

TR0=1;

SCON=0x50;

TH1=221;

TR1=1;

TI=1;

}

add(ucharm)//加1

{uchari;

if(an2==0)

{for(i=0;i<0xff;i++);

while(an2==0);

m++;

returnm;

}

sub(m)//减1

{uchari;

if(an3==0)

{for(i=0;i<0xff;i++);

while(an3==0);

m--;

returnm;

}

}

xianshi(uints1,uints2,uints3)//显示

{

printf("%d",s1);

printf("%d",s2);

printf("%u\n",s3);

}

voidmain()

{

uchardatai,j=0;

bitn=0;

TMOD=0x21;

c_initial();

ET0=1;

EA=1;

while

(1)

{

lab0:

while(an1==0);

if(an4==0)

{for(i=1;i<0xff;i++);//防弹跳

while(an4==0);

n=!

n;}

if(an5==0)xianshi(year,month,day);//显示年、月、日

else

{if(n==0)//24制式显示hour

xianshi(hour,minute,second);//显示时、分、秒

else

{if(hour>12)

xianshi(hour-12,minute,second);//12小时制式

else

xianshi(hour,minute,second);}

}

if(an1==0)

{while(an1==0);//调整年

while

(1)

{if(an2==0)

{year=add(year);

if(year==99)year=0;}

if(an3==0)

{year=sub(year);

if(year==0)

year=99;}

if(an1==0)

gotolab2;

}

lab2:

while(an1==0);//调整月

while

(1)

{

if(an2==0)

{month=add(month);

if(month>=13)

month=1;}

if(an3==0)

{month=sub(month);

if(month==0)

month=12;}

if(an1==0)gotolab3;

}

lab3:

while(an1==0);//调整天

while

(1)

{

if(an2==0)

{day=add(day);

if(month==1||month==3||month==5||month==7||month==8||month==10||month==12)

{

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

当前位置:首页 > 成人教育 > 自考

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

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