从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完成嵌入式故障字的实现,敬请期待!

相关推荐
星月心城2 小时前
八股文-JavaScript(第二天)
开发语言·javascript·ecmascript
m5655bj2 小时前
如何通过 C# 将 Markdown 转换为 PDF 文档
开发语言·pdf·c#
3824278272 小时前
python:yield用法
开发语言·python
梁下轻语的秋缘2 小时前
用 LoRa + W5500 做一个无线呼叫器
c语言·c++
wjs20242 小时前
WSDL 总结
开发语言
YoungHong19922 小时前
C++ 硬核基础:为什么函数重载不能只看返回值?
开发语言·c++
知识中的海王2 小时前
cloudflare email 邮箱混淆/加密/解密 PHP 源码版
开发语言·php
疑惑的杰瑞2 小时前
【C】函数与数组
c语言·开发语言·算法·可变参数
superman超哥2 小时前
仓颉内存分配优化深度解析
c语言·开发语言·c++·python·仓颉