工学第八章 8051内部资源C语言编程.docx

上传人:b****5 文档编号:24686000 上传时间:2023-05-31 格式:DOCX 页数:47 大小:399.46KB
下载 相关 举报
工学第八章 8051内部资源C语言编程.docx_第1页
第1页 / 共47页
工学第八章 8051内部资源C语言编程.docx_第2页
第2页 / 共47页
工学第八章 8051内部资源C语言编程.docx_第3页
第3页 / 共47页
工学第八章 8051内部资源C语言编程.docx_第4页
第4页 / 共47页
工学第八章 8051内部资源C语言编程.docx_第5页
第5页 / 共47页
点击查看更多>>
下载资源
资源描述

工学第八章 8051内部资源C语言编程.docx

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

工学第八章 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语言编程

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

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

(中断有关内容)

编号

中断源

入口地址

0

外部中断0

0003H

1

定时器/计数器0

000BH

2

外部中断1

0013H

3

定时器/计数器1

001BH

4

串行口中断

0023H

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

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;//置工作方式,

TR0=0;

TF0=0;

TH0=0xf8;

TL0=0x30;

TR0=1;//开定时器T0

for(;;)//无限循环

{

while(!

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

P1_0=!

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

TL0=0x30;//献给TL0赋值,精度要高一点!

TH0=0xf8;

TF0=0;

}

}

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

思路:

计数器初始化,如果计数总值小于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;//打开定时器T1

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机接收校验和进行校对。

B机接收正确结束,在此B发送一个正确的标志符“00”;否则发送错标志“FF”不正确要求重发,A机在重复发送。

本例晶振频率为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);//用异或判断收到的是否为bb,相同为“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);//等待B机回复发送是否正确

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步进电机的控制

步进电机的工作原理

步进电动机是用脉冲信号控制的,下图中定子有3对磁极,转子有4个齿,

工作过程:

A相通电:

A相磁极与0、2号齿对齐;

(A、B相通电,则0、1对A、B’,2、3对A’、B)

B相通电:

由于磁力线作用,B相磁极与1、3号齿对齐;

C相通电:

由于磁力线作用,C相磁极与0、2号齿对齐;

A相通电:

由于磁力线作用,A相磁极与1、3号齿对齐;

结论:

定子按A->B->C->A相轮流通电,则磁场沿A、B、C方向转动180度角,磁场每改变一次,转子转过30度。

在步进电机中,控制绕组每改变一次通电方式,称为一拍,每一拍转子就转过一个步距角。

上述的运行方式每次只有一个绕组单独通电,控制绕组每换接三次构成一个循环,故这种方式称为三相单三拍。

若按A-AB-B-BC-C-CA顺序通电,每次循环需换接6次,故称为三相六拍,因单相通电和两相通电轮流进行,故又称为三相单、双六拍。

步进电机的步距角按下式计算:

N是电动机工作拍数;Z是转子的齿数

三相三拍4齿的步距角=

三相六拍4齿的步距角=

实际采用的步进电机的步距角多为3度和1.5度,也有0.9度和1.8度,还可以细分。

步距角越小,机加工的精度越高。

为产生小步距角,定、转子都做成多齿的,图中转子40个齿,定子仍是6个磁极,但每个磁极上也有五个齿。

转速大小仅与脉冲频率成正比,通过改变脉冲频率的高低可以大范围地调节电机的转速,能实现快速起动、制动、反转,而且有自锁的能力,不需要机械制动装置,不经减速器也可获得低速运行。

转过一周的步数是固定的,只要不丢步,角位移误差不存在长期积累的情况,主要用于数字控制系统中,精度高,运行可靠。

步进电动机工业过程控制和仪表中的主要控制元件之一。

例:

三相六拍控制方式

步进电机与单片机的连接如图所示。

采用三相六拍控制方式正转绕组通电顺序为:

A→AB→B→BC→C→CA→A,P1口发出的控制字为01H→03H→02H→06H→04H→05H→01H;

反转绕组通电顺序为:

A→CA→C→CB→B→BA→A,P1口发出的控制字为01H→05H→04H→06H→02H→03H→01H;

由P2_0控制转向P2_0=0为正向,否则为反向。

由P2_1控制转速调节,P2_2控制退出转速调节。

源程序如下:

#include

#defineucharunsignedchar

#defineuintunsignedint

uchardataplus[7]={0x01,0x03,0x02,0x06,0x04,0x05,0x00};

//正向,0x00用于判断一个循环是否结束

uchardataminu[7]={0x01,0x05,0x04,0x06,0x02,0x03,0x00};

//反向

uchark=0;//中断标志,置初值

ucharidata*x;//存放通电顺序的数组地址

uchardl=1;//由dl控制转速,dl越小,速度越大

voidmain(void)

{bitcf;//控制转向的位变量

uintn,i;//n为步数

cf=P2_0;//控制转向

if(cf==0)x=plus;//送正向控制数组首地址

elsex=minu;//送反向控制数组首地址

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

if(P2_1==0)//由P2_1控制进入转速控制调节

{while

(1)//进入转速调节

{for(i=0;i<0xff;i++);//软件延时,以便能看到调节变化

while(P2_1==0);//P2_1按下,转速减小

dl++;

if(dl==100)dl=1;//dl最大为100

if(P2_2==0)gotolab;//用P2_2退出速度调节

}}

lab:

n=0x01ff;//设置步数

TH0=(65536-dl*500)/256;

TL0=(65536-dl*500)%256;//设置定时器初值,dl越小

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

当前位置:首页 > 外语学习 > 英语学习

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

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