Author Topic: I2C Atmel  (Read 16010 times)

0 Members and 1 Guest are viewing this topic.

Offline h2541Topic starter

  • Contributor
  • Posts: 13
  • Country: br
I2C Atmel
« on: July 21, 2016, 04:45:37 pm »

Hello,

did someone already using I2C on the card from atmel? If Yes, would you code examples that do not use the ASF? I find a lot of examples for UART but not for I2C.

Thank you
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11263
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #1 on: July 21, 2016, 05:04:11 pm »
What is "card"?

What MCU you are using?
Alex
 

Offline h2541Topic starter

  • Contributor
  • Posts: 13
  • Country: br
Re: I2C Atmel
« Reply #2 on: July 21, 2016, 05:07:26 pm »
It's SAMD 09
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11263
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #3 on: July 21, 2016, 05:11:33 pm »
Here is bare metal example for D21, it should work on D09 without modifications:
Code: [Select]
/*- Includes ----------------------------------------------------------------*/
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "hal.h"
#include "halGpio.h"
#include "i2c.h"

/*- Definitions -------------------------------------------------------------*/
HAL_GPIO_PIN(SDA,             A, 8);
HAL_GPIO_PIN(SCL,             A, 9);
#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           0xa0

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

/*- Implementations ---------------------------------------------------------*/

//-----------------------------------------------------------------------------
void i2c_init(void)
{
  HAL_GPIO_SDA_out();
  //HAL_GPIO_SDA_pullup();
  HAL_GPIO_SDA_pmuxen(I2C_SERCOM_PMUX);

  HAL_GPIO_SCL_out();
  //HAL_GPIO_SDA_pullup();
  HAL_GPIO_SCL_pmuxen(I2C_SERCOM_PMUX);

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

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

hal_gpio.h file that this code uses can be found here https://github.com/ataradov/mcu-starter-projects/tree/master/samd21 . Again, D09 and D21 are similar in this respect.
Alex
 
The following users thanked this post: cdev, 3db

Offline h2541Topic starter

  • Contributor
  • Posts: 13
  • Country: br
Re: I2C Atmel
« Reply #4 on: July 21, 2016, 05:56:28 pm »
Thank you, I adapted the code of the I2C  particularly for configuration  pin.
In the main, I have to do:
Code: [Select]
i2c_init();
i2c_write_start();
i2c_read(@; 0x8);
i2c_write_stop();

My card(master) needs to come retrieve data from an external component(slave). If I understood the functioning, my card is the master and it sends a START bit with the function i2c_write_start().

Then the master (SAM) to send the external component address with the function i2c_read(addres,size). But the data is broken down into two parts, the 15..8 bit then 7..0.

Then the master send the STOP bit with the function i2c_write_stop();

« Last Edit: July 21, 2016, 08:57:02 pm by h2541 »
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11263
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #5 on: July 21, 2016, 06:03:48 pm »
In the main, I have to do:
Why do you start write and then do read?

Functions i2c_write() and i2c_read() perform full write and read cycles.

i2c_write_start(), i2c_write_byte() and i2c_write_stop() are designed to perform "split" I2C write, just in case you don't have the entire buffer in the memory, for example.

But for me, it's not very clear because normally it's:

Slave address in this example is fixed to I2C_ADDRESS. You can change that if you want/need to, of course.

Code: [Select]
uint8_t data[] = {1, 2, 3, 4, 5, 6};
i2c_init();
i2c_write(data, 6);
Alex
 

Offline h2541Topic starter

  • Contributor
  • Posts: 13
  • Country: br
Re: I2C Atmel
« Reply #6 on: July 21, 2016, 06:19:30 pm »

But if I want to read data from my slave, I have to question the slave. The master must write the START bit, then the slave address. Then I must say to my master to read data from my slave.
Once the master has received the data from the slave, he should write the stop bit?
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11263
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #7 on: July 21, 2016, 06:33:34 pm »
That's exactly what i2c_read() function does. If you want to do all those things manually, then look at the code of that function.
Alex
 

Offline h2541Topic starter

  • Contributor
  • Posts: 13
  • Country: br
Re: I2C Atmel
« Reply #8 on: July 21, 2016, 06:55:42 pm »
Ok, if I want to do this:

Quote
Master : START         
Master : adress slave
Slave : data
Master: ACK   
Master : STOP 

I can do after i2c_init():
Code: [Select]
uint16_t i2c_read(void)
{
  SERCOM0->I2CM.ADDR.reg = I2C_ADDRESS | I2C_TRANSFER_READ;           //Master : adress slave

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

if (SERCOM0->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK)       // SLave responded with a NACK
{
SERCOM0->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);     // Stop condition
return false;
}

SERCOM0->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;      //Send NACK

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

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

return true;
}
« Last Edit: July 22, 2016, 06:11:53 pm by h2541 »
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11263
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #9 on: July 21, 2016, 08:38:33 pm »
To implement this all you need to do is:
1. Set I2C_ADDRESS to 0b1001 (A2) (A1) (A0) - substitute your (A2) (A1) (A0)
2.
Code: [Select]
  uint8_t buf[3];
  i2c_init();
  i2c_read(buf, 3);
After that buf[0]  will contain upper byte, buf[1]  will contain lower byte and buf[2]  will contain configuration register.
Alex
 

Offline h2541Topic starter

  • Contributor
  • Posts: 13
  • Country: br
Re: I2C Atmel
« Reply #10 on: July 21, 2016, 08:44:13 pm »
It is not necessary to describe the SDA and SCL signals?
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11263
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #11 on: July 21, 2016, 08:47:35 pm »
You assign them to pins in i2c_init(), but you don't have to manually wiggle them. That's the point of having I2C peripheral.
Alex
 

