Arduino:SPI读取ADC四字节数据组合为int32_t补码值出现异常结果,求排查思路
先分享几个大概率踩坑的点,你可以逐一排查验证:
1. 先确认原始读取的字节是否正确
很多时候拼接后的结果异常,根源是读取的单个字节本身就错了。建议你先把每次SPI传输的原始字节打印出来,对比ADC手册的预期格式:
int32_t readDataContinuous() { while (digitalRead(DRDYPin) == HIGH) {} digitalWrite(CSPin, LOW); SPI.beginTransaction(SPISettings(40000000, MSBFIRST, SPI_MODE1)); // 先读取单个字节并打印原始值 byte b0 = SPI.transfer(0x00); byte b1 = SPI.transfer(0x00); byte b2 = SPI.transfer(0x00); byte b3 = SPI.transfer(0x00); SerialBT.printf("Raw bytes: 0x%02X | 0x%02X | 0x%02X | 0x%02X\n", b0, b1, b2, b3); SPI.endTransaction(); digitalWrite(CSPin, HIGH); // 后续再测试拼接逻辑 int32_t resp = (int32_t)b0 << 24 | (int32_t)b1 << 16 | (int32_t)b2 << 8 | b3; SerialBT.println(resp); return resp; }
如果原始字节和手册描述不符,优先解决SPI通信问题,再处理拼接逻辑。
2. 字节顺序(端序)是否匹配ADC输出
你当前的逻辑是第一个读取的字节作为int32_t的最高位(MSB),但不少ADC的SPI输出是小端格式(先输出最低位字节),这种情况下拼接顺序完全反转,结果必然异常。
比如如果ADC是小端输出(先传LSB,最后传MSB),正确的拼接方式应该是:
int32_t resp = (int32_t)b3 << 24 | (int32_t)b2 << 16 | (int32_t)b1 << 8 | b0;
具体要对照ADC datasheet的「数据输出格式」章节,确认字节的传输顺序。
3. 有符号数的符号扩展是否正确
int32_t是有符号整数,当ADC输出的最高位(二进制)为1时,需要正确的符号扩展才能得到正确的负数。如果直接用无符号byte拼接,可能会出现符号位丢失的情况。
举个例子,如果ADC输出是24位有效数据+1位符号位填充,你可以这样处理:
// 假设b3是符号位字节,b2-b0是有效数据字节 int32_t resp = (int32_t)((b2 << 16) | (b1 << 8) | b0); // 符号位为1时,手动扩展高位 if (b3 & 0x80) { resp |= 0xFF000000; }
或者用union做类型转换,逻辑更直观(注意要匹配端序):
union ADCData { uint8_t bytes[4]; int32_t value; } adc_data; // 根据ADC的字节顺序赋值 adc_data.bytes[3] = b0; // 第一个读取的字节是MSB,放到union的最高位字节 adc_data.bytes[2] = b1; adc_data.bytes[1] = b2; adc_data.bytes[0] = b3; int32_t resp = adc_data.value;
4. SPI模式是否匹配ADC要求
你用的是SPI_MODE1(CPOL=0,CPHA=1),但不同ADC的SPI模式要求可能不同。一定要对照ADC datasheet,确认它的时钟极性(CPOL)和时钟相位(CPHA),模式不匹配会导致读取的字节全是乱码。
比如如果ADC要求SPI_MODE0,就把SPISettings里的模式改成SPI_MODE0试试。
5. CS引脚的时序是否符合ADC要求
有些ADC的时序要求是先拉低CS,再等待DRDY变低(CS拉低触发转换,DRDY变低表示转换完成),而你当前是先等DRDY再拉CS,这可能导致读取到旧数据或触发错误传输。
可以尝试调整时序:
int32_t readDataContinuous() { digitalWrite(CSPin, LOW); // 先拉低CS触发转换 while (digitalRead(DRDYPin) == HIGH) {} // 等待转换完成 SPI.beginTransaction(SPISettings(40000000, MSBFIRST, SPI_MODE1)); // ... 读取字节 ... SPI.endTransaction(); digitalWrite(CSPin, HIGH); // ... 后续处理 ... }
另外,之前注释掉的delayMicroseconds(100)可以先恢复,有时候ADC在DRDY变低后需要一点时间稳定数据,短延迟能避免读取到不稳定的字节。
内容的提问来源于stack exchange,提问作者CTO

