【问题标题】:I2C interface on TivaTiva 上的 I2C 接口
【发布时间】:2014-07-09 17:14:45
【问题描述】:

在 Tiva(Texas Instruments Cortex M4F ARM)TM4C129XNCZAD 上,我的 I2C 接口有问题。我通过端口 K 启用了 I2C 模块 4 上的主设备和通过端口 B 启用 I2C 模块 6 上的从设备。我已将两个 I2C 模块互连。使用德州仪器驱动程序库,我尝试使用 I2C_MASTER_CMD_SINGLE_SEND 命令发送 1 个字节。我花了很多时间让它工作,但 SCK 线保持低逻辑电平。我完全按照 TivaWare™ Peripheral Driver Library USER'S GUIDE 进行操作,但通信不起作用。有人有这方面的经验吗?

这是我的代码:

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_i2c.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/i2c.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "inc/tm4c129xnczad.h"

#define SLAVE_ADDRESS 0x3C

void  delay  (void)
{
    volatile uint32_t ui32Loop; 
    for(ui32Loop = 0; ui32Loop < 200000; ui32Loop++);
}

volatile  uint32_t  result;

int  main  (void)
{
    SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); 
    //
    // Enable the GPIO port that is used for the on-board LED.
    //
    SYSCTL_RCGCGPIO_R = SYSCTL_RCGCGPIO_R3 | SYSCTL_RCGCGPIO_R9 | SYSCTL_RCGCGPIO_R1;
    //
    // Do a dummy read to insert a few cycles after enabling the peripheral.
    //
    result = SYSCTL_RCGCGPIO_R;
    //
    // Enable the GPIO pin for the LED (PD3).  Set the direction as output, and
    // enable the GPIO pin for digital function.
    //
    GPIO_PORTD_AHB_DIR_R = 0x8;
    GPIO_PORTD_AHB_DEN_R = 0x8;
    GPIO_PORTK_DEN_R = 0xC0;        // Enable Port K for I2C module 4

    GPIO_PORTB_AHB_DEN_R = 0xC0;    // Enable Port B for I2C module 6

    SYSCTL_RCGCI2C_R = (1 << 4) | (1 << 6);  // Mode Clock Gating Control for I2C modules 4 and 6
    GPIO_PORTK_AFSEL_R = 0xC0;      // Alternate Function Select PK6, PK7
    GPIO_PORTB_AHB_AFSEL_R = 0xC0;  // Alternate Function Select PB6, PB7
    GPIOPinConfigure(GPIO_PK6_I2C4SCL);
    GPIOPinConfigure(GPIO_PK7_I2C4SDA);
    GPIOPinConfigure(GPIO_PB6_I2C6SCL);
    GPIOPinConfigure(GPIO_PB7_I2C6SDA);

    GPIOPinTypeI2C(GPIO_PORTK_BASE, 7);       // Configurtes SDA
    GPIOPinTypeI2CSCL(GPIO_PORTK_BASE, 6);    // Configurtes SCL
    GPIOPinTypeI2C(GPIO_PORTB_BASE, 7);       // Configurtes SDA
    GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, 6);    // Configurtes SCL

    I2CMasterInitExpClk(I2C4_BASE, SysCtlClockGet(), false);

    I2CSlaveEnable(I2C6_BASE);
    I2CSlaveInit(I2C6_BASE, SLAVE_ADDRESS);
    I2CMasterSlaveAddrSet(I2C4_BASE, SLAVE_ADDRESS, false);
    //
    // Loop forever.
    //
    while(1)
    {
        //
        // Turn on the LED.
        //
        GPIO_PORTD_AHB_DATA_R |= 0x8;

        I2CMasterDataPut(I2C4_BASE, 0x33);
        I2CMasterControl(I2C4_BASE, I2C_MASTER_CMD_SINGLE_SEND);
        //
        // Wait until the slave has received and acknowledged the data.
        //
        while(!(I2CSlaveStatus(I2C6_BASE) & I2C_SLAVE_ACT_RREQ));
        //
        // Read the data from the slave.
        //
        result = I2CSlaveDataGet(I2C6_BASE);
        //
        // Wait until master module is done transferring.
        //
        while(I2CMasterBusy(I2C4_BASE));
        //
        // Delay for a bit.
        //
        delay ();
        //
        // Turn off the LED.
        //
        GPIO_PORTD_AHB_DATA_R &= ~(0x8);
        //
        // Delay for a bit.
        //
        delay ();
    }
}

