我在嵌入式C中做了一些工作,加速度计将数据作为14位2的补码数返回.我将这个结果直接存储到一个uint16_t
.稍后在我的代码中,我试图将这个"原始"形式的数据转换为有符号整数,以表示/使用我的其余代码.
我无法让编译器了解我想要做什么.在下面的代码中,我正在检查第14位是否已设置(意味着数字为负),然后我想反转这些位并加1以获得数字的大小.
int16_t fxls8471qr1_convert_raw_accel_to_mag(uint16_t raw, enum fxls8471qr1_fs_range range) { int16_t raw_signed; if(raw & _14BIT_SIGN_MASK) { // Convert 14 bit 2's complement to 16 bit 2's complement raw |= (1 << 15) | (1 << 14); // 2's complement extension raw_signed = -(~raw + 1); } else { raw_signed = raw; } uint16_t divisor; if(range == FXLS8471QR1_FS_RANGE_2G) { divisor = FS_DIV_2G; } else if(range == FXLS8471QR1_FS_RANGE_4G) { divisor = FS_DIV_4G; } else { divisor = FS_DIV_8G; } return ((int32_t)raw_signed * RAW_SCALE_FACTOR) / divisor; }
遗憾的是,此代码不起作用.反汇编告诉我,由于某种原因,编译器正在优化我的语句raw_signed = -(~raw + 1);
如何实现我想要的结果?
数学运算在纸上,但我觉得由于某种原因,编译器正在与我战斗:(.
将14位2的补码值转换为16位有符号,同时保持该值只是以下因素:
int16_t accel = (int16_t)(raw << 2) / 4 ;
左移将符号位推入16位符号位位置,除以4会恢复幅度但保持其符号.除法避免了右移的实现定义行为,但通常会在允许的指令集上产生单个算术移位权.强制转换是必要的,因为它raw << 2
是一个int
表达式,除非int
是16位,否则除法将简单地恢复原始值.
然而,将加速度计数据左移两位并将其视为传感器首先是16位就更简单了.将所有内容归一化到16位有一个好处,即如果你使用一个高达16位的任意位数的传感器,代码就不需要改变.幅度将只是四倍,最不重要的两位将为零 - 没有信息获得或丢失,并且在任何情况下缩放都是任意的.
int16_t accel = raw << 2 ;
在这两种情况下,如果您想要无符号幅度,那么这只是:
int32_t mag = (int32_t)labs( (int)accel ) ;
我会做简单的算术.结果是14位有符号,表示为0到2 ^ 14-1的数字.测试数字是否为2 ^ 13或更高(表示否定)然后减去2 ^ 14.
int16_t fxls8471qr1_convert_raw_accel_to_mag(uint16_t raw, enum fxls8471qr1_fs_range range) { int16_t raw_signed = raw; if(raw_signed >= 1 << 13) { raw_signed -= 1 << 14; } uint16_t divisor; if(range == FXLS8471QR1_FS_RANGE_2G) { divisor = FS_DIV_2G; } else if(range == FXLS8471QR1_FS_RANGE_4G) { divisor = FS_DIV_4G; } else { divisor = FS_DIV_8G; } return ((int32_t)raw_signed * RAW_SCALE_FACTOR) / divisor; }
请检查我的算术.(我有13和14正确吗?)