嵌入式编程
C51
编程
1.C51
与标准C的差别
1)库函数不同
C51
的库函数是根据51单片机的硬件特点来设置的。
2)数据类型存在差别
C51
在标准C基础上扩展了4种数据类型
bit,sfr,sfr16,sbit
3)变量存储模式不同。存储器不同
4)C51
提供了针对51单片机中断的函数
5)头文件不同
#include<reg51.h>
6)程序结构存在差异
51单片机硬件资源有限,所以其编译系统不允许太多程序嵌套。
2.C51
程序结构
#include <reg51.h> //预处理命令
long fun1(); //函数说明
int main(void) //主函数
{
……
}
/* 每个函数都由“函数定义”和“函数体” 组成,main()函数是程序的入口 */
fun1(){ //功能函数
……
}
3.C51
关键字
auto | 存储类型说明 | 用以说明局部变量,缺省值为此 |
---|---|---|
const | 存储类型说明 | 在程序执行过程中不可更改的常量值 |
extern | 存储类型说明 | 在其他程序模块中说明了的全局变量 |
register | 存储类型说明 | 使用CPU 内部寄存的变量 |
static | 存储类型说明 | 静态变量 |
code | 存储器类型说明 | 程序存储器 |
data | 存储器类型说明 | 直接寻址的内部数据存储器 |
bdata | 存储器类型说明 | 可位寻址的内部数据存储器 |
idata | 存储器类型说明 | 间接寻址的内部数据存储器 |
pdata | 存储器类型说明 | 分页寻址的外部数据存储器 |
xdata | 存储器类型说明 | 外部数据存储器 |
bit | 位标量或位类型声明 | 声明一个位标量或位类型的函数 |
---|---|---|
sbit | 位变量声明 | 声明一个可位寻址变量 |
sfr | 特殊功能寄存器声明 | 声明一个特殊功能寄存器 |
sfr16 | 特殊功能寄存器声明 | 声明一个16位的特殊功能寄存器 |
interrupt | 中断函数说明 | 定义一个中断函数 |
reentrant | 再入函数说明 | 定义一个再入函数 |
using | 寄存器组定义 | 定义芯片的工作寄存器 |
_at_ | 绝对地址定义 | 定义变量的绝对地址 |
compact | 存储模式定义 | 变量保存在pdata 存储区 |
large | 存储模式定义 | 变量保存在xdata 存储区 |
small | 存储模式定义 | 变量保存在data存储区 |
data
都是对数据存储器而言
data
bdata #bit_data
idata #间接寻址
pdata #外部RAM
xdata #外部RAM
sfr、sfr16、sbit其声明语句只能放函数外。
不能定义bit、sbit、sfr、sfr16型指针和数组。
sbit、sfr、sfr16通常用在51单片机系统自带的头文件中,具体参见reg51.h或reg52.h。
eg.
sfr P1 = 0x90;//声明P1口,其地址90H
sfr16 T2 = 0xCC;/*声明52定时器2,16位的T2*/
sbit P1.0 = P1^0;//声明P1口第0位为P1.0
sbit TI = 0x98^0;/*声明98H(SCON)的第0位为TI*/
^代表字节中取字
P1字节中取第0位P1^0
_at_
关键字
可以使用 at 关键字来访问绝对地址。格式如下:
数据类型 【存储器类型】 标识符 _at_ 地址常数
char xdata m[10] _at_ 0x1000;
/*指定m数组从片外RAM的1000H开始 */
!!!!!!!!!!!!!!!!!!!
注意:
(1)绝对变量不能被初使化;
(2)bit型函数及变量不能用_at_指定。
4.C51
存储模式
a.SMALL
变量存储于内部RAM
(data
或idata
)。优点:访问速度快,缺点:空间有限。
b.COMPACT
变量存储于外部RAM
(pdata
)。优点:空间比SMALL
大、速度较SMALL
慢,但比较LARGE
快,是一种中间状态。
c.LARGE
存储于外部RAM
区64kB
(“xdata
”)。优点:空间大,可存变量多,缺点:速度慢
5.语法结构
1)分支结构
if(表达式){语句;}
if(表达式){语句1;} else{语句2;}
if(表达式1){语句1;}
else if(表达式2){语句2;}
……
else if(表达式n-1){语句n-1;}
else{语句n;}
switch (表达式){
case 常量表达式1:{语句1;} break;
case 常量表达式2:{语句2;} break;
……
case 常量表达式n-1:{语句n-1;} break;
default: {语句n;}
}
2)循环结构
(1) while语句
C51使用while语句实现当型循环,格式如下:
while (表达式) //条件
{
语句;
}
(2) do-while语句
C51使用do-while语句实现直到型循环,格式如下:
do
{
语句;
} //循环体
while (表达式); //条件
(3) for语句
for语句的功能强大,使用最为灵活,格式如下:
for (表达式1; 表达式2; 表达式3) {
语句;
} //循环体
例子
【例】编写一个延时1ms程序。
void delayms(unsigned char int j){
unsigned char i;
while(j--) {
for(i=0;i<125;i++)
{;}
}
}
6.中断应用实例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m30d43Tu-1603803640654)(C:\Users\16438\AppData\Roaming\Typora\typora-user-images\image-20200816144030129.png)]
【例6】利用单片机中断资源实现用按键控制LED灯的亮灭。
#include<reg51.h>
sbit P1_0=P1^0;
void led0() interrupt0{
P1_0=~P1_0;
}
void main(){
EA=1;
EX0=1;
IT0=1; #设置为下降沿触发
while(1);
}
中断函数可不提前申明,因为未在主函数中用
7.定时器应用实例
【例7】已知时钟频率为12MHz
,利用**T1
定时器,由P1.0
输出周期为2 ms占空比50%**的方波。
解:方波周期为2ms
,占空比50%,利用定时器定时1 ms**,每到1 ms则对**P1.0
**的输出电平翻转,依次循环即可输出方波。
a.方式0
根据TH1
存放高八位TL1
存放低五位原则,TH1=0XE0
; TL1=0X18
; TH1
、TL1
可以通过做除法取商及取余数的方式分别得到,即TH1=x/32
; TL1= x%32
。
#include<reg51.h>
sbit led0=P1^0;
void timer0() interrupt3{
TH1=0xE0;
TL1=0x18;
led0=~led0;
}
void main(){
EA=1;
ET1=1;
TMOD=0x00;
TH1=0xE0;
TL1=0x18;
TR1=1; #启动定时器1
while(1);
}
b.方式一
根据TH1
存放高八位,TL1
存放低八位原则,TH1=0XFC
; TL1=0X18
; (TH1=x
/256; TL1=
x%256)。
#include<reg51.h>
sbit led0=P1^0;
void timer0() interrupt3{
TH1=0xFC;
TL1=0x18;
led0=~led0;
}
void main(){
EA=1;
ET1=1;
TMOD=0x10;
TH1=0xFC;
TL1=0x18;
TR1=1; #启动定时器1
while(1);
}
c.方式二
8位,最大定时也不能达到**1ms
。因此,可以定时器设置为100μs**,设置变量循环10次。初值:TH1=156
;TL1=156
;
#include<reg51.h>
sbit led0=P1^0;
char i=0;
void timer0() interrupt3{
if(i<9){
i++;
}else{
led0=~led0;
i=0;
}
}
void main(){
EA=1;
ET1=1;
TMOD=0x20;
TH1=0x9C;
TL1=0x9C;
TR1=1; #启动定时器1
while(1);
}
d.跑马灯
【例8】编程控制8个LED实现跑马灯效果。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wlROzd87-1603803640656)(C:\Users\16438\AppData\Roaming\Typora\typora-user-images\image-20200816152431568.png)]
定时50ms
,计数二十次实现一秒延时
#include<reg51.h>
#include<stdio.h>
unsigned char i=0;
unsigned char k=0;
int LED[ ]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//定义流水灯状态数组
void timer0() interrupt1{
TH0=0x3C;
TL0=0xB0
k++;
}
void main(){
EA=1;
ET0=1;
TR0=1;
TMOD=0x01;
while(1){
if(k==20){
if(i==7){
i=0;
}else{
i++;
}
k=0;
P1=led[i];
}else{
k++;
}
}
}
8.串口应用实例——I/O拓展
【例9】利用串行口方式0作I/O拓展,系统晶振频率为**11.0592MHz
,编程实现数码管上循环显示数字0-9**。
只能用串口方式0同步输出
#include<reg51.h>
#include<stdio.h>
unsigned char i=0;
unsigned char k=0;
int LED[ ]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};//定义流水灯状态数组
void uart() interrupt4{
TI=0;
}
void Timer0() interrupt1{
TL0=0x00;
TH0=0x4C;
k++;
}
void main(){
EA=1;
ET0=1;
ES=1;
TR0=1;
//PCON=0x00;当有涉及到需要波特率加倍时设置
SCON=0x00;
TMOD=0x01;
TL0=0x00;
TH0=0x4C;
TI=0;
while(1){
if(k==20){
if(i==9){
i=0;
}else{
i++;
}
SBUF=LED[i];
}else{
k++;
}
}
}
9.串口应用实例——双机通信加握手
【例10】A、B两单片机异步通讯,分别编写发送机与接收机程序。
A机为发送机,B机为接收机。
A首先发送一个’10H
’,当B机接收到‘10H
’时,立即向A发送一个‘20H
’,表示同意接收,A接收到‘**20H
’**则握手成功,开始发送数据。
双方约定传输波特率为9600 bit/s**,双机的时钟频率均为11.0592 MHz,T1
工作在定时方式2,则**TH1=TL1=0E8H
,PCON
寄存器的SMOD
位为0。
########A机发送机程序#########
#include<reg51.h>
#include<stdio.h>
unsigned char flag=0;
void uart() interrupt4{
if(TI==1){
TI=0;
}else if(RI==1){
RI=0;
unsigned char a=0x20;
unsigned char b;
b=SBUF;
if(a==b){
flag=1;
}else{
flag=0;
}
}
}
void main(){
EA=1;
ET0=1;
ES=1;
TH0=TL0=0xfd;
TR0=1;
TMOD=0x02;
PCON=0x00;
SCON=0xD0;
TI=0;
SBUF=0x10;
while(1){
if(flag==1){
//发送程序
//设置奇偶校验位TB8
flag=0;
}
}
}
#######接收端同理#######
#include<reg51.h>
#include<stdio.h>
unsigned char flag=0;
void uart() interrupt4{
if(TI==1){
TI=0;
}else if(RI==1){
RI=0;
unsigned char a=0x10;
unsigned char b;
b=SBUF;
if(a==b){
flag=1;
}else{
flag=0;
}
if(flag==2){
//接收程序
//判断奇偶校验位RB8
}
}
}
void main(){
EA=1;
ET0=1;
ES=1;
TR0=1;
TMOD=0x02;
PCON=0x00;
SCON=0xD0;
TI=0;
while(1){
if(flag==1){
SBUF=0x20;
flag=2;
}
}
}
10.外部扩展简单I/O应用实例
例【11】:利用74LS244
作为输入口接8路开关k1
-k8
,读取开关状态,并将读得的状态通过74LS273
驱动发光二级管显示出来。
拓展I/O口时只需定义到外部I/O口地址进行寻址就可获取数据,一般通过指针p,*p就可获取该地址的内容,xdata
可直接定位到外部
#include<reg51.h>
unsigned char xdata *p;
unsigned char a;
void main(){
while(1){
p=0xbfff; //指向输入口输入数据
a=*P;
p=0x7fff; //指向输出口输出数据
*p=a;
}
}