博文
MCS-51单片机的C语言编程(2010-03-05 19:45:00)
摘要:§3-1 C语言与MCS-51
一、C语言特点 C语言是一种编译型程序设计语言,它兼顾了多种高级语言的特点,并具备汇编语言的某些特点,用C语言进行程序设计已经成为软件开发的一个主流。单片机系统的开发也适应了这个潮流。与汇编语言相比,用C语言开发单片机具有如下特点:
开发速度优于汇编语言;
软件的可读性和可维护性显著改善;
提供了库函数包含许多标准子程序,具有较强的数据处理能力;
关键字及控制转移方式更接近人的思维方式;
方便进行多人联合开发,进行模块化软件设计;
C语言本身并不依赖于机器硬件系统,移植方便;
适合运行嵌入式实时操作系统;
对于MCS-51单片机的C语言:
针对8051的特点对标准的C语言进行扩展。
对单片机的指令系统不要求十分了解,只要对8051单片机的存储结构了初步了解,就可以编写出应用软件。
寄存器的分配、不同存储器的寻址及数据类型等细节由编译器管理。
用C语言编写的应用程序必须经单片机的C语言编译器(简称C51)转换生成单片机可执行代码程序。支持MCS-51系列单片机的C语言编译器有很多种。如American Automation、Auoect、Bso/Tasking、KEIL等等。其中德国KEIL公司的C51编译器在代码生成方面领先,可产生最少代码,它支持浮点和长整数、重入和递归,使用非常方便。本章针对这种被广泛应用的KEIL C51编译器,介绍MCS-51单片机C语言的程序设计。
二、C51程序的开发过程
用C语言编写单片机应用程序和编写标准的C语言程序的不同之处,在于根据单片机的存储结构及内部资源定义C语言中的数据类型和变量,其他的语法规定、程序结构及程序设计方法与标准的C语言相同,所以在后面的几节中主要介绍如何定义C51中的变量的数据类型、存储类型、特殊功能寄存器以及中断函数,与标准C相同的部分就不再 述。
C51的开发过程和用其它语言包括汇编语言开发没有什么不同,其开发流程见下图:
例1:如图所示,P1口连接8只发光二极管,要求每隔0.5秒移动一次,当P2.0为高时,发光二极管左移,否则右移。
#include <reg51.h> //标准的8051头文件,定义了所有的SFR
#include<intrins.......
C51中的关键字(2010-03-05 19:44:00)
摘要:关键字 ;用 途 ;说 明
auto ;存储种类说明 ;用以说明局部变量,缺省值为此
break ;程序语句 ;退出最内层循环
case ;程序语句 ;Switch语句中的选择项
char ;数据类型说明 ;单字节整型数或字符型数据
const ;存储类型说明 ;在程序执行过程中不可更改的常量值
continue ;程序语句 ;转向下一次循环
default ;程序语句 ;Switch语句中的失败选择项
do ;程序语句 ;构成do..while循环结构
double ;数据类型说明 ;双精度浮点数
else ;程序语句 ;构成if..else选择结构
enum ;数据类型说明 ;枚举
extern ;存储种类说明 ;在其他程序模块中说明了的全局变量
flost ;数据类型说明 ;单精度浮点数
for ;程序语句 ;构成for循环结构
goto ;程序语句 ;构成goto转移结构
if ;程序语句 ;构成if..else选择结构
int ;数据类型说明 ;基本整型数
long ;数据类型说明 ;长整型数
register ;存储种类说明 ;使用CPU内部寄存的变量
return ;程序语句 ;函数返回
short ;数据类型说明 ;短整型数
signed ;数据类型说明 ;有符号数,二进制数据的最高位为符号位
sizeof ;运算符 ;计算表达式或数据类型的字节数
static ;存储种类说明 ;静态变量
struct ;数据类型说明 ;结构类型数据
......
(*(volatile unsigned char *)0x20)(2008-10-22 09:06:00)
摘要:对于(volatile unsigned char *)0x20我们再分析一下,它是由两部分组成:
1)(unsigned char *)0x20,0x20只是个值,前面加(unsigned char *)表示0x20是个地址,而且这个地址类型是unsigned char ,意思是说读写这个地址时,要写进unsigned char 的值,读出也是unsigned char 。
2)volatile,关键字volatile 确保本条指令不会因C 编译器的优化而被省略,且要求每次直接读值。例如用while((unsigned char *)0x20)时,有时系统可能不真正去读0x20的值,而是用第一次读出的值,如果这样,那这个循环可能是个死循环。用了volatile 则要求每次都去读0x20的实际值。
那么(volatile unsigned char *)0x20是一个固定的指针,是不可变的,不是变量。而char *u则是个指针变量。
再在前面加"*":*(volatile unsigned char *)0x20则变成了变量(普通的unsigned char变量,不是指针变量),如果#define i (*(volatile unsigned char *)0x20),那么与unsigned char i是一样了,只不过前面的i的地址是固定的。
那么你的问题就可解答了,(*(volatile unsigned char *)0x20)可看作是一个普通变量,这个变量有固定的地址,指向0x20。而0x20只是个常量,不是指针更不是变量。
......
文件扩展名及简要说明(2008-10-13 19:39:00)
摘要:S 汇编源代码文件
S3I Scream Tracker v3设备
S3M Scream Tracker v3的声音模块文件
SAM Ami专业文档;8位抽样数据
SAV 游戏保存文件
SB 原始带符号字节(8位)数据
SBK Creative Labs的Soundfont 1.0 Bank文件;(Soundblaster)/EMU SonndFont v1.x Bank文件
SBL Shockwave Flash对象文件
SC2 Microsoft Schedule+7文件格式;SAS目录(Windows 95/NT、OS/2、Mac)
SC3 SimCity 3000保存的游戏文件
SCC Microsoft Source Safe文件
SCD Matrix/Imapro SCODL幻灯片图像;Microsoft Schedule +7
SCF Windows Explorer命令文件
SCH Microsoft Schedule+1
SCI ScanVec Inspire本地文件格式
SCN True Space 2场景文件
SCP 拨号网络脚本文件
SCR Windows屏幕保护;传真图像;脚本文件
SCT SAS目录(DOS);Scitex CT位图;Microsoft FoxPro表单
SCT01 SAS目录(UNIX)
SCV ScanVec CASmate本地文件格式
SCX Microsoft FoxPro表单文件
SD Sound Designer 1声音文件
SD2 Sound Designer 2展平文件/数据分叉指令;SAS数据库(Windows 95/NT、OS/2、Mac)
SDF 系统数据文件格式—Legacy Unisys(Sperry)格式
SDK Roland S—系列软盘映像
SDL Smart Draw库文件
SDR Smart Draw绘图文件
SDS 原始Midi抽样转储标准文件
SDT SmartDraw模板
SDV 分号分隔的值文件
SDW Lotus WordPro图形文......
sizeof()(2008-10-06 20:16:00)
摘要:这是初学者问得最多的一个问题,所以这里有必要多费点笔墨。让我们先看一个结构体:
struct S1
{
char c;
int i;
};
问sizeof(s1)等于多少聪明的你开始思考了,char占1个字节,int占4个字节,那么加起来就应该是5。是这样吗你在你机器上试过了吗也许你是对的,但很可能你是错的!VC6中按默认设置得到的结果为8。
Why为什么受伤的总是我
请不要沮丧,我们来好好琢磨一下sizeof的定义——sizeof的结果等于对象或者类型所占的内存字节数,好吧,那就让我们来看看S1的内存分配情况:
S1 s1 = { 'a', 0xFFFFFFFF };
定义上面的变量后,加上断点,运行程序,观察s1所在的内存,你发现了什么
以我的VC6.0为例,s1的地址为0x0012FF78,其数据内容如下:
0012FF78: 61 CC CC CC FF FF FF FF
发现了什么怎么中间夹杂了3个字节的CC看看MSDN上的说明:
When applied to a structure type or variable, sizeof returns the actual size, which may include padding bytes inserted for alignment.
原来如此,这就是传说中的字节对齐啊!一个重要的话题出现了。
为什么需要字节对齐计算机组成原理教导我们这样有助于加快计算机的取数速度,否则就得多花指令周期了。为此,编译器默认会对结构体进行处理(实际上其它地方的数据变量也是如此),让宽度为2的基本数据类型(short等)都位于能被2整除的地址上,让宽度为4的基本数据类型(int等)都位于能被 4整除的地址上,以此类推。这样,两个数中间就可能需要加入填充字节,所以整个结构体的sizeof值就增长了。
让我们交换一下S1中char与int的位置:
struct S2
{
int i;
char c;
};
......
嵌入式系统设计要点(2008-09-26 19:07:00)
摘要:1.嵌入式系统
采用大容量EPROM来固化程序的专用系统,正在智能仪器和自动化等领域里得到广泛应用。传统设计方法用汇编语言编写程序,这主要是从保证速度和节省存储空间考虑,但编程费时,调试和排错很不容易。微电子技术的飞速发展,使高性能微处理器和大容量存储器的价格变得十分便宜,速度和存储容量不再是困扰设计者的主要问题。人们将ROMBIOS和CRT显示器等外设加进这类专用系统,并尝试用高级语言来开发其软件,即把通用计算机上的软件和硬件“嵌入”专用系统,构成所谓的嵌入式系统(EmbeddedSystem)。由于C语言容易编程、代码紧凑、可移植性和可维护性好,因而被普遍用于嵌入式程序的设计。
大多数嵌入式系统无操作系统支持,要由设计者提供所有低级I/O功能。系统I/O资源有限,程序必须固化在EPROM中,不能象在DOS下那样从磁盘装入和由用户编程。设计者要编写一个定位程序(Locator),把EXE格式的应用程序转换成可固化进EPROM的二进制文件(ROM图)。还要编写一个启动程序(runtimeStartupCode),与ROM图一起嵌入EPROM,先由它建立数据区和对系统硬件作必要初始化,然后调Main函数,执行应用程序。若想发挥C语言之优势,使用一些标准I/O语句,如用printf驱动显示器等,则要在嵌入式程序中加进经过修改的库函数。总之,C语言编程会使系统开发面临一 些新问题,要求设计人员具备软硬件方面的综合知识,才能正确进行系统调试和排错。
当然,如果拥有专用的嵌入式系统开发工具,设计工作便要省劲些。它们通常配有定位程序和可供设计者修改的启动程序样板,有些还能通过串口或并口,在PC机上联机调试程序,甚至有源级代码调试功能。利用工控机来设计系统,事情就更简单。
不过,专用开发工具和工控机价格昂贵,因此许多人在设计嵌入式系统时选择自己编写定位程序和启动程序,甚至编写可嵌入的I/O库函数。本文就嵌入式系统的程序设计方法及设计中可能遇到的问题作些讨论,供打算设计嵌入式程序的读者参考,有关......
UC/OS和UCLinux比较(2008-09-26 19:05:00)
摘要:uc/os和uclinux操作系统是两种性能优良源码公开且被广泛应用的的免费嵌入式操作系统,可以作为研究实时操作系统和非实时操作系统的典范。本文通过对c/os和uclinux的对比,分析和总结了嵌入式操作系统应用中的若干重要问题, 归纳了嵌入式系统开发中操作系统的选型依据。
两种开源嵌入式操作系统介绍
uc/os和uclinux操作系统,是当前得到广泛应用的两种免费且公开源码的嵌入式操作系统。uc/os适合小型控制系统,具有执行效率高、占用空间小、实时性能优良和可扩展性强等特点,最小内核可编译至2k。uclinux则是继承标准linux的优良特性,针对嵌入式处理器的特点设计的一种操作系统,具有内嵌网络协议、支持多种文件系统,开发者可利用标准linux先验知识等优势。其编译后目标文件可控制在几百k量级。
uc/os是一种免费公开源代码、结构小巧、具有可剥夺实时内核的实时操作系统。其内核提供任务调度与管理、时间管理、任务间同步与通信、内存管理和中断服务等功能。
uclinux是一种优秀的嵌入式linux版本。uclinux是micro-conrol-linux的缩写。同标准linux相比,它集成了标准linux操作系统的稳定性、强大网络功能和出色的文件系统等主要优点。但是由于没有mmu(内存管理单元),其多任务的实 现需要一定技巧。
两种嵌入式操作系统主要性能比较
嵌入式操作系统是嵌入式系统软硬件资源的控制中心,它以尽量合理的有效方法组织多个用户共享嵌入式系统的各种资源。其中用户指的是系统程序之上的所有软件。所谓合理有效的方法,指的就是操作系统如何协调并充分利用硬件资源来实现多任务。复杂的操作系统都支持文件系统,方便组织文件并易于对其规范化操作。
嵌入式操作系统还有一个特点就是针对不同的平台,系统不是直接可用的,一般需要经过针对专门平台的移植操作系统才能正常工作。进程调度、文件系统支持和系统移植是在嵌入式操作系统实际应用中最常见的问题,下文就从这几个角度入手对uc/os和uclinux进行分析比较。
进程调度
任务调度主要是协调任务对计算机系统内资源(如内存、i/o设备、cpu)的争夺使用。进程调度又称为cpu调度,其根本任务是按照某种原则为处于就绪状态的进程分......
C语言程序代码的优化方法(2008-09-17 21:04:00)
摘要:1、选择合适的算法和数据结构
应该熟悉算法语言,知道各种算法的优缺点,具体资料请参见相应的参考资料,有 很多计算机书籍上都有介绍。将比较慢的顺序查找法用较快的二分查找或乱序查找 法代替,插入排序或冒泡排序法用快速排序、合并排序或根排序代替,都可以大大 提高程序执行的效率。.选择一种合适的数据结构也很重要,比如你在一堆随机存 放的数中使用了大量的插入和删除指令,那使用链表要快得多。
数组与指针语句具有十分密码的关系,一般来说,指针比较灵活简洁,而数组则比 较直观,容易理解。对于大部分的编译器,使用指针比使用数组生成的代码更短, 执行效率更高。但是在Keil中则相反,使用数组比使用的指针生成的代码更短。。
3、使用尽量小的数据类型
能够使用字符型(char)定义的变量,就不要使用整型(int)变量来定义;能够使用 整型变量定义的变量就不要用长整型(long int),能不使用浮点型(float)变量就 不要使用浮点型变量。当然,在定义变量后不要超过变量的作用范围,如果超过变量的范围赋值,C编译器并不报错,但程序运行结果却错了,而且这样的错误很难发现。 在ICCAVR中,可以在Options中设定使用printf参数,尽量使用基本型参数(%c、%d、%x、%X、%u和s格式说明符),少用长整型参数(%ld、%lu、%lx和%lX格式说明 符),至于浮点型的参数(%f)则尽量不要使用,其它C编译器也一样。在其它条件不变的情况下,使用%f参数,会使生成的代码的数量增加很多,执行速度降低。
4、使用自加、自减指令
通常使用自加、自减指令和复合赋值表达式(如a-=1及a+=1等)都能够生成高质量的 程序代码,编译器通常都能够生成inc和dec之类的指令,而使用a=a+1或a=a-1之类的指令,有很多C编译器都会生成二到三个字节的指令。在AVR单片适用的ICCAVR、GCCAVR、IAR等C编译器以上几种书写方式生成的代码是一样的,也能够生成高质量的inc和dec之类的的代码。
5、减少运算的强度
可以使用运算量小但功能相同的表达式替换原来复杂的的表达式。如下:
(1)、求余运算。
a=a%8;
可以改为:......
void main(2008-05-21 15:28:00)
摘要:
0
推荐
void main(int argc, char* argv[ ])
假设程序编译成为 cp.exe
然后在命令行中输入
c:\>cp.exe doc1.txt doc2.txt
这行中有三个字符串分别为 cp.exe doc1.txt doc2.txt
则argc为3,即argc是命令行中参数的个数。
char *argv[]为一个指像字符串的数组。
argv[0]="cp.exe"
argv[1]="doc1.txt"
argv[2]="doc2.txt"
即命令行中各各字符串保存在 *argv[]中
在你运行程序以后,操作系统会自动将参数传给你。
例如你编译好的程序叫做program.exe
你运行 program a b
这个时候,argc = 3
argv[0] = "program"
argv[1] = "a"
argv[2] = "b"......
关于volatile关键字的说明以及测试(2008-05-08 13:54:00)
摘要:词性及解释
a. 挥发性的, 可变的, 不稳定的, 飞行的, 轻快的, 爆炸性的
n. 有翅动物, 挥发物
【计】 易失的
【医】 挥发性的
【经】 不稳定的, 易变的
volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如
操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行
优化,从而可以提供对特殊地址的稳定访问。
使用该关键字的例子如下:
int volatile nVint;
当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指
令刚刚从该处读取过数据。而且读取的数据立刻被保存。
例如:
volatile int i=10;
int a = i;
。。。//其他代码,并未明确告诉编译器,对i进行过操作
int b = i;
volatile 指出 i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的
汇编代码会重新从i的地址读取数据放在b中。而优化做法是,由于编译器发现两次从i读数据的代码之间
的代码没有对i进行过操作,它会自动把上次读的数据放在b中。而不是重新从i里面读。这样以来,如果
i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问
。
注意,在vc6中,一般调试模式没有进行代码优化,所以这个关键字的作用看不出来。下面通过插入汇编
代码,测试有无volatile关键字,对程序最终代码的影响:
首先用classwizard建一个win32 console工程,插入一个voltest.cpp文件,输入下面的代码:
#include <stdio.h>
void main()
{
int i=10;
int a = i;
printf("i= %d\n",a);
//下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道
__asm {
mov......