您好,欢迎访问三七文档
1嵌入式系统的C语言译自《CforEmbeddedSystems》讲稿刘永重译一、C语言基础1、什么是C?‘C’程序语言最初是由DennisRitchie在1971年为UNIX系统开发并实现的。C的一个最大优点是与任何特定的硬件或系统无关。这使得一个用户写的程序不作任何修改就能运行在几乎所有的机器上。C通常被称为中级计算机语言,因为它将高级语言的要素与汇编语言的功能结合了在一起。2、为什么用C?C非常灵活,而且可随心所欲。这种自由赋予C非常强大的功能,有经验的用户可以掌握;C是一个相对小的语言,但是它经久耐用;C有时被认为是“高级汇编语言”;低级(位操作)编程也容易实现;松类型(不象其它高级语言);C是结构化编程语言;C允许你创建你脑海中已有的任何任务。C保留了程序员知道正在做的事情的基本体系;它只需要他们明白地表达其意图。3、为什么不用C?文化的问题…当考虑转到C语言时,我们会遇到一些共同的问题:产生大而低效的代码;标准IO程序的雍余代码(printf,scanf,strcpy等);存贮器定位的使用:malloc(),alloc()…;堆栈的使用,在C中不很直接;在RAM和ROM中数据的声明;难于写中断服务程序。4、8位微控制器的ANSIC对于嵌入式系统,纯粹的ANSIC并不方便,因为:嵌入式系统与硬件打交道。ANSIC提供的在固定存贮空间用寄存器寻址的工具非常拙劣;几乎所有的嵌入式系统使用中断;ANSIC有各种类型的促进规则,对8位机来说绝对是性能杀手;一些微控制器结构没有硬件支持C堆栈;很多微控制器有多个存贮空间。5、打破一些C范例当在低端的8位微控制器上用C语言,应想法使代码变小。这意味着打破一些编程规则:开/关全局中断;使用GOTO语句;全局标号;全局寄存器段;指针支持。6、嵌入式与桌面编程嵌入式编程环境的主要特点:有限的RAM;有限的ROM;有限的栈空间;面向硬件编程;严格的定时(ISR,任务,…);很多不同种类的指针(far/near/rom/uni/paged/…);特殊关键字/标识符(@,interrupt,tiny,…)。7、汇编与C编译器只是一个能干的优秀汇编程序员。写能够转换为高效率汇编代码的好的C代码,比手工写高效率的汇编代码容易得多。C是终极解决办法,但其本身并未终结。8、为什么改用C?有很多原因用C语言而不用汇编:C使我们提高效益;用C写的代码更可靠;C代码更容易升级和扩展;不同平台之间更容易迁移;代码容易维护;文档、书籍、第三方库和程序都可得到。9、C代码结构如下图所示,一个C程序基本由以下部分组成:2预处理命令、类型定义、函数原型(声明传给函数的函数类型和变量)、变量和函数。一个程序必须有一个main()函数,每个命令行必须用分号(;)结束。10、C函数一个函数的结构如下:类型函数名(参数){本地变量C语句}311、C关键字1)数据类型charshortsignedunsignedintfloatlongdouble2)修饰符conststaticvolatilerestrict3)标识符structunionvoidenum4)选择体ifelseswitchcasedefault5)存贮指定registertypedefautoextern6)循环体dowhilefor7)跳转gotocontinuebreakreturn8)功能指定inline9)预处理指示#include#define#undef#line#error#pragma10)条件编译#if#ifdef#ifndef#elif#else#endif12、C操作符1)基本表达式和后缀操作符()子表达式和函数调用[]数组下标-结构指针.结构成员++增加(后缀)--减少(后缀)2)一元操作符!逻辑非~取补++增加(前缀)--减少(后缀)-一元减+一元加(类型)类型强制*间接指针&取地址sizeof大小3)赋值符=相等赋值+=加等于-=减等于*=乘等于/=除等于%=求余等于=左移位等于=右移位等于&=按位与等于^=按位异或等于|=按位或等于4)位操作&位与^位异或|位或位左移位右移5)数学运算*乘/除%求余+加-减6)关系运算小于=小于或等于大于=大于或等于==相等测试!=不等测试7)逻辑运算&&逻辑与||逻辑或8)条件运算?:条件测试9)序列,逗号4二、嵌入式编程1、变量变量的类型决定其可带值的类型。也就是说,为变量选择一个类型与我们使用这个变量的方法直接相关。我们将学习C的基本类型、怎样写常量和声明这些变量。1.1选择一个类型“值集合”是有限的。C的整数类型不能代表所有整数;它的浮点类型也不能代表所有浮点数。当声明一个变量并为它选择一个类型,你应紧记你需要的值和操作。1.2C的基本数据类型ANSI标准并没为本地类型规定尺寸大小,但CodeWarrior规定了。C只有一些基本数据类型:所有数量类型(除了char)缺省都是有符号的,例如:‘int’=‘signedint’。注意:INT型的大小依赖于不同的机器。1.3CodeWarrior数据类型例如,按ALT+F7打开工程的通用设置,选择“CompilerforHC08”面板并点击类型尺寸。这个窗口向你显示CodeWarrior编译器使用的标准类型设置。所有基本类型可以改变,尽管这可能不是个好主意。51.4数据类型的事实代码大小和执行时间的最大节约可通过为变量选择最合适的数据类型得到。8位微控制器内部的数据的长度是8位(一字节),然而C首选的数据类型是‘int‘。8位机处理8位数据类型比16位类型效率更高。“int“和大数据类型只有当所描述的数据的大小需要时才使用。当效率非常重要时,双精度和浮点操作效率低,应当避免。1.5选择数据类型8位微控制器选择数据类型有3个规则:1)用最可能小的类型来完成工作,大小越小占用存贮空间越少;2)若可能,用无符号类型;3)在表达式内声明以将数据类型减到最少需要。使用类型定义得到固定大小:1)根据编译器和系统而改变;2)移植到不同的机器代码不变;3)当值需要固定位时使用。打开文件:Lab1-Variables.mcp6Main函数内定义了三种不同类型的变量定义了一个数据类型的完整集合只写了意义最少的位;寄存器用于此目的。每个变量剩余位用:clr,x清除。变量在堆栈中有一个地址。7主函数外定义了三个不同类型的变量。主函数外定义了三个不同类型的变量。编译器为作用的变量保留了内存。本例中VarA是唯一使用的变量。8所有声明的全局变量均被使用。在这种情况下,编译器为所有变量保留了内存。92、存贮类修饰符以下关键字用于声明变量,以指定特定需要或内存中变量存贮的相关条件。staticvolatileconst这三个关键字,一起让我们不仅可写出好的代码,而且可写出紧凑的代码。2.1静态变量使用静态有二个主要功能:第一个最常用的用法是定义一个变量,在函数连续调用期间,变量不会消失。第二个使用静态的用法是限制变量的范围。在模块级定义时,能被整个模块中所有函数访问,不能被其它函数访问。这非常重要,因为当严格限制全局变量众所周知的问题时,它让我们获得所有全局变量执行性能的好处。因此,如果我们有必须被一些函数频繁访问的数据结构,就应当将函数放入同一模块中,并将结构声明为静态。这样所有函数能够访问而不必通过一个访问函数的上层,同时与数据结构无关的代码禁止访问它。这一技术是一种变通方法,立即可访问变量在小的机器上实质上取得了足够的性能。声明模块级静态变量(与将其设为全局相反)能取得一些其他潜在的益处。静态变量由于定义,只能被一组特定的函数访问。因此,编译器和连接器能够明智地选择变量在存贮空间的放置。例如,对于静态变量,编译器/连接器也许选择将一个模块中所有静态变量放在连续的区域,这样增加了各种优化机会,例如用简单的增加或减少代替重载。相反,全局变量在存贮空间的位置通常计划于优化编译器的哈稀算法,这排除了可能的优化。须着重指出,这些变量不会存贮在堆栈中,因为它们必须保存其值。下面给出一个静态变量怎样工作的例子:FILE1.c根据变量大小的不同,每个加操作用不同的方法完成。变量声明的内存区,每个变量有不同的大小(1、2和4字节)10#includeFILE2.h//包含文件FILE2.c//中的函数voidmain(void){//第一次进入MyFunction之前,myVar=0。MyFunction();//在FILE2.c中//第二次进入MyFunction之前,myVar=1。MyFunction();//在FILE2.c中}FILE2.cvoidMyFunction(void){//FILE2.C中定义//MyFunction函数staticcharmyVar=0;//本地变量//声明为staticmyVar=myVar+1;//尽管myVar是本地变量,但它保持了自己的值。}2.2静态函数一个静态函数只能被其所在模块中的其它函数调用。使用静态函数是结构化编程的好习惯。你也许惊讶地知道静态函数能产生小/快的代码。这是可能的,因为编译器在编译时确切地知道什么函数能调用一个给定的静态函数。因此,函数的相关内存区域能被调整,以致使用调用的一个短版本或跳转指令。潜在的改进甚至更好,编译器足够聪明地用跳转代替调用。2.3关键字“static”的使用在函数体声明静态的变量,在函数调用期间保持其质;在模块内声明静态的变量,(但在函数体之外)能被模块内所有函数访问;在模块内声明静态的函数,只能被模块内其它函数调用。对于嵌入式系统:封装持续生存的数据(包装);模块化编码(数据隐藏);在每个模块中隐藏内部处理。2.4可变(volatile)变量可变变量是其值在正常程序流程以外可能改变的变量。在嵌入式系统中,这种情况通过两种主要途径发生:通过一个中断服务程序,或作为硬件动作的结果。例如,通过一个串口接收到一个字符,结果串口状态寄存器更新,这完全在程序流程之外发生。很多程序员知道编译器不会试图优化一个volatile寄存器,而宁可每次重载它。在嵌入式设备中,将所有外设寄存器声明为volatile是一个好习惯。许多编译器供应商经常炫耀他们的代码优化,它们通常非常好,它们有些根本不明显,但能极大地减少周期和内存。但有时我们不想编译器聪明和优化一个部份,因为我们确实需要代码那样作。我们怎样才能达到呢?那么,访问定义为volatile的变量从不会被编译器优化。让我们分析一个例子,看看编译器是怎样处理一个volatile和一个非volatile变量…volatileunsignedcharPORTA@0x00;volatileunsignedcharSCS1@0x16;unsignedcharvalue;11voidmain(void){PORTA=0x05;/*PORTA=00000101*/PORTA=0x05;/*PORTA=00000101*/SCS1;value=10;}未使用Volatile关键字,编译器将其编译为:MOV#5,PORTALDA#10STA@value使用Volatile关键字后,编译器将其编译为:MOV#5,PORTAMOV#5,PORTALDASCS1LDX#10STX@value这段代码实际上不做任何事,但它很好地表达了优化怎样强烈地影响程序的结果。在main()中连续两次使用语句:PORTA=5,这没有意义,但让我们假设这是正确开发程序所必须的…在这两个语句之后,明显地有一条无意义语句“SCS1;”。让我们看当不使用volatile变量会发生什么…我们得到了优化过的汇编代码。重复的语句PortA=5消失了只剩下一句“move#5toPortA”。语句“SCS1;”似乎什么都不做,因此聪明的编译器将它消去了。最后,将10加载到累加器并作为值存贮。使用volatile关键字声明PORTA和SCS1,得到的汇编代码没有优化,连续两次在PortA写入数值5,然后将SCS1加载到累加器。最后由于累加器被使用,于是用X寄存器存贮数值10
本文标题:嵌入式系统的C语言
链接地址:https://www.777doc.com/doc-2440459 .html