This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] MSP430FR5969:I2C、其中 HDC2080卡在无限循环中

Guru**** 656470 points
Other Parts Discussed in Thread: HDC2080, MSP430FR5969
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1119733/msp430fr5969-i2c-with-hdc2080-stuck-in-an-infinite-loop

器件型号:MSP430FR5969
主题中讨论的其他器件: HDC2080

我正在尝试使用 基于  示例的 driverlib HAL、通过 I2C 连接使用 MSP430FR5969 (WISP 5.1)和 HDC2080温度/湿度传感器的电路板。 我将使用2k Ω 上拉电阻器、如果这起作用的话。 传感器经确认可与 Arduino 配合使用、但现在我想在 MSP 中复制该功能。

这是我当前使用的代码片段(为简洁起见、省略了其他不相关的器件。 如果有用、这是 在 CCS 11中的 WISP 固件的基础上构建的):

#define SENSOR_ADDRESS 0x40             // HDC2080 address
#define SENSOR_CONFIG_REGISTER 0x0F     // HDC2080 configuration register

void initI2C(){
        WDT_A_hold(WDT_A_BASE);

        //Set DCO frequency to 1MHz
        CS_setDCOFreq(CS_DCORSEL_0,CS_DCOFSEL_0);
        //Set ACLK = VLO with frequency divider of 1
        CS_initClockSignal(CS_ACLK,CS_VLOCLK_SELECT,CS_CLOCK_DIVIDER_1);
        //Set SMCLK = DCO with frequency divider of 1
        CS_initClockSignal(CS_SMCLK,CS_DCOCLK_SELECT,CS_CLOCK_DIVIDER_1);
        //Set MCLK = DCO with frequency divider of 1
        CS_initClockSignal(CS_MCLK,CS_DCOCLK_SELECT,CS_CLOCK_DIVIDER_1);

        // Configure Pins for I2C
        //Set P1.6 and P1.7 as Secondary Module Function Input.
        /*

        * Select Port 1
        * Set Pin 6, 7 to input Secondary Module Function, (UCB0SIMO/UCB0SDA, UCB0SOMI/UCB0SCL).
        */
        GPIO_setAsPeripheralModuleFunctionInputPin(
            GPIO_PORT_P1,
            GPIO_PIN6 + GPIO_PIN7,
            GPIO_SECONDARY_MODULE_FUNCTION
        );

        /*
         * Disable the GPIO power-on default high-impedance mode to activate
         * previously configured port settings
         */
        PMM_unlockLPM5();

        //Initialize Master
        EUSCI_B_I2C_initMasterParam param = {0};
        param.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK;
        param.i2cClk = CS_getSMCLK();
        param.dataRate = EUSCI_B_I2C_SET_DATA_RATE_400KBPS;
        param.byteCounterThreshold = 1;
        param.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP;
        EUSCI_B_I2C_initMaster(EUSCI_B0_BASE, &param);

        //Specify slave address
        EUSCI_B_I2C_setSlaveAddress(EUSCI_B0_BASE, SENSOR_ADDRESS);

        //Set in transmit mode
        EUSCI_B_I2C_setMode(EUSCI_B0_BASE, EUSCI_B_I2C_TRANSMIT_MODE);

        // Set timeout to prevent infinite loops
        EUSCI_B_I2C_setTimeout(EUSCI_B0_BASE, 1000);

        //Enable I2C Module to start operations
        EUSCI_B_I2C_enable(EUSCI_B0_BASE);
}

uint8_t readI2CRegister(uint8_t registerAddress){

    // Send single byte as address
    EUSCI_B_I2C_masterSendSingleByte(EUSCI_B0_BASE, registerAddress);

    // Delay until transmission completes
    while (EUSCI_B_I2C_isBusBusy(EUSCI_B0_BASE));

    // Switch to read mode
    EUSCI_B_I2C_setMode(SENSOR_ADDRESS, EUSCI_B_I2C_RECEIVE_MODE);

    // Receive start
    EUSCI_B_I2C_masterReceiveStart(SENSOR_ADDRESS);

    // Read data from register (and then send STOP)
    uint8_t data = EUSCI_B_I2C_masterReceiveSingle(SENSOR_ADDRESS);

    // Return data
    return data;
}

void writeI2CRegister(uint8_t registerAddress, uint8_t data){

    // Go to transmit mode
    EUSCI_B_I2C_setMode(SENSOR_ADDRESS, EUSCI_B_I2C_TRANSMIT_MODE);

    // Send single byte as address
    EUSCI_B_I2C_masterSendSingleByte(EUSCI_B0_BASE, registerAddress);

    // Delay until transmission completes
    while (EUSCI_B_I2C_isBusBusy(EUSCI_B0_BASE));

    // Send data
    EUSCI_B_I2C_masterSendSingleByte(EUSCI_B0_BASE, data);

    // Delay until transmission completes
    while (EUSCI_B_I2C_isBusBusy(EUSCI_B0_BASE));

}

