芯片型号
STM32G070
ARM cortex-m0
问题描述。
modbus 协议栈相关数据结构都是按照1字节对齐
故障代码
#pragma pack(1)
typedef struct
{
uint8_t slave_id;
uint8_t func_code;
uint16_t base_reg; // Little endian
uint16_t reg_num; // Little endian
int16_t *reg_value; // Little endian
} bModbusCbParm_t;
typedef struct
{
uint8_t addr;
uint8_t func;
uint8_t len;
uint16_t param[MY_DEVICE_MODBUS_REG_NUM];
} bModbusMasterSendReadRegsAck_t;
#pragma pack()
static int _bModbusRTUMasterParse(void *attr, uint8_t *in, uint16_t i_len, uint8_t *out,
uint16_t o_len)
{
bModbusCbParm_t param;
bModbusMasterSendReadRegsAck_t *r_ack = (bModbusMasterSendReadRegsAck_t *)in;
param.reg_value = (uint16_t *)r_ack->param; //这里会出错
}
问题分析
一个地址未对齐引起的 HardFault 异常 - STM32团队 ST意法半导体中文论坛 (stmicroelectronics.cn)
单片机(MCU)如何才能不死机之对齐访问(Aligned Access)-阿里云开发者社区 (aliyun.com)
解决方法
#define B_ALIGN_TO_2_BYTES(addr) (((uint32_t)(addr) + 1) & ~((uint32_t)1))
param.reg_value = (uint16_t *)B_ALIGN_TO_2_BYTES(r_ack->param);//按照内存分配规则,转换成双字节对齐。
以下是对这段代码的分析:
一、代码功能
这段 C/C++ 宏定义的作用是将给定的地址addr
对齐到 2 字节边界。
二、具体解释
-
((uint32_t)(addr) + 1)
:- 首先将输入地址
addr
强制转换为无符号 32 位整数类型。 - 然后对其加 1。这样做的目的是确保无论原始地址是奇数还是偶数,都能在下一步的按位与操作中向最近的偶数地址靠拢。
- 首先将输入地址
-
& ~((uint32_t)1)
:(uint32_t)1
表示无符号 32 位整数的数值 1。- 对其取反后,其二进制形式除了最低位为 0 外,其他位全为 1。
- 再与前面调整后的地址进行按位与操作,这样就会将地址的最低位强制设置为 0,从而实现对齐到偶数地址,也就是 2 字节边界。
例如,如果输入地址是奇数,如0x1001
,经过这个宏处理后会变为0x1002
,实现了对齐到 2 字节边界。如果输入地址已经是偶数,则保持不变。