【问题讨论】:

  • 你在 I2C 线上加了上拉电阻吗? I2C 是一个集电极开路信号系统,因此总线需要上拉电阻才能工作。对于您的测试,您可能可以启用 GPIO 的内部上拉(如果可用)。
  • 程序 GPIOPinTypeI2C() 打开内部上拉电阻,但 SDA 线在高电平等待。问题出在我的 SCL 方面。
  • 函数GPIOPinTypeI2CSCL呢?为两个 SCL 引脚(主/从)调用它。这可以解释为什么 SDA 可以,但 SCL 不行。
  • 如果这些函数被轮询,那么您可能无法按照您希望的方式执行此操作。 I2C 从机可能会延长时钟,直到您调用 I2CSlaveDataGet 函数,但您的代码可能会在等待能够发送的早期 I2C 主机调用之一上被阻塞。我对这部分的 I2C 驱动程序不够熟悉,无法诊断是否是问题所在。
  • 还要考虑到 I2C 操作的软件精度非常重要。 I2C 是一种反复无常的总线,设备也是。如果您说将错误的地址推送到 BUS 并且没有从机应答,那么在您的主机端,它是一些错误状态,它会立即卡在某个地方,直到状态/错误得到正确处理。请注意这样的事情。

标签: c embedded arm microcontroller


【解决方案1】:

问题已解决。有问题:

  • 填充外部引体向上是必不可少的。
  • 使用GPIOPinTypeI2C() 第二个参数作为位域而不是位号。
  • 过程SysCtlClockSet() 专门用于TM4C123 器件。而是使用g_ui32SysClock = SysCtlClockFreqSet((SYSCTL_OSC_MAIN | SYSCTL_XTAL_25MHZ | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_320), 40000000);
  • 对于主时钟设置,不要使用SysCtlClockGet() 程序。这也专用于 TM4C123 器件。而是使用I2CMasterInitExpClk(I2C4_BASE, g_ui32SysClock, false);

这是更新后的代码,

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_i2c.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/i2c.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "inc/tm4c129xnczad.h"

#define SLAVE_ADDRESS 0x3C

void    delay   (void)
{
  volatile uint32_t ui32Loop;   
    for(ui32Loop = 0; ui32Loop < 200; ui32Loop++);
}


volatile uint32_t  result;
    uint32_t    g_ui32SysClock;

