在嵌入式程序开发过程中,如果逻辑直接依赖于某传感器或某计算函数的返回值,极易造成代码高耦合,不利于我们对一个程序进行维护与修改,所以通过合理的解耦降低耦合度是开发中非常重要的一环,我个人在开发中常用的解耦方式如下。
在裸机编程(Bare-Metal)下,最常用的方式是状态码+返回值的处理形式,通过函数返回状态码,传入输出类型变量指针的形式获取数据,示例如下:
typedef enum {
Sensor_RET_OK = 0,
Sensor_RET_ERR,
Sensor_RET_TIMEOUT,
...
} Sensor_RET_Typedef;
// 读取传感器数据
Sensor_RET_Typedef sensor_read(float* value){
if (value == NULL){
return Sensor_RET_ERR;
}
if (/*超时逻辑*/){
return Sensor_RET_TIMEOUT;
}
/* 传感器读取逻辑 */ *value = 7.26f;
return Sensor_RET_OK;
}
// 调用数据
float temperature;
if (sensor_read(&temperature) == Sensor_RET_OK) {
std::cout << temperature << std::endl;
}
适用场景:
同步调用
驱动接口
返回多个状态类型
优点是状态与业务数据分离,接口清晰,当函数需要返回多个数据时,可以使用结构体对数据进行包装,便于读取数据。
typedef struct {
float temperature;
float humidity;
uint32_t timestamp;
} SensorData;
RetCode sensor_read(SensorData *data)
{
if (data == NULL) {
return RET_ERROR;
}
data->temperature = 25.5f;
data->humidity = 60.0f;
data->timestamp = get_tick();
return RET_OK;
}
在使用实时操作系统(RTOS)的情况下,为了防止多任务可能产生的变量竞争等不可靠情况,我们大多使用消息队列形式进行返回值的处理,以使用FreeRTOS的API演示如下:
typedef struct {
uint8_t id;
int value;
} Message;
QueueHandle_t message_queue;
/* 生产者任务 */
void producer_task(void *parameter)
{
Message message;
for (;;) {
message.id = 1;
message.value = 100;
/* 向队列发送消息 */
xQueueSend(
message_queue,
&message,
portMAX_DELAY
);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
/* 消费者任务 */
void consumer_task(void *parameter)
{
Message message;
for (;;) {
/* 等待并读取消息 */
if (xQueueReceive(
message_queue,
&message,
portMAX_DELAY) == pdPASS) {
/* 消费消息 */
process_message(message.id, message.value);
}
}
}
除了裸机常用的状态码+返回值与RTOS常用的消息队列方式,还有一种同时适用于两者,但需要更多项目工程化构建的方法——发布/订阅模式,在此不做论述。