i2c help on samd21

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

So i have been trying to communicate with an eeprom chip , 24LC256 

and according to the datasheet for a samd21 on page 564 

in the STATUS register bit 2  (RXNACK) will read 0 when the slave has acknowledged my previously sent address or data packet .. the problem is this ALWAYS read 0 even when i dont initiate the i2c or anything for that mater 

so at first i thought i was writing to the eeprom chip because i was checking this sucker each time to see if an ack was received and proceeding from there .

 

 

 

this code might hurt a professional's brain.. proceed with caution 

 

/*
 * SAM_D21_Learning.c
 *
 * Created: 8/10/2015 10:11:55 PM
 *  Author: amaya_000
 */ 

#include "sam.h"
#include <stdbool.h> 

uint8_t data ; 

//prototypes
_Bool init_i2c(uint8_t);
void update_speed(void);
void wait_sync(void);
void i2c_write_byte (uint8_t, uint16_t);
uint8_t i2c_read_byte(uint16_t);
void wait_sync(void);




int main(void)
{
   
    SystemInit();
	
	//enable port in PM
	PM->APBBMASK.bit.PORT_ = 1;
	PM->APBAMASK.bit.GCLK_ = 1; 
	
	//set up LED
	PORT->Group[0].DIRSET.reg = 1<<5 ;
	PORT->Group[0].OUTCLR.reg = 1<<5 ;

	//48mhz
	update_speed(); 
	

	
	
	/*
	if(init_i2c(0b10100100)) //initilize as write
	{

				
		i2c_write_byte(65,0xFFEE); 
		
	}
	
	
	if(i2c_read_byte(0xFFEE) == 65)
	{
		//PORT->Group[0].OUTSET.reg = 1<<5;
	}

	*/
	
	
	
   while(1)
   {
	   

		
	}//end while(1)


}//end main

void wait_sync()
{
	
	while(SERCOM4->I2CM.SYNCBUSY.bit.SYSOP == 1);
}

uint8_t i2c_read_byte(uint16_t memory_adress)
{
	
	
		
		
		//send an address 
		SERCOM4->I2CM.DATA.reg = memory_adress>>4;
		wait_sync() ;
		if(SERCOM4->I2CM.STATUS.bit.RXNACK == 0)
		{
			
			
			SERCOM4->I2CM.DATA.reg = memory_adress;
			wait_sync() ;
			SERCOM4->I2CM.ADDR.bit.ADDR = 0b10100101 ; //send a repeat start in read mode
			wait_sync() ;
			data  = SERCOM4->I2CM.DATA.reg ;
			wait_sync();
			
			return data;
		
		
			
		}
	
	return 0;
	
}
void i2c_write_byte (uint8_t byte , uint16_t memory_address)
{
	
	
	//send  high bytes of address to store data in the eeprom
	SERCOM4->I2CM.DATA.reg = memory_address>>4;
	wait_sync();
	
	//check  for ack
	if(SERCOM4->I2CM.STATUS.bit.RXNACK == 0)
	{
		//send low bye of address of where to store data on eeprom
		SERCOM4->I2CM.DATA.reg = memory_address;
		wait_sync();
		
		//check for ack
		if(SERCOM4->I2CM.STATUS.bit.RXNACK == 0)
		{
			//send actual data
			SERCOM4->I2CM.DATA.reg = byte;
			wait_sync();
			
			//check for ack
			if(SERCOM4->I2CM.STATUS.bit.RXNACK == 0)
			{
				//when writing in 1 byte mode you must generate a stop after the byte is sent and you receive a NACK
				//the stop mode lets initiates that actual writting within the eeprom..per its data sheet
				SERCOM4->I2CM.CTRLB.bit.CMD = 0x3 ;
				//PORT->Group[0].OUTSET.reg = 1<<5 ;
				
			}
		}
		
	}
}



_Bool init_i2c(uint8_t address)
{
	
	
	
	//enable i2c in PM
	PM->APBCMASK.bit.SERCOM4_ = 1;
	
	//configre the pins i2c will use
	PORT->Group[1].PMUX[6].bit.PMUXE = 0x2;
	PORT->Group[1].PMUX[6].bit.PMUXO = 0x2;
	PORT->Group[1].PINCFG[12].bit.PMUXEN = 1;
	PORT->Group[1].PINCFG[13].bit.PMUXEN = 1;
	
	
	
	//i2c uses 2 clocks the core clock and a slow clock
	//when using high speed the slow clock i used in certain
	//functions , which the i2c bus handles on its own
	//in that case 2 clocks must be set up
	//how ever i used just a standard speed 	
	GCLK->CLKCTRL.bit.GEN = 0x2 ;	//pick a generator
	GCLK->CLKCTRL.bit.ID = 0x18 ;	// ID will be your sercom4 instance 
	GCLK->CLKCTRL.bit.CLKEN = 1;	
	GCLK->GENCTRL.bit.SRC = 0x07 ;	//source : i used dfll since i have it running at 48mhz
	GCLK->GENCTRL.bit.ID = 0x2 ;	// same name as the generator you picked
	GCLK->GENCTRL.bit.GENEN = 1 ;	//enable the dam clock
	
	////////////
	
		
	GCLK->CLKCTRL.bit.GEN = 0x3 ;	//pick a generator
	GCLK->CLKCTRL.bit.ID = 0x13 ;	// ID will be your TC instance (TC3)
	GCLK->CLKCTRL.bit.CLKEN = 1;
	GCLK->GENCTRL.bit.SRC = 0x07 ;	//source : i used dfll since i have it running at 48mhz
	GCLK->GENCTRL.bit.ID = 0x3 ;	// same name as the generator you picked
	GCLK->GENCTRL.bit.GENEN = 1 ;	//enable the dam clock
	
	//select master mode in CTRLA per data sheet MASTER
	SERCOM4->I2CM.CTRLA.bit.MODE = 0x5;
	
	
	//set baudrate..need more research in this area
	SERCOM4->I2CM.BAUD.bit.BAUD  = 0xfC ; //i dont know why i chose this number;
	

	
	
	//enable and then writting to ADDR initiates the start sequence
	//the eeprom asks for the first byte to be  1010 + A2 A1 A0 + R/W  
	SERCOM4->I2CM.CTRLA.bit.ENABLE= 1;
	SERCOM4->I2CM.ADDR.bit.ADDR  = address ; //accesing addr will initiate a start bit with the address entered
	wait_sync();//wait for synch 
	if(SERCOM4->I2CM.INTFLAG.bit.MB == 1 && SERCOM4->I2CM.STATUS.bit.RXNACK == 0) // if master is on bus and ack received 
	{
		return true;
	} 
	else
	return false;
}


 

Last Edited: Thu. Oct 15, 2015 - 11:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

It is kind of hard to parse through all this. I'll give you the code that works tomorrow, I don't have it on this PC.

NOTE: I no longer actively read this forum. Please ask your question on www.eevblog.com/forum if you want my answer.

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

thanks , and yes it is hard to figure it out i was using a lot of hard coded numbers there just to test 

ill check back tomorrow

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

Here is the code.

 

Basic usage (written straight into the forum, so errors are possible):

Quote:

uint8_t addr = 5;

uint8_t byte;

 

i2c_init();
i2c_write(&addr, 1);

i2c_read(&byte, 1);

// Here byte contains the received data.

Attachment(s): 

NOTE: I no longer actively read this forum. Please ask your question on www.eevblog.com/forum if you want my answer.

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

Ahh thanks very much . I'll check this out the second I get home

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

I2C problem solved? I also encountered similar problems, I want a complete read and write EEPROM code.

China

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

I am also currently facing problems with i2c, but i don't get any signal on the pins using the posted code snippets.

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

Did you setup the pinmux to give the SERCOM access to the I2C pins?

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

    /* port mux configuration*/
    PORT->Group[0].PINCFG[PIN_PA22].reg = PORT_PINCFG_PMUXEN | PORT_PINCFG_INEN | PORT_PINCFG_PULLEN; /* SDA */
    PORT->Group[0].PINCFG[PIN_PA23].reg = PORT_PINCFG_PMUXEN | PORT_PINCFG_INEN | PORT_PINCFG_PULLEN; /* SCL */
    
    /*PMUX: even = n/2, odd: (n-1)/2 */
    PORT->Group[0].PMUX[PIN_PA22/2].reg = 0x02;
    PORT->Group[0].PMUX[PIN_PA23/2].reg = 0x20;
    
    /* APBCMASK */
    PM->APBCMASK.reg |= PM_APBCMASK_SERCOM1;
    
    /*gclk configuration for sercom1 module*/
    GCLK->CLKCTRL.reg =    GCLK_CLKCTRL_ID (SERCOM1_GCLK_ID_CORE) |
                        GCLK_CLKCTRL_ID (SERCOM1_GCLK_ID_SLOW) |
                        GCLK_CLKCTRL_GEN(0) |
                        GCLK_CLKCTRL_CLKEN;
        
    /* set configuration for SERCOM1 I2C module */
    SERCOM1->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN; /* smart mode enable */
    while (SERCOM1->I2CM.SYNCBUSY.reg);
    
    /* Set baudrate */
    uint32_t fgclk        = 8000000;    /* 8MHz */
    uint32_t fscl        = 1000; /* 1kHz SCL */
    uint32_t trise        = 215; /* 215 ns rising time */
    int32_t numerator    = fgclk - fscl*(10 + fgclk*trise/1000000000);
    int32_t denominator    = 2*fscl;
    int32_t tmp_baud = (int32_t)(div_ceil(numerator, denominator));
    SERCOM1->I2CM.BAUD.bit.BAUD = SERCOM_I2CM_BAUD_BAUD(tmp_baud);
    while (SERCOM1->I2CM.SYNCBUSY.reg);
    
    SERCOM1->I2CM.CTRLA.reg =    SERCOM_I2CM_CTRLA_ENABLE |            /* enable module */
                                SERCOM_I2CM_CTRLA_MODE_I2C_MASTER |    /* i2c master mode */
                                SERCOM_I2CM_CTRLA_SDAHOLD(3);        /* SDA hold time to 600ns */
    while (SERCOM1->I2CM.SYNCBUSY.reg);
    
    SERCOM1->I2CM.STATUS.reg |= SERCOM_I2CM_STATUS_BUSSTATE(1); /* set to idle state */
    while (SERCOM1->I2CM.SYNCBUSY.reg);

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
    PORT->Group[0].PMUX[PIN_PA22/2].reg = 0x02;
    PORT->Group[0].PMUX[PIN_PA23/2].reg = 0x20;

