MC9S12XS单片机原理 及嵌入式系统开发

编程语言的选择; C语言编程元素; C程序编译器与交叉编译器; CODEWARRIOR软件简介. 《MC9S12XS单片机原理及嵌入式系统开发》. 前言. 本章首先通过编程语言 ...
展开查看详情

1.MC9S12XS 单片机原理 及嵌入式系统开发 《 MC9S12XS 单片机原理及嵌入式系统开发》

2.第 4 章 C 语言的嵌入式编程  编程语言的选择  C 语言编程元素  C 程序编译器与交叉编译器  CODEWARRIOR 软件简介 《 MC9S12XS 单片机原理及嵌入式系统开发》

3.前言 本章首先通过编程语言的选择问题 介绍 C 语言编程的优点,然后讨论 C 语言进行程序设计时涉及的一些问 题,并简要介绍了 Freescale 公司的 单片机开发工具 —— CodeWarrior 的使用方法。 《 MC9S12XS 单片机原理及嵌入式系统开发》

4.4.1 编程语言的选择 为了确定嵌入式系统合适的编程语言,需要了解以 下问题: ① 计算机(如微控制器、微处理器或 DSP 芯片 等)只接受 “机器码 ”(即目标代码)指令。如果严格定 义,机器码才是计算机的语言,而不是程序员使用的其 他语言。但如果由程序员去解释机器码,则工作量是非 常巨大的,而且也容易出错,是不可行的。 ② 所有的软件,例如汇编语言、 C 语言、 C++ 语 言、 Java 语言等,为了能够被计算机执行,最终都必 须翻译成机器码。 ③ 嵌入式处理器的功能有限且内存有限,所以编程 语言必须具有高效率。 ④ 为嵌入式系统编程,经常需要对硬件进行底层访 问操作,这意味着至少要能够读写特定的存储器地址。 《 MC9S12XS 单片机原理及嵌入式系统开发》

5.4.1 编程语言的选择 当然,语言的选择问题还有一些并非技术方面的考 虑: ① 如果每个项目开发都从头编写代码,显然软件程 序员是不乐意的。编程语言必须能够支持创建灵活方便 的库,这样同类的项目可以重用那些经过充分测试的代 码模块。当使用新的处理器或升级处理器时,整个代码 系统移植到新系统应该是可行的,并且工作量尽可能 少。 ② 语言的选择应该具有通用性。这样才能保证比较 容易产生更多的有经验的开发人员,而且开发人员也容 易获得相关设计实例以及编程实践信息。 ③ 随着系统和处理器的不断升级,程序代码往往需 要经常进行维护。好的程序代码应该是容易被理解的, 而且并不仅仅容易被开发者理解,同时程序代码的维 护、升级也应该非常便利。 《 MC9S12XS 单片机原理及嵌入式系统开发》

6.4.1 编程语言的选择 C 语言的特性如下: ① 它属于 “中级语言 ”,不仅具有 “高级语言 ”的特征 (如支持函数和模块),还有 “低级语言 ”的特性(可以通 过指针访问硬件); ② 编程效率很高; ③ 十分流行且容易理解; ④ 即使是 PC 程序员,以前只使用过 Java 或 C++ 语 言,也能够很快理解 C 语言的语法和编程方法; ⑤ 每一个嵌入式处理器(从 8 位到 32 位或以上)都有 良好且得到充分验证的 C 编译器; ⑥ 容易找到 C 语言编程经验的开发人员; ⑦ 容易找到有关资料、培训课程及相关网站等技术支 持。 《 MC9S12XS 单片机原理及嵌入式系统开发》

7.4.2 C 语言编程元素 4.2.1 全局变量和局部变量 变量是程序运行时在内存中存放数据的一个存储空间。对嵌入式系统来 说,它是 RAM 或 ROM (甚至是处理器的寄存器)上的存储单元。全局变量是为 整个程序定义的,在程序运行中始终有效。用全局变量传递参数,是参数传递的 常用方法。局部变量是在某个函数内部声明的变量,它只能被该函数访问。在嵌 入式系统中,局部变量通常位于堆栈中。全局变量和局部变量的区别取决于在程 序中的什么位置声明它。全局变量必须在函数外部声明,而局部变量则必须在一 个函数内部声明。 由于程序是固化在 ROM 中的,而不是下载到 RAM 中的。除非在应用程序 运行开始后向 RAM 中下载什么, RAM 中的内容在开机时是随机的。这就要求在 用 C 语言开发嵌入式应用程序时不要使用初始化变量。 当希望在多个源文件中共享变量时,需要确保定义和声明的一致性。最好 的安排是在某个相关的 .c 文件中定义,然后在 .h 头文件中进行外部声明,在需要 使用的时候,只要包含对应的头文件即可。定义变量的 .c 文件也应该包含该头文 件,以便编译器检查定义和声明的一致性。 《 MC9S12XS 单片机原理及嵌入式系统开发》

