第六章C语言程序设计1关于实验安排10周和11周的周二晚上在兴隆山校区计算中心1号和3号机房(1、2班1号机房,3、4班2号机房,17:30前到)开放实验:11周、12周和13周在千佛山校区机械厂教学车间楼207(1、2班星期六全天;3、4班星期天全天:如有特殊事情者,可灵活选择;上午9:00——下午17:00)实验后撰写实验报告集成开发环境先用熟4.6版本;然后会用5.0版本第六章C语言程序设计2本周实验安排实验4:掌握最基本C语言程序的调试及运行方法1.建立一个C语言工程,并按要求编写求两个数最大值的C语言程序,编译并排错;2.使用全速调试,并观察程序运行结果;3.使用单步调试,并观察数据区、寄存器区、存储器区的数据变化。实验5:使用仿真方法,编写并调试复杂的C语言程序1.自定义一个包含10个无符号int型的数组,数组元素分别是10,9,8,7,6,5,4,3,2,1,0。使用冒泡排序法对该数组元素进行从小到大排序,调试并观察存储器区数据变化,程序要有注释。第六章C语言程序设计3本周实验安排2.基于上个程序,把排序好的数组进行数字滤波:去除最大的3个和最小的3个,剩下的求平均,调试并观察存储器区数据变化,程序要有注释。3.课本195页例2、例3,课本196页例4调试并观察存储器区数据变化。第六章C语言程序设计4第六章C语言程序设计§6-1C语言程序设计基础§6-2MC9S12的C编程与ANSIC的不同§6-3C程序设计举例内容提要:第六章C语言程序设计5单片机编程语言汇编语言优点:执行效率高,时序控制精确C语言优点:①程序开发速度快,提高开发工作效率;②软件调试直观;③可维护性好;④可移植性好(与汇编语言相比较)。单片机C语言单片机C语言和标准C语言的用法基本相同,但是单片机C语言是针对于资源少的MCU,它的编程方法和编程手段与PC机上使用C语言还是有很大差别。§6-1C语言程序设计基础第六章C语言程序设计6多文件C语言工程的头文件当代码量非常大时,程序员一般会把代码分成几个部分,然后写入到多个文件中。这些文件一般分为C文件和H文件。作为一般规则,以下内容应该放到H文件中:•预处理和宏定义•结构、联合和枚举声明•typedef声明•外部函数声明•全局变量声明•如果定义的变量仅仅被一个C文件所私有,那么不可放到H文件中。第六章C语言程序设计7其它C语言基础知识参见补充课件:“补充(自己看)_C语言基础.PPT”第六章C语言程序设计8CodeWarrior数据类型§6-2MC9S12的C编程与ANSIC的不同第六章C语言程序设计9自定义CodeWarrior数据类型在CodeWarrior环境下按ALT+F7打开工程的通用设置,选择“CompilerforHC12”面板然后点按钮“TypeSizes”。建议不要随意更改数据类型的设置第六章C语言程序设计10数据类型的事实代码大小和执行时间的最大节约可通过为变量选择最合适的数据类型而得到。16位微控制器内部的数据长度是16位(2字节),所以首选的数据类型是“int”。16位机处理16位数据比8位数据的效率更高。对于9S12单片机来说,16位数据类型效率最高,因此在使用中,尽量选择int类型数据。第六章C语言程序设计11存贮类修饰符以下关键字用于声明变量,以指定特定需要或内存中变量存贮的相关条件。static(静态变量)volatile(可变变量)const(只读变量)这三个关键字,一起让我们不仅可写出好的代码,而且可写出紧凑的代码。第六章C语言程序设计12static在函数体声明的静态变量,在函数调用期间保持其值;在模块内声明静态的变量(但在函数体之外),能被模块内所有函数访问;在模块内声明静态的函数,只能被模块内其它函数调用。如:staticintfunc(intx,inty);当一个C语言工程由多个程序员进行编写时,就不用担心自己所定义的函数与别人定义的重名了。如果在函数名和函数类型前加extern,则表示此函数是外部函数,可以被其他文件中的函数调用。如:externintfunc(intx,inty)。第六章C语言程序设计13File1.c#includeFile2.h/*包含文件FILE2.c中的函数*/voidmain(void){MyFunction();/*第一次进入MyFunction之前,myVar=0*/MyFunction();/*第二次进入MyFunction之前,myVar=1*/}File2.cvoidMyFunction(void){staticcharmyVar=0;/*本地变量声明为static*/myVar=myVar+1;/*myVar是本地变量,但它保持了自己的值*/}第六章C语言程序设计14可变(volatile)变量volatile的本意是“易变的”,就是该变量会以编译器无法预知的方式发生变化,请编译器不要做优化(所有的编译器的优化均假设编译器知道变量的变化规律)。也就是说是其值在正常程序流程以外可能改变的变量。在嵌入式系统中,这种情况通过两种主要途径发生:一是中断服务程序,二是硬件动作。例如,通过串口接收到一个字符,然后串口状态寄存器的值会发生改变,这完全在程序流程之外发生。在嵌入式设备中,最好将所有外设寄存器声明为volatile。第六章C语言程序设计15例子由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如:staticinti=0;intmain(void){...while(1){if(i)dosomething();}}/*Interruptserviceroutine.*/voidISR_2(void){i=1;}第六章C语言程序设计16程序的本意是希望ISR_2中断产生时,在main当中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被调用。如果将将变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。第六章C语言程序设计17Const变量关键字“const”,C语言中命名最差的关键字,并不表示恒量,而是代表“只读”。在嵌入式系统中,有很大的不同,这一会应会明白。const是C++中最常用的几个关键字之一,对确保程序的完整性有着重要的意义。它在调用者和被调用者之间传递这样一种信息:Baby,我不会改变你的,放心吧。Const声明可用于任何变量,它告诉编译器将其存贮在ROM代码。编译器保留了那个位置程序存贮器地址。由于位于ROM中,其值不能改变。第六章C语言程序设计18Const的用法:constunsignedshorta;unsignedshortconsta;constunsignedshort*a;unsignedshort*consta;第六章C语言程序设计19Constvolatile变量现在讨论一个深奥的问题,一个变量既能是常量,又能是可变量吗?如果是这样,这意味什么,怎样使用?答案是“能”。这个修饰符应该用于能出乎意料地改变的任何存贮器位置,因此需要volatile限定语,由于const该变量是只读的。第六章C语言程序设计20最明显的例子是硬件状态寄存器,象SCI状态寄存器SCS1。这个寄存器包含信号状态标志,如发送空、发送完成、接收满以及其它。这是一个可变寄存器由于这些标志的改变依赖于串行通信的状态,这也是只读,由于标志不能被程序直接改写,它们只对模块的状态作出响应。这个状态寄存器最佳声明方法是:#defineSCS1(*(constvolatileunsignedchar*)0x0016)第六章C语言程序设计21访问固定内存位置与PC机不同,嵌入式系统通常的特点是需要编程者访问一个指定的存贮器位置。练习:在某个项目中需要将绝对地址0x2FFA处整型变量的值设为0xAA55(编译器为纯粹的ANSI编译器)。完成这个任务的代码是:int*ptr;ptr=(int*)0x2FFA;*ptr=0xAA55;第六章C语言程序设计22访问I/O寄存器一个普通而有用的形式是使用如下的#define指示:#definePortA(*(volatileunsignedchar*)0x0000)这种情况下,PortA为地址0x0000处字符型变量。#define实际做的是每次发现PortA时放置一个构件。也就是说在代码中写:PortA=0x3F,实际做的就是告诉编译器0x0000是一个volatile-unsigned-char类型的指针,它的内容等于0x3F。第六章C语言程序设计23数组C语言允许程序员用几种不同方法存取数组的内容。选择最适合的方法来存取,可以提高代码的效率。数组的访问方法有:»硬编码:Array[0]=12*UNIT_VOLTS;编译时决定地址,执行速度快。»变址增加Array[index++]=12*UNIT_VOLTS;快速,比硬编码灵活。»数组指针*(ArrayPtr++)=12*UNIT_VOLTS;执行速度快,可读性差,可和循环一起使用。第六章C语言程序设计24C语言与汇编语言的混合编程在绝大多数场合采用C语言编程可完成预期的目的,但是对一些特殊情况进行编程时要结合汇编语言。汇编语言具有直接和硬件打交道、执行代码的效率高等特点,可以做到C语言所不能做到的一些事情:1.一个程序中的关键部分对执行速度有很高要求,实时性强。用汇编编程可以更有效的利用CPU的寄存器和指令集,用其产生的代码比用编译器产生的代码运行更快。2.对特定硬件接口的访问。例如对I/O端口的访问,或者用于禁止、启用中断系统。因此,选用C语言编程时,还需要夹杂一些汇编程序,通过这种混合编程的方法将C语言和汇编语言的优点结合起来,这已经成为MCU开发最流行的编程方法。第六章C语言程序设计25C中使用汇编目前大多数MCU系统,在C语言中使用汇编语言有两种情况:一种是汇编程序部分和C程序部分为不同模块或不同的文件,通常由C程序调用汇编程序模块的变量和函数(也可称为子程序或过程);另一种是嵌入式汇编,即在C语言程序中嵌入一段汇编语言程序。C语言中嵌入汇编语句:_asmAssemblyInstuction;asm(AssemblyInstruction);asm{AssemblyInstructionAssemblyInstruction}第六章C语言程序设计26C中调用汇编指令构成的子程序:当汇编程序和C程序为不同模块时,程序一般可分为若干个C程序模块和汇编程序模块,C程序模块通常是程序的主体框架,而汇编程序模块通常是若用C语言实现则效率不高的函数组成,也可以是已经成熟的、没有必要再转化成C语言的汇编子程序。在这种混合编程技术中,关键是参数的传递和函数的返回值。它们必须有完整的约定,否则数据的交换就可能出错。定义汇编子程序,定义格式如下:_子程序名代码……rts第六章C语言程序设计27注意:第一,在子程序名前加‘_’;第二,在C代码中调用汇编子程序时可直接调用:子程序名();第三,在子程序中不能使用映象寄存器的宏定义,只能用它们的直接地址;第四,汇编子程序的编写时,对于使用过的寄存器需要进行保护。在汇编中使用C语言在汇编中也可以调用C代码中的变量与子程序。使用C代码中定义的变量:在变量名前加‘_’或‘%’,例如unsignedcharPortA;asm(LDAA%PortA)调用C代码中定义的函数:在函数名前加‘_’,例如:function1();asm(BRA_function1);第六章C语言程序设计28C语言的中断CodeWarrior编译器提供了一个非ANSI的变通的方法,在源码中直接指定中断向量号t。表达式以interrupt关键字开始,接着是中断向量号,最后是函数原型。interrupt7voidRTI_ISR(void){/*RTI_ISRFuncti