3
\$\begingroup\$

So the setup I am using is a K22FN512 (Arm Cortex M4) along with a AGM01(9DOF sensor board, gyro/accel/mag). The board containing the sensors has an I2C interface to communicate with the two chips mounted on it. I have the breakout board Vdd and Vdio connected to the K22's 3.3v pin. The slave address and reset pins from the sensor board are connected to the K22 though GPIO.

Now I can not seem to get the slave to acknowledge anything at all. So far I have done the following:

  • Measured Vdd and Vdio, both fine
  • Measured SCL and SDA low/high voltages, both fine
  • Slowed down clock from 90kHz to ~1.5kHz
  • Switched the slave address from 0x20 to 0x21, still not acknowledging
  • Moved logic probes over to test points soldered on sensor breakout board, signal still strong no signs of cable/capacitance problems

I'm at a loss on what to try next. Below is my code, logic analyzer output, and links to the documentation of the hardware. The code is not complete, I intend to also handle multi-bytes reads and other stuff but right now I can't even read the device id register.

Logic analyzer output

Ref manual for K22:

Can't post more then two links since I'm under 10 rep (apparently the picture counts)

Gyroscope is the FXAS21002C

Breakout board is the BRKT-STBC-AGM01-QRC

Code:

    /*
    Sensor module header
    */
    #ifndef SENSORS_H
    #define SENSORS_H

    #include <cmsis_os.h>                       // CMSIS RTOS header file
    #include "MK22F51212.h"                     // CMSIS  Peripheral Access Layer for MK22F51212

    // I2C defs
    #define I2C_READ_BIT        0x01
    #define I2C_WRITE_BIT       0x00
    #define I2C_READ_BUFF         6u
    #define I2C_READ(add)       ((uint8_t)((((uint8_t)add) << 1) | I2C_READ_BIT));
    #define I2C_WRITE(add)      ((uint8_t)((((uint8_t)add) << 1) | I2C_WRITE_BIT));

     // Gyro address defs
     #define GYRO_ADD                   0x20
     #define GYRO_STATUS                0x00
     #define GYRO_X_MSB                 0x01
     #define GYRO_X_LSB                 0x02
     #define GYRO_Y_MSB                 0x03
     #define GYRO_Y_LSB                 0x04
     #define GYRO_Z_MSB                 0x05
     #define GYRO_Z_LSB                 0x06
     #define GYRO_DR_STATUS             0x07
     #define GYRO_F_STATUS              0x08
     #define GYRO_F_SETUP               0x09
     #define GYRO_F_EVENT               0x0A
     #define GYRO_INT_SRC_FLAG          0x0B
     #define GYRO_WHO_AM_I              0x0C
     #define GYRO_CTRL_REG0             0x0D
     #define GYRO_RT_CFG                0x0E
     #define GYRO_RT_SRC                0x0F
     #define GYRO_RT_THS                0x10
     #define GYRO_RT_COUNT              0x11
     #define GYRO_TEMP                  0x12
     #define GYRO_CTRL_REG1             0x13
     #define GYRO_CTRL_REG2             0x14
     #define GYRO_CTRL_REG3             0x15

     // RTX core 
     int Init_Thread_Sensors (void);
     void Thread_Sensors (void const *argument);

     // Sensor struct for sensors state
    typedef struct {
        int16_t gyro_x;
        int16_t gyro_y;
        int16_t gyro_z;
        int16_t accel_x;
        int16_t accel_y;
        int16_t accel_z;
        int16_t mag_x;
        int16_t mag_y;
        int16_t mag_z;
    }sensor;

    // Struct for I2C data
    typedef struct {
        uint8_t device;
        uint8_t reg;    
        uint8_t bytes;
        uint8_t data[I2C_READ_BUFF];
    }i2c_transfer;

    // Sensor model and mutex
    extern sensor sensorData;
    extern osMutexId sensor_mutex;

    // Functions
    void init_I2C(void);
    void init_IMU(void);
    void read_I2C(i2c_transfer*);
    void write_I2C(i2c_transfer*);

    #endif