8.4.2 C 语言编程元素 4.2.2 头文件 通常在一个程序的开始部分进行头文件包含操作。头文 件通常包括常量定义、变量定义、宏定义和函数声明等,程 序员可以在自己的程序中嵌入它们。内嵌库中最常见的头文 件是标准输入 / 输出文件( stdio.h ),该头文件包含用于输 出信息和接收用户键盘输入的函数声明。在很多情况下,出 于特定系统要求,程序员通常需要创建自己的头文件,并将 它们包含在程序中。 要包含一个头文件,必须在程序的开始部分使用编译预 处理指令 #include 。 《 MC9S12XS 单片机原理及嵌入式系统开发》

9.4.2 C 语言编程元素 4.2.3 编译预处理 1 .用于包含文件的 #include 指令 任何 C 程序首先都要包含那些准备使用的头文件和源 文件, include 是一个用于包含某个文件内容的预处理指令。 以下给出可以被包含的文件: • 包含代码文件:这些文件是已经存在的代码文件。 • 包含常量数据文件:这些文件是代码文件,可以有扩展 名 .const 。 • 包含字符串数据文件:这些文件是包含字符串的文件,可 以带扩展名 .string 、 .str 或者 .txt 。 • 包含初始数据文件:这些文件用于嵌入式系统掩模只读存 储器的初始或默认数据,启动程序运行后会被复制到 RAM 当中,可以具有扩展名 .init 《 MC9S12XS 。 单片机原理及嵌入式系统开发》

10.4.2 C 语言编程元素 4.2.3 编译预处理 2 .宏定义 #define 指令 C语言中允许用一个标识符来表示一个字符串,称为 宏。被定义为宏的标识符称为宏名。在编译预处理时,对程 序中所有出现的宏名,都用宏定义中的字符串去代换,这称 为宏代换或宏展开。宏定义是由源程序中的宏定义命令完成 的,宏代换是由预处理程序自动完成的。 《 MC9S12XS 单片机原理及嵌入式系统开发》

11.4.2 C 语言编程元素 4.2.3 编译预处理 2 .宏定义 #define 指令 对于宏定义再做以下几点说明: ① 宏定义是用宏名来表示一个字符串,在宏展开时又 以该字符串取代宏名,这只是一种简单的代换,字符串中可 以含任何字符,可以是常数,也可以是表达式,预处理程序 对它不作任何检查。如有错误,只能在编译已被宏展开后的 源程序时发现。 ② 宏定义不是说明或语句,在行末不必加分号,如加 上分号则连分号也会一起置换的。 ③ 宏定义必须写在函数之外,其作用域为宏定义命令 起到源程序结束,如果要终止其作用域可使用 #undef 命令。 《 MC9S12XS 单片机原理及嵌入式系统开发》

12.4.2 C 语言编程元素 4.2.3 编译预处理 3 .条件编译指令 条件编译指令包括 #if 、 #ifdef 、 #ifndef 、 #else 、 #elif 和 #endif 。这些指令 用于根据某个表达式有条件的编译一部分代码。可以仅在程 序开发过程中利用这些指令来编译部分调试代码。 指令 #if 和 #endif 用于选择性地编译某段代码。 #if 后 的表达式值为 TURE 或 FALSE 。如果是 TRUE , #if 和 #endif 之间的所有代码将被编译;否则,这些代码将被忽 略。 #else 和 #elif 指令可以用于更灵活选择编译功能,它们 也必须同 #if 和 #endif 一起使用。 《 MC9S12XS 单片机原理及嵌入式系统开发》

13.4.2 C 语言编程元素 4.2.4 数据类型 《 MC9S12XS 单片机原理及嵌入式系统开发》

