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.

[参考译文] MSP430F5329:如何在 I2C SMBus 协议中发送 NAK

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

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/648506/msp430f5329-how-do-you-send-a-nak-in-i2c-smbus-protocol

器件型号:MSP430F5329
主题中讨论的其他器件: MSP-EXP430FR2433MSPDRIVERLIBMSP430FR2433MSP430WARE

我一直在使用 MSP430F5329编写 SMBus 从器件、用于支持和不支持 PEC 的读取字、写入字和块读取协议。

我一直在使用 Raspberry Pi 上的 Python SMBus 库来测试代码。 我已经获得了使用 PEC 和不使用 PEC 的读取字。  

我无法使读取块正常工作、我知道我要发送的字节是正确的。 文档不存在。  我正在寻找其他东西。  

在更仔细地查看读取字和块读取中的 SMBus 协议的过程中、 主器件将从器件发送的最后一个数据字节裸机、然后主器件发送停止。 我以前没有注意到过它。

我的代码让我知道每次生成 I2C 中断并记录中断类型。 我从未见过一个 NAK 中断通过。 现在、我对 Raspberry Pi 代码非常怀疑。  

我想我也可以使用另一个 MSP430芯片作为 I2C 主设备、并选择 MSP-EXP430FR2433。 我在查看 MSPDRIVERLIB: 用于 MSP430器件的 DriverLib 中的库函数、但无法找到在接收到最后一个字节后发送 NAK 的方法、这是 SMBus 规范所要求的。  

例如、系统管理总线(SMBus)规范版本3.0中的图31  


  


我缺少什么?

如何发送主 NAK 来响应从器件发送数据字节高电平? 它似乎是自动执行的。   

