Issues with using I2C on SAMD21

Go To Last Post
4 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi all,

 

I'm trying to connect to the UBLOX M8Q MAX using the I2C on my SAMD21.
The address of this module is 0x42 and there are only a minimum needed. 0xFD, 0xFE and 0xFF from what i got out of the U-blox datasheet.

Currently I'm unable to setup the proper I2C master connecting.
After the setup of the master I have tried several write or read functions.
There are 3 of them included in the code
 

 

< 

#include "sam.h"
#include "tmr.h"
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

#define I2C_SERCOM_PORT_PMUX              PORT_PMUX_PMUXE_C_Val
#define I2C_SERCOM_PINS_PORT_GROUP        0
#define I2C_SERCOM_SDA_PIN                PORT_PA16
#define I2C_SERCOM_SCL_PIN                PORT_PA17

#define I2C_SERCOM                        SERCOM1
#define I2C_SERCOM_APBxMASK               PM_APBCMASK_SERCOM1
#define I2C_SERCOM_GCLK_ID_CORE           SERCOM1_GCLK_ID_CORE
#define I2C_SERCOM_CLK_GEN                0
#define I2C_SERCOM_IRQ_HANDLER            SERCOM1_Handler
#define I2C_SERCOM_IRQn                   SERCOM1_IRQn
#define Baudrate                          57600
#define Clockfreq                         8000000

#define FAST_MODE                          0x0
#define GPS_ADR                            0x42
#define BUF_SIZE                           3

uint8_t gpsreg = 0xFD;
uint8_t tx_buf[BUF_SIZE] = {1, 2, 3};
uint8_t rx_buf[BUF_SIZE];
uint8_t i;
volatile bool tx_done = false, rx_done = false;

enum
{
  I2C_TRANSFER_WRITE = 0,
  I2C_TRANSFER_READ  = 1,
};

void SERCOM1_Handler(void)
{
    if(I2C_SERCOM->I2CM.INTFLAG.bit.MB)
    {
        if(i == BUF_SIZE)
        {
            I2C_SERCOM->I2CM.CTRLB.bit.CMD = 0x3;
            while(I2C_SERCOM->I2CM.SYNCBUSY.bit.SYSOP);
            tx_done = true;
            i=0;
        }
        else
        {
            I2C_SERCOM->I2CM.DATA.reg = tx_buf[i++];
            while(I2C_SERCOM->I2CM.SYNCBUSY.bit.SYSOP);
        }
    }
    
    if(I2C_SERCOM->I2CM.INTFLAG.bit.SB)
    {
        if(i == (BUF_SIZE - 1))
        {
            I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT;
            while(I2C_SERCOM->I2CM.SYNCBUSY.bit.SYSOP);
            I2C_SERCOM->I2CM.CTRLB.bit.CMD = 0x3;
            while(I2C_SERCOM->I2CM.SYNCBUSY.bit.SYSOP);
            rx_buf[i++] = I2C_SERCOM->I2CM.DATA.reg;
            while(I2C_SERCOM->I2CM.SYNCBUSY.bit.SYSOP);
            rx_done = true;
        }
        else
        {
            I2C_SERCOM->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;
            while(I2C_SERCOM->I2CM.SYNCBUSY.bit.SYSOP);
            rx_buf[i++] = I2C_SERCOM->I2CM.DATA.reg;
            while(I2C_SERCOM->I2CM.SYNCBUSY.bit.SYSOP);
            I2C_SERCOM->I2CM.CTRLB.bit.CMD = 0x2;
            while(I2C_SERCOM->I2CM.SYNCBUSY.bit.SYSOP);
        }            
    }
}