14.4.2 C 语言编程元素 4.2.4 数据类型 在声明变量的时候,可以规定变量的访问 / 存储类型, C 语言有 6 个访问 / 存储关键字: extern 、 auto 、 static 、 register 、 const 和 volatile 。 ① extern 说明该变量在另一个目标代码文件中声明和定义过。这些 变量可以被所有函数访问。 ② auto 是默认的存储类型,在一个代码块内(或在一个函数头部作 为参量)声明的变量,无论有没有访问 / 存储关键字 auto ,都属于自动 存储类。该类具有自动存储时期、代码块的作用域和空链接,如果未初始 化,它的值是不确定的。在 S12 单片机中,这种类型的变量存放在栈 中。一旦某个函数(一段程序)结束任务,这些用 auto 声明的变量将从 栈中清除,不再有效。另外,只有声明这种变量的函数才有权访问该变 量。 《 MC9S12XS 单片机原理及嵌入式系统开发》

15.4.2 C 语言编程元素 4.2.4 数据类型 ③ static 存储类型与 auto 类型类似,但它存储在 RAM 中而不是栈 中,因此它在程序运行的整个过程中都有效。在 C 语言中,关键字 static 有三个明显的作用:在函数体,一个被声明为静态的变量在这一函数被调 用过程中维持其值不变;在模块内(但在函数体外),一个被声明为静态 的变量可以被模块内所用函数访问,但不能被模块外的函数访问,它是一 个本地的全局变量;在模块内,一个被声明为静态的函数只可被这一模块 内的其他函数调用,也就是说,这个函数被限制在声明它的模块的本地范 围内使用。 ④ register 声明的变量表明要求编译器使用(如果可能)微处理器中 的一个寄存器来存放该变量。使用微处理器的寄存器存储一个变量可以减 少总线访问存储单元的时间、加速程序的运行。因此,若某个变量在程序 中需要经常访问,可以考虑这种存储类型。 如果某个变量的值在程序运行中保持不变,则可以用 const 类型来 声明它,该变量通常存放在 ROM 中。一个 const 变量必须由程序员初始 《 MC9S12XS 单片机原理及嵌入式系统开发》 化。有的程序员认为“ const 意味着常数”,这种说法其实有一些问题,有

16.4.2 C 语言编程元素 4.2.4 数据类型 ⑤ const 有以下作用:关键字 const 的作用是为给读代码的人员传达 非常有用的信息,实际上,声明一个参数为常量是为了说明这个参数的应 用目的;为优化器提供一些附加的信息,使用关键字 const 也许能产生更 紧凑的代码;合理地使用关键字 const 可以使编译器很自然地保护那些不 希望被改变的参数,防止其被无意的代码修改,简而言之,这样可以减少 bug 的出现。有关 const 的所有用法,建议读者参考 Dan Saks 的文 章“ const T vs.T const” 。 ⑥ volatile 访问类型表示它所声明的变量值在程序运行中可能不经过 相关指令就发生变化。在 S12 单片机中,当某个变量的值被硬件输入端 口改变时,这些变量应该用 volatile 声明。 《 MC9S12XS 单片机原理及嵌入式系统开发》

17.4.2 C 语言编程元素 4.2.5 运算符 《 MC9S12XS 单片机原理及嵌入式系统开发》

18.4.2 C 语言编程元素 4.2.5 运算符 《 MC9S12XS 单片机原理及嵌入式系统开发》

19.4.2 C 语言编程元素 4.2.6 指针 指针是存放其他变量地址的变量。例如,一个字符型变量指针存放的是该 字符变量的地址。声明一个指针变量的格式与声明一个变量的格式相同,只是在 变量名前加一个 * 运算符。 指针在 C 语言中的另一个重要应用是动态内存分配。动态内存分配与我们 见到的其他内存分配方式不同,区别在于动态内存分配的存储单元在程序的运行 过程中才确定。这些分配的内存通常来自 RAM 中未被使用的部分,我们称这一部 分为堆。动态内存分配常常用在不知道 RAM 的大小又想充分利用 RAM 的资源的 情况下。动态内存分配的两个主要函数是 malloc() 和 free() 。 malloc() 函数用于 分配内存空间,而 free() 用于释放被分配的内存空间。 在嵌入式系统中程序设计中,程序员经常面临者要求去访问某特定的内存 位置的情况。此时可以利用指针方便的实现这个要求。 《 MC9S12XS 单片机原理及嵌入式系统开发》

