Problem using I2C on SAM21 MCU - Page 1
Forum section: electronics / general

author: DavedenHaan
author_rank: Contributor
timestamp: November 05, 2020, 10:19:06 am
content: Hi all,I'm new to programming with the SAMD21 and so far I'm not able to get an I2C connection going.I'm creating a custom PCB with this MCU and I'm currently coding everything on a development board with the same MCU, GPS and LTM module.I'm currently trying to connect to the GPS module using I2C but after many attempts I've have yet to see results on the logic analyzer.The GPS module is from Ublox and has 0x42 as address with only the 0xFD, 0xFE and 0xFF registers to get the data.Does anybody have a tip regarding the configuration of the I2C as well as sending the messages to the GPS module?Code:#include "sam.h"#include "tmr.h"#include "stdbool.h"#define I2C_SERCOM_SDA_PINPORT_PA16#define I2C_SERCOM_SCL_PINPORT_PA17#define I2C_SERCOM_PORT_PMUXPORT_PMUX_PMUXE_C#define I2C_SERCOM_APBCMASKPM_APBCMASK_SERCOM1#define I2C_SERCOMSERCOM1#define I2C_SERCOM_CLK_GEN0#define I2C_SERCOM_GCLK_IDSERCOM1_GCLK_ID_CORE#define I2C_ADDRESS0x42#define BUF_SIZE256uint8_t gpsregister = 0xFD;enum{I2C_TRANSFER_WRITE = 0,I2C_TRANSFER_READ = 1,};void i2c_pin(void){PORT->Group.WRCONFIG.reg =PORT_WRCONFIG_WRPINCFG |//Enables the configuration of PINCFGPORT_WRCONFIG_WRPMUX |//Enables the configuration of the PMUX for the selected pinsPORT_WRCONFIG_PMUXEN |//Enables the PMUX for the pinsPORT_WRCONFIG_PMUX(I2C_SERCOM_PORT_PMUX) |//Bulk configuration for PMUXPORT_WRCONFIG_PINMASK((uint16_t)((I2C_SERCOM_SDA_PIN | I2C_SERCOM_SCL_PIN) >> 16));//Selecting which pins are getting bulk configured}void i2c_config(void){PM->APBCMASK.reg |= I2C_SERCOM_APBCMASK;GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(I2C_SERCOM_GCLK_ID) | GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(I2C_SERCOM_CLK_GEN);I2C_SERCOM->I2CM.CTRLA.reg = SERCOM_I2CM_CTRLA_SPEED(0x0)|SERCOM_I2CM_CTRLA_SDAHOLD(0x03)|SERCOM_I2CM_CTRLA_RUNSTDBY|SERCOM_I2CM_CTRLA_MODE_I2C_MASTER;I2C_SERCOM->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN;while(I2C_SERCOM->I2CM.SYNCBUSY.bit.SYSOP);I2C_SERCOM->I2CM.BAUD.reg = SERCOM_I2CM_BAUD_BAUD(48);while(I2C_SERCOM->I2CM.SYNCBUSY.bit.SYSOP);I2C_SERCOM->I2CM.STATUS.reg |= SERCOM_I2CM_STATUS_BUSSTATE(1);while(I2C_SERCOM->I2CM.SYNCBUSY.bit.SYSOP);}bool i2c_write_start(void){I2C_SERCOM->I2CM.ADDR.reg = I2C_ADDRESS | I2C_TRANSFER_WRITE;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;}return true;}void i2c_write_stop(void){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);}bool i2c_write(uint8_t *data, int size){I2C_SERCOM->I2CM.ADDR.reg = I2C_ADDRESS | I2C_TRANSFER_WRITE;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);//dbg_log("I2C: RXNACK during write (address)\r\n");return false;}for (int i = 0; i < size; i++){I2C_SERCOM->I2CM.DATA.reg = data;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);//dbg_log("I2C: RXNACK during write (data)\r\n");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 = I2C_ADDRESS | 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 = 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;}int main(void){SystemInit();TMR_Init();i2c_pin();i2c_config();//i2c_master_transact();while (1){i2c_write_start();}}

