《C51单片机技术教程》.docx
《《C51单片机技术教程》.docx》由会员分享,可在线阅读,更多相关《《C51单片机技术教程》.docx(40页珍藏版)》请在冰豆网上搜索。
《C51单片机技术教程》
《C51单片机技术教程》
2008年12月16日
《C51单片机技术教程》田希晖薛亮儒人民邮电出版社
第1章C51单片机的组成及结构
第2章C51单片机指令系统
第3章单片机的C程序设计
第4章C51单片机内部资源
第5章单片机的系统扩展
第6章单片机的机间通信
第7章单片机人机交互的C编程
第8章KeilC51上机指南
第9章单片机系统综合实例
笔记不是把所有知识点都记下。
而是记那些需要学习的,或者总结得很好的东西。
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
指令系统的寻址方式和寻址空间
序号
寻址方式
使用符号和变量
寻址空间
1
立即数~
#data,#data16
程序存储器(片内片外)64KB
2
直接~
direct或data,#data16
内部RAM128B
数据存储器(片外RAM)64KB
访问SFR的唯一方法
3
寄存器~
R0~R7,A,B,Cy,DPTR
片内RAM128B
4
寄存器间接~
@R0,@R1,SP
片内RAM128B
@R0,@R1,@DPTR
数据存储器(片外RAM)64KB
5
相对~
PC+rel(偏移量)
程序存储器(片内片外)64KB
6
变址~
@A+DPTR,@A+PC
同上
7
位~
bit
内部RAM(20H~2FH),SFR
内部RAM数据传送指令示意图
C51的数据类型
1、基本类型、构造类型、指针类型、空类型
2、基本类型:
位型(bit)、字符型(char)、整型(int)、长整型(long)、浮点型(float)、双精度浮点型(double)
3、构造类型:
数组类型、结构体、共用体、枚举
C51系列单片机将int型变量的高位字节数存放在低地址字节中。
浮点数存储格式IEEE-754标准。
24位精度,尾数的最高位始终为“1”,因而不保存,内存中字节存储顺序如下:
1位符号位、8位指数位、23位尾数
地址
+0
+1
+2
+3
内容
M……M
M……M
EM……M
SE……E
其中,S符号位,E阶码,M尾数
C51的变量的存储类型
存储类型
与存储空间的对应关系
data
直接寻址片内数据存储区,访问速度快(128B)
bdata
可位寻址片内……,允许位与字节混合访问(16B)
idata
间接寻址片内……,可访问片内全部RAM地址空间(256B)
pdata
分页寻址片外……(256B)
xdata
片外……(64KB)
code
程序存储器(64KB)
变量说明举例:
datacharvar;//字符变量var定位在片内数据存储区
charcodeMSG[]="PARAMETER";//字符数组MSG[]定位在程序存储区
unsignedlongxdataarray[100];//无符号长整型数组定位在片外RAM区
bitlock;
unintxdatavector[10][4][4];
sfrP0=0x80;
charbdataflags;
sbitflag0=flags^0;
指针变量说明举例:
longxdata*px;//指针px指向long型xdata区,指针本身位于在默认存储区,指针长度为2B
charxdata*datapd;//指针pd指向字符型xdata区,自身在data区,长度为2B
datacharxdata*pd;//同上(等效)
dataint*pn;//和int*datapn及intr*pn等效,定义一个类型为int型的通用型指针,指针本身在data区,长度为3B。
说明:
指针指向的存储类型,即指向哪个存储区。
存储类型的声明位置在数据类型和指针名(如*px)之间,如无此项声明,则此指针型变量为通用型。
指针变量自身的存储类型,即指针处于什么区与自身的长度无关,该声明可位于声明语句的开关,也可在"*"和变量名之间。
……
#include特殊功能寄存器定义
#include绝对地址定义
位变量及其定义
1、位变量C51定义一般语法格式:
bit位变量名;如:
bitlock_bit;
2、采用字节寻址变量.位的方法。
如:
bdataintibase;sbitmybit=ibase^15;
3、函数可包含类型为bit的参数,也可将其作为返回值。
但注意,使用(#pragmadisable)或包含明确的寄存器组切换(usingn)的函数不能返回位值。
4、对特殊功能寄存器的位的定义。
例:
#include
sbitP1_1=P1^1;//无名位
sbitac=ACC^7;//无名位
RS1=1;RS0=0;//有名位
sbitOV=0xD0^2;
sbitCY=PSW^7;
5、对位变量定义的限制。
位变量不能定义为指针、数组。
<<左移
>>逻辑右移或算术右移,取决于计算机系统。
//看懂下面的就很容易理解头文件中的绝对地址访问了。
一般指针包括3字节:
2字节偏移和1字节存储器类型:
即
地址
+0
+1
+2
内容
存储器类型
偏移量高位
偏移量低位
其中,存储器类型编码如下:
存储器类型
idata
xdata
pdata
data
code
编码值
1
2
3
4
5
例,以xdata类型的0x1234地址作为指针可以表示如下:
地址
+0
+1
+2
内容
0x02
0x12
0x34
常数作指针时,必须注意正确定义存储类型和偏移。
例:
把常数0x40写入地址为0x8000的外部数据存储器。
#defineXBYTE((char*)0x20000L)
XBYTE[0x8000]=0x40;
其中,XBYTE被定义为(char*)0x20000L,0x20000L为一般指针,其存储类型为2,偏移量为0x0000,这样XBYTE成为指向xdata零地址的指针,而XBYTE[0x8000]则是外部数据存储器的0x80000绝对地址。
//后面还有头文件中其化存储区的绝对地址访问定义~
共用体变量可在不同时间内保存不同类型和长度的数据,从而提供了在同一存储单元中可以分时操作不同类型数据的功能。
unionU
{
uintword;
struct{ucharhi;ucharlo;}bytes;
};
unionUnewcount;
unitoldcount;
newcount.bytes.hi=TH1;
newcount.bytes.lo=TL1;
oldcount=newcount.word;
这样,定时器的计数值即可以按字节使用,也可以按字使用。
函数“声明”和“定义”:
“定义”是指对函数功能的确定,包括指定函数名、函数值类型,形参及其类型、函数体等,它是一个完整的、独立的函数单位;
而“声明”的作用则是把函数的名字、函数类型以及形参的类型、个数和顺序通知编译系统,以使在调用该函数时系统按此对照检查。
函数指针即函数入口地址。
对用函数的指针变量调用函数可归纳为如下几点:
(1)指向函数的指针变量的一般定义形式为:
函数值返回类型(*指针变量名)(函数形参表);
(2)在给函数指针变量赋值时,只需给出函数名。
(3)对指向函数的指针变量进行诸如p+n,p++,p--的运算是没有意义的。
C51的库函数
1字符函数库CTYPE.H
externbitF(char);
其中,F可以为isalpha/isalnum/iscntrl/islower/isupper/isdigital
externcharF(char);
其中,F可以为toint/toupper/tolower
2标准函数库STDLIB.H
externTatoX(char*S);
其中,T可以是float/long/int,X与T对应分别为f/l/i
void*malloc(unsignedintsize);//申请内存
voidfree(void*p);//释放内存
voidinitmempool(void*p,unsignedintsize);//清零内存区
3数学函数库MATH.H
externTabs(Tval);
其中,T可以是float/long/int/char
externfloatexp(floatx);//e的指数
externfloatlog/log10(floatx);//e或10的对数
externfloatsqrt(floatx);//平方根
externfloatsin/cos/tan(floatx);//三角函数
externfloatpow(floatx,floaty);//x的y次方
4绝对地址访问头文件ABSACC.H
#defineXBYTE((unsignedchar*)0x20000L;
PBYTE3
DBYTE4
CBYTE5
以上定义用来对C51系列单片机的存储空间进行地址访问,以字节为单位寻址。
只需将BYTE换成WORD,就可以实现以字为单位寻址。
5内部函数库INTRINS.H
//将变量循环左/右移n位
unsignedchar_crol_(unsignedcharval,unsignedcharn);
int_irol_int
long_lrol_long
unsignedchar_cror_(unsignedcharval,unsignedcharn);
int_iror_int
long_lror_long
//对应汇编的NOP指令,延时一个机器周期
void_nop_(void);
//测试给定的位参数x是否为1。
若为1,返回1,同时将该位复位为0;否则返回0。
bit_testbit_(bitx);
6访问SFR和SFR_bit地址头文件REGxxx.H
头文件reg51.h、reg52.h等文件中定义了C51单片机中的SFR寄存器名和相关的位变量名。
编程举例
循环队列是一种FIFO存储结构,在单片机应用程序中经常使用。
队列需要
队头指针listhead、队尾指针listtail、队列长度listlen、队列空标志listempty和队列满标志listfull。
初始时,listhead=listtail=0,listempty=listfull=0。
需要定义两个函数,操作队列listwrite()、listread()。
iswrite()函数的操作思路:
if(队列满)//退出;
else
{
//将数据写入listtail指向的数组单元
listtail++;
if(listtail==listlen)listtail=0;
listempty=0;
if(listhead==listtail)listfull=1;
}
listread()//函数的操作思路:
if(队列空)//退出
else
{
//将listtail指向的数组单元的内容读出;
listhead++;
if(listhead==listlen)listhead=0;
listfull=0;
if(listtail==listhead)listempty=1;
}
函数的实现:
#definelistlen=10;
unsignedcharlist[listlen];
charlistwrite(charx)
{
if(listfull)return0;
else
{
//将数据写入listtail指向的数组单元
listtail++;
if(listtail==listlen)listtail=0;
listempty=0;
if(listhead==listtail)listfull=1;
return1;
}
}
charlistread(char*x)
{
if(listempty)return0;
else
{
//将listtail指向的数组单元的内容读出;
listhead++;
if(listhead==listlen)listhead=0;
listfull=0;
if(listtail==listhead)listempty=1;
return1;
}
}
习题与思考:
(挑了几个问答~)
1.C语言中的类型是怎么分配的?
什么是赋值操作?
2.C语言的函数有什么特性?
函数的存储类型和数据类型的意义是什么?
3.当一个函数需要返回多个值时,可以怎么做?
4.编写把字符串s逆转的函数reverse()
5.把上面的函数写成递归函数。
第4章C51单片机的内部资源
重点及难点
单片机中断和定时与单片机通信的基本概念、单片机的中断系统、单片机的定时/计数器、单片机外部中断源的扩展、数据传递的方式、串行通信控制寄存器、MCS-51串行通信工作方式及其应用。
基本要求
1.掌握单片机中断和定时与单片机通信的基本概念
2.掌握单片机的中断源、中断控制、中断响应过程的基本概念及单片机系统的功能和使用方法
3.掌握单片机的定时/计数器的初值计算、工作方式控制寄存器的初始化、程序的设计方法和步骤
4.串行通信的基本方式、数据传送的次序、串行通信控制器的每一位的定义、串行通信的四种工作方式
5.了解单片机外部中断源扩展的常用方法和步骤
教学内容
1、中断系统
2、定时/计数器
3、串行通信接口
无条件传送方式、程序查询方式、中断传送方式
中断系统:
中断的基本概念、中断源、外部中断、内部中断、中断入口地址
中断控制:
TCON、IE、IP,SCON
中断控制寄存器汇总
寄存器名称
D7
D6
D5
D4
D3
D2
D1
D0
定时/计数器~
TCON(88H)
TF1
TR1
TF0
TR0
IE1
IT1
IE0
IT0
位地址
略
串行口控制寄存器
SCON(98H)
SM0
SM1
SM2
REN
TB8
RB8
TI
RI
位地址
略
中断允许寄存器
IE(A8H)
EA
/
/
ES
ET1
TX1
ET0
EX0
位地址
略
中断优先级寄存器
IP(B8H)
/
/
/
PS
PT1
PX1
PT0
PX0
位地址
略
中断响应:
中断响应的条件、中断响应的过程、中断响应时间、中断请求的撤销、寄存器组切换
//下面的话让人晕~是他没讲清,还是我没搞清呢~不要紧~看懂后面的就可以了。
寄存器组切换,在汇编语言中由编程者选择。
但对混合语言编程的连接器,汇编程序使用的组可被选定,因而连接器不能像普通存储器那样分配寄存器组。
在C51中,寄存器组选择取决于特定的编译器指令。
高优先组中断可以中断正在处理的低优先级程序,因而必须注意寄存器组。
除非可以确定未使用R0~R7(用汇编程序),最好给每种优先级程序分配不同的寄存器组。
中断不允许用于外部函数,它对函数目标代码影响有收下几点:
(1)当调用函数时,SFR的ACC、B、DPH、DPL、PSW(当需要时)入栈。
(2)如果不使用寄存器组切换,甚至中断函数所需的所有工作寄存器都入栈。
(3)函数退出前,所有的寄存器内容出栈。
(4)函数由8051的指令RETI终止。
中断服务程序使用的任何程序也使用一寄存器组。
递归程序可以使用,它们自己调用自己是因为它们依赖堆栈。
在中断程序的编写中应该注意以下几点:
(1)采用开中断总控制开关EA置位中断源的中断允许位;
(2)对外部中断INT0、INT1应选择中断触发方式;
(3)对于多个中断源中断,应设定中断优先级和预置IP;
(4)编写中断服务程序应注意保护现场和恢复现场,以免中断返回时丢失原寄存器、累加器中的信息;(具体要保护哪些东西视CPU中断机制和需要而定)
(5)若要在挂靠当前中断程序时禁止更高优先级中断,可以采用软件关CPU中断或禁止某中断源中断,在中断返回前再开放中断。
中断服务程序的完整语法如下:
返回值函数名([参数])[模式][重入]interruptn[usingn]
date:
2008年12月17日
要画图~!
如果通用语言简述就好了,但要描述准确哦。
电脑画图麻烦。
但要做笔记,还怕麻烦~?
EG:
[图]P1.4~P1.7接4个LED(即发光二极管),P1.0~P1.3接4个KEY(即开关),消抖电路用于产生中断请求信号,通过/INT0/(负电平输入不方便~)。
要求:
开始时LED全黑;每中断一次,开关状态反映到LED上,且开关断开时对应LED亮。
#include
sbitP1_0=P1^0;
……
sbitP1_7=P1^7;
unsignedcharbreaks;
bitflag;
voidsevice_int0()interrupt0using2
{
//INT0中断服务程序,使用第2组寄存器
flag=1;//置标志
breaks=P1;//存状态
}
voidmain()
{
IP=0x01;//置INT0高优先级中断
IE=0x81;//INT0开中断,CPU开中断
for(;;)
{
if(flag)//查询方式
{
P1_4=P1_0;
P1_5=P1_1;
P1_6=P1_2;
P1_7=P1_3;
flag=0;//处理完成清除标志
}
}
}
4.3定时/计数器
不管是独立的定时器芯片还是单片机内的定时器都有以下特点:
(1)定时/计数有多种方式,可以是计数方式也可以是定时方式。
(2)计数初值可设定,计数溢出时发出中断申请。
51单片机有两个加1计数器T0、T1。
分别由两个8位寄存器构成。
由TMOD、TCON设定工作方式。
两个定时器都有定时或事件计数功能,可用于定时控制、对外部事件计数和检测等场合。
1定时工作方式
计数机周期(fosc/12)
2计数工作方式
通过T0(P3.4)和T1(P3.5)对外部脉冲信号计数。
当检测到下降沿时,计数器加1.由于检测一个1到0的跳变需要2个机器周期,故最高计数频率为fosc/24。
虽然对输入信号占空比无特殊要求,但为了确保某个电平在变化之前至少被采样一次要求电平保持时间至少是一个完整的机器周期。
4.3.1TCON和TMOD
TCON:
TF1TR1TF0TR0IE1IT1IE0IT0
TMOD:
GATEC/TM1M0……
工作方式:
013位计数器(低5位,高8位)
116位。
。
2初值自动重装8位
32个8位,仅适用于T0
//下面描述得很好
工作方式3下,T0被拆成2个独立的8位计数器TL0和TH0。
其中TL0即可以计数使用,又可以定时使用,使用T0的控制位和控制引脚信号。
在方式3下,T0、T1的设置和使用是不同的。
(1)T0方式3
TL0:
使用T0原有控制资源(控制位和引脚信号)
TH0:
借用T1的TR1、TF1,只能作8位定时器。
(2)T0方式3下的T1
T0方式3时,T1仍然可工作于方式0~2.C/T控制位仍可使T1工作在定时器或计数器方式,只是由于其TR1、TF1被T0的TH0占用,因而没有计数溢出标志可供使用,计数溢出时只能将输出结果送到串行口,即用作串行口波特率发生器。
T0方式3下的T1方式2,因定时初值能自动恢复,用作波特率发生器更合适。
定时/计数器的应用实例
EG:
设单片机的fosc=12MHz,要求在P1.0脚上输出周期为2ms的方波。
分析:
周期为2ms的方波要求定时时间间隔1ms,每次时间到P1.0取反。
定时器计数率=fosc/12,机器周期=12/fosc=1us
计数次数=1000/(12/fosc)=1000/1=1000
定时初值-1000
(1)用定时器0的方式1编程,采用查询方式
#include
sbitP1_0=P1^0;
voidmain()
{
TMOD=0x01;//T0方式1
TR0=1;//启动T0
for(;;)
{
TH0=-(1000/256);
TL0=-(1000%256);
while(!
TF0){};//查询等待TF0置位
P1_0=!
P1_0;//~
TF0=0;//软件清TF0
}
}
(2)用定时器0的方式1编程,采用中断方式
voidinclude
sbitP1_0=P1^0;
voidtimer0()interrupt1using1
{
P1_0=!
P1_0;//~
TH0=-(1000/256);
TL0=-(1000%256);
}
voidmain()
{
TMOD=0x01;
TH0=-(1000/256);
TL0=-(1000%256);
EA=1;
ET0=1;
TR0=1;
do{}while
(1);
}
。
。
EG:
采用10MHz晶振,在P1.0脚上输出周期为2.5s,占空比20%的脉冲信号。
分析:
由于采用10MHz晶振,使用定时器最大定时几十ms。
即10ms定时,周期2.5s需要250次中断,占空比20%,高电平应为50次中断。
采用10ms定时,晶振fosc=10MHz,因此需定时器计数次数=8333
#include
#defineuncharunsignedchar
uchartime;//计数中断次数
ucharperiod=250;//周期为2.5s,中断周