从C到SIMULINK: 字节/字偏移 + 位偏移实现故障与故障字保存操作

从C到SIMULINK: 字节/字偏移 + 位偏移实现故障与故障字保存操作

一、宏定义本身的含义

c 复制代码
#define EPROM_READ_ERROR_POS    0,WORD16_BIT0
#define EPROM_WRITE_ERROR_POS   0,WORD16_BIT1
#define FLASH_READ_ERROR_POS    0,WORD16_BIT2

这几个宏展开后,并不是普通的表达式,而是"两个参数"的组合:

  • EPROM_READ_ERROR_POS 展开后是 0,WORD16_BIT0
  • EPROM_WRITE_ERROR_POS 展开后是 0,WORD16_BIT1
  • FLASH_READ_ERROR_POS 展开后是 0,WORD16_BIT2
    也就是说:
  • 第一个参数是:0
  • 第二个参数是:WORD16_BIT0 / WORD16_BIT1 / WORD16_BIT2
    这种写法的"意义"通常不是单独用,而是作为"位置描述符",传给另外一个宏,用来做"位操作",例如:
c 复制代码
#define SET_ERROR_STATUS(reg, pos)   ((reg) |= (1U << (pos##_BIT)))

这只是示意,实际项目里会更复杂一点,但思想是一致的:用宏把"字节/字偏移 + 位偏移"组合在一起,描述一个状态位的位置。

二、为什么要这么写?核心思想是"坐标式"定位