author: cv007
author_rank: Frequent Contributor
timestamp: November 05, 2020, 11:35:03 am
content: > I've have yet to see results on the logic analyzer.That could mean absolutely nothing seen on the bus, or that could mean outgoing is ok but the gps is not responding. I could see how the latter could be a possibility since you are not shifting the i2c address into position-I2C_SERCOM->I2CM.ADDR.reg = I2C_ADDRESS | I2C_TRANSFER_READ;I2C_SERCOM->I2CM.ADDR.reg = (I2C_ADDRESS<<1) | I2C_TRANSFER_READ; // <<-- correct

author: DavedenHaan
author_rank: Contributor
timestamp: November 05, 2020, 01:51:03 pm
content: Thankyou for the response.I tried this and no luck still.With the amount of different ways I have tried setting up the connection I do also think it is more a problem with calling the GPS in the correct way.https://www.u-blox.com/en/docs/UBX-13003221Reading the DDC part which includes the I2C I know that there are only a limited amount of steps needed to receive data.1. Start the I2C connection with GPS.2. Use read function together with 0xFF to access the data stream.3. Stop the I2C connection when done.https://www.eevblog.com/forum/microcontrollers/i2c-atmel/25/I also read through this post in which Alex included a lot of example code and answers related to I2C and SAMD21The new code I tried is also an alteration of the codes shown in this post.Can somebody verify if this is correct and what way I should implement the read function in a way that allows me to read the data stream?The pictures included are parts of the Ublox DDC(I2C) datasheet.Code:#include "sam.h"#include "stdlib.h"#include "stdint.h"#include "stdbool.h"#include "hal_gpio.h"//Definitions--------------------HAL_GPIO_PIN(SDA, A, 16);HAL_GPIO_PIN(SCL, A, 17);#define I2C_SERCOMSERCOM1#defineI2C_SERCOM_PMUXPORT_PMUX_PMUXE_C_Val#define I2C_SERCOM_GCLK_IDSERCOM1_GCLK_ID_CORE#define I2C_SERCOM_CLK_GEN0#define I2C_SERCOM_APBCMASKPM_APBCMASK_SERCOM1#define T_RISE215e-9#define F_CPU8000000#define I2C_ADDRESS0x42#define gpsreg0xFFenum{I2C_TRANSFER_WRITE = 0,I2C_TRANSFER_READ = 1,};enum{I2C_PINS_SDA = (1 << 0),I2C_PINS_SCL = (1 << 1),};static void timer_init(void){PM->APBCMASK.reg |= PM_APBCMASK_TC3;GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(TC3_GCLK_ID) |GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);TC3->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 | TC_CTRLA_WAVEGEN_MFRQ |TC_CTRLA_PRESCALER_DIV1024 | TC_CTRLA_PRESCSYNC_RESYNC;TC3->COUNT16.COUNT.reg = 0;TC3->COUNT16.CC.reg = (F_CPU / 1000ul / 1024) * 500;TC3->COUNT16.COUNT.reg = 0;TC3->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;TC3->COUNT16.INTENSET.reg = TC_INTENSET_MC(1);NVIC_EnableIRQ(TC3_IRQn);}int i2c_init(int freq){int baud = ((float)F_CPU / freq - (float)F_CPU * T_RISE - 10.0) / 2.0;if (baud < 0)baud = 0;else if (baud > 255)baud = 255;freq = (float)F_CPU / (2.0 * (5.0 + baud) + (float)F_CPU * T_RISE);PM->APBCMASK.reg |= I2C_SERCOM_APBCMASK;GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(I2C_SERCOM_GCLK_ID) |GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(I2C_SERCOM_CLK_GEN);I2C_SERCOM->I2CM.CTRLA.reg = SERCOM_I2CM_CTRLA_SWRST;while (I2C_SERCOM->I2CM.CTRLA.reg & SERCOM_I2CM_CTRLA_SWRST);I2C_SERCOM->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN;while (I2C_SERCOM->I2CM.SYNCBUSY.reg);I2C_SERCOM->I2CM.BAUD.reg = SERCOM_I2CM_BAUD_BAUD(baud);while (I2C_SERCOM->I2CM.SYNCBUSY.reg);I2C_SERCOM->I2CM.CTRLA.reg = SERCOM_I2CM_CTRLA_ENABLE |SERCOM_I2CM_CTRLA_MODE_I2C_MASTER |SERCOM_I2CM_CTRLA_SDAHOLD(3);while (I2C_SERCOM->I2CM.SYNCBUSY.reg);I2C_SERCOM->I2CM.STATUS.reg |= SERCOM_I2CM_STATUS_BUSSTATE(1);HAL_GPIO_SDA_in();HAL_GPIO_SDA_clr();HAL_GPIO_SDA_pmuxen(I2C_SERCOM_PMUX);HAL_GPIO_SCL_in();HAL_GPIO_SCL_clr();HAL_GPIO_SCL_pmuxen(I2C_SERCOM_PMUX);return freq;}static void sys_init(void) // enable 8Mhz OSC, no prescaler{SYSCTRL->OSC8M.bit.PRESC = 0;}static inline void pin_set_peripheral_function(uint32_t pinmux){uint8_t port = (uint8_t)((pinmux >> 16)/32);PORT->Group.PINCFG[((pinmux >> 16) - (port*32))].bit.PMUXEN = 1;PORT->Group.PMUX[((pinmux >> 16) - (port*32))/2].reg &= ~(0xF << (4 * ((pinmux >> 16) & 0x01u)));PORT->Group.PMUX[((pinmux >> 16) - (port*32))/2].reg |= (uint8_t)((pinmux & 0x0000FFFF) << (4 * ((pinmux >> 16) & 0x01u)));}void i2c_pin_init(){/* PA08 and PA09 set into peripheral function D*/pin_set_peripheral_function(PINMUX_PA16C_SERCOM1_PAD0); // SDApin_set_peripheral_function(PINMUX_PA17C_SERCOM1_PAD1); // SCL}bool i2c_start(int addr){I2C_SERCOM->I2CM.INTFLAG.reg = SERCOM_I2CM_INTFLAG_ERROR;I2C_SERCOM->I2CM.ADDR.reg = addr;while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) &&0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB));if (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK ||I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_ERROR){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);return false;}return true;}bool i2c_read_byte(uint8_t *byte, bool last){while (1){int flags = I2C_SERCOM->I2CM.INTFLAG.reg;if (flags & SERCOM_I2CM_INTFLAG_SB)break;if (flags & (SERCOM_I2CM_INTFLAG_MB | SERCOM_I2CM_INTFLAG_ERROR))return false;}if (last)I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | SERCOM_I2CM_CTRLB_CMD(3);elseI2C_SERCOM->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;*byte = I2C_SERCOM->I2CM.DATA.reg;return true;}bool i2c_read(uint8_t *data, int size){I2C_SERCOM->I2CM.ADDR.reg = I2C_ADDRESS | I2C_TRANSFER_WRITE; // I2C address = 0x42while (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);//dbg_log("I2C: RXNACK during write (address)\r\n");return false;}I2C_SERCOM->I2CM.DATA.reg = gpsreg; // send stream registerwhile (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);//dbg_log("I2C: RXNACK during write (data)\r\n");return false;}I2C_SERCOM->I2CM.ADDR.reg = I2C_ADDRESS | 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);return false;}I2C_SERCOM->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;for (int i = 0; i < size-1; i++) // read lower bite, upper bite and pec{data = 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;}if ((I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) ||(I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB)){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); // stop bit}return true;}int main(void){uint8_t data;sys_init();timer_init();i2c_pin_init();i2c_init(8000000);i2c_start(I2C_ADDRESS);/* Replace with your application code */while (1){i2c_read_byte(&data, true);}}

