基于MSP432G2553与HMC5883L的I2C通信数据读取问题求助
排查HMC5883L与MSP432G2553 I2C通信无数据问题
我明白你现在卡在I2C读HMC5883L数据这一步了,而且还踩了个大坑——代码里用的是MSP430的头文件,但实际硬件是MSP432,这俩芯片的外设寄存器完全不一样,旧代码肯定跑不起来!咱们一步步从硬件到软件排查,搞定这个问题。
一、先把硬件连接搞对,这是基础
- 供电要匹配:HMC5883L只能用3.3V供电,别接成5V烧了模块;MSP432的IO也是3.3V兼容,这点要注意。
- I2C引脚别接错:MSP432G2553的默认I2C引脚是P1.6(SCL)和P1.7(SDA),一定要对应接HMC5883L的SCL、SDA引脚。
- 别漏上拉电阻:I2C总线必须给SCL和SDA各接一个4.7kΩ左右的上拉电阻到3.3V,没这个电阻总线根本没法传输信号,很多新手都会忽略这点。
- 确认设备地址:HMC5883L的7位I2C地址是
0x1E(写操作地址是0x3C,读是0x3D),但这个地址会随ADDR引脚变化:接GND是0x1E,接VCC是0x1C,悬空是0x1D,别搞混了。
二、适配MSP432的I2C代码修正
下面给你一套能直接用的基础代码,每部分都标了关键说明,你可以直接替换旧代码:
1. I2C和GPIO初始化
#include "msp.h" #include <stdint.h> #define HMC5883L_ADDR 0x1E // HMC5883L的7位I2C地址 void I2C_Init(void) { // 配置P1.6(SCL)、P1.7(SDA)为I2C功能引脚 P1->SEL0 |= BIT6 | BIT7; P1->SEL1 &= ~(BIT6 | BIT7); // 复位I2C模块 EUSCI_B0->CTLW0 |= EUSCI_B_CTLW0_SWRST; // 配置主模式、SMCLK时钟源、7位地址、自动停止 EUSCI_B0->CTLW0 = EUSCI_B_CTLW0_SWRST | EUSCI_B_CTLW0_MODE_3 | // 启用I2C模式 EUSCI_B_CTLW0_MST | // 主设备模式 EUSCI_B_CTLW0_SYNC | // 同步通信 EUSCI_B_CTLW0_SSEL__SMCLK; // 时钟源选SMCLK(默认3MHz) // 设置I2C时钟为100kHz:3MHz / 30 = 100kHz EUSCI_B0->BRW = 30; // 释放复位,启动I2C模块 EUSCI_B0->CTLW0 &= ~EUSCI_B_CTLW0_SWRST; // 清除中断标志,避免干扰 EUSCI_B0->IFG &= ~(EUSCI_B_IFG_TXIFG | EUSCI_B_IFG_RXIFG); }
2. 向HMC5883L写入配置寄存器
HMC5883L需要先配置才能输出数据,比如设置测量模式、增益:
void HMC5883L_WriteReg(uint8_t reg, uint8_t data) { // 等待I2C总线空闲 while (EUSCI_B0->CTLW0 & EUSCI_B_CTLW0_TXSTP); // 发送起始信号+设备写地址 EUSCI_B0->I2CSA = HMC5883L_ADDR; EUSCI_B0->CTLW0 |= EUSCI_B_CTLW0_TR | EUSCI_B_CTLW0_TXSTT; // 等待发送缓冲区就绪 while (!(EUSCI_B0->IFG & EUSCI_B_IFG_TXIFG)); // 发送目标寄存器地址 EUSCI_B0->TXBUF = reg; while (!(EUSCI_B0->IFG & EUSCI_B_IFG_TXIFG)); // 发送配置数据 EUSCI_B0->TXBUF = data; while (!(EUSCI_B0->IFG & EUSCI_B_IFG_TXIFG)); // 发送停止信号 EUSCI_B0->CTLW0 |= EUSCI_B_CTLW0_TXSTP; while (EUSCI_B0->CTLW0 & EUSCI_B_CTLW0_TXSTP); }
3. 读取三轴数据
注意!HMC5883L的寄存器顺序是X高、X低、Z高、Z低、Y高、Y低,不是常规的X-Y-Z,别搞反了:
void HMC5883L_ReadData(int16_t *x, int16_t *y, int16_t *z) { uint8_t buf[6]; // 先指定要读取的起始寄存器(0x03是X轴高位寄存器) HMC5883L_WriteReg(0x03, 0x00); // 等待总线空闲 while (EUSCI_B0->CTLW0 & EUSCI_B_CTLW0_TXSTP); // 发送起始信号+设备读地址 EUSCI_B0->I2CSA = HMC5883L_ADDR; EUSCI_B0->CTLW0 &= ~EUSCI_B_CTLW0_TR; // 切换到接收模式 EUSCI_B0->CTLW0 |= EUSCI_B_CTLW0_TXSTT; // 连续读取6个字节数据 for (int i = 0; i < 6; i++) { // 等待接收缓冲区就绪 while (!(EUSCI_B0->IFG & EUSCI_B_IFG_RXIFG)); buf[i] = EUSCI_B0->RXBUF; // 最后一个字节读取前发送停止信号 if (i == 5) { EUSCI_B0->CTLW0 |= EUSCI_B_CTLW0_TXSTP; } } // 组合成16位带符号数据 *x = (buf[0] << 8) | buf[1]; *z = (buf[2] << 8) | buf[3]; *y = (buf[4] << 8) | buf[5]; }
4. 主函数示例
int main(void) { // 关闭看门狗 WDT_A->CTL = WDT_A_CTL_PW | WDT_A_CTL_HOLD; // 初始化I2C I2C_Init(); // 配置HMC5883L:连续测量模式、采样1次、增益1.3Ga HMC5883L_WriteReg(0x00, 0x70); // 配置寄存器A HMC5883L_WriteReg(0x01, 0x20); // 配置寄存器B HMC5883L_WriteReg(0x02, 0x00); // 模式寄存器:连续测量 int16_t x, y, z; while (1) { // 读取三轴数据 HMC5883L_ReadData(&x, &y, &z); // 这里可以加数据处理或UART打印逻辑,比如把x/y/z输出到串口 // 延时100ms再读取下一次(3MHz时钟下) __delay_cycles(300000); } }
三、额外排查小技巧
- 检查时钟配置:如果你的MSP432修改过SMCLK频率,要同步修改I2C初始化里的
BRW值(公式:BRW = SMCLK频率 / 目标I2C频率)。 - 用工具看总线信号:如果硬件连接没问题,用示波器或逻辑分析仪观察SCL和SDA的波形,看有没有起始信号、地址发送和设备应答,能快速定位是发送端还是接收端的问题。
- 交叉验证:用其他I2C设备(比如OLED模块)测试MSP432的I2C功能,排除芯片本身的问题;或者换一个HMC5883L模块,确认不是模块损坏。
内容的提问来源于stack exchange,提问作者Alex044
相关产品推荐
相关产品推荐