20.4.2 C 语言编程元素 4.2.7 条件语句、循环语句及无限循环语句 1 .条件语句 条件语句在程序中会经常多次使用。如果某个定义的条 件能够被满足,那么执行紧跟在条件语句之后的大括号内的 语句(或者不带大括号的语句),否则程序会转到下一条语 句或者转到另一组语句中执行。条件语句可以分为 if 语句和 switch 语句两大类。 《 MC9S12XS 单片机原理及嵌入式系统开发》

21.4.2 C 语言编程元素 4.2.7 条件语句、循环语句及无限循环语句 2 .循环语句 C 语言有三种不同的循环结构: for 循环、 while 循环和 do-while 循环。 for 循环的开头包含一条初始化语句、一个循环条件判断 和一条更新语句。在更新语句后是一组指令组成的循环体, 这组指令在循环条件满足之前重复执行。 while 循环与 for 循环类似,都是重复执行循环体内的指 令,但它在 while 后只有一个循环终止条件。 do-while 循环基本上和 while 循环完全一样,唯一的区 别是 do-while 循环先执行循环体语句,再判断终止条件。 《 MC9S12XS 单片机原理及嵌入式系统开发》

22.4.2 C 语言编程元素 4.2.7 条件语句、循环语句及无限循环语句 3 .无限循环语句 使用 C 语言总是会被提醒无限循环是不希望发生的, 因为这意味着程序永远都不会结束,但是无限循环是嵌入式 系统编程的一个特征。无限循环的硬件等价于一个系统时钟 (实时时钟)或者一个正在运行的空闲计时器。程序中的 main() 函数就是一个大循环,其间有对函数进行的调用以及 对中断的处理等程序,而最终它必须回到起始处,系统主程 序永远都不能出现停止状态。 《 MC9S12XS 单片机原理及嵌入式系统开发》

23.4.2 C 语言编程元素 4.2.8 函数 1 .函数定义 函数是完成某个特定任务的一段独立代码,它必须具备 三个特征:独立性、灵活性、可移植性。一个函数必须独立 于程序的其他代码,因为函数可以被不同的用户调用。例如 要求编写一个函数滤除输入信号中的噪声(过滤)。你的程 序将会被许多不同的程序调用,需要从不同的信号中去除某 种噪声。该函数必须独立于使用该函数的程序,并且必须足 够灵活。如果编写的去除噪声的函数只能去除某个预先设定 的频率段内的噪声,并且这个频率段范围无法改变,那么这 个函数的价值就非常有限,尤其是与同频率范围用户指定的 函数相比。最后,函数还必须是可移植的。 C 语言给工业界 带来的革命就是提供可以在不同的硬件平台上使用同一段代 《 MC9S12XS 单片机原理及嵌入式系统开发》

24.4.2 C 语言编程元素 4.2.8 函数 2 .主程序 主程序也是一种函数,区别在于当程序名被调用时,这 个函数首先被执行。主程序是程序执行的管理者,它包含程 序的总体结构,通过调用其他不同的函数来处理、完成具体 的任务,并因此避免亲自处理这些任务。可以把主函数想象 成一个在其他函数的帮助下控制各种命令执行的管理者。 《 MC9S12XS 单片机原理及嵌入式系统开发》

25.4.2 C 语言编程元素 4.2.8 函数 2 .主程序 《 MC9S12XS 单片机原理及嵌入式系统开发》

26.4.2 C 语言编程元素 4.2.8 函数 2 .主程序 《 MC9S12XS 单片机原理及嵌入式系统开发》

27.4.2 C 语言编程元素 4.2.8 函数 3 .函数原型 函数在被调用前必须在程序的头部声明,这些声明称为 函数原型。 函数声明的格式为: 《 MC9S12XS 单片机原理及嵌入式系统开发》

28.4.2 C 语言编程元素 4.2.8 函数 4 .函数定义 当某个函数在一个源文件的开始部分声明后,程序员必 须在该源文件或其他伴随的源文件中定义这个函数。函数还 可以在伴随的库文件中定义。函数定义可以在程序的任何地 方进行,但通常都位于主程序后。除了末尾没有分号外,函 数定义与函数声明的格式差不多。 《 MC9S12XS 单片机原理及嵌入式系统开发》

29.4.2 C 语言编程元素 4.2.8 函数 4 .函数定义 例如,假设前面声明的 compute 函数接受两个输入参 数作为一个向量,计算该向量的长度并返回该长度值。该函 数可以作如下定义: 《 MC9S12XS 单片机原理及嵌入式系统开发》