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.

[参考译文] CCS/MSP430FR2355:I2C 主设备多字节传输忽略第一个字节

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

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/788166/ccs-msp430fr2355-i2c-master-multi-byte-transmission-ignoring-first-byte

器件型号:MSP430FR2355
主题中讨论的其他器件:PCF8574APCF8574

工具/软件:Code Composer Studio

大家好、

我尝试通过 I2C 与 SSD1306 OLED 屏幕通信。 我遇到了 driverlib  EUSCI_B_I2C_masterSendMultiByteStart 函数的问题、它会忽略第一个传输字节。 它实际工作的唯一情况是默认 MSP 时钟(1MHz)和 I2C 的400kbps。

我将按如下方式配置外设:

GPIO_setPeripheralModuleFunctionInputPin (GPIO_PORT_P4、GPIO_PIN6 | GPIO_PIN7、GPIO_PRIMARY_MODULE_Function); 

EUSCI_B_I2C_initMasterParam i2cParam ={0}; i2cParam.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK; i2cParam.i2cClk = CS_getSMCLK (); i2cParam.datarate = EUSCI_B_I2C_SET_DATA_RATE_400KBPS; i2cParam.byteCounterThreshold = 1; i2cParam.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP; EUSCI_B_I2C_initMaster (EUSCI_B1_base、&i2cParam); EUSCI_B_I2C_setSlaveAddress (EUSCI_B1_BASE、SSD1306_address); EUSCI_B_I2C_setMode (EUSCI_B1_BASE、EUSCI_B_I2C_Transmit 模式); EUSCI_B_I2C_ENABLE (EUSCI_B1_BASE);

我将使用此函数发送命令:

void OLED_sendCommand (uint8_t cmd)
{
EUSCI_B_I2C_masterSendMultiByteStart (EUSCI_B1_BASE、0x00);

EUSCI_B_I2C_masterSendMultiByteNext (EUSCI_B1_base、cmd);

EUSCI_B_I2C_masterSendMultiByteStop (EUSCI_B1_BASE);
} 

现在、对于输出、这使用默认时钟(1MHz)和400kbps I2C:

这将使用默认时钟(1MHz)和100kbps I2C:

如果我将 I2C 时钟更改为100kbps、或将系统时钟更改为默认1MHz 以外的任何值、 则 EUSCI_B_I2C_masterSendMultiByteStart 会忽略第一个字节。 为什么会发生这种情况?

此致、