基普


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

    我不能说您在使用 RPi Python 库时看到的内容、但我可以让您对 MSP430FR2433和 MSP430F5329上的 I2C 外设操作进行一些澄清。 您在中使用 MSP430FR2433的特定 I2C 总线工作模式是 I2C 主接收器模式。 MSP430FR2xx_4xx 用户指南(www.ti.com/.../slau445g.pdf)的第23.3.5.2.2节对此进行了介绍。 在指南中:"停止条件是由自动停止生成或通过置位 UCTXSTP 位生成的。 从器件接收到的下一个字节后跟一个 NACK 和一个停止条件。 如果 eUSCI_B 模块当前正在等待 UCBxRXBUF 被读取、这个 NACK 会立即发生。"

    基本上、如果在与从器件的事务中的最后一个字节接收期间 UCTXSTP 被置位、MSP430FR2433上的 eUSCI_B I2C 外设将自动生成 NACK。 或者、如果您使用 eUSCI_B 自动停止生成、NACK 也将自动发生、并且您完全不必执行任何操作。 实际上、这就是 eusci_b_i2c_ex2_masterRxSingle DriverLib 代码示例的工作方式。 如果您在开始事务处理之前准确知道要在主器件端接收多少字节、那么自动停止生成非常方便。

    如果您使用 DriverLib 执行多字节接收操作、并且未使用自动停止生成、则需要将这些函数用于事务:

    extern void eUSCI_B_I2C_masterReceiveStart (uint16_t baseAddress);
    extern uint8_t EUSCI_B_I2C_ReceivmasterMultiByteNext (uint16_t baseAddress);
    extern uint8_t EUSCI_B_I2C_ReceivmasterMultiByteFinish (uint16_t baseAddress);

    调用序列有点复杂。 您将调用 EUSCI_B_I2C_masterReceiveStart()以使数据移动。 然后、您将等待 RX 中断标志调用 EUSCI_B_I2C_masterReceiveMultiByteNext ()以接收事务中的所有字节、最后一个字节除外。 对于最后一个字节、您不会等待 RX 中断。 相反,应立即调用 EUSCI_B_I2C_masterReceiveMultiByteFinish()。 这将为您设置 UCTXSTP、等待 RX 接收完成、并在末尾返回最后一个字节 通过执行此操作、NACK 发生在停止条件之后。 例如、如果您希望从从器件读取4个字节、则需要执行以下操作:

    uint8_t data[4];

    EUSCI_B_I2C_masterReceiveStart (EUSCI_ADDRESS);
    while (!(eUSCI_B_I2C_getInterruptStatus (eUSCI_address、eUSCI_B_I2C_Receive_INTERRUPT0))));
    DATA[0]= EUSCI_B_I2C_masterReceiveMultiByteNext (EUSCI_ADDRESS);
    while (!(eUSCI_B_I2C_getInterruptStatus (eUSCI_address、eUSCI_B_I2C_Receive_INTERRUPT0))));
    DATA[1]= EUSCI_B_I2C_masterReceiveMultiByteNext (EUSCI_ADDRESS);
    while (!(eUSCI_B_I2C_getInterruptStatus (eUSCI_address、eUSCI_B_I2C_Receive_INTERRUPT0))));
    DATA[2]= EUSCI_B_I2C_masterReceiveMultiByteNext (EUSCI_ADDRESS);
    DATA[3]= EUSCI_B_I2C_masterReceiveMultiByteFinish (EUSCI_ADDRESS);

    请注意、对于最后一个字节、我们不轮询 RX 中断标志;相反、我们直接转到'Finish' fxn 调用。 这一切也可以整合到中断处理程序中-这只是一个简单的轮询示例、用于显示基本概念。

    如果您有其他问题、请告诉我。

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

    我必须对此进行一些思考。 在块传输中、读取的第一个字节是要跟随的数据长度、而不是对 PEC 进行计数。  

    因此、对于主器件、我必须读取一个字节、而不是将该输入用于要读取的字节数(如果有 PEC、则在要读取的字节数基础上再添加一个字节) 当它到达最后一个字节时、它将发送 NAK、然后发送 Stop。

    正常、那么下一步是实施它。  星期一的活动。  

    谢谢、

    基普

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

    我想我非常了解这一点。  我对几件事感到困惑。

    要初始化:

    EUSCI_B_initMasterParam MP;
    
    void init_I2C_master_for_host (void)
    {
    MP.selectClockSource = EUSCI_B_CLOCKSOURCE_SMCLK;
    MP.i2cClk = 16000000;
    MP.datarate = EUSCI_B_I2C_SET_DATA_RATE_100KBPS;
    MP.byteThreshold = 255_I2C_Counter_Stop
    = MP.autoSTOPGeneration;EUSCI_NO_I2C = 255;
    
    //传递的值在 I2C_master_for_host.h 文件
    
    EUSCI_B_I2C_initMaster (I2C_HOST_BASE_ADDRESS、&MP);
    EUSCI_B_I2C_SET_SlaveAddress (I2C_HOST_BASE_ADDRESS、I2C_HOST_SLAVE_ADDRESS);
    EUSCI_B_I2C_setMode (I2C_HOST_BASE_ADDRESS、USCI_B)中定义;USCI_RUSCI_IS / SIT_WIT_RIC_B 发送模式?
    EUSCI_B_I2C_ENABLE (I2C_HOST_BASE_ADDRESS);
    EUSCI_B_I2C_ENABLEInterrupt (I2C_HOST_BASE_ADDRESS、0xFF);//所有中断
    } 

    第一个问题:我不理解 setMode。

    以下是文档中所述的 MSP430FR2xx_4xx_DriverLib_Users_Guide:

    空 EUSCI B I2C 设置模式(uint16 t baseAddress、uint8 t mode)
    设置 I2C 器件的模式。
    当接收参数设置为 EUSCI B I2C 发送模式时、地址将指示
    I2C 模块处于接收模式;否则、I2C 模块处于发送模式。
    参数

    baseAddress 是 USCI I2C 模块的基地址。
    模式 EUSCI B I2C 模块的有效值为:
    EUSCI B I2C 发送模式[默认值]
    EUSCI B I2C 接收模式

    修改后的位是 UCBxCTLW0寄存器的 UCTR。
    返回

     
    这是拼写错误吗? 当接收模式为发送模式时、它处于接收模式? 什么???


    正在继续...  那么、现在它已初始化:

    我要发送命令:通常我需要 (笑声)
    粗体表示主机发送、粗体表示从机发送

    uint8_t data[4];

    //我需要确保 setMode 为 transmit to start?

    EUSCI_B_I2C_masterSendMultiByteStart (EUSCI_ADDRESS、Command);

    //在开始之前,我是否将 setMode 更改为接收?  

    //我理解此部分主要有几个问题:

    EUSCI_B_I2C_masterReceiveStart (EUSCI_ADDRESS);//这会生成第二次启动或重新启动
    while (!(eUSCI_B_I2C_getInterruptStatus (eUSCI_address、eUSCI_B_I2C_Receive_INTERRUPT0))));
    DATA[0]= EUSCI_B_I2C_masterReceiveMultiByteNext (EUSCI_ADDRESS); //字节数= 3
    while (!(eUSCI_B_I2C_getInterruptStatus (eUSCI_address、eUSCI_B_I2C_Receive_INTERRUPT0))));
    DATA[1]= EUSCI_B_I2C_masterReceiveMultiByteNext (EUSCI_ADDRESS);//字节1
    while (!(eUSCI_B_I2C_getInterruptStatus (eUSCI_address、eUSCI_B_I2C_Receive_INTERRUPT0))));
    DATA[2]= EUSCI_B_I2C_masterReceiveMultiByteNext (EUSCI_ADDRESS);//字节2
    DATA[3]= EUSCI_B_I2C_masterReceiveMultiByteFinish (EUSCI_ADDRESS);//字节3  

    我正在执行所有中断驱动的操作、我有一个状态机。

    最后一条命令是否轮询、直至接收到数据字节3? 我无法在 ISR 中轮询。 我可以看到所有中断。 是否有更好的方法来实现此目的?

    也许当触发字节2的接收中断时、我应该设置 UCTXSTP? 然后等待接收中断获取数据[3]?

    我认为除了两个启动中断和一个或两 个发送中断之外、没有其他应该触发的中断?  




    另一侧还有一个问题。 当这结束时、从器件将获得一个 NAK 中断和一个停止中断? 还是其中一个? (如果有、哪一个?)

    谢谢、
    基普
     

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

    您好 Kip、

    [引用 user="Kipton Moravec"]这是拼写错误吗? 当接收模式为发送模式时、它处于接收模式? 什么???

    是的、这是错误打印。  我已通知开发人员、这将在下一个 DriverLib / MSP430Ware 版本中修复。  正确的注释应为:"当 MODE 参数设置为 EUSCI_B_I2C_Transmit 模式时、地址将指示 I2C 模块处于发送模式;否则、I2C 模块处于接收模式。"

    关于您的其余问题:

    -在调用 EUSCI_B_I2C_masterSendMultiByteStart()之前,您确实需要使用 EUSCI_B_I2C_setMode (I2C_0_BASE、EUSCI_B_I2C_TRANSMITY_MODE)将模式设置为发送器。

    -很明显、在发出重复起始信号时、无需将模式更改为接收器;这是从 EUSCI_B_I2C_ReceivmasterStart (I2C_0_BASE)内完成的。

    -您是正确的、您不想在 ISR 中轮询。  对于 ISR 方法,可以使用 EUSCI_B_I2C_ReceivmasterMultiByteStop()而不是 EUSCI_B_I2C_masterReceiveMultiByteFinish()。  这会设置停止条件生成位。  棘手的问题是要解决时间问题。  为了使 NACK 由主器件正确生成、需要在为最后一个字节计时之前设置该位。  这是关键计时领域。  如果您知道要接收的字节数、字节计数器功能非常有用、因为它将自动生成 STOP 和 NACK。

    -在主接收器模式下,当停止条件通过时,您将获得一个停止中断。  您将无法从主器件生成的 NACK 中获取 NACK 中断。  只有当从 NACK 是时、您才会得到该值。

    Walter

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

    这是一个代码 SNipped I 在带有虚拟主机的 MSP-EXP430FR2433 LaunchPad 上实现、通过从从器件读取1字节来模仿您的用例。

    #include "msp430.h"
    #include "driverlib.h"
    
    #define DCOFREQ 8000000
    
    #define I2C_0_BASE (EUSCI_B0_BASE)
    #define I2C_Receive_buffer_size (32)
    
    uint8_t I2C_0_rxBuffer[NT_receiving_bufferize];INT8_0_bac_size
    
    
    
    
    = uintn_0 = rxtes0;intnvoid = 0 = r2C_0 = rxtes0;intnvoid = 0 = t_0;txt_
    EUSCI_B_I2C_initMasterParam I2C_0;
    
    // SMCLK = MCLK = DCO = 4MHz
    // ACL = 32kHz
    CS_initClockSignal (CS_FLLREF、CS_REFOCLK_SELECT、CS_Clock_divider);
    CS_initClockSignal (CS_ACLK、CS_REFOCLK_select、CS_Clock_divider);
    CS_initClockSignal (CS_MCLK、CS_DCOCLKDIV_SELECT、CS_CLOCK_DIVIDER_1);
    CS_initClockSignal (CS_SMCLK、CS_DCOCLKDIV_SELECT、CS_CLOCK_DIVIDER_1);
    CS_initFLL (((DCOFREQ/1000)、DCOFREQ/32768);
    CS_clearAllOscFlagsWithTimeout (1000);
    
    // I2C 端口多路复用选择
    P1OUT = 0;
    P1DIR |= BIT0 | BIT1 | BIT4 | BIT3 | BIT6;
    P1SEL0 |= BIT2 | BIT3;
    PM5CTL0 &=~LOCKLPM5;
    
    // I2C 设置
    I2C_0.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK;
    I2C_0.i2cClk = DCOFREQ;
    I2C_0.datarate = EUSCI_B_I2C_SET_DATA_RATE 400KBPS;
    I2C_0.byteCounterThreshold = 0;
    I2C_0.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP;
    
    EUSCI_B_I2C_DISABLE (I2C_0_BASE);
    EUSCI_B_I2C_initMaster (I2C_0_BASE、&I2C_0);
    EUSCI_B_I2C_ENABLE (I2C_0_BASE);
    
    _delay_cycles (8000000);
    }
    
    uint8_t I2C_0_readCmd (uint8_t 从器件、uint8_t cmd)
    {
    P1OUT |= BIT0 | BIT4 | BIT5 | BIT6;
    
    //设置从器件地址并等待总线
    EUSCI_B_I2C_setSlaveAddress (I2C_0_BASE、从器件);
    EUSCI_B_I2C_clearInterrupt (I2C_0_BASE、
    EUSCI_B_I2C_Receive_INTERRUPT0 ||
    EUSCI_B_I2C_Transmit INTERRUPT0 ||
    EUSCI_B_I2C_STOP_INTERRUPT );
    EUSCI_B_I2C_enableInterrupt (I2C_0_BASE、
    EUSCI_B_I2C_Receive_INTERRUPT0 ||
    EUSCI_B_I2C_STOP_INTERRUPT );
    I2C_0_rxBytesRemaining = 0;
    
    //将命令写入到从属方
    EUSCI_B_I2C_setMode (I2C_0_BASE、EUSCI_B_I2C_Transmit 模式);
    EUSCI_B_I2C_masterSendMultiByteStart (I2C_0_BASE、cmd);
    while (!(EUSCI_B_I2C_getInterruptStatus (I2C_0_BASE、EUSCI_B_I2C_Transmit INTERRUPT0))))
    {
    IF (EUSCI_B_I2C_getInterruptStatus (I2C_0_BASE、EUSCI_B_I2C_NAK_INTERRUPT))
    {
    
    返回0;
    }
    }
    
    //从器件的读取响应
    EUSCI_B_I2C_masterReceiveStart (I2C_0_BASE);
    while (EUSCI_B_I2C_isBusy (I2C_0_BASE));
    EUSCI_B_I2C_DisableInterrupt (I2C_0_BASE、
    EUSCI_B_I2C_Receive_INTERRUPT0 ||
    EUSCI_B_I2C_STOP_INTERRUPT );
    
    P1OUT &=~BIT0;
    返回 I2C_0_rxCnt;
    }
    
    void run (void)
    {
    I2C_0_readCmd (0x0B、0x02);
    __no_operation();
    _DELAY_CYCLES (1000000);
    }
    
    void main (void)
    {
    WDTCTL = WDTPW | WDTHOLD;
    _enable_interrupt ();
    Setup();
    while (1)
    {
    run();
    }
    }
    
    #pragma vector=USCI_B0_vector
    __interrupt void I2C_0_ISR (void)
    {
    uint8_t 数据;
    
    P1OUT |= BIT1;
    switch (__evo_in_range (UCB0IV、0x1E))
    {
    情况0x00:break; //向量0:无中断中断;
    情况0x02:break; //向量2:ALIFG 中断;
    情况0x04:break; //向量4:NACKIFG 中断;
    情况0x06:break; //向量6:STT IFG 中断;
    情况0x08:
    P1OUT &=~BIT4;
    中断; //向量8:STPIFG 中断;
    情况0x0a:中断; //向量10:RXIFG3中断;
    情况0x0c:中断; //向量14:TXIFG3中断;
    情况0x0E:中断; //向量16:RXIFG2 break;
    情况0x10:中断; //向量18:TXIFG2中断;
    情况0x12:中断; //向量20:RXIFG1 break;
    情况0x14:中断; //向量22:TXIFG1中断;
    情况0x16: //向量24:RXIFG0中断;
    //接收字节
    数据= EUSCI_B_I2C_masterReceiveMultiByteNext (I2C_0_BASE);
    
    //如果这是第一个字节(RX 长度无效),则保存要读取的长度
    //并将接收计数器重置为0
    if (I2C_0_rxBytesRemaining = 0x00)
    {
    I2C_0_rxBytesRemaining =数据;
    if (I2C_0_rxBytesRemaining > I2C_Receive_buffer_size)
    {
    I2C_0_rxBytesRemaining = I2C_Receive_buffer_size;
    }
    I2C_0_rxCnt = 0;
    }
    
    //如果这不是第一个字节,则将响应附加到 RX
    //缓冲区
    其他
    {
    I2C_0_rxBuffer[I2C_0_rxCnt++]=数据;
    I2C_0_rxBytesRemaining --;
    }
    
    //发送 NACK 并在最后一个字节进入时停止
    if (I2C_0_rxBytesRemaining = 1)
    {
    P1OUT &=~BIT4;
    EUSCI_B_I2C_masterReceiveMultiByteStop (I2C_0_BASE);
    }
    中断;
    情况0x18:break; //向量26:TXIFG0中断;
    情况0x1a:break; //向量28:BCNTIFG 中断;
    情况0x1c:中断; //向量30:时钟低电平超时中断;
    情况0x1E:中断; //向量32:第9位中断;
    默认值:break;
    }
    
    P1OUT &=~BIT1;
    }
    

    这提供了以下逻辑跟踪:

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    请注意、上面的代码不包括错误处理、超时或综合状态机等方面的任何内容。 目标是展示如何实现 SMBus 用例。

    此致、
    Walter
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    Walter、我对您的代码有一个问题、因为我正在对其进行修改以供我使用。

    在子例程 I2C_0_readCmd (...)中
    您可以清除
    EUSCI_B_I2C_clearInterrupt (I2C_0_BASE、
    EUSCI_B_I2C_Receive_INTERRUPT0 ||
    EUSCI_B_I2C_Transmit INTERRUPT0 ||
    EUSCI_B_I2C_STOP_INTERRUPT );

    然后启用:

    EUSCI_B_I2C_enableInterrupt (I2C_0_BASE、
    EUSCI_B_I2C_Receive_INTERRUPT0 ||
    EUSCI_B_I2C_STOP_INTERRUPT );

    但您可以在以下方面进行测试:
    while (!(EUSCI_B_I2C_getInterruptStatus (I2C_0_BASE、EUSCI_B_I2C_Transmit INTERRUPT0))))

    这是如何工作的? 这些库函数之一中是否有使能中断?

    基普