author: cv007
author_rank: Frequent Contributor
timestamp: November 05, 2020, 07:53:01 pm
content: >I tried this and no luck still.You will probably get more help if you specify what 'no luck' means, or from the original post what 'get an I2C connection going' means.Your logic analyzer shows nothing happening on the pins? then maybe you have a problem setting up the pins (not much wrong can do there except choosing the wrong pins). The pins are not both at a high state at idle? then maybe you forgot the pullups (could also enable internal pullup for these pins if have no external pullups). You are putting out an address packet with no response? then your address is most likely wrong (you do have to move that 0x42 address up 1 bit as I said previously). And so on.Since you have a logic analyzer, first job is to at least get something to show up on the pins. If you are already beyond that point, then say so.

author: ataradov
author_rank: Super Contributor
timestamp: November 05, 2020, 08:26:35 pm
content: Here is example of an I2C masterhttps://github.com/ataradov/dgw/blob/master/embedded/i2c_master.cWell, it looks like your code is based on that already.But where is the actual code for the transaction? Why are you calling start in a loop?But yes, get some logic analyzer captures. There are a lot of things that can go wrong.Plus a typical I2C transaction when the datasheet talks about registers is this:1. Start2. Write (register address)3. Stop or repeated start4. Read.5. Stop

author: DavedenHaan
author_rank: Contributor
timestamp: November 06, 2020, 11:58:28 am
content: Thankyou both for the responses.Regarding the logic analyzer.The first attachment is the output im currently getting. (channel 1 and 2 are connected to SDA and SCL + a GND is connected)As u can see the output is emptyMy first goal is to get any output from the SDA and SCL lines but so far it always showed an empty sheet.Since I cannot get any output on the analyzer I'm using the debugger in Atmel studio.Code:int main(void){//uint8_t data;// Set the initializations - system, timer, GPIO pins and I2C PinsSystemInit();TMR_Init();GPIO_Init();i2c_pin_init();//----------------------------------------------------------------// Continue the code for I2Ci2c_init(8000000);GPS_HIGH();//Set GSP enable pin HIGH to enable GPS modulei2c_start(I2C_ADDRESS);//Start the I2C connection with GPS on address 0x42i2c_write_byte(gpsreg);//Send 0xFD to thei2c_start(I2C_ADDRESS);//restart the I2C data transfer on 0x42i2c_read_byte(&byte, true);//Read the data coming from the GSPi2c_stop();GPS_LOW();/* Replace with your application code */while (1){}}I can walk through the code with the debugger until I get to the i2c_write_byte part.It stays in the while loop on the intflag and never passes it