Here you are writing  PMUX[11].reg twice with different values so PA22 will not have the correct mux setting.
/Lars

 

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

I don't use Atmel code so I don't know for sure if what you're doing there is correct but it seems right. Have you enabled SERCOM, GCLK_SERCOMx_CORE and GCLK_SERCOM_SLOW?

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

Thanks, i changed it to

PORT->Group[0].PMUX[PIN_PA22/2].reg = 0x22;

But it still doesnt work.

Any other ideas?

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

Hello all ,

 

I am using ATSAMHA1G16A microcontroller ( with capacitive sensors)  for I2C communication using Atmel Start .Right now I am only trying to get the Signals and observe them on oscilloscope without any slave. But I am not getting any Signal.Here I am posting my Code to check .I am also attaching a snap shot from Atmel start so that core and clock frequencies can also be seen by you. I am using a function I2C_0_Init and will post my Code for this function separately.

 

 

#include <atmel_start.h>

int main(void)
{

 
 atmel_start_init();
 
 // Initializing I2C communication   // Code for this function can be found other section of this post
 I2C_0_init() ;

 /* smart mode enabled by setting the bit SMEN as 1 */

 SERCOM0->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN;

 // synchronizing check

 while(SERCOM0->I2CM.SYNCBUSY.reg);
 
 
 /*  Baud register is loaded */ 
 
 SERCOM0->I2CM.BAUD.reg = SERCOM_I2CM_BAUD_BAUD(48) ;  // confused about it , 
 
 /* synchronization busy */
 
 while(SERCOM0->I2CM.SYNCBUSY.reg);
 
 
  /* SERCOM0 peripheral enabled by setting the ENABLE bit as 1*/

  SERCOM0->I2CM.CTRLA.reg |= SERCOM_I2CM_CTRLA_ENABLE | SERCOM_I2CM_CTRLA_MODE_I2C_MASTER | SERCOM_I2CM_CTRLA_SDAHOLD(2) ;
  
   // Setting I2C clock to idle state
   SERCOM0->I2CM.STATUS.reg = SERCOM_I2CM_STATUS_BUSSTATE(1);
   
   /* SERCOM Enable synchronization busy */
   while(SERCOM0->I2CM.SYNCBUSY.reg);
   

 while (1) {
  
  
              }
 
}

 

 

 

Code for I2C_0_Init

 

void I2C_0_init(void)
{
 I2C_0_CLOCK_init();
 I2C_0_PORT_init();
}

 

void I2C_0_CLOCK_init(void)
{
 _pm_enable_bus_clock(PM_BUS_APBC, SERCOM0);
 _gclk_enable_channel(SERCOM0_GCLK_ID_CORE, CONF_GCLK_SERCOM0_CORE_SRC);
 _gclk_enable_channel(SERCOM0_GCLK_ID_SLOW, CONF_GCLK_SERCOM0_SLOW_SRC);
}

 

void I2C_0_PORT_init(void)
{

 gpio_set_pin_pull_mode(PA08,
                        // <y> Pull configuration
                        // <id> pad_pull_config
                        // <GPIO_PULL_OFF"> Off
                        // <GPIO_PULL_UP"> Pull-up
                        // <GPIO_PULL_DOWN"> Pull-down
                        GPIO_PULL_OFF);

 gpio_set_pin_function(PA08, PINMUX_PA08C_SERCOM0_PAD0);

 gpio_set_pin_pull_mode(PA09,
                        // <y> Pull configuration
                        // <id> pad_pull_config
                        // <GPIO_PULL_OFF"> Off
                        // <GPIO_PULL_UP"> Pull-up
                        // <GPIO_PULL_DOWN"> Pull-down
                        GPIO_PULL_OFF);

 gpio_set_pin_function(PA09, PINMUX_PA09C_SERCOM0_PAD1);
}

 

 

I am also attaching an snap shot from Atmel start here.

 

Any Kind of help will be highly appreciated.

 

Attachment(s): 

Last Edited: Mon. Jun 25, 2018 - 12:55 PM