学习一种编程语言,最重要的是建立一个练习环境,边学边练才能学好。keil软件是目前最流行开发80c51系列单片机的软件,keil提供了包括c编译器、宏汇编、连接器、库管理和一个功能强大的仿真调试器等在内的完整开发方案,通过一个集成开发环境(µvision)将这些部份组合在一起。
学习之前请先安装keilc51软件,本站提供下载请到推荐栏里下载。。。
在学会使用汇编语言后,学习c语言编程是一件比较容易的事,我们将通过一系列的实例介绍c语言编程的方法。图1-1所示电路图使用89c51单片机作为主芯片,这种单片机性属于80c51系列,其内部有8k的flash rom,可以反复擦写,非常适于做实验。89c51的p1引脚上接8个发光二极管,p3.2~p3.4引脚上接4个按钮开关,我们的任务是让接在p1引脚上的发光二极管按要求发光。
1.1 简单的c程序介绍
例1-1: 让接在p1.0引脚上的led发光。
/************************************************
单灯闪烁程序
*************************************************/
#include “reg51.h”//这一句是将51的常用端口,内部寄存器等的定义文件包含进这段程序
sbit p1_0=p1^0;
void main()
{ p1_1=0;
}
这个程序的作用是让接在p1.0引脚上的led点亮。下面来分析一下这个c语言程序包含了哪些信息。
1)“文件包含”处理。
程序的第一行是一个“文件包含”处理。
所谓“文件包含”是指一个文件将另外一个文件的内容全部包含进来,所以这里的程序虽然只有4行,但c编译器在处理的时候却要处理几十或几百行。这里程序中包含reg51.h文件的目的是为了要使用p1这个符号,即通知c编译器,程序中所写的p1是指80c51单片机的p1端口而不是其它变量。这是如何做到的呢?
打开reg51.h可以看到这样的一些内容:
/*--------------------------------------------------------------------reg51.h
header file for generic 80c51 and 80c31 microcontroller.
copyright (c) 1988-2001 keil elektronik gmbh and keil software, inc.
all rights reserved.
--------------------------------------------------------------------------*/
/* byte register */
sfr p0 = 0x80;
sfr p1 = 0x90;
sfr p2 = 0xa0;
sfr p3 = 0xb0;
sfr psw = 0xd0;
sfr acc = 0xe0;
sfr b = 0xf0;
sfr sp = 0x81;
sfr dpl = 0x82;
sfr dph = 0x83;
sfr pcon = 0x87;
sfr tcon = 0x88;
sfr tmod = 0x89;
sfr tl0 = 0x8a;
sfr tl1 = 0x8b;
sfr th0 = 0x8c;
sfr th1 = 0x8d;
sfr ie = 0xa8;
sfr ip = 0xb8;
sfr scon = 0x98;
sfr sbuf = 0x99;
/* bit register */
/* psw */
sbit cy = 0xd7;
sbit ac = 0xd6;
sbit f0 = 0xd5;
sbit rs1 = 0xd4;
sbit rs0 = 0xd3;
sbit ov = 0xd2;
sbit p = 0xd0;
/* tcon */
sbit tf1 = 0x8f;
sbit tr1 = 0x8e;
sbit tf0 = 0x8d;
sbit tr0 = 0x8c;
sbit ie1 = 0x8b;
sbit it1 = 0x8a;
sbit ie0 = 0x89;
sbit it0 = 0x88;
/* ie */
sbit ea = 0xaf;
sbit es = 0xac;
sbit et1 = 0xab;
sbit ex1 = 0xaa;
sbit et0 = 0xa9;
sbit ex0 = 0xa8;
/* ip */
sbit ps = 0xbc;
sbit pt1 = 0xbb;
sbit px1 = 0xba;
sbit pt0 = 0xb9;
sbit px0 = 0xb8;
/* p3 */
sbit rd = 0xb7;
sbit wr = 0xb6;
sbit t1 = 0xb5;
sbit t0 = 0xb4;
sbit int1 = 0xb3;
sbit int0 = 0xb2;
sbit txd = 0xb1;
sbit rxd = 0xb0;
/* scon */
sbit sm0 = 0x9f;
sbit sm1 = 0x9e;
sbit sm2 = 0x9d;
sbit ren = 0x9c;
sbit tb8 = 0x9b;
sbit rb8 = 0x9a;
sbit ti = 0x99;
sbit ri = 0x98;
熟悉80c51内部结构的读者不难看出,这里都是一些符号的定义,即规定符号名与地址的对应关系。注意其中有
sfr p1 = 0x90;
这样的一行(上文中用黑体表示),即定义p1与地址0x90对应,p1口的地址就是0x90(0x90是c语言中十六进制数的写法,相当于汇编语言中写90h)。
从这里还可以看到一个频繁出现的词:sfr
sfr并标准c语言的关键字,而是keil为能直接访问80c51中的sfr而提供了一个新的关键词,其用法是:
sfrt 变量名=地址值。
2)符号p1_0来表示p1.0引脚。
在c语言里,如果直接写p1.0,c编译器并不能识别,而且p1.0也不是一个合法的c语言变量名,所以得给它另起一个名字,这里起的名为p1_0,可是p1_0是不是就是p1.0呢?你这么认为,c编译器可不这么认为,所以必须给它们建立联系,这里使用了keil c的关键字sbit来定义,sbit的用法有三种:
第一种方法:sbit 位变量名=地址值
第二种方法:sbit 位变量名=sfr名称^变量位地址值
第三种方法:sbit 位变量名=sfr地址值^变量位地址值
如定义psw中的ov可以用以下三种方法:
sbit ov=0xd2 (1)说明:0xd2是ov的位地址值
sbit ov=psw^2 (2)说明:其中psw必须先用sfr定义好
sbit ov=0xd0^2 (3)说明:0xd0就是psw的地址值
因此这里用sfr p1_0=p1^0;就是定义用符号p1_0来表示p1.0引脚,如果你愿意也可以起p10一类的名字,只要下面程序中也随之更改就行了。
3)main称为“主函数”。
每一个c语言程序有且只有一个主函数,切必须有一个主函数,其放置的位置不要求,可以放在程序最后(推荐),函数后面一定有一对大括号“{}”,在大括号里面书写其它程序。
从上面的分析我们了解了部分c语言的特性,下面再看一个稍复杂一点的例子。
例1-2 让接在p1.0引脚上的led闪烁发光
/*************************************************
单灯闪烁程序
*************************************************/
#include “reg51.h”
#define uchar unsigned char
#define uint unsigned int
sbit p10=p1^0;
/*延时程序
由delay参数确定延迟时间
*/
void mdelay(unsigned int delay)
{ unsigned int i;
for(;delay》0;delay--)
{ for(i=0;i《124;i++)
{;}
}
}
void main()
{ for(;;)
{ p10=!p10; //取反p1.0引脚
mdelay(1000);
}
}
程序分析:主程序main中的第一行暂且不看,第二行是“p1_0=!p1_0;”,在p1_0前有一个符号“!”,符号“!”是c语言的一个运算符,就像数学中的“+”、“-”一样,是一种运算任号,意义是“取反”,即将该符号后面的那个变量的值取反。
注意:取反运算只是对变量的值而言的,并不会自动改变变量本身。可以认为c编译器在处理“!p1_0”时,将p1_0的值给了一个临时变量,然后对这个临时变量取反,而不是直接对p1_0取反,因此取反完毕后还要使用赋值符号(“=”)将取反后的值再赋给p1_0,这样,如果原来p1.0是低电平(led亮),那么取反后,p1.0就是高电平(led灭),反之,如果p1.0是高电平,取反后,p1.0就是低电平,这条指令被反复地执行,接在p1.0上灯就会不断“亮”、“灭”。
该条指令会被反复执行的关键就在于main中的第一行程序:for(;;),这里不对此作详细的介绍,读者暂时只要知道,这行程序连同其后的一对大括号“{}”构成了一个无限循环语句,该大括号内的语句会被反复执行。
第三行程序是:“mdelay(1000);”,这行程序的用途是延时1s时间,由于单片机执行指令的速度很快,如果不进行延时,灯亮之后马上就灭,灭了之后马上就亮,速度太快,人眼根本无法分辨。
这里mdelay(1000)并不是由keil c提供的库函数,即你不能在任何情况下写这样一行程序以实现延时。如果在编写其它程序时写上这么一行,会发现编译通不过。那么这里为什么又是正确的呢?注意观察,可以发现这个程序中有void mdelay(…)这样一行,可见,mdelay这个词是我们自己起的名字,并且为此编写了一些程序行,如果你的程序中没有这么一段程序行,那就不能使用mdelay(1000)了。有人脑子快,可能马上想到,我可不可以把这段程序也复制到我其它程序中,然后就可以用mdelay(1000)了呢?回答是,那当然就可以了。还有一点需要说明,mdelay这个名称是由编程者自己命名的,可自行更改,但一旦更改了名称,main()函数中的名字也要作相应的更改。
mdelay后面有一个小括号,小括号里有数据(1000),这个1000被称之“参数”,用它可以在一定范围内调整延时时间的长短,这里用1000来要求延时时间为1000毫秒,要做到这一点,必须由我们自己编写的mdelay那段程序决定的,详细情况在后面循环程序中再作分析,这里就不介绍了。
在设计PCB图时我们应注意什么?
基于RISC-V内核单片机移植RTOS实时操作系统(一)
上汽奥迪第一款车型很有可能会在2022年正式推出
严防处理器厂坐大 触控IC商戮力强化大尺寸OGS方案
李飞飞又多了一个新的身份——斯坦福以人为本人工智能研究院的联合主任
C语言的入门简介和三个简单的C语言程序详细说明
Quartus.II使用简介与第一个工程实例
储能bms是什么意思
报警器8脚otp声音ic,NV020C漏电报警器语音芯片方案
光电转换器的作用和工作原理
成都建设全国首个5G规模组网试验体系,已经走在全国前列
车展上的增程式电动汽车,以理想的One吸引了很多的目光
中国联通王晓初:规划5万个5G基站已开通2.8万个
“鹞鹰”Ⅱ察打一体无人机首次试飞成功
应对功能安全标准,飞思卡尔完善汽车主动安全产品线
让你感受下OPPOR11这次发布会有多洋气,数十位重量级大咖力推!
AMD Renoir APU配置信息曝光,有望在2020年上半年发布
巅峰对决!荣耀Magic和iPhone 7对比评测
自制lm317可调电源 简洁易做
苹果面向开发者宣布,SKAdNetwork 2.0 已支持来源 App ID 和转化值