I'm using an STM LIS3DSH accelerometer with my TM4C1294 dev board communicating with I2C module 2. The LIS3DSH has a "who am I" register where it returns a specific byte value when queried. I had this returning the proper byte when doing the communication using the standard peripheral library, however when moving the code to a function, it no longer returned the proper byte value, so I figured I had some timing/configuration issue with my I2C communication.
After much fiddling and unable to get things to work, I decided to go down another route: Bypass the standard peripheral library and read/write directly from/to registers to see if I could get more insight into the problem. I've done this by following the "Initialization and Configuration" for "Configure the I2C Module to Transmit a Single Byte as a Master" as specified in section 18.4.1 of the TM4C1294NCPDT datasheet.
Then to read the data, I've followed the "Master Single RECEIVE" flowchart in section 18.3.6.1 of the TM4C1294NCPDT datasheet. No luck though. I'm not getting the expected byte returned from the accelerometer, I just get a value of zero when reading the I2C2 data register. And I've verified I'm not receiving an error in the status register either.
I've triple checked my code. I'm wondering if anyone would kindly be a second pair of eyes to review my code and/or make any suggestions. It's very short and well commented and I would be INCREDIBLY appreciative for any help. I've attached the code, it's just a main.c file.
Thanks in advance,
Terence
#include <stdint.h> #include <stdbool.h> #include "inc/tm4c1294ncpdt.h" #include "driverlib/sysctl.h" #include "driverlib/rom_map.h" uint32_t g_ui32SysClock; int main() { // Configure the system clock MAP_SysCtlMOSCConfigSet(SYSCTL_MOSC_HIGHFREQ); // Set the configuration of the main oscillator (MOSC) control. // SYSCTL_MOSC_HIGHFREQ -- Indicates the MOSC is greater than 10MHz g_ui32SysClock = MAP_SysCtlClockFreqSet( (SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480), 120000000); // Set the system clock frequency to run from the PLL at 120MHz // SYSCTL_XTAL_25MHZ -- Indicates an external crystal frequency of 25 MHz // SYSCTL_OSC_MAIN -- Use external crystal or oscillator // SYSCTL_USE_PLL -- Selects PLL output as system clock // SYSCTL_CFG_VCO_480 -- Sets PLL VCO output to 480 MHz /////////////////////////////////////////////////////////////////////////////////////////////////////////// // The following of reading comes from page 1297 of the datasheet // "Configure the I2C Module to Transmit a Single Byte as a Master" // 1. Enable the I2C clock using the RCGCI2C register in the System Control module (see page 391). SYSCTL_RCGCI2C_R |= (1 << 2); // I2C2 while((SYSCTL_RCGCI2C_R & (1 << 2)) == 0); // 2. Enable the clock to the appropriate GPIO module via the RCGCGPIO register in the System // Control module (see page 382). To find out which GPIO port to enable, refer to Table // 26-5 on page 1808. SYSCTL_RCGCGPIO_R |= (1 << 10); // Port L while((SYSCTL_PRGPIO_R & (1 << 10)) == 0); // 3. In the GPIO module, enable the appropriate pins for their alternate function using the // GPIOAFSEL register (see page 770). To determine which GPIOs to configure, see Table // 26-4 on page 1797. // Also, note from page 771: "When using the I2C module, in addition to setting the GPIOAFSEL // register bits for the I2C clock and data pins, the data pins should be set to open drain // using the GPIO Open Drain Select (GPIOODR) register (see examples in �Initialization and // Configuration� on page 753)." I believe we do this in the next step. GPIO_PORTL_AFSEL_R |= ((1 << 1) | 1); // AF for PL0 and PL1 // 4. Enable the I2CSDA pin for open-drain operation. See page 775. GPIO_PORTL_ODR_R |= ((1 << 1) | 1); // Open drain for PL0 and PL1 // 5. Configure the PMCn fields in the GPIOPCTL register to assign the I2C signals to the appropriate // pins. See page 787 and Table 26-5 on page 1808. GPIO_PORTL_PCTL_R |= ((2 << 4) | 2); // Alternate function 2 for pins 0 and 1 // 6. Initialize the I2C Master by writing the I2CMCR register with a value of 0x0000.0010. I2C2_MCR_R |= 0x00000010; // 7. Set the desired SCL clock speed of 100 Kbps by writing the I2CMTPR register with the correct // value. The value written to the I2CMTPR register represents the number of system clock periods // in one SCL clock period. The TPR value is determined by the following equation: // TPR = (System Clock/(2*(SCL_LP + SCL_HP)*SCL_CLK))-1; // TPR = (20MHz/(2*(6+4)*100000))-1; // TPR = 9 // Write the I2CMTPR register with the value of 0x0000.0009. uint32_t TPR = (g_ui32SysClock/(2*(6+4)*100000))-1; I2C2_MTPR_R |= TPR; // 8. Specify the slave address of the master and that the next operation is a Transmit by writing the // I2CMSA register with a value of 0x0000.0076. This sets the slave address to 0x3B. //I2C2_MSA_R = 0x00000076; I2C2_MSA_R |= (0x1E << 1) | 0; // 9. Place data (byte) to be transmitted in the data register by writing the I2CMDR register with the // desired data. I2C2_MDR_R |= 0x0F; // 10. Initiate a single byte transmit of the data from Master to Slave by writing the I2CMCS register // with a value of 0x0000.0007 (STOP, START, RUN). I2C2_MCS_R |= 0x07; // 11. Wait until the transmission completes by polling the I2CMCS register's BUSBSY bit until it has // been cleared. while((I2C2_MCS_R & (1 << 6)) != 0); /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Now do the read from the register. // I've come up with the following from "Figure 18-9. Master Single RECEIVE" from the datasheet (pg 1291). // Slave address (0x1E) with read bit I2C2_MSA_R = (0x1E << 1) | 1; // Wait until the bus is no longer busy while((I2C2_MCS_R & (1 << 6)) != 0); // Initiate stop/start/run I2C2_MCS_R |= 0x07; // Wait until the bus is no longer busy while((I2C2_MCS_R & (1 << 6)) != 0); // Read data uint32_t x = I2C2_MDR_R; while(1); } #ifdef __NADA__ #include <stdint.h> #include <stdbool.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/rom.h" #include "driverlib/rom_map.h" #include "driverlib/sysctl.h" #include "driverlib/systick.h" #include "driverlib/uart.h" #include "driverlib/fpu.h" #include "utils/uartstdio.h" #define SLAVE_ADDRESS 0x1E // 0011110b #define WHO_AM_I_REGISTER 0x0F #define WHO_AM_I_EXPECTED 0x3F uint32_t g_ui32SysClock; void IntHandler(void) { int i = 1; } int main(void) { // Configure the system clock MAP_SysCtlMOSCConfigSet(SYSCTL_MOSC_HIGHFREQ); // Set the configuration of the main oscillator (MOSC) control. // SYSCTL_MOSC_HIGHFREQ -- Indicates the MOSC is greater than 10MHz g_ui32SysClock = MAP_SysCtlClockFreqSet( (SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480), 120000000); // Set the system clock frequency to run from the PLL at 120MHz // SYSCTL_XTAL_25MHZ -- Indicates an external crystal frequency of 25 MHz // SYSCTL_OSC_MAIN -- Use external crystal or oscillator // SYSCTL_USE_PLL -- Selects PLL output as system clock // SYSCTL_CFG_VCO_480 -- Sets PLL VCO output to 480 MHz SysCtlDelay(40000000); SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C2); while(!SysCtlPeripheralReady(SYSCTL_PERIPH_I2C2)); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOL); while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOL)); GPIOPinTypeI2CSCL(GPIO_PORTL_BASE, GPIO_PIN_1); GPIOPinTypeI2C(GPIO_PORTL_BASE, GPIO_PIN_0); GPIOPinConfigure(GPIO_PL0_I2C2SDA); GPIOPinConfigure(GPIO_PL1_I2C2SCL); I2CIntRegister(I2C2_BASE, IntHandler); I2CMasterIntEnable(I2C2_BASE); I2CIntRegister(I2C2_BASE, IntHandler); I2CMasterIntEnable(I2C2_BASE); I2CMasterInitExpClk(I2C2_BASE, SysCtlClockGet(), true); //I2CMasterEnable(I2C2_BASE); //SysCtlDelay(40000000); I2CMasterSlaveAddrSet(I2C2_BASE, SLAVE_ADDRESS, false); while(I2CMasterBusy(I2C2_BASE)); uint32_t errorCode = I2CMasterErr(I2C2_BASE); I2CMasterDataPut(I2C2_BASE, WHO_AM_I_REGISTER); I2CMasterControl(I2C2_BASE, I2C_MASTER_CMD_SINGLE_SEND); //while(I2CMasterBusy(I2C2_BASE)); //I2C_MASTER_ERR_NONE; /* if(errorCode != I2C_MASTER_ERR_NONE) { int i = 0; } */ I2CMasterSlaveAddrSet(I2C2_BASE, SLAVE_ADDRESS, true); while(I2CMasterBusy(I2C2_BASE)); I2CMasterControl(I2C2_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE); while(I2CMasterBusy(I2C2_BASE)); //unsigned char dataRead = I2CMasterDataGet(I2C2_BASE); while(1); } #endif