相信很多做嵌入式的朋友,做过一些项目后,会有一个瓶颈期,就是明明熟悉了C语言的所有语法,但拿到一个项目不知道如何下手,或者明明知道一个项目该怎么做,去设计代码时,却总会遇到一些逻辑上自相矛盾的地方。这时想要提升自己的代码能力,却又不知道学什么,心想C语言不就那么点东西吗?
如题:什么是好的嵌入式C代码? 其实语言只是一种工具,重要的是思想!在本文中,我将从代码结构和代码逻辑上来阐述。代码结构上将阐述分层设计的要点;逻辑上将从状态机、查表法、信号槽
、设计模式(23种)
、面向对象(封装、继承、多态)
、防御性编程
等思想,来解决我们经常遇到的逻辑冲突、减少重复代码、增加代码的观赏性和可读性。
如果当你听过很多大道理,依然写不好C代码,就来读读这篇文章吧!!!
分层设计是代码的
照妖镜
,我们通常可以通过它一眼看出一份代码的好坏。
嵌入式程序主要分为:物理层、功能接口层、应用层。
先看一个典型的分层设计模型:
通常一个项目涉及多人开发,每人负责不同层级。层级之间面临着前期接口的不透明和频繁更改,合并代码时难免存在冲突;代码合并后,在底层更改了接口名,必须人为地通知上层去调用,随着接口增多,合并代码的压力也越大。那么我们如何无视这些未知的冲突,进行快乐的编程呢?也就是说我们可以根本不关心对方接口的名字和实现方式,我们只需要在前期约定格式(对于一个函数接口,所谓的格式即:参数和返回值),各自去实现和调用即可。
来将函数和变量限制在本文件内,避免命名冲突
串口中断回调函数
。我们在编写驱动时,可以模仿这些芯片厂商,比如编写一个ADC驱动:
ADC.h
#ifndef _APP_ADC_H_
#define _APP_ADC_H_
typedef enum{
ADC1,
ADC2,
}ADC_ID_TYPEDEF;
typedef void (*adcGatherCallBack)(ADC_ID_TYPEDEF adc, uint32_t value);
void adc_callback_register(adcGatherCallBack adcCB);
#endif
注意:函数指针和注册函数都是在底层编写
ADC.c
static adcGatherCallBack adcGatherCB;
void adc_callback_register(adcGatherCallBack adcCB)
{
adcGatherCB = adcCB;
}
void adc_task(void)
{
while (1) {
uint32_t adc_reading = 0;
for (int i = 0; i < NO_OF_SAMPLES; i++) {
adc_reading += adc1_get_raw((adc1_channel_t)channel);
}
adc_reading /= NO_OF_SAMPLES;
uint32_t voltage = esp_adc_cal_raw_to_voltage(adc_reading, adc_chars);
adc_channel = ADC1;
adcGatherCB(adc_channel,voltage);//底层当成已有函数使用,在应用层可灵活定义回调函数实现具体操作
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
注意:adcGatherCB(adc_channel,voltage);
是一个adcGatherCallBack
类型的指针,不需要去实现,只需要将ADC通道和值传入,待应用层将回调函数注册进来,这个指针就指向回调函数,实现回调函数的实现。
APP.c(应用层)
/*ADC回调函数*/
static void adcCallBack(ADC_ID_TYPEDEF adc_channel,uint32_t value)
{
//电压值处理
switch(adc_channel)
{
case ADC1:
//显示电池电压
printf("Channel: ADC%d\tVoltage: %dmV\n", adc_channel, value);
break;
case ADC2:
//显示VBUS电压
break;
default:
break;
}
}
/*注册所有回调*/
static void app_callback_register()
{
adc_callback_register(adcCallBack);
}
/*主逻辑初始化*/
void app_init(void)
{
app_callback_register();
}
学会这三板斧,已经能解决很多常见问题,当大佬看到你代码时,会想:嗯,这小伙子还有点东西,但不多,是个可塑之才。
typedef struct
{
char *help_list;
uint8 permission; //操作权限。0:支持读写 1:只读
gpio_port gpio_port_idx;
uint8 pin_index;
Ifx_P *port;
uint8 default_state;
}GPIO_LIST;
GPIO_LIST gpio_listor[] =
{
// help_list permission port_idx pin Ifx_P * default_state
{
"MCU_LED_EN(RW)\t", GPIO_RW, port13, 0, &MODULE_P13 ,0 }, //MCU_RUN_LED
{
"MCU_CAN0_STB(RW)", GPIO_RW, port01, 4, &MODULE_P01 ,0 }, //CAN0_STB
{
"MCU_CAN1_STB(RW)", GPIO_RW, port01, 6, &MODULE_P01 ,0 }, //CAN1_STB
{
"MCU_CAN2_STB(RW)", GPIO_RW, port01, 7, &MODULE_P01 ,0 }, //CAN2_STB
{
"MCU_CAN3_STB(RW)", GPIO_RW, port00, 9, &MODULE_P00 ,0 }, //CAN3_STB
{
"MCU_CAN4_STB(RW)", GPIO_RW, port34, 3, &MODULE_P34 ,0 }, //CAN4_STB
{
"MCU_ETH_EN(RW)\t", GPIO_RW, port12, 0, &MODULE_P12 ,0 }, //ETH_PHY_EN
{
"MCU_PHY_RST(RW)\t", GPIO_RW, port11, 14, &MODULE_P11 ,0 }, //ETH_PHY_RST
{
"J2A_9296_EN1(RW)", GPIO_RW, port22, 7, &MODULE_P22 ,1 }, //CAM_EN1
{
"J2A_9296_EN2(RW)", GPIO_RW, port22, 8, &MODULE_P22 ,1 }, //CAM_EN2
{
"J2A_9296_EN3(RW)", GPIO_RW, port22, 9, &MODULE_P22 ,1 }, //CAM_EN3
{
"CAME_OFF(RW)\t", GPIO_RW, port33, 2, &MODULE_P33 ,0 }, //CAM_POW
};
#define GPIO_MAX_NUM sizeof(gpio_listor)/sizeof(GPIO_LIST)
void init_gpio(void)
{
for(uint8 i = 0; i < GPIO_MAX_NUM; i++)
{
if(gpio_listor[i].permission == GPIO_RW)
{
IfxPort_setPinMode(gpio_listor[i].port, gpio_listor[i].pin_index, IfxPort_Mode_outputPushPullGeneral);
IfxPort_setPinState(gpio_listor[i].port, gpio_listor[i].pin_index, gpio_listor[i].default_state);
}
}
}
不是一看到switch/case就是状态机,要有状态的切换。
假如有如下状态关系:
代码参考网上:
//定义枚举类型STATE_t表示状态机状态:
typedef enum{
STATE1 = 0,
STATE2,
STATE3,
STATE4,
}STATE_t;
//定义ACTION_MAP_t结构体类型,表示状态机状态属性:
typedef void (*STATE_ACTION)(void);
typedef struct ACTION_MAP
{
STATE_t stStateID;
STATE_ACTION EnterAct;
STATE_ACTION RunningAct;
STATE_ACTION ExitAct;
}ACTION_MAP_t;
//建立“动作”表:
void state1_entry(void)
{
printf("state1_entry\n");
}
void state1_do(void)
{
printf("state1_do\n");
}
void state1_exit(void)
{
printf("state1_exit\n");
}
void state2_entry(void)
{
printf("state2_entry\n");
}
void state2_do(void)
{
printf("state2_do\n");
}
void state2_exit(void)
{
printf("state1_exit\n");
}
void state3_entry(void)
{
printf("state3_entry\n");
}
void state3_do(void)
{
printf("state3_do\n");
}
void state3_exit(void)
{
printf("state3_exit\n");
}
void state4_entry(void)
{
printf("state4_entry\n");
}
void state4_do(void)
{
printf("state4_do\n");
}
void state4_exit(void)
{
printf("state4_exit\n");
}
ACTION_MAP_t actionMap[] =
{
{
STATE1, state1_entry, state1_do, state1_exit},
{
STATE2, state2_entry, state2_do, state2_exit},
{
STATE3, state3_entry, state3_do, state3_exit},
{
STATE4, state4_entry, state4_do, state4_exit},
};
//定义枚举类型EVENT_t表示事件:
typedef enum
{
EVENT1 = 0,
EVENT2,
EVENT3,
EVENT4,
EVENT5,
EVENT_MAP_END
}EVENT_t;
//定义EVENT_MAP_t结构体类型,表示事件表属性:
typedef struct EVENT_MAP
{
EVENT_t stEventID;
STATE_t stCurState;
STATE_t stNextState;
}EVENT_MAP_t;
//根据状态机流程图建立事件表:
EVENT_MAP_t eventMap[] =
{
{
EVENT1, STATE1, STATE2},
{
EVENT2, STATE2, STATE3},
{
EVENT3, STATE3, STATE4},
{
EVENT4, STATE4, STATE1},
{
EVENT5, STATE1, STATE4},
{
EVENT_MAP_END, 0, 0},
};
//定义状态机结构体类型:
typedef struct FSM
{
STATE_t stCurState;
STATE_t stNextState;
ACTION_MAP_t *pActionMap;
EVENT_MAP_t *pEventMap;
}FSM_t;
//定义状态机注册函数:
void fsm_init(FSM_t* pFsm,EVENT_MAP_t* pEventMap,ACTION_MAP_t *pActionMap)
{
pFsm->stCurState = 0;
pFsm->stNextState = EVENT_MAP_END;
pFsm->pEventMap = pEventMap;
pFsm->pActionMap = pActionMap;
}
//定义状态机转换函数:
void fsm_state_transfer(FSM_t* pFsm, EVENT_t stEventID)
{
uint8_t i = 0;
for(i=0; pFsm->pEventMap[i].stEventID<EVENT_MAP_END; i++)
{
if((stEventID == pFsm->pEventMap[i].stEventID)
&& (pFsm->stCurState == pFsm->pEventMap[i].stCurState))
{
pFsm->stNextState = pFsm->pEventMap[i].stNextState;
return;
}
}
}
//定义动作执行函数:
void action_perfrom(FSM_t* pFsm)
{
if(EVENT_MAP_END != pFsm->stNextState)
{
pFsm->pActionMap[pFsm->stCurState].ExitAct();
pFsm->pActionMap[pFsm->stNextState].EnterAct();
pFsm->stCurState = pFsm->stNextState;
pFsm->stNextState = EVENT_MAP_END;
}
else
{
pFsm->pActionMap[pFsm->stCurState].RunningAct();
}
}
//测试
int main(void)
{
int i = 0;
FSM_t stFsmWeather; //定义状态机
fsm_init(&stFsmWeather,eventMap,actionMap); //注册状态机
while(1)
{
usleep(10);
printf("i = %d\n",i++);
action_perfrom(&stFsmWeather);
//利用i产生EVENT1~EVENT5
if(0 == (i%11))
{
fsm_state_transfer(&stFsmWeather,EVENT1);
}
if(0 == (i%31))
{
fsm_state_transfer(&stFsmWeather,EVENT2);
}
if(0 == (i%74))
{
fsm_state_transfer(&stFsmWeather,EVENT3);
}
if(0 == (i%13))
{
fsm_state_transfer(&stFsmWeather,EVENT4);
}
if(0 == (i%19))
{
fsm_state_transfer(&stFsmWeather,EVENT5);
}
}
return 0;
}
信号槽的概念来自QT,我们可以用C语言模拟信号槽的机制。
代码参考网上:
#ifndef _SIMPLE_SIGNAL_SOLTS_H_
#define _SIMPLE_SIGNAL_SOLTS_H_
#include "string.h"
typedef void (*SIMPLE_SIGNAL)(void *signal, void *pArg);
typedef void (*SIMPLE_SOLTS) (void *pArg);
#define SIMPLE_SOLTS_T(FuncName) void(FuncName)(void *pArg)
#define SIMPLE_EMIT(signal, arg) if (signal != NULL)signal(&signal, arg)
#define SIMPLE_SIGNAL_SOLTS_MAX_SOLTS 10 //一个信号最多连接槽的数量
#define SIMPLE_SIGNAL_SOLTS_MAX_SIGNAL 10 //信号最多数量
ErrorStatus SimpleSignalSolts_Connect(SIMPLE_SIGNAL *signal, SIMPLE_SOLTS solts);
#endif
#include "SimpleSignalSolts.h"
#include
//信号结构
typedef struct
{
void *signleID; //信号的指针的指针,保存信号的指针,根据指针的地址确定是否是唯一
SIMPLE_SOLTS soltsTable[SIMPLE_SIGNAL_SOLTS_MAX_SOLTS];
uint8_t soltsCount;
}SIMPLE_SIGNAL_T;
//信号表结构
typedef struct
{
SIMPLE_SIGNAL_T signalTable[SIMPLE_SIGNAL_SOLTS_MAX_SIGNAL];
uint8_t signalCount;
}SIMPLE_SIGNAL_SOLTS_HANDLE_T;
SIMPLE_SIGNAL_SOLTS_HANDLE_T handle =
{
.signalCount = 0,
};
static void SimpleSignalSolts_Signal(void *signal, void *pArg)
{
uint8_t i, j;
SIMPLE_SOLTS solts;
for (i = 0; i < handle.signalCount; i++) //查找是否是同一个信号
{
if (handle.signalTable[i].signleID == signal) //这里注意,signleID保存的是指针的地址,
{
for (j = 0; j < handle.signalTable[i].soltsCount; j++)
{
solts = handle.signalTable[i].soltsTable[j];
if (solts != NULL)
{
solts(pArg);
}
}
}
}
}
/******************************************************************
* @函数说明: 连接信号与槽
* @输入参数: SIMPLE_SIGNAL *singnal 信号的指针(指针的指针)
SIMPLE_SOLTS solts 槽
* @输出参数: 无
* @返回参数: ErrorStatus
******************************************************************/
ErrorStatus SimpleSignalSolts_Connect(SIMPLE_SIGNAL *signal, SIMPLE_SOLTS solts)
{
if (signal == NULL || solts == NULL) //查空
{
return ERROR;
}
uint8_t i;
if (handle.signalCount > SIMPLE_SIGNAL_SOLTS_MAX_SIGNAL) //错误
{
handle.signalCount = 0