原创 嵌入式C语言杂记

2008-2-17 15:00 3967 8 9 分类: MCU/ 嵌入式

C 语言模块化程序设计需理解如下概念:


1) 模块即是一个.c 文件和一个.h 文件的结合,头文件(.h)中是对于该模块接口的声明;


2) 某模块提供给其它模块调用的外部函数及数据需在.h 中文件中冠以extern 关键字声明;


3) 模块内的函数和全局变量需在.c 文件开头冠以static 关键字声明;


       (4)永远不要在.h 文件中定义变量!定义变量和声明变量的区别在于定义会产生内存分配的操作,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从其它模块寻找外部函数和变量。







单任务程序典型架构


        1)从CPU 复位时的指定地址开始执行;


        2)跳转至汇编代码startup 处执行;


        3)跳转至用户主程序main 执行,在main 中完成:


                        a.初试化各硬件设备;


                         b.初始化各软件模块;


                      c.进入死循环(无限循环),调用各模块的处理函数。


下面是几个"著名"的死循环:


         1)操作系统是死循环;


     (2WIN32 程序是死循环;


         3)嵌入式系统软件是死循环;


         4)多线程程序的线程处理函数是死循环。





中断是嵌入式系统中重要的组成部分,但是在标准C 中不包含中断。许多编译开发商在标准C 上增加了对中断的支持,提供新的关键字用于标示中断服务程序 (ISR) ,类似于__interrupt #program interrupt 等。当一个函数被定义为ISR 的时候,编译器会自动为该函数增加中断服务程序所需要的中断现场入栈和出栈代码。


中断服务程序需要满足如下要求:


    (1)不能返回值;


    (2)不能向ISR 传递参数;


    (3) ISR 应该尽可能的短小精悍;


    (4) printf(char * lpFormatString,…) 函数会带来重入和性能问题,不能在ISR 中采用。




函数指针首先要理解以下三个问题:


1C 语言中函数名直接对应于函数生成的指令代码在内存中的地址,因此函数名可以直接赋给指向函数的指针;


2)调用函数实际上等同于"调转指令+参数传递处理+回归位置入栈",本质上最核心的操作是将函数生成的目标代码的首地址赋给CPU PC 寄存器;


3)因为函数调用的本质是跳转到某一个地址单元的code 去执行,所以可以"调用"一个根本就不存在的函数实体。






请拿出你可以获得的任何一本大学《微型计算机原理》教材,书中讲到,186 CPU 启动后跳转至绝对地址0xFFFF0 (对应C 语言指针是0xF000FFF00xF000 为段地址,0xFFF0 为段内偏移)执行,请看下面的代码:


typedef void (*lpFunction)();/*定义一个无参数、无返回类型的函数指针类型*/


lpFunction lpReset = (lpFunction) 0xF000FFF0;/*定义一个函数指针,指向CPU启动后所执行第一条指令的位置*/


lpRest();/*调用函数*/



在以上的程序中,我们根本没有看到任何一个函数实体,但是我们却执行了这样的函数调用:lpReset() ,它实际上起到了"软重启"的作用,跳转到CPU 启动后第一条要执行的指令的位置。


记住:函数无它,唯指令集合耳;你可以调用一个没有函数体的函数,本质上只是换一个地址开始执行指令!


数组vs.动态申请


在嵌入式系统中动态内存申请存在比一般系统编程时更严格的要求,这是因为嵌入式系统的内存空间往往是十分有限的,不经意的内存泄露会很快导致系统的崩溃。


   所以一定要保证你的malloc free 成对出现,"谁申请,就由谁释放"原则。不满足这个原则,会导致代码的耦合度增大.



基本上,动态申请内存方式可以用较大的数组替换。对于编程新手,笔者推荐你尽量采用数组!嵌入式系统可以以博大的胸襟接收瑕疵,而无法"海纳"错误。毕竟,以最笨的方式苦练神功的郭靖胜过机智聪明却范政治错误走反革命道路的杨康。


给出原则:


         1)尽可能的选用数组,数组不能越界访问(真理越过一步就是谬误,数组越过界限就光荣地成全了一个混乱的嵌入式系统);


         2)如果使用动态申请,则申请后一定要判断是否申请成功了,并且malloc free 应成对出现!


掌握并深入理解关于数据指针、函数指针、动态申请内存、const volatile 关键字等的相关知识,是一个优秀的C 语言程序设计师的基本要求。当我们已经牢固掌握了上述技巧后,我们就已经学会了C 语言的99%,因为C 语言最精华的内涵皆在内存操作中体现。


我们之所以在嵌入式系统中使用C 语言进行程序设计,99%是因为其强大的内存操作能力!


如果你爱编程,请你爱C 语言;


如果你爱C 语言,请你爱指针;


如果你爱指针,请你爱指针的指针!


动画是无所谓有,无所谓无的,静止的画面走的路多了,也就成了动画。随着时间的变更,在屏幕上显示不同的静止画面,即是动画之本质。所以,在一个嵌入式系统的LCD 上欲显示动画,必须借助定时器。没有硬件或软件定时器的世界是无法想像的:


       1) 没有定时器,一个操作系统将无法进行时间片的轮转,于是无法进行多任务的调度,于是便不再成其为一个多任务操作系统;


       2) 没有定时器,一个多媒体播放软件将无法运作,因为它不知道何时应该切换到下一帧画面;


       3) 没有定时器,一个网络协议将无法运转,因为其无法获知何时包传输超时并重传之,无法在特定的时间完成特定的任务。



因此,没有定时器将意味着没有操作系统、没有网络、没有多媒体,这将是怎样的黑暗?所以,合理并灵活地使用各种定时器,是对一个软件人的最基本需求!

PARTNER CONTENT

文章评论1条评论)

登录后参与讨论

用户75997 2008-3-1 01:17

高明
相关推荐阅读
用户1163055 2008-05-21 20:02
【转】vista系统自带驱动备份误删恢复DriverStore\FileRepository办法
大家千万别为了优化系统而删去系统分区里 的驱动备份。否则会后悔的。我已经尝到了苦头。很多USB设备都不能用。现在我有解决办法,我已经用成功了。首先,下载imagex6000这个程序,大概就几百KB的大...
用户1163055 2008-02-05 15:47
【原】如何删除顽固的updspapi.dll
    几周前在D盘无缘预估出现了像9fdbfea32d98d91a4502dfeb2898f190的几个文件夹,里面都包含Update文件夹和updspapi.dll文件无论如何都删不掉,之前没在意...
用户1163055 2008-02-02 18:36
【原】优先级反转
总结一下优先级反转通俗说,就是高优先级的任务反而要等低优先级任务完成后才能进行。哈哈,见图:按邵教授的说法,UC/OS II具备动态优先级的功能,可是看到一位学长的博客中提到,他在面试过程中面试官提到...
用户1163055 2008-02-02 17:37
【原】可重入函数
        之前完全没有可重入函数的概念,可能是因为应付C语言考试,而没仔细看书吧,现在要好好补一补了。。。先来说说可重入函数        所谓的可重入,按我的理解,就是随时随地都可以被调用。无...
用户1163055 2008-02-01 13:57
【转】班门弄斧之移植心得(UC/OS)
在此我首先感谢在我学习UCOS过程中提供不少帮助的网上的各位大哥大姐,没有他们的支持和帮助,我很难这么快学会,甚至很可能在这烦躁的代码学习过程中放弃   移植UCOS之前,你首先应该做好三件事:   ...
EE直播间
更多
我要评论
1
8
关闭 站长推荐上一条 /3 下一条