.

    /*----------------------------------------------------------------------------
    *      Sensor thread, I2C0 used
    *---------------------------------------------------------------------------*/
    #include "Sensors.h"

    // RTX info
    osThreadId tid_Thread_Sensors;                          // thread id
    osThreadDef (Thread_Sensors, osPriorityNormal, 1, 0);   // thread object

    void init_I2C(void){

        // Enable module clock
        SIM_SCGC4 |= SIM_SCGC4_I2C0_MASK;   

        // Config pin mux for I2C bus
        SIM_SCGC5 |= SIM_SCGC5_PORTB_MASK;
        PORTB_PCR0 = PORT_PCR_MUX(0x02) | PORT_PCR_ODE_MASK | PORT_PCR_PE_MASK |PORT_PCR_PS_MASK;       // SCL
        PORTB_PCR1 = PORT_PCR_MUX(0x02) | PORT_PCR_ODE_MASK | PORT_PCR_PE_MASK |PORT_PCR_PS_MASK;       // SDA

        // Config gpio
        SIM_SCGC5 |= SIM_SCGC5_PORTC_MASK;
        PORTC_PCR5 = PORT_PCR_MUX(0x01) | PORT_PCR_ODE_MASK;    // RST
        PORTC_PCR6 = PORT_PCR_MUX(0x01) | PORT_PCR_ODE_MASK;    // SA   

        GPIOC_PDDR |= (1<<5) | (1<<6);
        GPIOC_PSOR |= 1<<5; 
        GPIOC_PCOR |= 1<<6;

        // Config I2C0  
        I2C0_F = I2C_F_MULT(0x02) | I2C_F_ICR(0x3F);    
        I2C0_C1 = I2C_C1_IICEN_MASK;    
    }

    void init_IMU(void){

    }

    int Init_Thread_Sensors (void) {

        init_I2C();

        tid_Thread_Sensors = osThreadCreate (osThread(Thread_Sensors),       NULL);
        if(!tid_Thread_Sensors) return(-1);

        return(0);
    }

    void Thread_Sensors (void const *argument) {
        i2c_transfer packet;
        while (1) {
            packet.device = GYRO_ADD;
            packet.reg = GYRO_WHO_AM_I;     
            read_I2C(&packet);

            osThreadYield();                                            // suspend thread
        }
    } 

    void read_I2C(i2c_transfer* data){

        // Wait for bus ready
        while(I2C0_S & I2C_S_BUSY_MASK);

        // Trigger Start signal
        I2C0_C1 |= I2C_C1_TX_MASK | I2C_C1_MST_MASK;        

        // Send Write command
        I2C0_D = I2C_WRITE(data->device);

        // Wait for ACK     
        //while((I2C0_S & I2C_S_RXAK_MASK)==1){}; 
        while((I2C0_S & I2C_S_IICIF_MASK)==0){};   
        I2C0_S |= I2C_S_IICIF_MASK;

        // Write reg address
        I2C0_D = data->reg;

        // Wait for ACK
        //while((I2C0_S & I2C_S_RXAK_MASK)==1){};  
        while((I2C0_S & I2C_S_IICIF_MASK)==0){};   
        I2C0_S |= I2C_S_IICIF_MASK;

        // Repeat start to engage read
        I2C0_C1 |= I2C_C1_RSTA_MASK;    

        // Send Read device command
        I2C0_D = I2C_READ(data->device);

        // Wait for ACK     
        //while((I2C0_S & I2C_S_RXAK_MASK)==1){};  
        while((I2C0_S & I2C_S_IICIF_MASK)==0){};   
        I2C0_S |= I2C_S_IICIF_MASK;  

        // Switch to RX mode
        I2C0_C1 &= ~I2C_C1_TX_MASK;
        I2C0_C1 |= I2C_C1_TXAK_MASK;    

        // Trigger Read and wait for Data
        data->data[0] = I2C0_D;
        //while((I2C0_S & I2C_S_TCF_MASK)==0){};     
        while((I2C0_S & I2C_S_IICIF_MASK)==0){};   
        I2C0_S |= I2C_S_IICIF_MASK;  

        // Store Sata
        data->data[0] = I2C0_D;

        // Trigger Stop
        I2C0_C1 &= ~(I2C_C1_MST_MASK | I2C_C1_TX_MASK); 
    }

    void write_I2C(i2c_transfer* data){

    }

Solution found

Turns out I need to read the doc's closer, had the device in reset and didn't pull SPI_CS_B high like I was suppose to.

\$\endgroup\$
  • \$\begingroup\$ Is the SA0 line at the correct level (low) ? \$\endgroup\$ – Steve G Jan 15 '16 at 20:46
1
\$\begingroup\$

Things to check:

1) Check pin 8 on gyro (I2C_B/SPI). This pin selects between I2C and SPI modes. You need it to be configured for I2C mode. There is probably a jumper to control this. You want it to be LOW for I2C mode.

2) Check pin 17 on the gyro (SPI_CS_B). The datasheet says this pin must be tied high if you are using I2C mode.

3) Double check power on the gyro board. Make sure it is 3.3.

4) If there is any way to independently check the gyro board in any way, just to gain confidence that it is functional, that would be a good thing to do.

5) capture SCL and SDA with a oscilloscope instead of logic analyzer to look for signal integrity problems. Maybe the gyro is trying to pull data low during ack low, but something else is pulling it high?

Good luck!

\$\endgroup\$
  • \$\begingroup\$ Good answer mkeith, but a small point... SDA/SCL is never driven high, it is only driven low and it is only pulled high by the pull-up resistor. So any device driving low (for an ACK) will override the pull-up. \$\endgroup\$ – Steve G Jan 15 '16 at 21:14
  • \$\begingroup\$ @SteveG, not supposed to be driven high. Doesn't mean it can't happen due to some design error. For example, wrong value pullup (2.2 Ohms instead of 2.2k). However, the data line is toggling, so there is probably nothing pinning it high. \$\endgroup\$ – mkeith Jan 15 '16 at 21:20
  • \$\begingroup\$ fair point. As you suggested, checking functionality of the board with eval software would be a good idea. \$\endgroup\$ – Steve G Jan 15 '16 at 21:54
  • \$\begingroup\$ According to the docs, the board ships in I2C mode by default. You have to remove and add resistors to enable SPI mode. \$\endgroup\$ – Adam Haun Jan 15 '16 at 22:05
  • 2
    \$\begingroup\$ Debugging is an art in itself. However with this post you helped me realize I should really read the documentation closer then I was. I made a false assumption that both resets were active high, which they are not. The accel/mag is active high reset while the gyro is active low reset. Also completely missed that I had to tie SPI_CS_B high. Now its not only acknowledging, but giving me the correct data as well at 100kHz! \$\endgroup\$ – Dave851 Jan 15 '16 at 23:32
0
\$\begingroup\$

Have you checked the jumpers on the breakout board? The quick reference card shows what looks like jumpers for selecting the SDA/SCL line and controlling the reset signals. Try checking the reset signals for each sensor and make sure they're not held in reset.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.