嵌入式 C 语言编程总结
目录:
- 全局变量
1.全局变量
在纯 C 语言(Pure C)开发的嵌入式程序中,需要在多处用到同一个变量,需要注意几点:
- 不要在头文件中对变量进行定义
- 头文件中变量的声明添加关键字 extern
- 在相应的 .c 文件中对变量进行定义
这样,其它地方需要用到该全局变量时,仅需要包含 .h 头文件即可,不必再用 extern 声明变量。
example 1.1
在 .h 头文件中定义全局变量 value:
// filename: var.h
#ifndef VAR_H
#define VAR_H
extern const double value;
extern double varValue;
#endif
在 .c 文件中对 value 进行赋值(定义):
// filename: var.c
#include "var.h"
const double value = 1.0;
double varValue = 1.0;
在 main.c 中对使用 value 的值:
#include "var.h"
#include "stdio.h"
int main( int argc, char ** argv ) {
double v = 0.0;
v = value;
printf( "value is %.2f\n", v );
varValue = 2.0;
printf("varValue is %.2f\n", varValue);
return 0;
}
可以在控制台中看到输出
value is 1.00
varValue is 1.00
注意: 该例中定义的变量 value 为 const 类型,以防止在其它文件中对值进行修改,若需要其它文件能够修改值,则取消 const
关键字修饰(如 varValue)。
2.程序接口
在嵌入式开发中,经常需要选择与上位机进行通讯的协议(如 Uart,SPI 通讯协议),由于 C 语言里面并没有 CPP 中命名空间以及接口(纯虚函数)等的概念,
所以通常编写特定协议的代码时需要对不同协议中定义的函数接口进行区分,但在函数使用时,就需要用到特定协议的头文件中所声明的接口。
example 2.1
以串口初始化为例,可能就有这样的两个接口:
// uart initialization
void uartInit();
// spi initializaiton
void spiInit();
而后在主程序中需要调用串口初始化函数(这里假设这两种串口协议不会同时使用,否则的话情况就比较复杂):
#include "uart.h"
#include "spi.h"
int main( int argc, char ** argv ) {
uartInit();
// spiInit();
// do something else
}
在实际使用中,肯定会对串口通讯进行封装,提供许多 API,那么在使用 UART 协议的时候就需要把之前所有用到与 SPI 协议相关的
函数全部替换成 UART 相关的函数。为了简化函数的使用,可以利用 宏定义
,向主程序提供一个统一的接口。
为了定义统一的接口,引入新的头文件 com.h
:
#ifndef COM_H
#define COM_H
#define COM_UART
// #define COM_SPI
#if defined (COM_UART)
#include "uart.h"
#define comInit() uartInit()
// end of COM_UART
#elif defined (COM_SPI)
#include "spi.h"
#define comInit() spiInit()
// end of COM_SPI
#endif
#endif
这里我们认为 UART 的优先级最高,在定义了 COM_UART 之后就会使用 uart 通讯协议,不论是否定义过 COM_SPI 。
在主程序 main.c 中,我们可以调用 comInit() 对串口进行初始化:
#include "com.h"
int main( int argc, char** argv ) {
comInit();
return 0;
}
分别在 uart 和 spi 的实现文件里的初始化函数中加入打印字符串的功能:
// uart.c
#include "uart.h"
#include <stdio.h>
void uartInit() {
printf("UART Init\n");
}
// spi.c
#include "spi.h"
#include <stdio.h>
void spiInit() {
printf("SPI Init\n");
}
运行程序,可以在控制台中得到以下信息:
UART Init
注释掉 com.h 中对 COM_UART 的定义,取消 COM_SPI 的注释,再运行程序,可以看到:
SPI Init
通过宏定义,就可以为整个项目提供统一的串口函数接口。
程序接口的优缺点
优点:
- 统一了函数接口,方便程序迭代。
- 便于项目管理。
缺点:
尽管通过宏定义,可以方便的对外提供统一的函数接口,但是每当添加一个新的协议时,都需要在
com.h
头文件中添加新的条件分支和额外的宏定义。当串口协议出现了特殊的函数接口时,进行宏定义时需要考虑到这些特殊的情况
上述代码是假设 UART 协议和 SPI 协议是不会同时使用的(要么使用 UART 协议,要么使用 SPI 协议,考虑的情况较为简单)条件判断的复杂程度为 O(n);而实际上有些程序的接口中可能需要同时使用 A 实现,又要使用 B 实现,那么复杂程度将会变为 O(n^2)。
总而言之,具体项目需要具体分析,这里仅是给出一种简化代码使用的思路。