【0基础嵌入式学习日志】Day02:函数封装、结构体指针与传感器阈值判断
2026/6/26 2:08:52 网站建设 项目流程

一、前言

今天继续进行嵌入式 C 语言基础学习。Day01 主要完成了最基础的 C 工程结构搭建,包括 include、src、build 目录划分,头文件定义,GCC 编译和 GitHub 上传。

Day02 的学习重点是进一步把代码写得更像真实工程,包括函数封装、结构体指针传参、传感器数据模拟、故障阈值判断以及 Makefile 自动编译。

本次练习的目标不是写复杂功能,而是理解嵌入式软件中常见的基本流程:

系统初始化 → 传感器数据更新 → 故障判断 → 状态输出
二、Day02 工程结构

本次 Day02 工程目录如下:

day02
├── Makefile
├── include
│ ├── fault_code.h
│ └── system_type.h
├── src
│ └── main.c
└── build
└── day02_test

其中:

include:用于存放头文件;
src:用于存放源文件;
build:用于存放编译生成的可执行文件;
Makefile:用于自动执行 GCC 编译命令;
README.md:用于记录项目说明。

相比 Day01,Day02 不再只是简单地把所有代码都写在 main() 函数中,而是将不同功能拆分为多个函数,使代码结构更加清晰。

三、系统状态结构体 system_type.h

首先在 day02/include/system_type.h 中定义系统状态结构体:

#ifndefSYSTEM_TYPE_H#defineSYSTEM_TYPE_Htypedefstruct{intled_state;floatvoltage;floatcurrent;floattemperature;unsignedintfault_code;}SystemStatus;#endif

这里定义了一个结构体 SystemStatus,用于统一管理系统状态。

结构体中包含:

led_state:LED 状态;
voltage:传感器电压;
current:传感器电流;
temperature:传感器温度;
fault_code:系统故障码。

使用结构体的好处是可以将系统相关数据集中管理,而不是分散定义多个变量。例如:

SystemStatus sys;

这样 sys 就代表当前系统的整体状态。
四、故障码定义 fault_code.h

然后在 day02/include/fault_code.h 中定义故障码:

#ifndefFAULT_CODE_H#defineFAULT_CODE_H#defineFAULT_NONE0x0000#defineFAULT_LOW_VOLTAGE0x0001#defineFAULT_OVER_CURRENT0x0002#defineFAULT_OVER_TEMP0x0004#defineFAULT_SENSOR_ERROR0x0008#endif

这些故障码采用宏定义的方式表示。

其中:

FAULT_NONE 表示无故障;
FAULT_LOW_VOLTAGE 表示低电压故障;
FAULT_OVER_CURRENT 表示过电流故障;
FAULT_OVER_TEMP 表示过温故障;
FAULT_SENSOR_ERROR 表示传感器异常故障。

这里使用 0x0001、0x0002、0x0004 这类数值,是为了通过不同 bit 位表示不同故障状态。这样一个 fault_code 变量就可以同时记录多个故障。

例如:

fault_code = 0x0001 | 0x0002 | 0x0004;

最终结果为:

fault_code = 0x0007

表示同时存在低电压、过电流和过温故障。
五、主程序 main.c

Day02 的主程序放在 day02/src/main.c 中。

首先引入头文件:

#include<stdio.h>#include"system_type.h"#include"fault_code.h"

其中:

stdio.h 用于使用 printf() 输出信息;
system_type.h 用于使用 SystemStatus 结构体;
fault_code.h 用于使用故障码宏定义。
六、系统初始化函数

第一个函数是系统初始化函数:

voidsystem_init(SystemStatus*sys){sys->led_state=0;sys->voltage=12.5f;sys->current=1.2f;sys->temperature=35.6f;sys->fault_code=FAULT_NONE;}

该函数的作用是给系统状态赋初始值。

这里使用了结构体指针:

SystemStatus *sys

在函数内部通过:

sys->voltage

访问结构体成员。

需要注意的是:

结构体变量访问成员:使用 .
结构体指针访问成员:使用 ->

例如:

sys.voltage

用于普通结构体变量;

sys->voltage

用于结构体指针。
七、传感器数据更新函数

第二个函数是传感器数据更新函数:

voidsensor_update(SystemStatus*sys){sys->voltage=9.5f;sys->current=2.5f;sys->temperature=72.0f;}

该函数用于模拟传感器采集到的新数据。

这里故意设置了三个异常值:

电压 9.5V,低于阈值 10.0V;
电流 2.5A,高于阈值 2.0A;
温度 72.0℃,高于阈值 60.0℃。

这样后续故障检测函数就可以触发对应故障。
八、故障检测函数

第三个函数是故障检测函数:

voidfault_check(SystemStatus*sys){sys->fault_code=FAULT_NONE;if(sys->voltage<10.0f){sys->fault_code|=FAULT_LOW_VOLTAGE;}if(sys->current>2.0f){sys->fault_code|=FAULT_OVER_CURRENT;}if(sys->temperature>60.0f){sys->fault_code|=FAULT_OVER_TEMP;}}