在嵌入式代码中,经常会遇到这样的需求:

  • 某个寄存器或变量(比如 16 位的 status word)
  • 在这个字里,用不同的 bit 表示不同的错误或状态
    比如你这段名字看起来就是:
  • EPROM_READ_ERROR 在 status word 的 bit 0
  • EPROM_WRITE_ERROR 在 status word 的 bit 1
  • FLASH_READ_ERROR 在 status word 的 bit 2
    很多工程会用"坐标"的方式来描述一个位:
  • 第 0 个:第几个字(word index)
  • 第 1 个:该字中的第几位(bit index)
    所以:
  • EPROM_READ_ERROR_POS = (0号字, bit0)
  • EPROM_WRITE_ERROR_POS = (0号字, bit1)
  • FLASH_READ_ERROR_POS = (0号字, bit2)
    这就是你看到 0,WORD16_BIT0 这种写法的来源:两个参数分别表示:
  • 0 :第 0 个 16 位字(比如 status_word[0]
  • WORD16_BIT0:该字里的 bit 0
    注意:WORD16_BIT0 本身很可能也是一个宏,比如:
c 复制代码
#define WORD16_BIT0  0
#define WORD16_BIT1  1
#define WORD16_BIT2  2

它只是一个"位编号"的别名,增加代码可读性。


三、这些宏通常怎么用?

一般不会直接展开 0,WORD16_BIT0 这种逗号表达式去用,而是作为参数传给别的"统一操作宏"。

举例(示意):

假设有 16 位状态数组:

c 复制代码
uint16 ErrorStatus[1];

然后定义一个"设置某一位"的宏,接受 "word_index, bit_index":

c 复制代码
#define SET_ERROR_BIT(word_idx, bit_idx) \
    do { \
        ErrorStatus[(word_idx)] |= (1U << (bit_idx)); \
    } while (0)

那么你就可以这样写:

c 复制代码
SET_ERROR_BIT(EPROM_READ_ERROR_POS);   // 宏展开后:SET_ERROR_BIT(0,WORD16_BIT0)
SET_ERROR_BIT(EPROM_WRITE_ERROR_POS);  // 展开为:SET_ERROR_BIT(0,WORD16_BIT1)
SET_ERROR_BIT(FLASH_READ_ERROR_POS);   // 展开为:SET_ERROR_BIT(0,WORD16_BIT2)

有些工程甚至会把逗号表达式再包一层,比如:

c 复制代码
#define POS_TO_WORD_IDX(pos)   ( (pos), 0 )  // 仅仅是示意,实际会更复杂
#define POS_TO_BIT_IDX(pos)    ( (pos), 1 )  // 同样示意

但在你给的这段里,最简单的理解就是:

  • EPROM_READ_ERROR_POS 是"位置描述符"
  • 这个"位置"由两部分组成:字偏移 0 + 位偏移 WORD16_BIT0

四、为什么要分成两个字段,而不是直接写 bit 0?

这是为了支持"跨多个字"的状态字结构。比如:

  • 某个错误状态数组有 10 个字:
    • ErrorStatus[0]:bit 0~15 分别表示某些错误
    • ErrorStatus[1]:bit 0~15 表示另外一些错误
    • ...
      通过 (word_idx, bit_idx) 的组合,可以统一表示任意一个错误位,例如:
c 复制代码
#define SOME_ERROR_POS    1,WORD16_BIT5   // 第1个字,第5位

而你的这几个 EPROM/FLASH 错误目前都在第 0 个字里,所以第一个参数都是 0,后面是不同的 bit。

五、ARM 嵌入式开发的常见风格

  1. 寄存器/状态数组经常是"多字结构"
  2. 要统一通过 DEM(诊断事件管理)或 MemIf(内存接口)这类模块来设置/获取错误位
  3. 为了保持代码的可读性和可移植性,会做一层抽象:
    • 每个错误有一个"位置宏"(XXX_POS
    • 通用操作只关心"位置",不用知道具体是哪个 bit、哪个字
      好处是:
  • 将来如果你把某个错误从 word0 移到 word1,只要改 #define XXX_POS 1,WORD16_BITx
  • 所有使用这个错误位的地方都不用改

六、实际例子:可能的真实使用风格(伪代码)

一种可能的工程写法:

c 复制代码
/* 定义位索引 */
#define WORD16_BIT0  0
#define WORD16_BIT1  1
#define WORD16_BIT2  2
/* 定义错误位置(字索引, 位索引) */
#define EPROM_READ_ERROR_POS    0,WORD16_BIT0
#define EPROM_WRITE_ERROR_POS   0,WORD16_BIT1
#define FLASH_READ_ERROR_POS    0,WORD16_BIT2
/* 状态变量(16位数组) */
static uint16 ErrorStatus[1];
/* 设置某位为1 */
#define ERR_SET(word, bit) \
    ((ErrorStatus[(word)]) |= (1U << (bit)))
/* 清除某位 */
#define ERR_CLR(word, bit) \
    ((ErrorStatus[(word)]) &= ~(1U << (bit)))
/* 使用 */
ERR_SET(EPROM_READ_ERROR_POS);   // 设置 EPROM 读错误
ERR_CLR(EPROM_WRITE_ERROR_POS);  // 清除 EPROM 写错误

这样就很好理解:EPROM_READ_ERROR_POS 只是"位置坐标",ERR_SET 这个宏按坐标去操作对应的 bit。

#=# 七、总结一句话

  • 这些宏定义不是"普通表达式",而是"位置描述符":
    • 前面的 0 表示第几个 16 位字(word index)
    • 后面的 WORD16_BITx 表示该字中的第几位(bit index)
  • 通常不直接使用展开后的 0,WORD16_BIT0,而是传给一个"按坐标操作位"的通用宏,用来设置/清除错误状态位。
  • 这种写法在 ARM 嵌入式、AUTOSAR 风格代码里很常见,目的是提高抽象层次和可维护性。

最后

我们会在下一篇更新如何使用SIMULINK完成嵌入式故障字的实现,敬请期待!

相关推荐
Echo缘8 分钟前
关于在.cpp文件中包含c的头文件,编译报错问题
c语言·开发语言
人道领域12 分钟前
【零基础学java】(反射)
java·开发语言
ghie909020 分钟前
GPS抗干扰算法MATLAB实现
开发语言·算法·matlab
ytttr87328 分钟前
基于MATLAB解决车辆路径问题(VRP)
开发语言·matlab
我是海飞28 分钟前
杰理 AC792N WebSocket 客户端例程使用测试教程
c语言·python·单片机·websocket·网络协议·嵌入式·杰理
沛沛老爹29 分钟前
Web开发者突围AI战场:Agent Skills元工具性能优化实战指南——像优化Spring Boot一样提升AI吞吐量
java·开发语言·人工智能·spring boot·性能优化·架构·企业开发
一只爱学习的小鱼儿33 分钟前
在QT中使用饼状图进行数据分析
开发语言·qt·数据分析
项目題供诗1 小时前
C语言基础(三)
c语言·c++
亓才孓1 小时前
[认识异常和错误]java
java·开发语言
码农水水1 小时前
中国电网Java面试被问:流批一体架构的实现和状态管理
java·c语言·开发语言·面试·职场和发展·架构·kafka