author: DavedenHaan
author_rank: Contributor
timestamp: November 06, 2020, 12:44:03 pm
content: Update on the scope output and code:The code below combines the write into read.when debugging I can see that the SCL clock is being pulled low on the MB part and the acknowledgment comes through since it goes past the if statement.253 or 0xFD (the register I want the data from) gets written as well and the SCL clock gets pulled LOW.The 0x42 address gets written again with the READ command as well.The issue right now comes after the address is written again.Attachment 3 and 4 show the debug state and output of the SDA and SCL at that position.when I step 1 more further it never goes out of the while loop .Code:bool i2c_read(uint8_t *data, int size){I2C_SERCOM->I2CM.ADDR.reg = (I2C_ADDRESS<<1) | I2C_TRANSFER_WRITE; // I2C address + write commandwhile (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);//dbg_log("I2C: RXNACK during write (address)\r\n");return false;}I2C_SERCOM->I2CM.DATA.reg = gpsreg; // send the register to be readwhile (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);//dbg_log("I2C: RXNACK during write (data)\r\n");return false;}I2C_SERCOM->I2CM.ADDR.reg = (I2C_ADDRESS<<1) | I2C_TRANSFER_READ; //Send GPS address + Read modewhile (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);return false;}I2C_SERCOM->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;for (int i = 0; i < size-1; i++) // read lower bite, upper bite and pec{data = 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;}if ((I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) || (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB)){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); // stop bit}return true;}In the mainCode:int main(void){//uint8_t data;// Set the initializations - system, timer, GPIO pins and I2C PinsSystemInit();TMR_Init();GPIO_Init();i2c_pin_init();//----------------------------------------------------------------// Continue the code for I2CGPS_HIGH();i2c_init(8000000);i2c_read(&byte, 2);GPS_LOW();/* Replace with your application code */while (1){}}

author: ataradov
author_rank: Super Contributor
timestamp: November 06, 2020, 05:14:01 pm
content: i2c_init() takes I2C clock in kHz, there is no way it will work with 8 MHz. There is a limiter, but it will still limit it at the highest possible value.You need to capture the exact moment SDL/SCL change state, not just fixed levels. So remove all your breakpoints, run the code normally and start the logic analyzer just before. It is important to know what exactly was sent on the bus.Do not combine anything, just use the functions as is. They known to work, and with your modifications it is hard to review them for mistakes.

author: DavedenHaan
author_rank: Contributor
timestamp: November 06, 2020, 06:54:44 pm
content: I did what u suggested and I stripped the combinations and used the functions the way u created them without modifications.I finally got some output on the analyser.the 0xFD message does not seem to get send after the connection address is writtenmain code used:Code:int main(void){//uint8_t data;// Set the initializations - system, timer, GPIO pins and I2C PinsSystemInit();TMR_Init();GPIO_Init();i2c_pin_init();//----------------------------------------------------------------// Continue the code for I2CGPS_HIGH();i2c_init(100);//Set GSP enable pin HIGH to enable GPS modulei2c_start(I2C_ADDRESS);//Start the I2C connection with GPS on address 0x42i2c_write_byte(gpsreg);//Send 0xFD to thei2c_start(I2C_ADDRESS);//restart the I2C data transfer on 0x42i2c_read_byte(&byte, true);//Read the data coming from the GSPi2c_stop();GPS_LOW();/* Replace with your application code */while (1){}}

author: ataradov
author_rank: Super Contributor
timestamp: November 06, 2020, 07:04:48 pm
content: Your slave does not respond to the initial addressing. Nothing past that matters.You should try I2C_ADDRESS instead of (I2C_ADDRESS<<1).And double check your electrical connections.

author: DavedenHaan
author_rank: Contributor
timestamp: November 06, 2020, 07:25:56 pm
content: I changed the (addr<<1) back to the original.With this the address that gets send is 0x21Regarding the connection that should be fine.I'm using a Sodaq dev board to make sure my code works before applying it to my own PCB.Same CPU and GPS modules.Code:bool i2c_start(int addr){I2C_SERCOM->I2CM.INTFLAG.reg = SERCOM_I2CM_INTFLAG_ERROR;I2C_SERCOM->I2CM.ADDR.reg = addr;while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) &&0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB));if (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK ||I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_ERROR){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);return false;}return true;}The functions that are used:Code:int i2c_init(int freq){int baud = ((float)F_CPU / freq - (float)F_CPU * T_RISE - 10.0) / 2.0;if (baud < 0)baud = 0;else if (baud > 255)baud = 255;freq = (float)F_CPU / (2.0 * (5.0 + baud) + (float)F_CPU * T_RISE);PM->APBCMASK.reg |= I2C_SERCOM_APBCMASK;GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(I2C_SERCOM_GCLK_ID) |GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(I2C_SERCOM_CLK_GEN);I2C_SERCOM->I2CM.CTRLA.reg = SERCOM_I2CM_CTRLA_SWRST;while (I2C_SERCOM->I2CM.CTRLA.reg & SERCOM_I2CM_CTRLA_SWRST);I2C_SERCOM->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN;while (I2C_SERCOM->I2CM.SYNCBUSY.reg);I2C_SERCOM->I2CM.BAUD.reg = SERCOM_I2CM_BAUD_BAUD(baud);while (I2C_SERCOM->I2CM.SYNCBUSY.reg);I2C_SERCOM->I2CM.CTRLA.reg = SERCOM_I2CM_CTRLA_ENABLE |SERCOM_I2CM_CTRLA_MODE_I2C_MASTER |SERCOM_I2CM_CTRLA_SDAHOLD(3);while (I2C_SERCOM->I2CM.SYNCBUSY.reg);I2C_SERCOM->I2CM.STATUS.reg |= SERCOM_I2CM_STATUS_BUSSTATE(1);HAL_GPIO_SDA_in();HAL_GPIO_SDA_clr();HAL_GPIO_SDA_pmuxen(I2C_SERCOM_PMUX);HAL_GPIO_SCL_in();HAL_GPIO_SCL_clr();HAL_GPIO_SCL_pmuxen(I2C_SERCOM_PMUX);return freq;}bool i2c_start(int addr){I2C_SERCOM->I2CM.INTFLAG.reg = SERCOM_I2CM_INTFLAG_ERROR;I2C_SERCOM->I2CM.ADDR.reg = addr;while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) &&0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB));if (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK ||I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_ERROR){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);return false;}return true;}bool i2c_write_byte(uint8_t byte){I2C_SERCOM->I2CM.DATA.reg = byte;while (1){int flags = I2C_SERCOM->I2CM.INTFLAG.reg;if (flags & SERCOM_I2CM_INTFLAG_MB)break;if (flags & (SERCOM_I2CM_INTFLAG_SB | SERCOM_I2CM_INTFLAG_ERROR))return false;}if (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);return false;}return true;}bool i2c_read_byte(uint8_t *byte, bool last){while (1){int flags = I2C_SERCOM->I2CM.INTFLAG.reg;if (flags & SERCOM_I2CM_INTFLAG_SB)break;if (flags & (SERCOM_I2CM_INTFLAG_MB | SERCOM_I2CM_INTFLAG_ERROR))return false;}if (last)I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | SERCOM_I2CM_CTRLB_CMD(3);elseI2C_SERCOM->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;*byte = I2C_SERCOM->I2CM.DATA.reg;return true;}bool i2c_stop(void){if ((I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) ||(I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB)){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);}return true;}

author: ataradov
author_rank: Super Contributor
timestamp: November 06, 2020, 07:35:53 pm
content: Your waveforms looks fine, but your device does not respond to that address.If you say that you tested with some other device, can you attach corresponding logic analyzer captures?

author: DavedenHaan
author_rank: Contributor
timestamp: November 06, 2020, 07:50:03 pm
content: I will try to get that something regarding that tomorrow.The bootloader that was on the board is removed and I'm using a Jlink programmer to program the board.Unfortunately Atmel studio does not like writing Arduino code to the board with the programmer.The code that is used for that and worked was the following.It's written by Sodaq to use with their boards.I tried it when I got it to see if it all worked but and I got output from the GPS module.Code:#define WIRE_BUS 1#define SERIAL_BUS 2#define GPS_INTERFACE WIRE_BUS#if (GPS_INTERFACE == WIRE_BUS)#include <Wire.h>#define GPS_STREAM Wire#define GPS_ADR 0x42#elif (GPS_INTERFACE == SERIAL_BUS)#define GPS_STREAM Serial#define GPS_BAUD 57600#else#error "Invalid GPS_INTERFACE"#endif#define CONSOLE_STREAM SerialUSB#define CONSOLE_BAUD 9600#define READ_TIMEOUT_MS 100#define BUFFER_SIZE 256uint8_t buffer;void setup(){gpsEnable(true);#if (GPS_INTERFACE == WIRE_BUS)Wire.begin();#elif (GPS_INTERFACE == SERIAL_BUS)GPS_STREAM.begin(GPS_BAUD);#endifCONSOLE_STREAM.begin(CONSOLE_BAUD);}void loop(){//Console -> GPSmemset(buffer, 0, BUFFER_SIZE);writeGPS(readConsole());//GPS -> Consolememset(buffer, 0, BUFFER_SIZE);writeConsole(readGPS());}void gpsEnable(bool state){//Enable GPS modulepinMode(GPS_ENABLE, OUTPUT);digitalWrite(GPS_ENABLE, state);}size_t readGPS(){#if (GPS_INTERFACE == WIRE_BUS)return readUbloxI2cStream();#elif (GPS_INTERFACE == SERIAL_BUS)return readSerialStream((Stream*)&GPS_STREAM);#endif}size_t readConsole(){return readSerialStream((Stream*)&CONSOLE_STREAM);}#if (GPS_INTERFACE == WIRE_BUS)size_t readUbloxI2cStream(){uint16_t count = 0;Wire.beginTransmission(GPS_ADR);Wire.write((uint8_t)0xFD);Wire.endTransmission(false);Wire.requestFrom(GPS_ADR, 2);count = (uint16_t)(Wire.read() << 8) | Wire.read();count = (count > BUFFER_SIZE) ? BUFFER_SIZE : count;if (count) {for (size_t i = 0; i < (count-1); i++) {Wire.requestFrom(GPS_ADR, 1, false);buffer = Wire.read();}Wire.requestFrom(GPS_ADR, 1);buffer[count-1] = Wire.read();}return count;}#endifsize_t readSerialStream(Stream* stream){uint32_t last = millis();size_t count = 0;while ((count < BUFFER_SIZE) && (millis() < (last + READ_TIMEOUT_MS))) {if (stream->available()) {buffer[count++] = stream->read();last = millis();}}return count;}void writeGPS(size_t count){#if (GPS_INTERFACE == WIRE_BUS)Wire.beginTransmission(GPS_ADR);Wire.write(buffer, count);Wire.endTransmission();#elif (GPS_INTERFACE == SERIAL_BUS)for (size_t i = 0; i < count; i++) {GPS_STREAM.write(buffer);}#endif}void writeConsole(size_t count){for (size_t i = 0; i < count; i++) {CONSOLE_STREAM.write(buffer);}}

author: cv007
author_rank: Frequent Contributor
timestamp: November 06, 2020, 08:09:03 pm
content: Don't know what ublox device you have, and you should maybe say which one so no one has to guess when trying to lookup info, but a few I found indicate they are using a 'true' address, which then has to be shifted to move past the rw bit, and I imagine that remains true for all the modules-QuoteYou also now have a GPS_ENABLE() that you show in one of your codes, but seems to be missing in others. Not sure what that does, or how long it takes for the gps module before it then starts to respond to its ddc bus.probably a good guess to what you may be using-https://support.sodaq.com/Boards/One/and if so, the gps enable simply powers the module, although the backup power pin is connected directly to power. Cannot find any info on 'boot time' but I guess you either wait a known period after power on or simply wait until you get an ack. No ack = not ready to communicate. The code you were provided by ataradov has return values you should be checking. If you enable the gps, and it takes some amount of time to 'boot', if you also check the return value from the start function you will then know whether the module is ready or not. Just trying to continue without checking whether the module ack'd the address is not a good plan.

author: DavedenHaan
author_rank: Contributor
timestamp: November 07, 2020, 10:49:37 am
content: The ublox device I'm using is the SAM M8Q.My bad I thought I mentioned it in a post but it seems I have forgotten to do that sorry.https://www.u-blox.com/en/docs/UBX-13003221This is the datasheet I've found the most information about the use of DDC (I2C) with this specific GPS module.The 2 attachments show the way I decided to create the GPS_Enable.I've set the PIN_A28 as an output and with the GPS_HIGH and LOW I can control it like the GPS_enable does in the arduino code.Both are in in de GPIO.H and GPIO.C in the projects.The sodaq board I'm currently using is the:https://support.sodaq.com/Boards/Sara_AFF/I will look more into the True Address part because I do think it has to do something with the requirements for the GPS connection to be established.The DDC part gets me a bit confused over standard I2C.

author: rstofer
author_rank: Super Contributor
timestamp: November 07, 2020, 06:03:34 pm
content: The documents said the address needs to be shifted such that you actually send 0x82 or 0x83 depending on the R/W bit. What does the logic analyzer say?Some LA software will decode I2C and this is quite helpful.

author: DavedenHaan
author_rank: Contributor
timestamp: November 07, 2020, 06:18:31 pm
content: With the start written like below it sends it to 0x42. As seen in the attachmentIn the Main the start function is used as i2c_start(I2C_ADDRESS);and I2C_ADDRESS here is a define set as 0x42Code:bool i2c_start(int addr){I2C_SERCOM->I2CM.INTFLAG.reg = SERCOM_I2CM_INTFLAG_ERROR;I2C_SERCOM->I2CM.ADDR.reg = (addr<<1);while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) &&0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB));if (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK ||I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_ERROR){I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);return false;}return true;}

