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.

[参考译文] MSP430FR4133:I2C 澄清

Guru**** 2589280 points
Other Parts Discussed in Thread: TMP117, MSP430FR4133, CC3220SF, CC3120

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

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/770253/msp430fr4133-i2c-clarification

器件型号:MSP430FR4133
主题中讨论的其他器件:TMP117CC3220SFCC3120MSPWARE

我对该 MCU 非常陌生、因此我想了解如何与 I2C 进行通信。 我了解如何设置 GPIO 引脚以便能够与从器件通信、但我的问题是、我的从器件 TMP117上有多个寄存器用于设置限制、偏移、配置等 数据表显示、TMP 有一个指针寄存器、用于将数据发送到我要写入的相应寄存器、但我对如何启动该寄存器有点困惑。 我查看了电路板的示例代码、我已经读取了驱动程序文件、但我不知道如何写入器件上的特定寄存器。 驱动程序看起来只能将数据发送到从机的基址、而不能专门发送到其寄存器。 我弄错了吗? 如果是、是否有这样做的示例?

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

    要将 MSP430FR4133设置为 I2C 主器件、可在 以下位置找到一些软件示例:dev.ti.com/.../

    至于直接连接到 TMP117、我不确定是否有这样的示例代码。 有一个使用此温度传感器的 TI 设计具有一些软件。 它适用于我们的无线 MCU 之一、因此固件与 MSP430的固件略有不同。

    查看 TMP117数据表、第7.5.3节介绍了接口、图21和22显示了使用指针寄存器(PR)进行写入和读取操作。 表3显示了寄存器映射、最左列显示了 PR 指向这些寄存器的值。

    PR 似乎是每次 I2C 写入启动后的第一个字节。

    用于寄存器写入的 Ex I2C 数据包:起始地址、具有写入位的从地址、PR 值、用于寄存器的 Data1、用于寄存器的 Data2、停止
    Ex I2C 寄存器读取:起始地址、具有写入位的从地址、PR 值、重新启动、具有读取功能的从地址、来自 reg 的 Data1、来自 reg 的 Data2、停止

    如果您有任何疑问、请告诉我、
    JD
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我在读取 eusci_b_i2c 文件的同时阅读这些示例、但我仍然有点困惑。 基本上、我想使用该 MCU 做什么、我已经使用 CC3220SF、我非常熟悉它用于 I2C 的驱动程序、但我似乎无法将其转换为 MSP430。 我觉得我对信息有些不知所措、如果您能为我提供一些指导、我将提供 CC3220代码来为您提供参考:

    这将是我向从器件发送数据:

    //设置低限温度*/
    txBuffer[0]= LOW_LIM_REG;
    txBuffer[1]= LOWIM_HByte;
    txBuffer[2]= LOWIM_LByte;
    i2cTransaction3.slaveAddress = TMP117_address;
    i2cTransaction3.writeBuf = txBuffer;
    i2cTransaction3.writeCount = 3;
    i2cTransaction3.readBuf = rxBuffer;
    i2cTransaction3.ReadCount = 0;
    I2C_transfer (i2c、&i2cTransaction3); 

    这将从从器件读取:

    /*指向 T 环境寄存器并读取其2个字节*/
    txBuffer[0]= TEMP_REG;
    i2cTransaction1.slaveAddress = TMP117_address;
    i2cTransaction1.writeBuf = rxBuffer;
    i2cTransaction1.writeCount = 1;
    i2cTransaction1.readBuf = rxBuffer;i2cTransaction1.readBuffer
    i2cTransaction1.ReadCount = 2; 

    我相信我正在了解 MSP430上的 I2C、但我只需要稍微推动一下。 在阅读完所有信息后、我注意到所有示例都是设置时钟。 我不知道 CC3220是否已经为我完成了这项工作、或者我是否真的需要设置时钟、但基本上我只是尝试读取温度并将其显示在 LCD 上。 我不需要任何中断、因为当我发送 MCU 命令时、温度传感器将配置为仅读取。

    在该 MCU 上初始化参数时、我只需执行以下操作:

    I2C_Handle i2c;
    I2C_Params i2cParams;
    
    //创建 I2C 以供使用*/
    I2C_Params_init (&i2cParams);
    i2cParams.bitrate = I2C_400kHz;
    i2c = I2C_open (Board_I2C_TMP、&i2cParams); 

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    只需注意、从传感器温度寄存器接收到的数据是一个2字节的数字信号。 在代码中使用 eusci_b_i2c 驱动程序时、如何从缓冲区获取该值并将其设置为我要调整和显示的变量。 使用3220,就像将第一个字节左移8位并"或"将其与第二个字节放在一起一样简单:
    温度=((Rxbuffer[0]<< 8)| Rxbuffer [1])
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    嗨、Chris、

    我更新了您的帖子以使用我们的代码发布工具。 有一个 如果您单击"Insert code、attach files and more ..."按钮、 发布后保留格式的幻灯片中的链接。

    我不太熟悉 CC3120上的 MCU、但 MSP430确实有许多时钟选项、在我们甚至使用 I2C 模块之前、需要先配置这些选项。 还存在一些额外的复杂性、以提供更大的灵活性和功耗优化。

    对于第二个问题、在驱动程序级别示例 MSP430fr413x_eusci_i2c_standard_master.c 中、应存在一个 ReceiveBuffer[]、用于保存任何接收到的数据。


    现在、我不想完全切换您的设备、但由于您更熟悉 SimpleLink 平台、我想介绍 MSP DriverLib。 它可能更适合您使用进行开发、因为它更类似、因为它基于 API、而不是我以前链接过您的直接寄存器级别示例。

    MSP430FR4133 Driverlib 可在 MSPware 中找到、网址为 :dev.ti.com/.../

    大多数情况下都有相同的 I2C 示例、但使用示例文件夹下的 I2C driverlib API。 不过、我没有看到"standard_master"示例、这可能是我链接的驱动程序级别示例中最全面的示例。 用户指南和 API 指南将列出并描述所有 API。 以下是 USCI_B_I2C 的函数: dev.ti.com/.../

    继续回答问题、我很乐意为您提供帮助!

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

    我已经使 I2C 事务最终工作、但接收到的数据没有以正确的方式存储。  

    #include "TempSensorMode.h"
    #include "hal_lcd.h"
    #include "main.h"
    #include "driverlib.h"
    #include "Board.h"
    
    #include 
    
    /*温度传感器十六进制地址*/
    #define TMP117_address 0x48
    
    // TMP117寄存器的十六进制地址*/
    #define TEMP_REG 0x00
    #define CONFIG_REG 0x01
    #define HIGH_LIM_REG 0x02
    #define LOW_LIM_REG 0x03
    #define EEPROM_UNLOCK_REG 0x04
    #define TEMP_OFFSET_REG 0x07
    
    //*设置温度阈值保持(高电平和低电平)*/
    const uint8_t LowLim_HByte = 0x0b;
    const uint8_t LowLim_LByte = 0x00;
    const uint8_t HighLim_HByte = 0x0E;
    const uint8_t HighLim_LByte = 0x80;cont
    配置= 0xint8_t Uint8_Hbyte
    = 0x0E;cont Uint8_t 配置
    
    
    //以 kHz 为单位的 SMCLK 的目标频率
    #define CS_SMCLK_NEW_FREQUENCY IN_kHz 1000
    
    //SMCLK/FLLRef 比率
    #define CS_SMCLK_FLLREF_RA比率30
    
    //指定预期接收数据计数。
    #define RXCOUNT 0x02
    
    #define CALADC_15V_30C *((unsigned int *) 0x1A1A) //温度传感器校准-30 C
    #define CALADC_15V_85C *((unsigned int *) 0x1A1C) //温度传感器校准-85 C
    
    volatile unsigned char * tempUnit =&BAKMEM4_H; //温度单元
    volatile unsigned short * degC =(volatile unsigned short *)&BAKMEM5; //测量
    volatile unsigned short * degF =(volatile unsigned short *)&BAKMEM6; //华氏度量
    
    uint8_t RXData;
    //uint8_t TXData = 0;
    //uint8_t TXByteCtr;
    
    void initGPIO ()
    {
    // I2C 引脚5.2 (SDA)和5.3 (SCL)
    GPIO_setPeripheralModuleFunctionInputPin (GPIO_PORT_UCB.S.、GPIO_PIN_UCB.S.、GPIO_Function_UCB.S.);
    GPIO_setPeripheralModuleFunctionInputPin (GPIO_PORT_UCB0SDA、GPIO_PIN_UCB0SDA、GPIO_FUNCTION_UCB0SDA);
    
    //禁用 GPIO 上电默认高阻抗模式以激活
    //先前配置的端口设置
    PMM_unlockLPM5();
    }
    
    void initI2C()
    {
    //设置 DCO FLL 基准= REFO
    CS_initClockSignal (CS_FLLREF、CS_REFOCLK_SELECT、CS_Clock_divider);
    
    //设置比率和所需的 MCLK 频率并初始化 DCO
    CS_initFLSettle (CS_SMCLK_frequised_in_kHz、CS_SMCLK_FLLREF_Ratio);
    
    //set ACLK = VLO、分频器为1
    CS_initClockSignal (CS_ACLK、CS_VLOCLK_select、CS_clock_divider);
    
    //set SMCLK = DCO、分频器为1
    CS_initClockSignal (CS_SMCLK、CS_DCOCLKDIV_SELECT、CS_CLOCK_DIVIDER_1);
    
    //set MCLK = DCO、分频器为1
    CS_initClockSignal (CS_MCLK、CS_DCOCLKDIV_SELECT、CS_CLOCK_DIVIDER_1);
    
    initGPIO();
    
    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 = RXCOUNT;
    param.autoSTOPGeneration = EUSCI_B_I2C_SEND_STOP_Automatically _ON_BYTECOUNT_T_THRESHOLD;
    EUSCI_B_I2C_initMaster (EUSCI_B0_BASE、param);
    
    //指定从器件地址
    EUSCI_B_I2C_setSlaveAddress (EUSCI_B0_BASE、TMP117_address);
    
    //将主设备设置为接收模式
    EUSCI_B_I2C_setMode (EUSCI_B0_BASE、EUSCI_B_I2C_Receive_mode);
    
    //启用 I2C 模块以启动操作
    EUSCI_B_I2C_ENABLE (EUSCI_B0_BASE);
    
    EUSCI_B_I2C_clearInterrupt (EUSCI_B0_BASE、EUSCI_B_I2C_Receive_INTERRUPT0 + EUSCI_B_I2C_BYTE_COUNTER_INTERRUPT + EUSCI_B_I2C_NAK_INTERRUPT);
    
    //启用主机接收中断
    EUSCI_B_I2C_enableInterrupt (EUSCI_B0_BASE、EUSCI_B_I2C_Receive_INTERRUPT0 + EUSCI_BI2C_BYTE_COUNTER_INTERRUPT + EUSCI_B_I2C_NAK_INTERRUPT);
    }
    
    void tempSensor (){
    
    WDT_A_HOLD (WDT_A_base);//停止看门狗计时器
    
    initi2C();
    
    while (* tempSensorRunning)
    {
    _DELAY_CYCLES (2000);
    
    IF (*温度传感器运行)
    {
    //在唤醒时打开 LED1以计算温度并更新显示
    P1OUT |= BIT0;
    
    while (EUSCI_B_I2C_Sending = EUSCI_B_I2C_masterIsStopSent (EUSCI_B0_BASE));
    
    EUSCI_B_I2C_masterReceiveStart (EUSCI_B0_BASE);
    
    //输入 LPM0,带中断
    _bis_SR_register (CPUOFF+GIE);
    
    //计算温度,单位为摄氏度和华氏度
    带符号短整型温度=(RXData * 0.0078125)* 10;//((RXData[0]<<8)|RXData[1]);
    *°C =((长)温度);
    *°F =(*°C)* 9 / 5 + 320;
    
    //更新 LCD 上的温度
    displayTemp();
    
    P1OUT &=~BIT0;
    }
    }
    
    
    
    //-------------------------------------------------------------- //
    void tempSensorModeInit()
    {
    *tempSensorRunning=1;
    
    displayScrollText ("TEMPSENSOR mode");
    }
    
    void displayTemp()
    {
    ClearLCD();
    
    //根据 tempUnit 状态选择 C 或 F
    Int deg;
    如果(* tempUnit = 0)
    {
    showChar ('C'、pos6);
    DEG =*°C;
    }
    其他
    {
    showChar ('F'、pos6);
    DEG =*°F;
    }
    
    //处理负值
    如果(deg < 0)
    {
    deg *=-1;
    //负号
    LCDMEM[pos1+1]|= 0x04;
    }
    
    //手柄显示高达999.9度
    如果(de>= 1000)
    showChar ((deg/1000)%10 +"0"、pos2);
    如果(de>= 100)
    showChar ((deg/100)%10 +"0"、pos3);
    如果(de>= 10)
    showChar ((deg/10)%10 +"0"、pos4);
    如果(DEG>=1)
    showChar ((deg/1)%10 +"0"、pos5);
    如果(de==0){
    while (1)
    displayScrollText ("error");
    }
    
    //小数点
    LCDMEM[pos4+1]|= 0x01;
    
    //度符号
    LCDMEM[pos5+1]|= 0x04;
    }
    
    #if defined (__TI_Compiler_version__)|| Defined (__IAR_systems_ICC__)
    #pragma vector=USCI_B0_Vector
    __interrupt
    #elif defined (__GNU_)
    __attribute__(interrupt (USCI_B0_Vector
    
    
    )#void (_USCI_BIC0
    静态 uint8_t 计数= 0;
    开关(__evo_in_range (UCB0IV、USCI_I2C_UCBIT9IFG))
    {
    USCI_NONE 案例: //无中断中断中断;
    中断;
    USCI_I2C_UCALIFG 案例: //仲裁丢失
    中断;
    案例 USCI_I2C_UCNACKIFG://接收到 NAK (仅限主器件)
    EUSCI_B_I2C_masterReceiveStart (EUSCI_B0_BASE);
    中断;
    USCI_I2C_UCSTTIFG 案例: //用自己的地址检测到起始条件(只适用于从机模式)
    中断;
    USCI_I2C_UCSTPIFG 案例: //检测到停止条件(主机和从机模式)
    中断;
    USCI_I2C_UCRXIFG3案例: // RXIFG3
    中断;
    USCI_I2C_UCTXIFG3案例: // TXIFG3
    中断;
    USCI_I2C_UCRXIFG2案例: // RXIFG2
    中断;
    USCI_I2C_UCTXIFG2案例: // TXIFG2
    中断;
    USCI_I2C_UCRXIFG1案例: // RXIFG1
    中断;
    USCI_I2C_UCTXIFG1案例: // TXIFG1
    中断;
    USCI_I2C_UCRXIFG0案例: // RXIFG0
    //获取 RX 数据
    RXData = EUSCI_B_I2C_masterReceiveSingle (
    EUSCI_B0_BASE
    );
    如果(++COUNT >= RXCOUNT){
    计数= 0;
    _BIC_SR_REGISTER_ON_EXIT (CPUOFF);//退出 LPM0
    }
    中断;//向量24:RXIFG0中断;
    USCI_I2C_UCTXIFG0案例: // TXIFG0
    中断;
    案例 USCI_I2C_UCBCNTIFG://达到字节计数限制(UCBxTBCNT)
    GPIO_toggleOutputOnPin (
    GPIO_PORT_P1、
    GPIO_PIN0
    );
    中断;
    案例 USCI_I2C_UCCLTOIFG://时钟低电平超时-时钟保持低电平的时间过长
    中断;
    案例 USCI_I2C_UCBIT9IFG://在发送的第9位上生成(用于调试)
    中断;
    默认值:
    中断;
    }
    }
    

    它存储来自 TMP117的数据的第二个字节、例如246。 这是直接来自传感器的数字信号、我需要乘以0.0078125以获得正确的温度、然后乘以10以在 LCD 上正确显示。 我得到的是1.9、这是246 * 0.0078125的正确值、但这会消除数据的第一个字节。 来自 TMP117的信号应该是2个字节、在调试时、我注意到存储了第一个字节、但随后被覆盖。 我尝试通过使缓冲区 uint16而不是 uint8来解决这个问题、但这不起作用、然后我尝试让它存储2个字节、将缓冲区初始化为 RXData[2]、但出现一个错误、提示它是可修改的变量。 我能否获得一些有关数据存储语法的帮助?  

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    当 RXData 是一个数组时、可通过 RXData[0]等访问这些元素
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    #if defined (__TI_Compiler_version__)|| defined (__IAR_systems_icc_)
    #pragma vector=USCI_B0_vector
    __interrupt
    #elif defined (__GNU__)
    __attribute__(interrupt (USCI_B0_vector)))#endif USCI_B0_vector __interrupt #elif
    
    ISR (void
    
    静态 uint8_t 计数= 0;
    开关(__evo_in_range (UCB0IV、USCI_I2C_UCBIT9IFG))
    {
    USCI_NONE 案例: //无中断中断中断;
    中断;
    USCI_I2C_UCALIFG 案例: //仲裁丢失
    中断;
    案例 USCI_I2C_UCNACKIFG://接收到 NAK (仅限主器件)
    EUSCI_B_I2C_masterReceiveStart (EUSCI_B0_BASE);
    中断;
    USCI_I2C_UCSTTIFG 案例: //用自己的地址检测到起始条件(只适用于从机模式)
    中断;
    USCI_I2C_UCSTPIFG 案例: //检测到停止条件(主机和从机模式)
    中断;
    USCI_I2C_UCRXIFG3案例: // RXIFG3
    中断;
    USCI_I2C_UCTXIFG3案例: // TXIFG3
    中断;
    USCI_I2C_UCRXIFG2案例: // RXIFG2
    中断;
    USCI_I2C_UCTXIFG2案例: // TXIFG2
    中断;
    USCI_I2C_UCRXIFG1案例: // RXIFG1
    中断;
    USCI_I2C_UCTXIFG1案例: // TXIFG1
    中断;
    USCI_I2C_UCRXIFG0案例: // RXIFG0
    //获取 RX 数据
    RXData[0]= EUSCI_B_I2C_ReceivmasterSingle (
    EUSCI_B0_BASE
    );
    RXData[1]= EUSCI_B_I2C_ReceivmasterSingle (
    EUSCI_B0_BASE
    );
    如果(++COUNT >= RXCOUNT){
    计数= 0;
    _BIC_SR_REGISTER_ON_EXIT (CPUOFF);//退出 LPM0
    }
    中断;//向量24:RXIFG0中断;
    USCI_I2C_UCTXIFG0案例: // TXIFG0
    中断;
    案例 USCI_I2C_UCBCNTIFG://达到字节计数限制(UCBxTBCNT)
    中断;
    案例 USCI_I2C_UCCLTOIFG://时钟低电平超时-时钟保持低电平的时间过长
    中断;
    案例 USCI_I2C_UCBIT9IFG://在发送的第9位上生成(用于调试)
    中断;
    默认值:
    中断;
    }
    } 

    我将 RXData 更新为一个数组、但它似乎仍然只占用一个字节并复制它。 在查看了多字节示例之后、似乎唯一可行的方法是创建状态机?

    在测试我们的一些编码解决方案后进一步对此进行扩展。 我尝试从 TMP117读取2个字节、然后使用位移和逻辑"OR"组合两个字节。 现在组合的2个字节将被操作以输出到 LCD 上。 我的问题是、此代码似乎只读取一个字节。

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

    嗨、Christopher、

    中断只会针对每个字节触发、因此每次只能在 RXbuff 中存储一个字节。  因此、您应该在每次 ISR 触发时递增指针、然后在每次完整事务后重置指针。  

    根据您的代码、我认为这样的代码可以正常工作:   

    //获取 RX 数据
    RXData[count]= EUSCI_B_I2C_ReceivmasterSingle (
    EUSCI_B0_BASE
    );
    
    如果(++COUNT >= RXCOUNT){
    计数= 0;
    _BIC_SR_REGISTER_ON_EXIT (CPUOFF);//退出 LPM0
    } 

    尝试一下、让我们知道它是否符合您的预期。   

    JD

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    这是一种很棒的方式! 我没有意识到 ISR 可以这样工作。 感谢您的帮助、我一定会继续答复、因为我将尝试在同一代码中进行写入以在 TMP117上设置不同的寄存器。