Offline h2541Topic starter

  • Contributor
  • Posts: 13
  • Country: br
Re: I2C Atmel
« Reply #12 on: July 21, 2016, 08:50:34 pm »
Ok, but the ACk of the master and slave will do as the chronogram
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11263
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #13 on: July 21, 2016, 08:52:18 pm »
Yes, they will. I2C is a standard and I2C peripheral implements that standard.
Alex
 

Offline h2541Topic starter

  • Contributor
  • Posts: 13
  • Country: br
Re: I2C Atmel
« Reply #14 on: July 21, 2016, 08:55:39 pm »
OK fine. Anyway thank you for your help.
 

Offline h2541Topic starter

  • Contributor
  • Posts: 13
  • Country: br
Re: I2C Atmel
« Reply #15 on: July 22, 2016, 09:25:03 am »
I modified the function i2c_read that it returns me the result of the buffer:
Code: [Select]
uint16_t i2c_read(uint8_t *data, int size)
{
SERCOM0->I2CM.ADDR.reg = I2C_ADDRESS | I2C_TRANSFER_READ;

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

if (SERCOM0->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK)
{
SERCOM0->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);
//return false;
}

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

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

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

return data;
}

in the main
Code: [Select]
uint8_t buf3[3];
uint16_t voltage=i2c_read(&buf3, 3);

But the problem is that when I do vary my voltage value that turns my slave

 But all this I need to use the function i2c_read? In this function, I have to place the lines containing the comments while in otherwise the program crashes .

« Last Edit: July 22, 2016, 06:16:51 pm by h2541 »
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11263
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #16 on: July 22, 2016, 04:17:51 pm »
Ok, you clearly don't know C, so why start from braking a working code? Why not see that is works as is?

To get the value from my working code do this:
Code: [Select]
  uint8_t buf[3];
  i2c_init();
  i2c_read(buf, 3);
  uint16_t value = ((uint16_t)buf[0] << 8) | buf[1];
  // here value has the result you need.

Your code will always return the same value, because you are returning two lower bytes of the address of the supplied buffer. There are other mistakes as well.
Alex
 

Offline h2541Topic starter

  • Contributor
  • Posts: 13
  • Country: br
Re: I2C Atmel
« Reply #17 on: July 22, 2016, 04:59:28 pm »
I rework the code base and it still gives me the same value except this time it's 3164 (C5C) in decimal.
But when I vary the voltage it returns me constantly that value C5C
With buf
  • = 92; buf [1] = 92 and buf [2] = 92

and value = 23644 (5C5C).

But what remains amazing is that the DATA register does not change and stay at 0
« Last Edit: July 22, 2016, 05:04:09 pm by h2541 »
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11263
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #18 on: July 22, 2016, 05:26:07 pm »
Can you show your code as it stands right now. And also what type of sensor you are using and how it is connected to the MCU?
Alex
 

Offline h2541Topic starter

  • Contributor
  • Posts: 13
  • Country: br
Re: I2C Atmel
« Reply #19 on: July 22, 2016, 05:35:47 pm »
Code: [Select]
/*- Definitions -------------------------------------------------------------*/
HAL_GPIO_PIN(SDA,             A, 8);
HAL_GPIO_PIN(SCL,             A, 9);
#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           0xa0

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

/*- Implementations ---------------------------------------------------------*/

//-----------------------------------------------------------------------------
void i2c_init(void)
{
  HAL_GPIO_SDA_out();
  //HAL_GPIO_SDA_pullup();
  HAL_GPIO_SDA_pmuxen(I2C_SERCOM_PMUX);

  HAL_GPIO_SCL_out();
  //HAL_GPIO_SDA_pullup();
  HAL_GPIO_SCL_pmuxen(I2C_SERCOM_PMUX);

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


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

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

if (SERCOM0->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK)
{
SERCOM0->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);
return false;
}

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

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

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

return true;
}

Main
Code: [Select]
                        sys_init();
                        i2c_init();
                        uint8_t buf[3];
i2c_read (&buf,3);


But why when I chang the voltage it's the same value
« Last Edit: July 22, 2016, 06:14:30 pm by h2541 »
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11263
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #20 on: July 22, 2016, 05:41:37 pm »
Well, why did you comment out waiting for salve to be on the bus? You can't just comment out thing from the code and expect it to work. If those parts were not needed, they would not be there.

Also, this code expects shifted address, so it must be  0b10010010, assuming that (A2) (A1) (A0) are 001 in your schematic.

Also, you did not answer the question about the type of sensor and how it is connected to the MCU.
Alex
 

Offline h2541Topic starter

  • Contributor
  • Posts: 13
  • Country: br
Re: I2C Atmel
« Reply #21 on: July 22, 2016, 05:50:48 pm »
I comment out waiting for salve to be on the bus because  it remains blocked in the two "while" I commented and it never comes out.

SDA(4)==> PA14
SCL(3)==> PA15
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11263
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #22 on: July 22, 2016, 05:52:14 pm »
I comment out waiting for salve to be on the bus because  it remains blocked in the two "while" I commented and it never comes out.
And instead of commenting this code out you should really think why there is no slave. In this case you have the wrong address.
Alex
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11263
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #23 on: July 22, 2016, 06:01:42 pm »
And as I said, this code needs shifted address, so it must read 0b10010010.
Alex
 
The following users thanked this post: h2541

Offline h2541Topic starter

  • Contributor
  • Posts: 13
  • Country: br
Re: I2C Atmel
« Reply #24 on: July 22, 2016, 06:06:46 pm »
Yes , it was the wrong address sorry. now I can take my changes thank you
« Last Edit: July 22, 2016, 06:11:15 pm by h2541 »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf