I2C problems when sending and reading data (ATSAMD21G18A)

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

I am once again asking for your support :D this time with I2C. I used the I2C code provided by the user Alexru from this post (comment 4). I only changed the pin initialization and i think i did that correctly, furthermore i checked how it is supposed to be set up in the datasheet and the options set seem good. The clock is good i checked with a scope, and when i check the SDA line there is some data (it's not the data i sent tho, do i get both the response and the send data over SDI when i monitor it? i assume so). The sensor i am trying to communicate with is TCN75 however when i send the instructions to the sensor my code is stuck on this line (when calling i2c_read from the TCN75_readTempC function): 

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

Main code: 
 

#include "sam.h"
#include "includes/USART.h"
//#include "includes/SPI.h"
//#include "includes/23LC1024.h"
#include "includes/I2C.h"
#include "includes/TCN75.h"

#define DelayTicks(ticks)              {volatile uint32_t n=ticks; while(n--);}				   //takes 8 cycles
#define DelayMs(ms)                    DelayTicks(MS_TO_DLYTICKS(ms))
#define F_CPU 8000000
#define CYCLES_IN_DLYTICKS_FUNC        8
#define MS_TO_DLYTICKS(ms)	       (uint32_t)(F_CPU / 1000 * ms / CYCLES_IN_DLYTICKS_FUNC) // ((float)(F_CPU)) / 1000.0

int main(void){

	SystemInit();
	usart_init();
	//init_SPI();
	i2c_init();
	TCN75_begin();

	uint8_t arr [] = {0xff};
	while (1) {
		float readval;
		readval = TCN75_readTempC();
		usart_putNumber((int)readval);
		TCN75_begin();
	}
}

TCN75 code: 
 

#pragma once

#define TA_REG      0b00000000
#define CONFIG_REG  0b00000001
#define THYST_REG   0b00000010
#define TLIM_REG    0b00000011

void TCN75_begin(){
	uint8_t arr[] = {CONFIG_REG};
	uint8_t arr1[] = {0b01100000};  // start setting for sensor
	uint8_t ta[] = {TA_REG};
	// PORT->Group[0].OUTSET.reg = PORT_PA08;
	i2c_write(arr, 1);
	i2c_write(arr1, 1);
	// PORT->Group[0].OUTCLR.reg = PORT_PA08;
	i2c_write_stop();
	// PORT->Group[0].OUTSET.reg = PORT_PA08;
	i2c_write(ta, 1);
	i2c_write_stop();
	// PORT->Group[0].OUTCLR.reg = PORT_PA08;
}

float TCN75_readTempC(){
	uint8_t data[2];
	PORT->Group[0].OUTSET.reg = PORT_PA08;
	i2c_read(data, 2);
	int _temperature, _tempreg;
	_tempreg = data[0] << 8;
	_tempreg |= data[1];
	_tempreg >>= 4;
	_temperature =( float ) _tempreg / 16;

	return _temperature;
}

I2C code:
 

#pragma once

#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

#include "sam.h"

#define I2C_SERCOM				 SERCOM0
#define I2C_SERCOM_PMUX			 PORT_PMUX_PMUXE_C_Val
#define I2C_SERCOM_GCLK_ID		 SERCOM0_GCLK_ID_CORE
#define I2C_SERCOM_CLK_GEN		 0
#define I2C_SERCOM_APBCMASK		 PM_APBCMASK_SERCOM0

#define I2C_ADDRESS				 0x48 // sensor I2C 

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

void i2c_init(void){
        I2C_SERCOM->I2CM.CTRLA.reg |= !SERCOM_I2CM_CTRLA_ENABLE;
	PORT->Group[0].OUTSET.reg = PORT_PA08;
	PORT->Group[0].PINCFG[8].reg |= PORT_PINCFG_PULLEN;

	//Configure SDA pmux
	PORT->Group[0].PINCFG[8].reg |= PORT_PINCFG_PMUXEN;
	PORT->Group[0].PMUX[8 >> 1].bit.PMUXE = PORT_PMUX_PMUXE_C_Val;

	PORT->Group[0].OUTSET.reg = PORT_PA09;
	PORT->Group[0].PINCFG[9].reg |= PORT_PINCFG_PULLEN;

	//Configure SCL pmux
	PORT->Group[0].PINCFG[9].reg |= PORT_PINCFG_PMUXEN;
	PORT->Group[0].PMUX[9 >> 1].bit.PMUXO = PORT_PMUX_PMUXE_C_Val;

	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.CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN;
	while (I2C_SERCOM->I2CM.SYNCBUSY.reg);

	I2C_SERCOM->I2CM.BAUD.reg = SERCOM_I2CM_BAUD_BAUD(48);
	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);
	while (I2C_SERCOM->I2CM.SYNCBUSY.reg);
        I2C_SERCOM->I2CM.CTRLA.reg |= SERCOM_I2CM_CTRLA_ENABLE;
}

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);
		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_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;
}

bool i2c_write_byte(uint8_t byte){
	I2C_SERCOM->I2CM.DATA.reg = byte;

	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_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);
		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;
}

bool i2c_busy(void){
	bool busy;

	I2C_SERCOM->I2CM.ADDR.reg = I2C_ADDRESS | I2C_TRANSFER_WRITE;

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

	busy = (0 != (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK));

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

	return busy;
}

Any advice is appreciated.

This topic has a solution.
Last Edited: Wed. Sep 22, 2021 - 08:46 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

 

do not access the sercom before

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);

is done, so move

I2C_SERCOM->I2CM.CTRLA.reg |= !SERCOM_I2CM_CTRLA_ENABLE;

below these lines.

 

Furthermore, you need to enable the I2C first, and thant change the state of the internal statemachine, so swap the lines here

 

I2C_SERCOM->I2CM.STATUS.reg |= SERCOM_I2CM_STATUS_BUSSTATE(1);
	while (I2C_SERCOM->I2CM.SYNCBUSY.reg);
        I2C_SERCOM->I2CM.CTRLA.reg |= SERCOM_I2CM_CTRLA_ENABLE;

 

Hope this helps

 

Flo1991

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

First of all sorry for the really late response, I tried what you suggested however it still gets stuck on the same part. 

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If annyone is interested I used the code from the arduino core and changed it slightly, after that everything is working as intended.