void I2C_Init(void)
{
    while(I2C_SERCOM->I2CM.SYNCBUSY.bit.ENABLE);
    
    I2C_SERCOM->I2CM.CTRLA.bit.ENABLE = 0;
    
    while(I2C_SERCOM->I2CM.SYNCBUSY.bit.SWRST);
    
    /* Perform a software reset */
    I2C_SERCOM->I2CM.CTRLA.bit.SWRST = 1;

    /* Wait for synchronization */
    while(I2C_SERCOM->I2CM.CTRLA.bit.SWRST);

    /* Wait for synchronization */
    while(I2C_SERCOM->I2CM.SYNCBUSY.bit.SWRST || I2C_SERCOM->I2CM.SYNCBUSY.bit.ENABLE);
    
        
    //Using the WRCONFIG register to bulk configure both  for being configured the SERCOM2 SDA and SCL pins
    PORT->Group[I2C_SERCOM_PINS_PORT_GROUP].WRCONFIG.reg =
    PORT_WRCONFIG_WRPINCFG|                                                    //Enables the configuration of PINCFG
    PORT_WRCONFIG_WRPMUX |                                                    //Enables the configuration of the PMUX for the selected pins
    PORT_WRCONFIG_PMUXEN |                                                    //Enables the PMUX for the pins
    PORT_WRCONFIG_PMUX(I2C_SERCOM_PORT_PMUX) |                                //Bulk configuration for PMUX
    PORT_WRCONFIG_PINMASK( I2C_SERCOM_SDA_PIN | I2C_SERCOM_SCL_PIN);        //Selecting which pins are getting bulk configured

    PM->APBCMASK.reg |= I2C_SERCOM_APBxMASK;                                        //Enable the SERCOM under the PM

    GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(I2C_SERCOM_GCLK_ID_CORE) |                    //Provide necessary clocks to the peripheral
    GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(I2C_SERCOM_CLK_GEN);

    I2C_SERCOM->I2CM.CTRLA.reg = SERCOM_I2CM_CTRLA_MODE_I2C_MASTER;                    //

    I2C_SERCOM->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN;                            //Configure I2C in SMART mode (will automatically ACK after reading buffer)

    I2C_SERCOM->I2CM.CTRLA.bit.INACTOUT = SERCOM_I2CM_CTRLA_INACTOUT(0);
    
    uint16_t BAUD_REG = ((float)Clockfreq / (float)(2 * Baudrate)) - 1;
    I2C_SERCOM->I2CM.BAUD.bit.BAUD = BAUD_REG;
    
    while (I2C_SERCOM->I2CM.SYNCBUSY.bit.ENABLE);                                    //Wait for SYNCBUSY bit before enabling peripheral
    I2C_SERCOM->I2CM.CTRLA.reg |= SERCOM_I2CS_CTRLA_ENABLE;                            //Enable the peripheral
    
}

bool i2c_write(uint8_t *data, int size)
{
    I2C_SERCOM->I2CM.ADDR.reg = SERCOM_I2CM_ADDR_ADDR(GPS_ADR); //

    while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB));

    if (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK)
    {
        I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);

        return false;
    }

    for (int i = 0; i < size; i++)
    {
        I2C_SERCOM->I2CM.DATA.reg = data[i];

        while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB));

        if (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK)
        {
            I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);

            return false;
        }
    }
    I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);

    return true;
}

bool i2c_read(uint8_t *data, int size)
{
    I2C_SERCOM->I2CM.ADDR.reg = GPS_ADR | I2C_TRANSFER_READ;

    while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB));

    if (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK)
    {
        I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);
        //dbg_log("I2C: RXNACK during read (address)\r\n");
        return false;
    }

    I2C_SERCOM->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;

    for (int i = 0; i < size-1; i++)
    {
        data[i] = I2C_SERCOM->I2CM.DATA.reg;
        while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB));
    }

    if (size)
    {
        I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT;
        I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);
        data[size-1] = I2C_SERCOM->I2CM.DATA.reg;
    }

    return true;
}

 void i2c_master_transaction(void)
 {
     i = 0;
     
     I2C_SERCOM->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;
     while(I2C_SERCOM->I2CM.SYNCBUSY.bit.SYSOP);
     
     I2C_SERCOM->I2CM.ADDR.reg = (GPS_ADR << 1) | I2C_TRANSFER_WRITE;
     while(!tx_done);
     i = 0;
     I2C_SERCOM->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;
     while(I2C_SERCOM->I2CM.SYNCBUSY.bit.SYSOP);
     
     I2C_SERCOM->I2CM.CTRLB.reg = (GPS_ADR << 1) | I2C_TRANSFER_READ;
     while(!rx_done);
     
     I2C_SERCOM->I2CM.INTENCLR.reg = SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB;
 }

int main(void)
{
    /* Initialize the SAM system */
    SystemInit();
    TMR_Init();
    I2C_Init();
    i2c_master_transaction();
    
    //uint8_t buffer[256];
    //uint8_t gpsreg = 0xFD;
    /* Replace with your application code */
    while (1)
    {
        i2c_write(gpsreg, 1);
        i2c_read(gpsreg, 1);
    }
}

