关闭

嵌入式系统C语言编程的心得体会(宏应用篇)

773人阅读 评论(0) 收藏 举报
分类:
在C语言里面宏定义的应用是很灵活的,但是一般讲C语言的书往往并不详细讲述宏的应用,只是简单提一下。
宏的应用是程序模块化,通用化一个重要手段。嵌入式系统做多了就会发现其实没给项目应用的底层函数都十分相似,甚至是完全一样,工作方式也很固定。为了能简化编程工作量,通常都是从以往做的项目中将有用的函数先考到一起在做整理,不要的部分就删掉,这样一来,程序看起来似乎一样,但是又有所不同,删来改去的,常常是改了这里,漏了那里的程序调试和维护起来也很不方便。但是使用宏定义的预编译命令就可以很好的解决这样的问题,在原则篇里面讲的程序分三层来编写,HAL层里就是和芯片相关的驱动程序,在不同场合下我们可能使用中断方式访问器件可以可能用查询方式来处理,但是不论使用什么方式只是除非方式不同而已,处理却是一样的。处于这种考虑采用宏定义的选择编译方式就可以很轻松的改变使用触发方式,而且经过测试后沉淀下来的函数语句可靠性比较高,不容易出错,具体做法后面在慢慢解释。说了这么多无非是想说明用好宏的好处,引起重视,是合理偷懒的有效手段。
    接下来要仔细说怎么用了喔:)
    (一)#include 
        包含命令这个大家最熟悉了,写嵌入式C程序一定要用的,不用不行。但是熟悉的东西还是有些地方也许并不熟悉。那就是#include<> 和#include""的区别。有些编译器对这个两个用法并不敏感,但是我遇到的也就我们教研室用的所有器件C编译器都对这个敏感。没有处理来就编译就会通不过。
        <>表示括号里面的文件在编译器默认放置文件的地方搜索包含文件
        ""表示引号里面的文件在当前文件的相同位置处搜索包含文件
        当使用<>时,文件又不在编译器初始默认位置时,要记得手动修改编译器的包含路径。通常在工程设置里面有此选项。要注意的是这个针对工程的,新建工程时要在修改一次。使用""时要建被包含文件和包含文件放在同个目录下
    (二)#define,#nudef,#ifndef,#ifdef,#endif
        前面两个可以单独用,后面#ifndef,#ifdef要和#endif成对使用,错编译也会通不过,而且出现的错误提示比较怪异,这时要认真检查一下这里是否出问题了。
       #define是定义,可以定义常量,可以定义函数,还可以定义编译条件。
       #nudef是不定义,是为了实现互斥才使用的
       #ifndef,#ifdef,#endif条件编译,和条件定义的关注命令
    (三)宏函数的应用
       宏函数长得很像函数,但它不是函数。和函数最大的区别就在于,不能返回值,所以操作的输入和输出参数都要在定义里面申明。而且用宏定义的宏函数是一段代码在使用的时候和一般函数几乎一样,但是在编译和运行的时候就有很大的差别。在编译的时候宏函数完成的是替换,函数则是增加跳转指令和堆栈处理指令。在运行的时候宏函数不占用堆栈和出栈时间,而函数要先进行跳转在堆栈,运行函数内容,出栈在跳转。用时较多,因此在函数内容很少,且操作很简单的情况下应多用宏函数,这样可以提高程序的执行效率。这在以后讲程序优化的时候在说了。
       宏函数怎么定义?
       1、简单的单行宏函数定义
          格式:#define 名称(参数) 字符串
          例1:#define ISLOCK  (PIND&0x08)>>3
               解释:使用ISLOCK表示(PIND&0x08)>>3,目的只是便于阅读,因为读程序的时候总是拿着电路图对照是很麻烦的事情。以后改设计的时候只要调整(PIND&0x08)>>3就行了,不用在程序里翻找相关操作,这就降低的调试的难度,也提高了软件的可移植性
               注:当字符串采用{}括起来的时候里面好像是要加分号,有的能通过有的不能,这个还没搞清楚

          例2: #define YLED_OE(x)    {DDRD=(x) ? DDRD|0x10 : DDRD&~0x10;}
               解释:这里定义一个宏函数YLED_OE来使能黄色LED操作,内含就是改变IO端口成为输出状态,改变的只是DDR响应的位,采用函数就显得程序编译起来很臃肿,自己采用给DDR寄存器幅值不利用程序阅读和日后移植,因此采用宏,看起来想函数好理解,当位0时使能执行冒号前面的语句,位1时不使能,执行冒号后面的语句,(来源:参看唐浩强编写的C语言设计里面相关内容的经典例子#define MAX(x,y) (x)>(y)? (x):(y))
          
       2、较为复杂的多行宏函数定义
          格式:#define 名称(参数) do{         \
                                        语句1  \
                                        语句2  \
                                         ...
                                        语句n  \ 
                                     }while(0) 
          例1:#define exInt(num, op)   do{ \
       if(op==1)  \
                                              EIMSK |= (1<<num); \
       else                 \
                                           EIMSK &= ~(1<<num);\
                         }while(0)
               解释:这是个外部中断允许操作宏函数,这里判断op参数,这里的判断和前面的方法不一样了,这里采用的是和正常C语句一样的方式,判断成功后执行响应操作,在嵌入系统C编程里面采用1<<num的方式加上位逻辑运算来进行位操作是很常见的,也是效率很高的做法,以后一起总结。
               注:这里只是抛砖引玉,这样的做法并不见得最合理,只是提供个参考。
    (四)条件编译的应用
        例1:#define INCLUDE_UART0       /*使用串口0*/
             /*#undef INCLUDE_UART0      /\*不使用串口0*/

             #ifdef INCLUDE_UART0
             #define UART0_EN           1         /*为1时使用串口0,为0时不使用*/
             #undef  UART0_BAUD_RATE
             #define UART0_BAUD_RATE    19200     /*串口0波特率*/
             #undef  UART0_TX_BUF_SIZE
             #define UART0_TX_BUF_SIZE  100       /*串口发送缓存区长度(字节)0~255*/
             #undef  UART0_RX_BUF_SIZE
             #define UART0_RX_BUF_SIZE  100       /*串口接收缓存区长度(字节)0~255*/
             #endif
       例2:#if UART0_EN
            void uart0_Init(void)    
            {            
              Uart0TxCount = 0;                  /* Clear number of characters to send*/
              Uart0TxRdPtr = 0;                  /* Initialize transmit buffer read pointer*/
              Uart0TxWrPtr = 0;                  /* Initialize transmit buffer write pointer*/

              UCSR0B = (1<<RXCIE)|(1<<RXEN)|(1<<TXEN);  /* 设置收发使能,接受中断允许*/
              UCSR0C = (3<<UCSZ0);           /* 数据格式 8位+1位停止位,无效验*/ 

              UBRR0L=((SYS_CLK_RATE/16/UART0_BAUD_RATE)-1)%256;  /* 设置波特率寄存器低字节 */
              UBRR0H=((SYS_CLK_RATE/16/UART0_BAUD_RATE)-1)/256;  /* 设置波特率寄存器高字节 */
  
           }
           #endif
           这个比较简单,就不解释了
    (五)要牢记的几点
        1、宏是替换关系
        2、宏只在编译时起作用
        3、当函数内容很简单时采用用宏
        4、宏可以作为HAL层向上层程序提供操作功能
        5、条件编译要仔细检测互斥关系
        6、想到什么好办法记得告诉我,呵呵
        7、遇到编译不通过要查C语言的书,确认用法正确
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    最新评论