Files
STM32F4-Base/Drivers/BSP/NETCFG/net_cfg.c
T

286 lines
8.8 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 模块名称:Network Configuration Core (配置核心)
* 模块功能:RAM 配置副本 + 工厂默认 + 加载/保存编排 + 实际 MAC 影子 +
* IPv4 字符串转换;磁盘格式 magic 'NCFG' + ver + CRC-16(MODBUS)
* + 18B 字段(两端小端一致,sizeof 含尾部对齐填充)
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
* 作者:王建锋
* 创建日期:2026-09-10
* 修改记录:
* v1.0 2026-09-10 王建锋 创建初始版本
*/
/* 头文件包含区 */
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include "net_cfg.h"
#include "net_cfg_store.h"
#define DBG_TAG "[NET_CFG]"
#include "dbg_log.h"
/* 私有宏定义区 */
#define NET_CFG_MAGIC 0x4647434Eu /* 'NCFG'(文件字节序 N,C,G,F;小端装载) */
#define NET_CFG_VER 1u /* 磁盘格式版本 */
/* 工厂默认值(单一来源:s_cfg 初值与 s_cfg_defaults 共用,避免两处漂移) */
#define NET_CFG_DEF_IP { 192u, 168u, 1u, 100u }
#define NET_CFG_DEF_MASK { 255u, 255u, 255u, 0u }
#define NET_CFG_DEF_GW { 192u, 168u, 1u, 1u }
#define NET_CFG_DEF_MAC { 0u, 0u, 0u, 0u, 0u, 0u }
/* 磁盘格式(两端布局一致:u32 4 对齐;字段区 18B 连续,CRC 覆盖之) */
typedef struct {
uint32_t magic; /* NET_CFG_MAGIC */
uint16_t ver; /* 格式版本 */
uint16_t crc; /* CRC-16/MODBUS(覆盖 ip..mac 共 18B) */
uint8_t ip[4];
uint8_t mask[4];
uint8_t gw[4];
uint8_t mac[6];
} net_cfg_blob_t;
/* 静态变量定义区 */
/* RAM 配置副本:以工厂默认值静态初始化(落 .data),自启动即有效——
* 不依赖 net_cfg_load 是否被调用(load 仅在文件有效时覆盖) */
static net_cfg_t s_cfg = {
.ip = NET_CFG_DEF_IP,
.mask = NET_CFG_DEF_MASK,
.gw = NET_CFG_DEF_GW,
.mac = NET_CFG_DEF_MAC,
};
static uint8_t s_active_mac[6]; /* 芯片实际 MAC 影子(netTask 回写) */
static uint8_t s_active_valid; /* 影子有效标志(0 = 尚未回写) */
static net_cfg_apply_cb_t s_apply_cb; /* "立即应用"平台回调(未注册 = 空操作) */
/* 函数定义区 */
/*
* 函数功能:CRC-16/MODBUS(无查表逐位算法)
* 入口参数:p_data - 数据指针 const uint8_t *
* len - 数据长度 size_t
* 返回值:CRC-16 值 uint16_t
* 限定条件:p_data 非空(len 可为 0,返回初始值 0xFFFF)
* 函数说明:文件私有(双端共享、避免跨模块依赖:模拟器不编译 App/util;
* 数据量 ≤32B,性能无关)
*/
static uint16_t s_crc16_modbus(const uint8_t *p_data, size_t len) {
uint16_t crc = 0xFFFFu;
for (size_t i = 0u; i < len; i++) {
crc = (uint16_t)(crc ^ (uint16_t)p_data[i]);
for (int b = 0; b < 8; b++) {
if (crc & 0x0001u) {
crc = (uint16_t)((crc >> 1) ^ 0xA001u);
}
else {
crc = (uint16_t)(crc >> 1);
}
}
}
return crc;
}
/*
* 函数功能:填充工厂默认配置
* 入口参数:p_cfg - 输出结构指针(不可为 NULL) net_cfg_t *
* 返回值:无
* 限定条件:p_cfg 非空
* 函数说明:与既有硬编码组网值一致(192.168.1.100/24,gw .1,MAC 全零
* = 芯片内置),保证首启/存储异常时行为与旧版本相同
*/
static void s_cfg_defaults(net_cfg_t *p_cfg) {
const net_cfg_t def = {
.ip = NET_CFG_DEF_IP,
.mask = NET_CFG_DEF_MASK,
.gw = NET_CFG_DEF_GW,
.mac = NET_CFG_DEF_MAC,
};
*p_cfg = def;
}
/*
* 函数功能:判断 6 字节 MAC 是否全零
* 入口参数:p_mac - MAC 指针(不可为 NULL) const uint8_t *
* 返回值:true - 全零;false - 非全零
* 限定条件:p_mac 非空
*/
static bool s_mac_is_zero(const uint8_t *p_mac) {
for (int i = 0; i < 6; i++) {
if (p_mac[i] != 0u) {
return false;
}
}
return true;
}
/*
* 函数功能:从存储加载网络配置到 RAM 副本
* 入口参数:无
* 返回值:恒 0(RAM 恒有效:文件缺失/CRC 错回退工厂默认) int
* 限定条件:MCU 端应在存储就绪后调用;未就绪时文件读失败自动回退
* 函数说明:见 net_cfg.h 同名函数说明
*/
int net_cfg_load(void) {
net_cfg_blob_t blob;
if (net_cfg_store_read((uint8_t *)&blob, sizeof(blob)) != 0) {
/* 文件缺失/不可读(首启、存储未就绪):工厂默认 */
s_cfg_defaults(&s_cfg);
DBG_INFO("load: no valid file, factory defaults");
return 0;
}
if (blob.magic != NET_CFG_MAGIC || blob.ver != NET_CFG_VER
|| s_crc16_modbus(blob.ip, 18u) != blob.crc) {
/* 格式损坏:工厂默认并留痕(不让坏配置带崩网络) */
s_cfg_defaults(&s_cfg);
DBG_ERROR("load: blob invalid (magic/ver/crc), factory defaults");
return 0;
}
memcpy(s_cfg.ip, blob.ip, 4u);
memcpy(s_cfg.mask, blob.mask, 4u);
memcpy(s_cfg.gw, blob.gw, 4u);
memcpy(s_cfg.mac, blob.mac, 6u);
DBG_INFO("load: OK ip=%u.%u.%u.%u",
(unsigned)s_cfg.ip[0], (unsigned)s_cfg.ip[1],
(unsigned)s_cfg.ip[2], (unsigned)s_cfg.ip[3]);
return 0;
}
/*
* 函数功能:保存当前 RAM 配置到存储
* 入口参数:无
* 返回值:0 - 成功;-1 - 写失败 int
* 限定条件:MCU 端应在存储就绪后调用
* 函数说明:组装磁盘格式经后端写盘;下次上电生效
*/
int net_cfg_save(void) {
net_cfg_blob_t blob;
memcpy(blob.ip, s_cfg.ip, 4u);
memcpy(blob.mask, s_cfg.mask, 4u);
memcpy(blob.gw, s_cfg.gw, 4u);
memcpy(blob.mac, s_cfg.mac, 6u);
blob.magic = NET_CFG_MAGIC;
blob.ver = NET_CFG_VER;
blob.crc = s_crc16_modbus(blob.ip, 18u);
if (net_cfg_store_write((const uint8_t *)&blob, sizeof(blob)) != 0) {
DBG_ERROR("save: store write FAILED");
return -1;
}
DBG_INFO("save: OK (file written)");
return 0;
}
/*
* 函数功能:注册"立即应用"回调
* 入口参数:cb - 回调函数(NULL = 注销) net_cfg_apply_cb_t
* 返回值:无
* 限定条件:应用启动早期注册一次即可
* 函数说明:见 net_cfg.h 同名函数说明
*/
void net_cfg_set_apply_cb(net_cfg_apply_cb_t cb) {
s_apply_cb = cb;
}
/*
* 函数功能:请求立即应用当前 RAM 配置
* 入口参数:无
* 返回值:无
* 限定条件:无(未注册回调时空操作)
* 函数说明:见 net_cfg.h 同名函数说明
*/
void net_cfg_request_apply(void) {
if (s_apply_cb != NULL) {
s_apply_cb();
}
}
/*
* 函数功能:取 RAM 配置副本(只读指针)
* 入口参数:无
* 返回值:RAM 配置副本指针(恒非空) const net_cfg_t *
* 限定条件:无
*/
const net_cfg_t *net_cfg_get(void) {
return &s_cfg;
}
/*
* 函数功能:更新 RAM 配置副本(不写盘)
* 入口参数:p_cfg - 新配置指针(不可为 NULL) const net_cfg_t *
* 返回值:无
* 限定条件:p_cfg 非空
*/
void net_cfg_set(const net_cfg_t *p_cfg) {
if (p_cfg == NULL) {
return;
}
s_cfg = *p_cfg;
}
/*
* 函数功能:回写芯片实际 MAC(影子区)
* 入口参数:p_mac - 芯片读回的实际 MAC(不可为 NULL) const uint8_t *
* 返回值:无
* 限定条件:net_init 成功后由 netTask 上下文调用
*/
void net_cfg_set_active_mac(const uint8_t *p_mac) {
if (p_mac == NULL) {
return;
}
memcpy(s_active_mac, p_mac, 6u);
s_active_valid = 1u;
}
/*
* 函数功能:取显示用 MAC
* 入口参数:p_out - 6 字节输出缓冲(不可为 NULL) uint8_t *
* 返回值:无
* 限定条件:p_out 非空
* 函数说明:存储非全零 → 存储值;否则芯片实际(已回写);否则全零
*/
void net_cfg_get_mac(uint8_t *p_out) {
if (p_out == NULL) {
return;
}
if (!s_mac_is_zero(s_cfg.mac)) {
memcpy(p_out, s_cfg.mac, 6u);
}
else if (s_active_valid != 0u) {
memcpy(p_out, s_active_mac, 6u);
}
else {
memset(p_out, 0, 6u);
}
}
/*
* 函数功能:4 字节 IPv4 格式化为点分十进制字符串(每段 3 位补零)
* 入口参数:p_ip - 4 字节 IP(不可为 NULL) const uint8_t *
* p_buf - 输出缓冲(不可为 NULL) char *
* len - 缓冲长度 size_t
* 返回值:无
* 限定条件:p_ip/p_buf 非空;len ≥ 16
* 函数说明:"192.168.001.100"(定长 11 字符,UI 编辑光标按固定偏移定位字节)
*/
void net_cfg_fmt_ipv4(const uint8_t *p_ip, char *p_buf, size_t len) {
if (p_ip == NULL || p_buf == NULL || len == 0u) {
return;
}
(void)snprintf(p_buf, len, "%03u.%03u.%03u.%03u",
(unsigned)p_ip[0], (unsigned)p_ip[1],
(unsigned)p_ip[2], (unsigned)p_ip[3]);
}