>

 

Last Edited: Thu. Oct 29, 2020 - 03:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Please see Tip #1 in my signature, below, for how to properly post source code:

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have tried a few more things but when checking it on a logic analyzer the decoder only shows the sending of the start bit.

I'm unfamiliar with the setup of I2C on a SAMD21 so I'm not sure if the configuration of the SERCOM1 as I2C master is correct.

I've read the datasheet and tried various of options but I'm unable figure out what the problem is so far.
The added code is just for the setup of the right SERCOM pins and as a master with SMART mode enabled.

 

#define I2C_SERCOM_PORT_PMUX              PORT_PMUX_PMUXE_C_Val
#define I2C_SERCOM_PINS_PORT_GROUP        0
#define I2C_SERCOM_SDA_PIN                PORT_PA16
#define I2C_SERCOM_SCL_PIN                PORT_PA17

#define I2C_SERCOM                        SERCOM1
#define I2C_SERCOM_APBxMASK               PM_APBCMASK_SERCOM1
#define I2C_SERCOM_GCLK_ID_CORE           SERCOM1_GCLK_ID_CORE
#define I2C_SERCOM_CLK_GEN                0
#define I2C_SERCOM_IRQ_HANDLER            SERCOM1_Handler
#define I2C_SERCOM_IRQn                   SERCOM1_IRQn
#define Baudrate                          57600
#define Clockfreq                         8000000

void I2C_Init(void)
{
	while(I2C_SERCOM->I2CM.SYNCBUSY.bit.ENABLE);
	
	I2C_SERCOM->I2CM.CTRLA.bit.ENABLE = 0;
	
	while(I2C_SERCOM->I2CM.SYNCBUSY.bit.SWRST);
	
	/* Perform a software reset */
	I2C_SERCOM->I2CM.CTRLA.bit.SWRST = 1;

	/* Wait for synchronization */
	while(I2C_SERCOM->I2CM.CTRLA.bit.SWRST);

	/* Wait for synchronization */
	while(I2C_SERCOM->I2CM.SYNCBUSY.bit.SWRST || I2C_SERCOM->I2CM.SYNCBUSY.bit.ENABLE);
	
		
	//Using the WRCONFIG register to bulk configure both  for being configured the SERCOM2 SDA and SCL pins
	PORT->Group[I2C_SERCOM_PINS_PORT_GROUP].WRCONFIG.reg =
	PORT_WRCONFIG_WRPINCFG|													//Enables the configuration of PINCFG
	PORT_WRCONFIG_WRPMUX |													//Enables the configuration of the PMUX for the selected pins
	PORT_WRCONFIG_PMUXEN |													//Enables the PMUX for the pins
	PORT_WRCONFIG_PMUX(I2C_SERCOM_PORT_PMUX) |								//Bulk configuration for PMUX
	PORT_WRCONFIG_PINMASK( I2C_SERCOM_SDA_PIN | I2C_SERCOM_SCL_PIN);		//Selecting which pins are getting bulk configured

	PM->APBCMASK.reg |= I2C_SERCOM_APBxMASK;										//Enable the SERCOM under the PM

	GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(I2C_SERCOM_GCLK_ID_CORE) |					//Provide necessary clocks to the peripheral
	GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(I2C_SERCOM_CLK_GEN);

	I2C_SERCOM->I2CM.CTRLA.reg = SERCOM_I2CM_CTRLA_MODE_I2C_MASTER;					//

	I2C_SERCOM->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN;							//Configure I2C in SMART mode (will automatically ACK after reading buffer)

	I2C_SERCOM->I2CM.CTRLA.bit.INACTOUT = SERCOM_I2CM_CTRLA_INACTOUT(0);
	
	uint16_t BAUD_REG = ((float)Clockfreq / (float)(2 * Baudrate)) - 1;
	I2C_SERCOM->I2CM.BAUD.bit.BAUD = BAUD_REG;
	
	while (I2C_SERCOM->I2CM.SYNCBUSY.bit.ENABLE);									//Wait for SYNCBUSY bit before enabling peripheral
	I2C_SERCOM->I2CM.CTRLA.reg |= SERCOM_I2CS_CTRLA_ENABLE;							//Enable the peripheral
	
	
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

https://community.atmel.com/comm... ? For example, I don't see "I2C->STATUS.reg |= SERCOM_I2CM_STATUS_BUSSTATE(1);" in your I2C_init().