使用PIC16+CCS C通过I2C读取ADXL343数据异常求助
问题背景
使用PIC16微控制器、CCS C编译器,通过I2C协议读取ADXL343加速度计的X/Y/Z轴数据,Arduino测试可正常获取数值,但自制C代码始终输出X=65535、Y=65535、Z=65535。
相关代码片段
adxl343.h
enum { adxl343_IIC_ADDR1 = 0x53 , /* Normal Address */ adxl343_I2C_WRITE = 0xa6, adxl343_I2C_READ = 0xa7, };
main.c(原代码标注为main.h,应为笔误)
iic_power_enable(); delay_ms(1000); adxl343_write_reg(adxl343_IIC_ADDR1,adxl343_REG_POWER_CTL,0x08); delay_ms(2000); errorReadData=0; adxl343_t xyz; int i = 0; int8 lsb = 0; int8 msb = 0; int ack = 0; i2c_custom_start(); ack = i2c_custom_write(iic_addr | adxl343_I2C_WRITE); errorReadData+=i2c_ACK != ack; assert(i2c_ACK == ack); ack = i2c_custom_write(adxl343_OUT_X_MSB); errorReadData+=i2c_ACK != ack; assert(i2c_ACK == ack); i2c_custom_start(); ack = i2c_custom_write(iic_addr | adxl343_I2C_READ); errorReadData+=i2c_ACK != ack; assert(i2c_ACK == ack); msb = i2c_custom_read(TRUE); lsb = i2c_custom_read(TRUE); xyz.x = make16(lsb, msb); msb = i2c_custom_read(TRUE); lsb = i2c_custom_read(TRUE); xyz.y = make16(lsb, msb); msb = i2c_custom_read(TRUE); lsb = i2c_custom_read(FALSE); /* End register reading. */ xyz.z = make16(lsb, msb); i2c_custom_stop(); TRACE1("\r\nZ=%Lu", xyz.z); TRACE1("\r\nX=%Lu", xyz.x); TRACE1("\r\nY=%Lu", xyz.y);
i2c_custom.h
int i2c_custom_read(int ack) {return i2c_read(ack);} int i2c_custom_write(int8 data) {return i2c_write(data);} void i2c_custom_stop() {i2c_stop();}
核心问题排查
I2C地址拼接错误:
代码中用iic_addr | adxl343_I2C_WRITE拼接写地址,但adxl343_I2C_WRITE本身已是完整的I2C写地址(0x53左移1位得到0xa6),按位或操作会破坏正确地址。比如若iic_addr为0x53,0x53 | 0xa6 = 0xf7,完全偏离ADXL343的真实地址,导致通信失败,读取到默认的0xFF字节,组合后就是65535。应直接使用adxl343_I2C_WRITE或adxl343_IIC_ADDR1 << 1,而非按位或。数据字节顺序错误:
ADXL343的OUT_X_MSB是16位数据的高字节,OUT_X_LSB是低字节,正确的16位数值组合应为make16(msb, lsb),但代码中用了make16(lsb, msb),会导致字节反转。不过当前返回全0xFF,说明通信问题是主因,此错误为次要问题。iic_addr变量未明确定义:
代码中使用了iic_addr但未给出定义,若该变量不是0x53,会进一步加剧地址拼接错误,直接导致I2C通信无法建立。初始化配置缺失(次要):
仅设置POWER_CTL为测量模式(0x08),未配置DATA_FORMAT寄存器(如量程、数据对齐),但Arduino能正常读取说明硬件本身无问题,此配置缺失并非当前返回全65535的直接原因。
修复建议
修正I2C地址写法,将:
ack = i2c_custom_write(iic_addr | adxl343_I2C_WRITE); // ... ack = i2c_custom_write(iic_addr | adxl343_I2C_READ);改为:
ack = i2c_custom_write(adxl343_I2C_WRITE); // ... ack = i2c_custom_write(adxl343_I2C_READ);修正字节顺序,将
make16(lsb, msb)改为make16(msb, lsb)。若保留
iic_addr变量,应使用(iic_addr << 1)作为写地址,(iic_addr << 1) | 1作为读地址,而非直接按位或预定义的adxl343_I2C_WRITE/adxl343_I2C_READ。
内容的提问来源于stack exchange,提问作者Aizaz Afridi