author: cv007
author_rank: Frequent Contributor
timestamp: November 07, 2020, 06:19:06 pm
content: >The DDC part gets me a bit confused over standard I2C.Appears they just do not want to use the I2C name, just like others may call it Twi. Its still I2C whatever they name it.You already have shown you can get the start address to go out, and with the correct address 0x42<<1|rw, so would guess you are simply not giving enough time for the gps to 'boot'. The examples you have shown simply start communicating a few instructions after you power on the gps, and most likely the gps is simply not ready yet. If you use the true/false return values of the called functions, it will prevent you from assuming you can take the next step.The following is just using code you have already shown, where you can simply check how many bytes are available, which also can be used to check if the gps is communicating.Code://returns bytes available- 0 also can mean any error//gps address will be at 0xFF if return value is >0, so can then read either the bytes available or until read a 0xFFuint16_t gpsBytesAvailable(){uint8_t len;if( i2c_start(I2C_ADDRESS<<1|0) &&i2c_write_byte(0xFD) &&i2c_start(I2C_ADDRESS<<1|1) &&i2c_read_byte(&len, false) && //high byte (0xFD)i2c_read_byte(&len, true) ) { //low byte (0xFE)return (uint16_t)&len;}return 0;//may have to deal with any errors, send a stop or whatever- this is just a simple example}>What does the logic analyzer say?says in reply #8 shows the address going out correctly when using 0x42<<1