该函数主要完成三个判断:

电压 < 10.0V → 低电压故障
电流 > 2.0A → 过电流故障
温度 > 60.0℃ → 过温故障

其中这句代码比较关键:

sys->fault_code |= FAULT_LOW_VOLTAGE;

|= 表示在原有故障码基础上增加一个故障位。

这样做的好处是可以让一个 fault_code 同时保存多个故障状态,而不是只能表示单一故障。
九、系统状态打印函数

第四个函数是系统状态打印函数:

voidsystem_print(constSystemStatus*sys){printf("LED state: %d\n",sys->led_state);printf("Voltage: %.2f V\n",sys->voltage);printf("current: %.2f A\n",sys->current);printf("Temperature: %.2f C\n",sys->temperature);printf("Fault code: 0x%04X\n",sys->fault_code);if(sys->fault_code&FAULT_LOW_VOLTAGE){printf("Fault: Low voltage\n");}if(sys->fault_code&FAULT_OVER_CURRENT){printf("Fault: Over current\n");}if(sys->fault_code&FAULT_OVER_TEMP){printf("Fault: Over temperature\n");}if(sys->fault_code==FAULT_NONE){printf("System normal\n");}}

该函数主要用于打印系统状态和具体故障信息。

这里使用:

sys->fault_code & FAULT_LOW_VOLTAGE

判断故障码中是否包含低电压故障。

需要注意:

|= 用于设置故障位
& 用于判断故障位

这两个符号是本次练习中非常重要的内容。
十、主函数流程

最后在 main() 函数中调用前面定义好的函数:

intmain(void){SystemStatus sys;system_init(&sys);sensor_update(&sys);fault_check(&sys);system_print(&sys);return0;}

整体流程如下:

  1. 定义系统状态变量 sys
  2. system_init(&sys):初始化系统状态
  3. sensor_update(&sys):模拟传感器数据更新
  4. fault_check(&sys):根据阈值判断故障
  5. system_print(&sys):打印系统状态和故障信息

这里的 &sys 表示取 sys 的地址,并传给函数。
十一、GCC 编译方式

一开始使用 GCC 手动编译:

gcc day02/src/main.c -Iday02/include-Wall-Wextra-oday02/build/day02_test

其中:

gcc:调用 GCC 编译器;
day02/src/main.c:指定源文件;
-Iday02/include:指定头文件目录;
-Wall:开启常见警告;
-Wextra:开启更多警告;
-o day02/build/day02_test:指定输出文件。

运行程序:

./day02/build/day02_test

十二、Makefile 自动编译

手动输入 GCC 命令比较长,因此进一步学习了 Makefile。

day02/Makefile 内容如下:

CC=gcc CFLAGS=-Wall-Wextra-Iinclude TARGET=build/day02_test SRC=src/main.c all:$(CC)$(SRC)$(CFLAGS)-o $(TARGET)run:./$(TARGET)clean:rm-f $(TARGET)

这里:

CC = gcc 表示使用 GCC 编译器;
CFLAGS 表示编译参数;
TARGET 表示生成的可执行文件;
SRC 表示源文件;
all 表示默认编译任务;
run 表示运行程序;
clean 表示清理编译结果。

进入 day02 目录后,可以直接执行:

make

完成编译。

执行:

makerun

运行程序。

执行:

makeclean

删除编译生成的可执行文件。
十三、运行结果

程序运行结果如下:

LED state:0Voltage:9.50V current:2.50A Temperature:72.00C Fault code: 0x0007 Fault: Low voltage Fault: Over current Fault: Over temperature

可以看到,程序成功检测到了三个故障:

低电压故障
过电流故障
过温故障

最终故障码为:

0x0007

其含义为:

0x0001 | 0x0002 | 0x0004 = 0x0007

说明多个故障可以通过一个变量同时表示。
十四、遇到的问题和解决方法

  1. 变量名拼写错误

编译过程中遇到过类似错误:

SystemStatus has no member named ‘failt_code’

原因是把:

fault_code

误写成了:

failt_code

C 语言中变量名必须完全一致,一个字母写错都会导致编译失败。修改拼写后,程序可以正常编译。

  1. Makefile 找不到

一开始在工程根目录直接执行:

make

出现错误:

make: *** No targets specified and no makefile found. Stop.

原因是 Makefile 放在 day02 文件夹中,而当时终端所在位置是工程根目录。

解决方法是先进入 day02 目录:

cd /root/Embedded_14Days/day02

然后再执行:

makemakerun

最终成功完成自动编译和运行。
十六、项目源码

本次 Day02 学习代码已上传至 GitHub:

https://github.com/jdai10590-afk/Embedded-C-Learning-Projects/tree/main/day02

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询