Helder 销售

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

    我还尝试通过配置以下方式启用中断模式:

    GPIO_setPeripheralModuleFunctionInputPin (GPIO_PORT_P4、GPIO_PIN6 | GPIO_PIN7、GPIO_PRIMARY_MODULE_Function); 

    EUSCI_B_I2C_initMasterParam i2cParam ={0}; i2cParam.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK; i2cParam.i2cClk = CS_getSMCLK (); i2cParam.datarate = EUSCI_B_I2C_SET_DATA_RATE_400KBPS; i2cParam.byteCounterThreshold = 1; i2cParam.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP; EUSCI_B_I2C_initMaster (EUSCI_B1_base、&i2cParam); EUSCI_B_I2C_setSlaveAddress (EUSCI_B1_BASE、SSD1306_address); EUSCI_B_I2C_setMode (EUSCI_B1_BASE、EUSCI_B_I2C_Transmit 模式); EUSCI_B_I2C_ENABLE (EUSCI_B1_BASE); EUSCI_B_I2C_clearInterrupt (EUSCI_B1_base、EUSCI_B_I2C_Transmit INTERRUPT1 | EUSCI_B_I2C_NAK_INTERRUPT); EUSCI_B_I2C_enableInterrupt (EUSCI_B1_base、EUSCI_B_I2C_Transmit INTERRUPT1 | EUSCI_B_I2C_NAK_INTERRUPT); _bis_SR_register (GIE);

    但是、在首次调用  EUSCI_B_I2C_masterSendMultiByteStart (EUSCI_B1_base、0x00)后、它停留在  

    //轮询发送中断标志。
    while (!(HWREG16 (baseAddress + OFS_UCBxIFG)& UCTXIFG)); 

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

    您好!

    关于 EUSCI_B_I2C_masterSendMultiByte   函数的使用、您可以参阅以下代码示例:e2e.ti.com/.../eusci_5F00_b_5F00_i2c_5F00_ex3_5F00_masterTxMultiple.c

    或者、我想、如果在 EUSCI_B_I2C_masterSendMultiByteNext (EUSCI_B1_base、cmd)之后添加一些延迟、这是可以的。

    伊斯天

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

    您好!

    添加延迟没有帮助。 我还复制了示例(修改为 EUSCI_B1)、但仍然停留在  

    //轮询发送中断标志。
    while (!(HWREG16 (baseAddress + OFS_UCBxIFG)& UCTXIFG)); 

    基于该示例、我正在使用(重)的完整代码如下所示:

    /*-版权所有-、BSD
    *版权所有(c) 2017、德州仪器(TI)公司
    *保留所有权利。
    *
    *
    只要
    符合以下条件*、允许以源代码和二进制形式重新分发和使用:
    *
    *源代码的重新分发必须保留上述版权
    声明*、此条件列表和以下免责声明。
    *
    ***二进制形式的再发行必须在
    
    *随发行提供的文档和/或其他材料中复制上述版权声明、本条件列表和以下免责声明。
    *
    ***未经
    
    事先书面许可、不得使用德州仪器公司的名称或*其贡献者的名称认可或推广从本软件衍生的产品*。
    *
    *本软件由版权所有者和贡献者"按原样"提供
    *、
    
    不承担任何明示或暗示的保证、包括但不限于*适销性和特定用途适用性的暗示保证*。 在任何情况下、版权所有者或
    *贡献者都不对任何直接、间接、偶然、特殊、
    *模范、 或相应的损害(包括但不限于
    *采购替代产品或服务;丧失使用、数据或利润;
    *或业务中断)、但出于任何责任理论
    、*无论是在合同中、严格责任还是由于
    使用本软件而以任何方式产生的侵权行为(包括疏忽或*其他)
    、*即使已获悉可能会发生此类损坏。
    *--/版权--*//*********
    
    //
    //! 此示例展示了如何将 I2C 模块配置为主器件//
    ! 中断驱动模式下的多字节传输。 从器件的地址
    //! 模块的设置。
    //!
    //! 演示- EUSCI_B0 I2C 主器件 TX 多个字节到 MSP430从
    器件//!
    //! 说明:此演示通过 I2C 总线连接两个 MSP430。 主
    器件//! 发送到从器件。 这是主代码。 它很好
    //! 传输一组数据并演示如何实现 I2C
    //! 主发送器使用 USCI_B0 TX 中断发送多个字节。
    //! ACLK = n/a、MCLK = SMCLK = BRCLK =默认 DCO =~1MHz
    //!
    //! /|\/|\
    //! MSP430FR2xx_4xx 电路板10k 10k MSP430FR2xx_4xx 电路板
    //! 从器件 || 主器件
    //! -------- |||---
    //! | UCB0SDA|<-|--+->|UCB0SDA |
    //! | || | |
    //! | || | |
    //! | UCB.S.|<---- |UCB.S. |
    //! | | | |
    //!
    //! 此示例使用以下外设和 I/O 信号。 您必须
    //! 查看这些内容并根据您自己的董事会需要进行更改:
    //! - I2C 外设
    //! - GPIO 端口外设(用于 I2C 引脚)
    //! - SCL
    //! - SDA//
    !
    //! 此示例使用以下中断处理程序。 要使用此示例
    //! 在您自己的应用程序中、您必须将这些中断处理程序添加到
    您的//! 矢量表。
    //! - USCI_B0_Vector。
    ////
    *********
    #include "driverlib.h"
    #include "Board.h"
    
    //*********
    //
    //设置从机模块的地址。 这是以
    //以下格式发送的7位地址:
    //[A6:A5:A4:A3:A1:A0:RS]
    //
    第一个字节的"RS"位置为零表示主
    设备//发送(发送)数据到所选的从设备,而在该位置为1
    表示主设备接收来自从设备的数据。
    ////
    *****************
    #define SLAVE_ADDRESS 0x38
    
    //*********
    //
    // SMCLK 的目标频率,单位为 kHz
    //
    //*********
    #define CS_SMCLK_NOVE_FREQUENCY IN_kHz 1000
    
    //*********
    //
    /SMCLK/FLLRef 比率
    //
    //*********
    #define CS_SMCLK_FLLREF_RA比30
    
    //指向 TX 数据
    uint8_t TXData 的指针= 0;
    uint8_t TXByteCtr;
    
    void main (void)
    {
    WDT_A_HOLD (WDT_A_base);
    
    //设置 DCO FLL 基准= REFO
    cs_initClockSignal (
    CS_FLLREF、
    CS_REFOCLK_SELECT、
    CS_CLOCK 分频器_1
    );
    
    //设置比率和所需的 MCLK 频率并初始化 DCO
    cs_initFLSettle (
    CS_SMCLK_REVed_frequency in_kHz、
    CS_SMCLK_FLLREF_Ratio
    );
    
    //set ACLK = VLO、分频器为1
    cs_initClockSignal (
    CS_ACLK、
    CS_VLOCLK_SELECT、
    CS_CLOCK 分频器_1
    );
    
    //set SMCLK = DCO、分频器为1
    cs_initClockSignal (
    CS_SMCLK、
    CS_DCOCLKDIV_SELECT、
    CS_CLOCK 分频器_1
    );
    
    //set MCLK = DCO、分频器为1
    cs_initClockSignal (
    CS_MCLK、
    CS_DCOCLKDIV_SELECT、
    CS_CLOCK 分频器_1
    );
    
    //为 I2C 配置引脚
    GPIO_setPeripheralModuleFunctionInputPin (
    GPIO_PORT_P4、
    GPIO_PIN6、
    GPIO_PRIMARY_MODULE_FUNCTION
    );
    GPIO_setPeripheralModuleFunctionInputPin (
    GPIO_PORT_P4、
    GPIO_PIN7、
    GPIO_PRIMARY_MODULE_FUNCTION
    );
    
    /*
    *禁用 GPIO 上电默认高阻抗模式以激活
    *先前配置的端口设置
    *
    PMM_unlockLPM5 ();
    
    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_100KBPS;
    param.byteCounterThreshold = 0;
    param.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP;
    EUSCI_B_I2C_initMaster (EUSCI_B1_base、&param);
    
    //指定从器件地址
    EUSCI_B_I2C_setSlaveAddress (EUSCI_B1_BASE、
    从器件地址
    );
    
    //将主设备设置为接收模式
    EUSCI_B_I2C_setMode (EUSCI_B1_BASE、
    EUSCI_B_I2C_Transmit 模式
    );
    
    //启用 I2C 模块以启动操作
    EUSCI_B_I2C_ENABLE (EUSCI_B1_BASE);
    
    EUSCI_B_I2C_clearInterrupt (EUSCI_B1_base、
    EUSCI_B_I2C_Transmit INTERRUPT1 +
    EUSCI_B_I2C_NAK_INTERRUPT
    );
    //启用主机接收中断
    EUSCI_B_I2C_enableInterrupt (EUSCI_B1_BASE、
    EUSCI_B_I2C_Transmit INTERRUPT1 +
    EUSCI_B_I2C_NAK_INTERRUPT
    );
    while (1)
    {
    _DELAY_CYCLES (1000); //传输之间的延迟
    TXByteCtr = 4; //加载 TX 字节计数器
    TXData = 0;
    
    while (EUSCI_B_I2C_Sending = EUSCI_B_I2C_masterIsStopSent (EUSCI_B1_BASE));
    
    EUSCI_B_I2C_masterSendMultiByteStart (EUSCI_B1_BASE、TXData++);
    
    _bis_SR_register (CPUOFF + GIE); //输入带中断的 LPM0
    //保持在 LPM0中直到所有数据
    //是 TX
    //增加数据字节
    }
    }
    
    #pragma vector=USCI_B1_vector
    __interrupt void USCIB1_ISR (void)
    {
    switch (__evo_in_range (UCB1IV、USCI_I2C_UCBIT9IFG)
    ){
    USCI_NONE 案例: //无中断中断中断;
    中断;
    USCI_I2C_UCALIFG 案例: //仲裁丢失
    中断;
    案例 USCI_I2C_UCNACKIFG://接收到 NAK (仅限主器件)
    //重新发送启动(如果 NACK)
    EUSCI_B_I2C_masterSendStart (EUSCI_B1_BASE);
    中断;
    USCI_I2C_UCTXIFG0案例: // TXIFG0
    //检查 TX 字节计数器
    IF (TXByteCtr)
    {
    EUSCI_B_I2C_masterSendMultiByteNext (EUSCI_B1_BASE、TXData++);
    //测量 TX 字节计数器
    TXByteCtr --;
    }
    其他
    {
    EUSCI_B_I2C_masterSendMultiByteStop (EUSCI_B1_BASE);
    //退出 LPM0
    _BIC_SR_REGISTER_ON_EXIT (CPUOFF);
    }
    中断;
    默认值:
    中断;
    }
    
    
    

    我尝试了一些其他方法。 总结:

    • EUSCI_B1轮询方法:失败、跳过数据的第一个字节
    • EUSCI_B0轮询方法:失败、跳过数据的第一个字节
    • EUSCI_B1中断示例:失败(甚至没有启动通信、被捕获在发送中断标志中)
    • EUSCI_B0中断示例: 器件上电后成功。 如果没有下电上电、有时会起作用、有时不起作用(被捕获在发送中断标志中)

    我还尝试使用 PCF8574A、只是为了确保导致问题的不是 I2C 器件。 至少我知道从器件不是问题。

    此致、

    Helder

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好!
    您的意思是 卡在 这里:
    空 EUSCI_B_I2C_masterSendMultiByteStart (uint16_t baseAddress、uint8_t txData)
      //存储当前发送中断使能
      uint16_t txieStatus = HWREG16 (baseAddress + OFS_UCBxIE)& UCTXIE;
      //禁用发送中断使能
      HWREG16 (baseAddress + OFS_UCBxIE)&=~(UCTXIE);
      //发送启动条件。
      HWREG16 (baseAddress + OFS_UCBxCTLW0)|= UCTR + UCTXSTT;
      //轮询发送中断标志。
      while (!(HWREG16 (baseAddress + OFS_UCBxIFG)& UCTXIFG));
      //发送单字节数据。
      HWREG16 (baseAddress + OFS_UCBxTXBUF)= txData;
      //恢复发送中断使能
      HWREG16 (baseAddress + OFS_UCBxIE)|= txieStatus;
    您可以 通过调试模式检查 UCAxTXBUF 和 UCTXIFG 寄存器、并找到原因。
      您能否发布代码、因为版本太多。 无法通过示例代码找到您的问题。
    或者您能否发布一个代码来 重现您的问题?
    伊斯天
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好!

    我上面提到的修改后的示例代码不起作用。 我认为上面的代码可能会重现问题。

    另一件事是、我连接了示波器以查看总线线路、并发现当微控制器拉低 SDA 线路时、SDA 会一直保持低电平(即使我重新启动微控制器)、直到我断开并再次连接 PCF8574的电源。

    OLED 具有几乎相同的行为、除非我不能使 SDA 线路变为高电平、即使对 OLED 进行下电上电也是如此。

    此致、

    Helder

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

    您好!

    PCF8574 仅支持100kHz I2C。 请仔细检查。

    我稍后将使用 Salve 来模拟您的情况。

    伊斯天

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

    您好!

    我回来了、这真的需要我很多时间。  一个小小的错误。

    您提供的代码有问题。

        EUSCI_B_I2C_clearInterrupt(EUSCI_B1_BASE,
                EUSCI_B_I2C_TRANSMIT_INTERRUPT1 +
                EUSCI_B_I2C_NAK_INTERRUPT
                );
        //Enable master Receive interrupt
        EUSCI_B_I2C_enableInterrupt(EUSCI_B1_BASE,
                EUSCI_B_I2C_TRANSMIT_INTERRUPT1 +
                EUSCI_B_I2C_NAK_INTERRUPT
                );
    EUSCI_B_I2C_Transmit INTERRUPT1应替换为 EUSCI_B_I2C_Transmit INTERRUPT0、因为作为主器件、只能使用中断0。
    我的结果如下:
    伊斯天
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好!

    就是这样、这真的是一个微小的错误、没有被注意到、抱歉。 非常感谢您的帮助! 关于 I2C 跳过字节、至少在中断模式下、我求出了一个

    while (EUSCI_B_I2C_isBusy (EUSCI_B1_BASE)== EUSCI_B_I2C_BUS_BUS_BUSY); 

    在  EUSCI_B_I2C_masterSendMultiByteStart 之后。  在不进行位检查的情况下添加延迟也会得到解决。

    唯一的问题是延迟(或标志检查)只在 START 位之后工作。 甚至未检查  EUSCI_B_I2C_Sending 或  EUSCI_B_I2C_Sending 或 STOP  是否正常。 这适用于频率高于1MHz 的 CPU 时钟(至少在我的案例中)。

    再次感谢、

    Helder