




已阅读5页,还剩76页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
3.4 嵌入式C语言程序设计基础,C 语言是美国的Dennis Ritchie在1972年设计发明的。它由早期的编程语言BCPL( Basic Combined Programming Language) 发展演变而来。,随着微型计算机的日益普及,出现了许多C 语言版本。由于没有统一的标准,使得这些C 语言之间出现了一些不一致的地方。为了改变这种情况,美国国家标准研究所(ANSI)为C 语言制定了一套ANSI标准,成为现行的C语言标准。,3.4 嵌入式C语言程序设计基础,C语言的预处理伪指令 嵌入式程序设计中的函数及函数库 嵌入式程序设计中常用的C语言语句 嵌入式程序设计中C语言的变量、数组、结构和联合,1、C语言的预处理伪指令,文件包含伪指令 宏定义伪指令 条件编译伪指令,文件包含伪指令,1文件包含的概念 文件包含是指一个源文件可以将另一个源文件的全部内容包含进来。 2文件包含处理命令的格式 include “包含文件名” include include 宏标识符 区别仅在于: (1)使用双引号:系统首先到当前目录下查找被包含文件,如果没找到,再到系统指定的“包含文件目录”(由用户在配置环境时设置)去查找。 (2)使用尖括号:直接到系统指定的“包含文件目录”去查找。一般地说,使用双引号比较保险。 (3)先宏扩展后,再按前述方式查找,例如: #include #include “common44b.h“ #include “common44blib.h“,文件包含伪指令,3文件包含的优点 一个大程序,通常分为多个模块,并由多个程序员分别编程。有了文件包含处理功能,就可以将多个模块共用的数据(如符号常量和数据结构)或函数,集中到一个单独的文件中。这样,凡是要使用其中数据或调用其中函数的程序员,只要使用文件包含处理功能,将所需文件包含进来即可,不必再重复定义它们,从而减少重复劳动。 4说明 (1)编译预处理时,预处理程序将查找指定的被包含文件,并将其复制到#include命令出现的位置上。,文件包含伪指令,(2)常用在文件头部的被包含文件,称为“标题文件”或“头部文件”,常以“h”(head)作为后缀,简称头文件。在头文件中,除可包含宏定义外,还可包含外部变量定义、结构类型定义等。 (3)一条包含命令,只能指定一个被包含文件。如果要包含n个文件,则要用n条包含命令。 (4)文件包含可以嵌套,即被包含文件中又包含另一个文件。,文件包含伪指令,宏定义伪指令,1无参宏定义的一般格式 #define 标识符 语言符号字符串 其中:“define”为宏定义命令;“标识符”为所定义的宏名,通常用大写字母表示,以便于与变量区别;“语言符号字符串”可以是常数、表达式、格式串等。 使用宏定义的优点 (1)可提高源程序的可维护性 (2)可提高源程序的可移植性 (3)减少源程序中重复书写字符串的工作量,例: #define LED8ADDR (*(volatile unsigned char *)(0x21400000) void Digit_Led_Symbol( int value ) if( value =0) ,2带参宏定义的一般格式 #define 宏名(形参表) 语言符号字符串 带参宏的调用和宏展开 (1)调用格式:宏名(实参表) (2)宏展开:用宏调用提供的实参字符串,直接置换宏定义命令行中、相应形参字符串,非形参字符保持不变。 说明 (1)定义有参宏时,宏名与左圆括号之间不能留有空格。否则,编译系统将空格以后的所有字符均作为替代字符串,而将该宏视为无参宏。 (2)有参宏的展开,只是将实参作为字符串,简单地置换形参字符串,而不做任何语法检查。在定义有参宏时,在所有形参外和整个字符串外,均加一对圆括号。,宏定义伪指令,例: #define SQR(x,y) sqrt(x)*(x)+(y)*(y) ?为什么使用(X),(Y) #define min(x1,x2) (x1 x2)?x1:x2) 练习:如何用参数宏实现十六进制0-0xf到ascii 0-F的转换?,宏定义伪指令,#define RECTANGLE_AREA( a, b ) a * b #define RECTANGLE_AREA( a, b ) (a * b) #define RECTANGLE_AREA( a, b ) (a) * (b),示例:如下用法可能导致错误。 #define SQUARE( a ) (a) * (a) int a = 5; int b; b = SQUARE( a+ ); / 结果:a = 7,即执行了两次增1。,3条件宏定义 一般格式 ifdef 标识符 程序段1; else 程序段2; endif 功能: 当“标识符”已经被#define命令定义过,则编译程序段1,否则编译程序段2。 (1)在不同的系统中,一个int 型数据占用的内存字节数可能是不同的。,宏定义伪指令,宏定义伪指令,(2)利用条件编译,还可使同一源程序即适合于调试(进行程序跟踪、打印较多的状态或错误信息),又适合高效执行要求。 关于#ifndef #endif命令 格式与#ifdef #endif命令一样,功能正好与之相反。,* File: 44BLIB.H * Author: embest * Desc: Samsung 44B0X CPU function declare and common define #ifndef _44BLIB_H_ #define _44BLIB_H_ #ifdef _cplusplus extern “C“ #endif,宏定义伪指令,4.宏释放 格式:#undef 宏标识符 例如: #define BLOCK_SIZE 512 Buf = BLOCK_SIZE*blks; #undef BLOCK_SIZE,宏定义伪指令,条件编译伪指令,1一般格式 if 条件表达式 程序段1; else 程序段2; endif 2功能:当表达式为非0(“逻辑真”)时,编译程序段1,否则编译程序段2。,#define MLCD_320_240 (3) #define ELCD_640_480 (4) #define SLCD_160_160 (5) #define LCD_TYPE MLCD_320_240 #if(LCD_TYPE=TLCD_160_240) #define SCR_XSIZE (160) #define SCR_YSIZE (240) #define LCD_XSIZE (160) #define LCD_YSIZE (240) #elif(LCD_TYPE=VLCD_240_160) #define SCR_XSIZE (240) #define SCR_YSIZE (160) #define LCD_XSIZE (240) #define LCD_YSIZE (160),条件编译伪指令,3.4 嵌入式C语言程序设计基础,C语言的预处理伪指令 嵌入式程序设计中的函数及函数库 嵌入式程序设计中常用的C语言语句 嵌入式程序设计中C语言的变量、数组、结构和联合,函数的定义,C语言是通过函数来实现模块化程序设计的。所以较大的C语言应用程序,往往是由多个函数组成的,每个函数分别对应各自的功能模块。任何函数(包括主函数main())都是由函数说明和函数体两部分组成。根据函数是否需要参数,可将函数分为无参函数和有参函数两种。,函数的定义,(1)无参函数的一般形式 函数类型 函数名( void ) 说明语句部分; 可执行语句部分; 注意:在旧标准中,函数可以缺省参数表。但在新标准中,函数不可缺省参数表;如果不需要参数,则用“void”表示,主函数main()例外。,(2)有参函数的一般形式 函数类型 函数名( 数据类型 参数,数据类型 参数2 ) 说明语句部分; 可执行语句部分; 有参函数比无参函数多了一个参数表。调用有参函数时,调用函数将赋予这些参数实际的值。 为了与调用函数提供的实际参数区别开,将函数定义中的参数表称为形式参数表,简称形参表。,函数的定义,函数的返回值与函数类型,语言的函数兼有其它语言中的函数和过程两种功能,从这个角度看,又可把函数分为有返回值函数和无返回值函数两种。 1函数返回值与return语句 有参函数的返回值,是通过函数中的return语句来获得的。 (1)return语句的一般格式: return ( 返回值表达式 ); (2)return语句的功能:返回调用函数,并将“返回值表达式”的值带给调用函数。 注意:调用函数中无return语句,并不是不返回一个值,而是一个不确定的值。为了明确表示不返回值,可以用“void”定义成“无(空)类型”。,2函数类型 在定义函数时,对函数类型的说明,应与return语句中、返回值表达式的类型一致。 如果不一致,则以函数类型为准。如果缺省函数类型,则系统一律按整型处理。 良好的程序设计习惯:为了使程序具有良好的可读性并减少出错,凡不要求返回值的函数都应定义为空类型;即使函数类型为整型,也不使用系统的缺省处理。,函数的返回值与函数类型,3函数原形说明 extern static,3.4 嵌入式C语言程序设计基础,C语言的预处理伪指令 嵌入式程序设计中的函数及函数库 嵌入式程序设计中常用的C语言语句 嵌入式程序设计中C语言的变量、数组、结构和联合,常用的C语言语句,一个完整的语言程序,是由一个main()函数(又称主函数)和若干个其它函数结合而成的,或仅由一个main()函数构成。 源程序书写格式 1) 所有语句都必须以分号“;”结束,函数的最后一个语句也不例外。 2) 程序行的书写格式自由,既允许1行内写几条语句,也允许1条语句分写在几行上。 如果某条语句很长,一般需要将其分写在几行上。 3) 允许使用注释。 C语言的注释格式为: /* */,常用语句 (1)条件语句 (2)开关语句 (3)循环语句,1if语句,一般格式 if(表达式) 语句组1; else 语句组2; (1)if语句中的“表达式”必须用“(”和“)”括起来。 (2)else子句(可选)是if语句的一部分,必须与if配对使用,不能单独使用。 (3)当if和else下面的语句组,仅由一条语句构成时,也可不使用复合语句形式(即去掉花括号)。,if语句的执行过程 (1)缺省else子句时 当“表达式”的值不等于0(即判定为“逻辑真”)时,则执行语句组1,否则直接转向执行下一条。 (2)指定else子句时 当“表达式”的值不等于0(即判定为“逻辑真”)时,则执行语句组1,然后转向下一条语句;否则,执行语句组2。 if语句的嵌套与嵌套匹配原则 if语句允许嵌套。所谓if语句的嵌套是指,在“语句组1”或(和)“语句组2”中,又包含有if语句的情况。 if语句嵌套时,else子句与if的匹配原则:与在它上面、距它最近、且尚未匹配的if配对。,1if语句,* name:Led_Display * func:Led Display control function * para: LedStatus - leds status * ret:none * modify: * comment: */ void Led_Display(int LedStatus) led_state = LedStatus; if(LedStatus ,1if语句,2switch语句,switch(表达式) case 常量表达式1:语句组;break; case 常量表达式2:语句组;break; case 常量表达式:语句组;break; default:语句组;break; 执行过程 (1)当switch后面“表达式”的值,与某个case后面的“常量表达式”的值相同时,就执行该case后面的语句(组);当执行到break语句时,跳出switch语句,转向执行switch语句的下一条。,(2)如果没有任何一个case后面的“常量表达式”的值,与“表达式”的值匹配,则执行default 后面的语句(组)。然后,再执行switch语句的下一条。 说明 (1)switch后面的“表达式”,可以是int、char和枚举型中的一种。 (2)每个case后面“常量表达式”的值,必须各不相同,否则会出现相互矛盾的现象(即对表达式的同一值,有两种或两种以上的执行方案)。 (3)case后面的常量表达式仅起语句标号作用,并不进行条件判断。系统一旦找到入口标号,就从此标号开始执行,不再进行标号判断,所以必须加上break语句,以便结束switch语句。,2switch语句,void Uart1_RxFifoErrorInt(void) rI_ISPC=BIT_UERR01; switch(rUERSTAT1) /to clear and check the status of register bits case 1: Uart_Printf(“Overrun errorn“); break; case 2: Uart_Printf(“Parity errorn“); break; case 4: Uart_Printf(“Frame errorn“); break; case 8: Uart_Printf(“Breake detectn“); break; default : break; ,2switch语句,3、循环语句,for语句的一般格式 for(变量赋初值;循环继续条件;循环变量增值) 循环体语句组; for语句的执行过程 (1)求解“变量赋初值”表达式。 (2)求解“循环继续条件”表达式。如果其值非0,执行(3);否则,转至(4)。 (3)执行循环体语句组,并求解“循环变量增值”表达式,然后转向(2) (4)执行for语句的下一条语句。 说明 (1)“变量赋初值”、“循环继续条件”和“循环变量增值”部分均可缺省,甚至全部缺省,但其间的分号不能省略。,(2)当循环体语句组仅由一条语句构成时,可以不使用复合语句形式,如上例所示。 (3)“循环变量赋初值”表达式,既可以是给循环变量赋初值的赋值表达式,也可以是与此无关的其它表达式(如逗号表达式)。 例如: for(sum=0;i=100;i+) sum += i; for(sum=0,i=1;i=100;i+) sum += i; (4)“循环继续条件”部分是一个逻辑量,除一般的关系(或逻辑)表达式外,也允许是数值(或字符)表达式。,3、循环语句,* name: Digit_Led_Test * func:8-segment digit LED test function * para: none * ret: none * modify: * comment: */ void Digit_Led_Test(void) int i; /* display all digit from 0 to F */ for( i=0; i16; i+ ) Digit_Led_Symbol(i); Delay(4000); ,3、循环语句,while语句格式 while(循环继续条件) 循环体语句组; 执行过程 1)求解“循环继续条件”表达式。如果其值为非0,转2);否则转3)。 2)执行循环体语句组,然后转1)。 3)执行while语句的下一条。 显然,while循环是for循环的一种简化形式(缺省“变量赋初值”和“循环变量增值”表达式)。,3、循环语句,void Uart1_RxFifoInt(void) rI_ISPC=BIT_URXD1; / if(rUFSTAT1=0) / Uart_Printf(“time outn“); while( (rUFSTAT1 ,3、循环语句,Do while语句格式 do 循环体语句组; while(循环继续条件); /*本行的分号不能缺省*/ 当循环体语句组仅由一条语句构成时,可以不使用复合语句形式。 执行过程 (1)执行循环体语句组。 (2)计算“循环继续条件”表达式。如果“循环继续条件”表达式的值为非 0(真),则转向(1)继续执行;否则,转向(3)。 (3)执行do-while的下一条语句。,3、循环语句,do-while循环语句的特点是:先执行循环体语句组,然后再判断循环条件。 do-while语句比较适用于处理:不论条件是否成立,先执行1次循环体语句组的情况。 除此之外,do-while语句能实现的,for语句也能实现,而且更简洁。,3、循环语句,#if N = 1000 static void insert_sort(char *strings, int n) char *v, *t; char *strp, *endp; int i;,endp = #endif,3、循环语句,3.4 嵌入式C语言程序设计基础,C语言的预处理伪指令 嵌入式程序设计中的函数及函数库 嵌入式程序设计中常用的C语言语句 嵌入式程序设计中C语言的变量、数组、结构和联合,char chr = 127; int sum = 200; chr += 1; sum += chr; ?sum=?,1、变量,在语言中,要求对所有用到的变量,必须先定义、后使用;且称在定义变量的同时进行赋初值的操作为变量初始化。 (1)变量定义的一般格式 存储类型 数据类型 变量名 , 变量名2; 例如,float radius, length, area; (2)变量初始化的一般格式 存储类型 数据类型 变量名=初值, 变量名2=初值2; #ifndef UCHAR typedef unsigned char UCHAR; #endif,(3)数据类型 char unsigned char short unsigned short int unsigned int float double long double 占用空间字节数?printf(“%dn”,sizeof();,1、变量,(3)存储类型 auto:自动,局部变量; register:寄存器,在cpu内部,高速; extern:外部变量,全局,函数外部定义的变量; static:静态,分内部、外部2种,在函数内部定义的静态变量是内部静态变量。,1、变量,2、数组,数组同变量一样,也必须先定义、后使用。 1)一维数组是只有1个下标的数组,定义形式如下: 数据类型 数组名常量表达式, 数组名2常量表达式2; (1)“数据类型”是指数组元素的数据类型。 (2)数组名,与变量名一样,必须遵循标识符命名规则。 (3)“常量表达式”必须用方括号括起来,指的是数组的元素个数(又称数组长度),它是一个整型值,其中可以包含常数和符号常量,但不能包含变量。 注意:C语言中不允许动态定义数组。,/* Digit Symbol table*/ int Symbol = DIGIT_0, DIGIT_1, DIGIT_2, DIGIT_3, DIGIT_4, DIGIT_5, DIGIT_6, DIGIT_7, DIGIT_8, DIGIT_9, DIGIT_A, DIGIT_B, DIGIT_C, DIGIT_D, DIGIT_E, DIGIT_F; /* LED segment table */ int Seg = SEGMENT_A, SEGMENT_B, SEGMENT_C, SEGMENT_D, SEGMENT_E, SEGMENT_F, SEGMENT_G, SEGMENT_P;,2、数组,(4)数组元素的下标,是元素相对于数组起始地址的偏移量,所以从0开始顺序编号。 (5)数组名中存放的是一个地址常量,它代表整个数组的首地址。同一数组中的所有元素,按其下标的顺序占用一段连续的存储单元。,2、数组,2)一维指针数组和一维数组指针 格式 数据类型 *数组名常量表达式=地址,地址,; 数据类型 (*标识符)=数组标识符;,2、数组,3) 二维数组 定义方式如下: 数据类型 数组名行常量表达式列常量表达式=初值表, 初值表; 1数组元素在内存中的排列顺序为“按行存放”,即先顺序存放第一行的元素,再存放第二行,以此类推。 2. 设有一个m*n的数组x,则第i行第j列的元素xij在数组中的位置为:i*n+j(注意:行号、列号均从0开始计数)。,2、数组,4)二维指针数组格式 数据类型 *标识符m n=地址,地址,;,2、数组,5)二维数组指针 格式 数据类型 (*标识符) n=数组标识符;,2、数组,6)多重指针 格式 数据类型 *标识符=&指针;,2、数组,3、结构,结构类型定义 struct 结构类型名 /* struct是结构类型关键字*/ 数据类型 数据项1; 数据类型 数据项2; 数据类型 数据项; ; /* 此行分号不能少!*/,/* bitmap struct */ typedef struct BITMAP INT8U ucFlags; / combination of flags above INT8U ucBitsPix; / 1, 2, 4, or 8 INT16U usWidth; / in pixels INT16U usHeight; / in pixels INT32U ulTransColor; / transparent color for 8bpp bitmaps INT8U *pucStart; / bitmap data pointer STRU_BITMAP, *pSTRU_BITMAP;,3、结构,struct NETWORKFRAME struct ETHERHDR eth_hdr; struct IPHDR ip_hdr; struct UDPHDR udp_hdr; union struct TFTPHDR tftp_hdr; struct DHCPHDR dhcp_hdr; ; _attribute_(packed);,4、联合,3.5 嵌入式C语言程序设计技巧,变量定义 参数传递 循环条件 以空间换时间 数学方法解决问题 使用位操作 嵌入汇编,一、变量定义,在变量声明的时候,最好把所有相同类型的变量放在一起定义,这样可以优化存储器布局。由下例可以看出:,一、变量定义,对于局部变量类型的定义,使用short或char来定义变量并不是总能节省存储空间。有时使用32位int或unsinged int局部变量更有效率一些,如下图所示:,一、变量定义,变量定义中,为了精简程序,程序员总是竭力避免使用冗余变量。但有时使用冗余变量可以减少存储器访问的次数这可以提高系统性能。,二、参数传递,为了使单独编译的C语言程序和汇编程序能够互相调用,定义了统一的函数过程调用标准ATPCS。ATPCS定义了寄存器组中的R0R3作为参数传递和结果返回寄存器,如果参数数目超过四个,则使用堆栈进行传递。 内部寄存器的访问速度是远远大于存储器的,所以要尽量使参数传递在寄存器里面进行,即应尽量把函数的参数控制在四个以下。,三、循环条件,计数循环是程序中十分常用的流程控制结构,一般有以下两种形式: for (loop=1;loop=limit;loop+) for (loop=limit;loop!=0;loop-) 这两种循环形式在逻辑上并没有效率差异,但是映射到具体的体系结构中时,就产生了很大的不同,如下图所示。,四、以空间换时间,计算机程序中最大的矛盾是空间和时间的矛盾,从这个角度出发逆向思维来考虑程序的效率问题,比如若系统的实时性要求很高,内存还有剩余,则我们就有可以用以空间换时间的方法来提高程序执行的效率。,五、数学方法解决问题,数学是计算机之母,计算机的发展是以数学为依据和基础的,所以在编写程序的时候,适当地采用一些数学方法会对程序的执行效率有数量级的提高,如下例所示:,六、使用位操作,一般的位操作是用来控制硬件的,或者做数据变换使用,但是,灵活的位操作可以减少除法和取模的运算有效地提高程序运行的效率,如下例所示:,七、嵌入汇编,汇编语言是效率最高的计算机语言,但是它地可读性较差,因此在C语言编程中为了获得程序的高效率,我们可以采用变通的方法-嵌入汇编、混合编程。,C语言编程中声明变量的几个重要关键词,_packed 用于将一个结构体在存储器中紧凑存储 volatile 用于告诉编译器不要将对该变量的操作优化掉 _align(n) 用于控制变量的n字节对齐存储,附加小知识,_packed,通常ARM编译器会按以下原则自动存放结构体: 起始地址为结构体成员中最大数据宽度的整数倍; 通过插入填充位,使对结构体成员的访问可以按对齐访问操作。,附加小知识,struct uint8 X; uint32 Y; uint8 Z; uint16 M; tmp;,struct uint8 X; uint32 Y; uint8 Z; uint16 M; tmp;,_packed struct uint8 X; uint32 Y; uint8 Z; uint16 M; tmp;,_packed,使用_packed关键词定义结构体,可以把插入的填充位全部去除,称之为结构体的紧凑存储。,附加小知识,_packed,紧凑存储的结构体可以使用指针方便的顺序读取所有成员内容; 缩小结构体的存储空间; 缺点:降低结构体成员的访问速度。,附加小知识,_packed struct uint8 X; uint32 Y; uint8 Z; uint16 M; tmp;,typedef struct uint8 X; uint8 Z; uint16 M; uint32 Y; tmp;,struct uint8 X; uint32 Y; uint8 Z; uint16 M; tmp;,_packed,最理想的编写方式是合理的设计结构体成员的位置,使各成员自然的紧凑存储。,附加小知识,volatile,有些变量在中断中被改变,而在正常程序中被读取。这种操作可能被编译器优化,而使得在正常程序中无法获知变量被改变后的内容。,附加小知识, uint8 flag; while( flag = FALSE ) , extern uint8 flag; void _irq INT(void) flag = TRUE; ,编译器认为在一个函数中重复读取一个变量无意义,所以将该操作优化处理.,volatile,附加小知识,使用volatile关键词定义变量后,编译器不会把该变量的任何读写操作优化。,_align(n),变量存放的起始地址和对齐字节数与变量的结构有关,有时为了控制
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 学前教育机构师资队伍建设与管理中的家庭教育协同研究报告
- 安全师年试题及答案
- 安全规程教育试题及答案
- 零售业数字化供应链协同与供应链协同平台建设实践报告
- 中国区域划分及省份课件
- 法治安全教育主题班会
- 公文管理课件
- 中国八大景点课件
- 护理职业生涯规划
- 防疫培训系列课件下载
- 社区工作者经典备考题库(必背300题)
- 2023年陕西韩城象山中学高一物理第二学期期末联考试题(含答案解析)
- DB4401-T 102.1-2020 建设用地土壤污染防治+第1部分:污染状况调查技术规范-(高清现行)
- 仓库组长岗位说明书
- 农业产业园可行性研究报告
- 实验2:基本数据类型、运算符与表达式
- 常州建筑水电安装施工专项方案
- 增强教师职业认同感、荣誉感、幸福感-课件
- Q∕GDW 12130-2021 敏感用户接入电网电能质量技术规范
- 幼儿园大班绘本:《没有牙齿的大老虎》 PPT课件
- 黑龙江省龙东地区中考地理真题试题含答案
评论
0/150
提交评论