author: DavedenHaan
author_rank: Contributor
timestamp: November 07, 2020, 07:11:57 pm
content: I implemented the example u createdUnfortunately is still gets stuck on the Not Acknowledged part.Code:uint16_t gpsBytesAvailable(){uint8_t len;if( i2c_start(I2C_ADDRESS<<1|0) &&i2c_write_byte(0xFD) &&i2c_start(I2C_ADDRESS<<1|1) &&i2c_read_byte(&len, false) &&i2c_read_byte(&len, true)){return (uint16_t)&len;}return 0;//may have to deal with any errors, send a stop or whatever- this is just a simple example}MainCode:int main(void){//uint8_t data;// Set the initializations - system, timer, GPIO pins and I2C PinsSystemInit();TMR_Init();GPIO_Init();i2c_pin_init();//----------------------------------------------------------------// Continue the code for I2CGPS_HIGH();i2c_init(100);//Set GSP enable pin HIGH to enable GPS modulegpsBytesAvailable();GPS_LOW();/* Replace with your application code */while (1){}}

author: ataradov
author_rank: Super Contributor
timestamp: November 07, 2020, 07:15:17 pm
content: Can you actually get a capture from the working hardware.As it was mentioned - add a delay after the initialization and before this transaction.gpsBytesAvailable() checks if there was answer. You need to call it in a loop until there is an answer. Calling it once is equivalent to your code.

author: DavedenHaan
author_rank: Contributor
timestamp: November 07, 2020, 07:34:52 pm
content: When placed in a loop it indeed gave some feedback.After a few attempts to connect the connection goes through.The first time it comes back with 00 00 but after that every reply is 00 E1.

author: ataradov
author_rank: Super Contributor
timestamp: November 07, 2020, 07:36:00 pm
content: So it does work, but GPS module needed some time to boot up.

author: DavedenHaan
author_rank: Contributor
timestamp: November 07, 2020, 08:09:22 pm
content: Yes it seems like that was the case.Thankyou all so much for the help with this!Next up I'm gonne change it so the output that shows up will be in GPS coordinates etc.Will be doing that the upcoming days.When I'm through with that I will post an update in case someone is ever in need of something similar.