MSP430单片机C语言的基本结构.doc
MSP430单片机C语言的基本结构 王晓宁 2013/1/28随着单片机处理速度的加快和存储容量的加大以及相应的开发软件中增强的代码优化功能,用C语言编写的程序其代码效率和运行速度已堪与汇编程序相媲美,而且C语言程序因其平易性、结构化、易维护性和可移植性而日益广泛地应用于单片机的开发中。用C语言编程时必须结合单片机的特点,不同品牌、不同系列或不同型号的单片机其内部资源、寄存器名称等都会不同,因此其软件的开发必须结合实际的硬件来进行。但C语言的程序结构还是有许多共性的,下面结合MSP430单片机大致说一下C程序的基本结构。C语言的程序结构还是比较规范的,一般包括头文件、宏定义、变量定义、函数定义、一个主函数main( )、以及中断处理函数等。在多文件的管理中,还包括自定义的头文件等。根据不同的情形,一个完整的C程序可以有不同的具体结构,但其框架基本上还是固定的。下面通过几个简单的小例子来看一下C程序的几种结构。 在此之前,先说一下C中的赋值方式。很多情况下,C程序就是在那儿完成一些赋值操作。|= 是“或”运算,&=是“与”运算,= 是“异或”运算。比如BIT0已经在430的头文件里定义为:BIT0=0x0001,0x0001是16进制计数,转成2进制就是(低八位),那么P1OUT|=BIT0,是一个什么结果?这是一个“或”运算(英文称or)。首先P1OUT是一个在头文件中定义好的寄存器,是8位的,因此,我们可以把P1OUT就当成一个变量名好了,只不过这个变量是在头文件中定义的,我们直接拿来用即可。P1OUT的取值就是在之间(二进制)。因此不管P1OUT原先的值是多少,P1OUT|=BIT0就是(设P1OUT原先的值为xxxxxxxx)xxxxxxxx跟进行或运算,最低位(即BIT0)的值肯定是1的,而对其它位没有影响。因此这样赋值后P1OUT=xxxxxxx1,达到了让BIT0位等于1(称为“置1”)的目的。P1OUT&=BIT0的结果呢?首先,这是一个“与”运算(英文称and),而“”在C中是一个求反的符号(即1变0,0变1),则BIT0=,也即把BIT0原来各位的值全求反了。那么不论P1OUT原来的值是多少,就假设为xxxxxxxx,则xxxxxxxx跟相“与”的结果就是xxxxxxx0,也就是BIT0这一位肯定是0的(称为“清0”或“置0”),而对其它位没有影响。P1OUT=BIT0呢?这称为“异或”运算(英文称为xor)(10=1,01=1,00=0,11=0),BIT0=,设P1OUT=xxxxxxx1,则(xxxxxxx1)()=xxxxxxx0;若P1OUT=xxxxxxx0,则(xxxxxxx0)()=xxxxxxx1,因此不管P1OUT的值原先是多少,经过P1OUT=BIT0的运算后,BIT0这一位总是取反的(原先是1变为0,原先是0变为1)。综上,P1OUT|=BIT0是赋1运算,P1OUT&=BIT0是赋0运算,P1OUT=BIT0是求反运算,而且都是只对BIT0位产生影响,对其它位是没有影响的(其它位还保留它们原先的值)。这是一种很好的处理方法,因为单片机的寄存器,其每一位都是有控制作用的,我们只改变我们想赋值的那一位,而对其它位不造成影响。当然其它一些赋值方法也是可以的,如:WDTCTL=WDTPW+WDTHOLD等。另外BIT6+BIT0是互不影响的,因为BIT6=,BIT0=,因此BIT6+BIT0=,各位之间互不干扰(或称为互不打架)。很多位相加也是同理:BIT7+BIT6+BIT3+BIT1=+=,各位互不影响。因此,可以有这样的赋值运算:P1DIR|=BIT6+BIT0,P1OUT&=(BIT7+BIT5+BIT1)等等。这些事情在许多单片机的书及资料中都有叙述,可能没有讲得这么细。例1:C程序的基本结构。图1的例子基本说明了C程序的基本结构,不论你的程序是大还是小,头文件、主函数总是需要的。注意图中红色的标注,可以帮助我们理解在程序中每条语句是怎样被执行的。图1. C程序的基本结构如果要打一个比较形象的比喻,上面这个小例子的基本结构就是通过while(1)语句让CPU在那儿不停地跑圈,一直不停地依次执行相同的任务:间隔地点亮红色和绿色LED。如果画一张图的话,应该像图2所示。图2. CPU在跑圈,周围有一些可以产生中断的事件,但CPU不一定理会例2:C程序的宏定义。图3示例了宏定义的方法,通过宏定义可以把许多操作和语句定义为有字面含义的名称,如图3程序中的Red_On_Green_Off,一读就知道是让红色LED点亮,绿色LED熄灭。有人说过:用C语言编程,无论变量、语句还是函数都是可以念出来并像让人听起来就懂的。因此给变量命名、函数命名、宏定义等,都要起一些一看就知其意的名字,读起来可以朗朗上口的,有助于程序的可读性和相互交流,也有助于你自己事后的修改和维护。图3. C程序中的宏定义和引用例3:C程序中函数的定义和调用。定义函数和调用函数是C语言最突出的特点之一,也最有挑战性,函数的英文名称是Function,字面意义上就是一个功能,数学里的函数不就是一个功能吗?,就是完成了一个功能,一个抛物线的功能。C语言中,通过定义函数完成相应的功能,并通过在主函数main( )中调用函数来实现其功能。在函数定义中还可以给函数传递参数(形参),函数中还可以有复杂的数学运算及逻辑处理。函数可以有返回值,也可以没有返回值(只完成某些操作)。在图4的示例中只定义了几个简单的函数,没有传递参数,没有复杂的数学或逻辑运算,也没有返回值。很多情况下,有些函数定义了,但在主函数中没有调用它,则这个函数是不起作用的。图4. 函数的定义和调用更为规范的做法是在主函数main( )前面先声明函数,然后在主函数的后面定义函数,可使整个程序的结构清晰、条理。例4:多文件管理和头文件。如果程序很大,则可以进行功能划分,把它模块化。如图5的工程中,有Blink_and_Delay.c, Blink_and_Delay.h, Lighting Two LEDs with Multi C files.c三个文档。图5. 多文档的管理图6. 文档1图7. 文档2图8. 文档3例5.中断及其处理。图9示例了中断及其中断服务程序的编写方法。图9. 中断及其处理,本例中运用了低功耗模式0中断是控制器或处理器为了快速响应及处理外部事件(如按键、触摸屏、AD转换结束、定时器计时到、以及与外部模块接口等等)而采取的一种应对机制。中断就是一种临时打断,如图2中所示,CPU本来在那儿跑圈,如果我们的程序中允许了某些中断并开启了总中断,则当这些中断事件发生时,CPU就会响应这些中断,也就是CPU先去处理这些中断事件(或称突发事件),处理完后,CPU再返回到原来的被中断点,继续处理它原来的事情。这个过程会涉及到PC(程序计数器)值的变化及SP(堆栈指针)的改变等等,不过PC和SP的变化在我们的C程序中是不可见的,或者说是我们不需要关心的,我们只要写好我们的C语言就一切OK了。以下各图示例了430的头文件中的宏定义,包括各寄存器的定义,寄存器中各位的定义等等。这些头文件中的内容千万不要动,不要改变,否则会很麻烦的。除非你很清楚你在干什么。(后面还有一些文字,请耐心看到最后一页)下列各图示例了.cmd文件中各寄存器地址的定义。实际上在CCS的编译环境下还包括了许多的头文件,包括数学运算,输入输出,等等。这些就是MSP430单片机C程序的大致框架。如此说来我们的C程序在那儿干什么?它就是在那里赋值和运算还有中断处理,当然这一切需要结合着单片机内的硬件(其内部资源)以及单片机外的硬件电路安排来进行,这就是软件+硬件。至于汇编,你先不要碰它好了,什么时候需要了,再去处理它也不迟。我们的任务已经很明确和简单了,我们手头上有许多寄存器和控制寄存器,寄存器内有很多位(0或者1),这些就是我们的操作对象,当我们把一切都设置好了以后,单片机里的千军万马就听从我们的调遣了。单片机的外面呢?同样的有千军万马!外面的电阻、电容、电感,二极管、三极管、场效应管、稳压器、模拟集成电路、数字集成电路、千奇百怪的传感器、通信模块、控制模块、LCD、按键、触摸键,上位机、下位机、USB、以太网、物联网 , 何止是千军万马,数不胜数啊。因此,我们才刚刚上路哎。希望这些能对大家能有所帮助,不妥之处敬请批评指正。 2014/5/21(简单修改)