int main(void)
{
    g_ui32SysClock = SysCtlClockFreqSet((SYSCTL_OSC_MAIN | SYSCTL_XTAL_25MHZ | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_320), 40000000); 
    //
    // Enable the GPIO port that is used for the on-board LED.
    //
    SYSCTL_RCGCGPIO_R = SYSCTL_RCGCGPIO_R3 | SYSCTL_RCGCGPIO_R9 | SYSCTL_RCGCGPIO_R1;
    //
    // Do a dummy read to insert a few cycles after enabling the peripheral.
    //
    result = SYSCTL_RCGCGPIO_R;
    //
    // Enable the GPIO pin for the LED (PD3).  Set the direction as output, and
    // enable the GPIO pin for digital function.
    //
    GPIO_PORTD_AHB_DIR_R = 0x8;
    GPIO_PORTD_AHB_DEN_R = 0x8;

    SYSCTL_RCGCI2C_R = (1 << 4) | (1 << 6); // Mode Clock Gating Control for I2C modules 4 and 6

    GPIOPinConfigure(GPIO_PK6_I2C4SCL);
    GPIOPinConfigure(GPIO_PK7_I2C4SDA);
    GPIOPinConfigure(GPIO_PB6_I2C6SCL);
    GPIOPinConfigure(GPIO_PB7_I2C6SDA);

    GPIOPinTypeI2C(GPIO_PORTK_BASE, (1 << 7));       // Configures SDA
    GPIOPinTypeI2CSCL(GPIO_PORTK_BASE, (1 << 6));    // Configures SCL
    GPIOPinTypeI2C(GPIO_PORTB_BASE, (1 << 7));       // Configures SDA
    GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, (1 << 6));    // Configures SCL

    I2CMasterInitExpClk(I2C4_BASE, g_ui32SysClock, false);

    I2CSlaveEnable(I2C6_BASE);
    I2CSlaveInit(I2C6_BASE, SLAVE_ADDRESS);
    I2CMasterSlaveAddrSet(I2C4_BASE, SLAVE_ADDRESS, false);
    //
    // Loop forever.
    //
    while(1)
    {
        //
        // Turn on the LED.
        //
        GPIO_PORTD_AHB_DATA_R |= 0x8;

        I2CMasterDataPut(I2C4_BASE, 0x33);
        I2CMasterControl(I2C4_BASE, I2C_MASTER_CMD_SINGLE_SEND);
        //
        // Wait until the slave has received and acknowledged the data.
        //
        while(!(I2CSlaveStatus(I2C6_BASE) & I2C_SLAVE_ACT_RREQ));           
        //
        // Read the data from the slave.
        //
        result = I2CSlaveDataGet(I2C6_BASE);
        //
        // Wait until master module is done transferring.
        //
        while(I2CMasterBusy(I2C4_BASE));
        //
        // Delay for a bit.
        //
        delay   ();
        //
        // Turn off the LED.
        //
        GPIO_PORTD_AHB_DATA_R &= ~(0x8);
        //
        // Delay for a bit.
        //
        delay   ();
    }
}

【讨论】:

    【解决方案2】:

    我能够让 I2C 在 TIVA Launchpad TM4C123GH6PM 上运行。我使用了 Tivaware 库。我创建了 3 个函数,init 和 write。以下是函数。

    初始化

    void initI2C0(void)
    {
       SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
    
       //reset I2C module
       SysCtlPeripheralReset(SYSCTL_PERIPH_I2C0);
    
       //enable GPIO peripheral that contains I2C
       SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    
       // Configure the pin muxing for I2C0 functions on port B2 and B3.
       GPIOPinConfigure(GPIO_PB2_I2C0SCL);
       GPIOPinConfigure(GPIO_PB3_I2C0SDA);
    
       // Select the I2C function for these pins.
       GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2);
       GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);
    
       // Enable and initialize the I2C0 master module.  Use the system clock for
       // the I2C0 module.  The last parameter sets the I2C data transfer rate.
       // If false the data rate is set to 100kbps and if true the data rate will
       // be set to 400kbps.
       I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);
    
       //clear I2C FIFOs
       HWREG(I2C0_BASE + I2C_O_FIFOCTL) = 80008000;
    }
    

    I2C 写入函数

    void writeI2C0(uint16_t device_address, uint16_t device_register, uint8_t device_data)
    {
       //specify that we want to communicate to device address with an intended write to bus
       I2CMasterSlaveAddrSet(I2C0_BASE, device_address, false);
    
       //register to be read
       I2CMasterDataPut(I2C0_BASE, device_register);
    
       //send control byte and register address byte to slave device
       I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
    
       //wait for MCU to finish transaction
       while(I2CMasterBusy(I2C0_BASE));
    
       I2CMasterSlaveAddrSet(I2C0_BASE, device_address, true);
    
       //specify data to be written to the above mentioned device_register
       I2CMasterDataPut(I2C0_BASE, device_data);
    
       //wait while checking for MCU to complete the transaction
       I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
    
       //wait for MCU & device to complete transaction
       while(I2CMasterBusy(I2C0_BASE));
    }
    

    TIVA + I2C 的完整代码可以在here 找到。

    参考

    【讨论】:

    • @Parag 码字本来就是,为什么要改码。完整代码在github上
    猜你喜欢
    • 1970-01-01
    • 2020-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-17
    • 2018-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多