void main(void) {

    ...

    // Init I2C
    initI2C();
    
    // Enable measurements on the HDC2080 with mostly default settings
    writeI2CRegister(SENSOR_CONFIG_REGISTER, 0x01);
    
    uint8_t temp1;
    while(true){
        ...
        temp1 = readI2CRegister(0x00);
        ...
    }
}

运行该函数时、代码看起来像卡在"eUSCI_B_I2C_masterSendSingleByte"中(在 driverlib 的 eusci_b_i2c.c 中)。 更具体地说、它在这里循环:

//Poll for transmit interrupt flag.
while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;

如何进一步调试此问题?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您能否使用示波器或数字分析仪捕捉波? 它可以帮助您了解发生了什么。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    此程序包含对  EUSCI_B_I2C_masterSendSingleByte 的多个调用、并且该函数包含两个您引用的代码出现。

    如果您可以指定 其挂起的精确代码(包括调用栈)、这将提供提示。

    未经请求:WriteI2CRegister 发出两个独立的1字节(写入)事务、这不会执行任何操作(设置寄存器地址两次)。 您可能希望使用 EUSCI_B_I2C_masterSendMultiByteStart、后跟 EUSCI_B_I2C_masterSendMultiByteFinish 来进行单个2字节事务。 这可能不是您症状的原因。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    根据上面的代码、看起来在线路上发送的唯一内容是启动条件。 没有别的了...

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    当然。 调试器指示它卡在"eUSCI_B_I2C_masterSendSingleBye"(eusci_b_i2c.c 的第172行)中

    调用堆栈如下所示:

    TI MSP430 USB1/MSP430 (Suspended)
    	EUSCI_B_I2C_masterSendSIngleByte(unsigned int, unsinged char)() at eusci_b_i2c.c:186 0x005E96
    	writeI2CRegister(unsigned char, unsigned char)() at main.c:139 0x006028
    	main() at main.c:170 0x00556C
    	_c_int00_noargs() at boot.c:125 0x0062F4 (the entry point was reached)

    这里是 eUSCI I2C 寄存器。 似乎有一个错误指示、但我不确定这是否会影响我看到的环路:

    感谢您使用多字节事务的指针! 我还将为此更新我的代码。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    >EUSCI_B_I2C_setTimeout (EUSCI_B0_BASE、1000);

    此函数不用于接受数字、而是接受几个特定位。 结果是在 UCB0CTLW1中设置了一组异常选项(我没有尝试弄清楚它们的交互方式)。

    请尝试以下类似方法:

    >EUSCI_B_I2C_setTimeout (EUSCI_B0_BASE、 EUSCI_B_I2C_TIMEOUT_34_MS); //总线超时34ms

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    现在、代码更改似乎使能发送一些位。 然而,这一问题仍然不完整。 现在、我将其作为我的写入代码:

    void writeI2CRegister(uint8_t registerAddress, uint8_t data){
    
        // Go to transmit mode
        EUSCI_B_I2C_setMode(SENSOR_ADDRESS, EUSCI_B_I2C_TRANSMIT_MODE);
    
        // Send single byte as address
        EUSCI_B_I2C_masterSendMultiByteStart(EUSCI_B0_BASE, registerAddress);
    
        // Delay until transmission completes
        // Code loops infinitely here
        while (EUSCI_B_I2C_isBusBusy(EUSCI_B0_BASE));
    
        // Send data
        EUSCI_B_I2C_masterSendMultiByteFinish(EUSCI_B0_BASE, data);
    
        // Delay until transmission completes
        while (EUSCI_B_I2C_isBusBusy(EUSCI_B0_BASE));
    
    }

    现在、当我尝试呼叫时:

    writeI2CRegister(0x0F, 0x01);

    我得到这个。 看起来根本不发送第二个字节:

    不过、无限循环位置似乎已经发生了变化。 现在位于:

    while (EUSCI_B_I2C_isBusBusy(EUSCI_B0_BASE));

    在首次调用"EUSCI_B_I2C_masterSendMultiByteStart"之后。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    总线在两个字节之间很忙--等待你做一些事--所以你不应该等待它结束--只需删除那行。

    如果您需要 sendMultiByteNext、也可以使用它。 这三种设计使您只需依次调用 Start/[N*] Next/Finish Back-Back 即可。

    MultiByteFinish (以及 SingleByte)通过在当前/下一个字节(某种回写操作)之后请求停止来完成。 最好在事务完成后(或在下一个事务开始之前)等待"停止"完成、方法如下:

    > while (EUSCI_B_I2C_Sending _stop = EUSCI_B_I2C_masterIsStopSent (EUSCI_B0_BASE))/* empty*/;

    它会测试 UCTXSTP 是否(仍然)置1。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    啊、我明白了。 我已将写入代码更改为此代码、现在该部件按预期工作:

    void writeI2CRegister(uint8_t registerAddress, uint8_t data){
    
        // Go to transmit mode
        EUSCI_B_I2C_setMode(SENSOR_ADDRESS, EUSCI_B_I2C_TRANSMIT_MODE);
    
        // Send single byte as address
        EUSCI_B_I2C_masterSendMultiByteStart(EUSCI_B0_BASE, registerAddress);
    
        // Send data
        EUSCI_B_I2C_masterSendMultiByteFinish(EUSCI_B0_BASE, data);
    
        // Make sure I2C stop condition is sent
        while (EUSCI_B_I2C_SENDING_STOP == EUSCI_B_I2C_masterIsStopSent(EUSCI_B0_BASE));
    
    }

    但是、读取器件仍会卡在"eUSCI_B_I2C_masterReceiveSingle"中、调用栈为:

    TI MSP430 USB1/MSP430 (Suspended)
    	EUSCI_B_I2C_masterReceiveSingle(unsigned int)() at eusci_b_i2c.c:540 0x006124
    	readI2CRegister(unsigned char)() at main.c:139 0x006072
    	main() at main.c:222 0x00584E
    	_c_int00_noargs() at boot.c:125 0x006166 (the entry point was reached)

    更具体地说、它在这里无限循环:

    //Polling RXIFG0 if RXIE is not enabled
    if(!(HWREG16(baseAddress + OFS_UCBxIE) & UCRXIE0)) {
        while(!(HWREG16(baseAddress + OFS_UCBxIFG) & UCRXIFG0));
    }

    如果有用、我还稍微更新了我的寄存器读取代码、以检查停止条件而不是总线忙(以及在每次读取寄存器调用开始时切换回传输模式-这是必要的吗?)。 现在看起来是这样的:

    uint8_t readI2CRegister(uint8_t registerAddress){
    
        // Switch to transmit mode
        EUSCI_B_I2C_setMode(SENSOR_ADDRESS, EUSCI_B_I2C_TRANSMIT_MODE);
    
        // Send single byte as address
        EUSCI_B_I2C_masterSendSingleByte(EUSCI_B0_BASE, registerAddress);
    
        // Delay until I2C stop condition is sent
        while (EUSCI_B_I2C_SENDING_STOP == EUSCI_B_I2C_masterIsStopSent(EUSCI_B0_BASE));
    
        // Switch to read mode
        EUSCI_B_I2C_setMode(SENSOR_ADDRESS, EUSCI_B_I2C_RECEIVE_MODE);
    
        // Receive start
        EUSCI_B_I2C_masterReceiveStart(SENSOR_ADDRESS);
    
        // Read data from register (and then send STOP)
        uint8_t data = EUSCI_B_I2C_masterReceiveSingle(SENSOR_ADDRESS);
    
        // Delay until I2C stop condition is sent
        while (EUSCI_B_I2C_SENDING_STOP == EUSCI_B_I2C_masterIsStopSent(EUSCI_B0_BASE));
    
        // Return data
        return data;
    }

    读取操作是否还需要其他操作?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我以前没有注意到这一点:

    >EUSCI_B_I2C_masterReceiveStart (sensor_address);
    >uint8_t data = EUSCI_B_I2C_ReceivmasterSingle (sensor_address);

    这些器件需要 I2C 单元(EUSCI_B0)而不是 I2C 地址。 尝试:

    >EUSCI_B_I2C_masterReceiveStart (EUSCI_B0_BASE);
    >uint8_t data = EUSCI_B_I2C_ReceivmasterSingle (EUSCI_B0_BASE);

    您正在使用的函数集管理 UCTR (读取/写入方向)位本身,因此您实际上不必调用 setMode()。 还有其他函数需要调用它。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    嗯。 现在、似乎卡在"eUSCI_B_I2C_ReceivmasterStart"内。 更具体地说、在该部分中:

    //Poll for transmit interrupt flag.
    while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;

    调用堆栈如下所示:

    TI MSP430 USB1/MSP430 (Suspended)
    	EUSCI_B_I2C_masterSendSingleByte(unsigned int, unsigned char)() at eusci_b_i2c.c:186 0x005DDE
    	readI2CRegister(unsigned char)() at main.c:135 0x006144
    	...

    以下是最新的读取代码:

    uint8_t readI2CRegister(uint8_t registerAddress){
        // Send single byte as address
        EUSCI_B_I2C_masterSendSingleByte(EUSCI_B0_BASE, registerAddress);
    
        // Delay until I2C stop condition is sent
        // while (EUSCI_B_I2C_SENDING_STOP == EUSCI_B_I2C_masterIsStopSent(EUSCI_B0_BASE));
    
        // Receive start
        EUSCI_B_I2C_masterReceiveStart(EUSCI_B0_BASE);
    
        // Read data from register (and then send STOP)
        uint8_t data = EUSCI_B_I2C_masterReceiveSingle(EUSCI_B0_BASE);
    
        // Return data
        return data;
    }

    我已经注释掉了停止条件等待调用、因为它似乎也无限循环。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    查看源、看起来 EUSCI_B_I2C_ReceivmasterSingle 不会请求停止。 SendSingleByte (开始/读取字节/停止)的对应方是  

    uint8_t DATA = EUSCI_B_I2C_ReceivmasterSingleByte (EUSCI_B0_BASE);