Author Topic: I2C Atmel  (Read 16000 times)

0 Members and 1 Guest are viewing this topic.

Offline OliverK

  • Contributor
  • Posts: 19
  • Country: nl
  • student electrical engineering
Re: I2C Atmel
« Reply #25 on: June 17, 2017, 11:14:23 am »
Could u send me your complete code, im working on a simillair project with I2C and a samd10 witch is almst the same
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11258
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #26 on: June 17, 2017, 05:34:01 pm »
Alex
 

Offline OliverK

  • Contributor
  • Posts: 19
  • Country: nl
  • student electrical engineering
Re: I2C Atmel
« Reply #27 on: July 11, 2017, 02:17:36 pm »
Hi i have one question about your code

Code: [Select]
void i2c_pins(int mask, int value)

{

  if (mask & I2C_PINS_SDA)

  {

    HAL_GPIO_SDA_out();

    HAL_GPIO_SDA_write(value & I2C_PINS_SDA);

  }

  else

  {

    HAL_GPIO_SDA_in();

    HAL_GPIO_SDA_clr();

  }



  if (mask & I2C_PINS_SCL)

  {

    HAL_GPIO_SCL_out();

    HAL_GPIO_SCL_write(value & I2C_PINS_SCL);

  }

  else

  {

    HAL_GPIO_SCL_in();

    HAL_GPIO_SCL_clr();

  }



  HAL_GPIO_SDA_pmuxdis();

  HAL_GPIO_SCL_pmuxdis();

}

Could you explain what int mask and int value are for?
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11258
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #28 on: July 11, 2017, 05:17:45 pm »
This is just a feature that lets you set any arbitrary values on the SCL and SDA pins, going around normal I2C peripheral.

I needed this, because I used SCL and/or  SCL low on reset as an entry condition for the bootloader.
Alex
 

Offline d-jokic

  • Contributor
  • Posts: 25
  • Country: us
Re: I2C Atmel
« Reply #29 on: October 02, 2017, 11:38:05 pm »
Hi,

Regarding your project, where can I find the following files: stdlib.h, stdint.h, and stdbool.h? Thanks

Here is a complete I2C master https://github.com/ataradov/dgw/blob/master/embedded/i2c_master.c
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11258
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #30 on: October 02, 2017, 11:38:58 pm »
Regarding your project, where can I find the following files: stdlib.h, stdint.h, and stdbool.h? Thanks
They are a part of the standard C library, they come with a compiler.
Alex
 

Offline d-jokic

  • Contributor
  • Posts: 25
  • Country: us
Re: I2C Atmel
« Reply #31 on: October 02, 2017, 11:54:00 pm »
That's what I've realized, but for some reason it was throwing an error. It's better now; no idea why that happened. My second issue is the hal.h file. I believe that there is an entire hal file that my project should have. I'm not familiar with what hal is, and was wondering if this is correct.

Thanks for your help!
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11258
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #32 on: October 02, 2017, 11:55:23 pm »
My second issue is the hal.h file.
What file are you talking about? There is a complete project that is self sufficient.
Alex
 

Offline d-jokic

  • Contributor
  • Posts: 25
  • Country: us
Re: I2C Atmel
« Reply #33 on: October 02, 2017, 11:58:39 pm »
In the code you posted on the forum, there is an include hal.h. This file isn't in the github .
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11258
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #34 on: October 03, 2017, 12:01:43 am »
In the code you posted on the forum, there is an include hal.h. This file isn't in the github .
That file was taken from a different project. Just use the whole project from GitHub.

You can't just copy-paste things and expect them to work. The code posted here is just to illustrate the idea, you will have to adopt it to your needs anyway.
Alex
 

Offline d-jokic

  • Contributor
  • Posts: 25
  • Country: us
Re: I2C Atmel
« Reply #35 on: October 03, 2017, 12:30:12 am »
Ok, makes sense. I misunderstood and thought they were one and the same. In your i2c_master.c file, I can't find anywhere where you declare the slave address.

Also what is "T_RISE" and how is it used in i2c_init? One other thing is that F_CPU is not declared anywhere, however it's used in the initialization functions. I'm not sure how that's supposed to work.

Sorry for all the questions, I'm very new to embedded (and Atmel) and usually rely heavily on comments to figure out what code does. I'm assuming the name is a give away however, I'm not familiar with it.

Thank you!
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11258
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #36 on: October 03, 2017, 01:02:04 am »
Ok, makes sense. I misunderstood and thought they were one and the same. In your i2c_master.c file, I can't find anywhere where you declare the slave address.
Have you tried to read the code? It is right there "bool i2c_start(int addr)".

Also what is "T_RISE" and how is it used in i2c_init?
Read the datasheet.

One other thing is that F_CPU is not declared anywhere, however it's used in the initialization functions. I'm not sure how that's supposed to work.
It is declared in the project settings or in the Makefile.
Alex
 

Offline d-jokic

  • Contributor
  • Posts: 25
  • Country: us
Re: I2C Atmel
« Reply #37 on: October 03, 2017, 06:12:42 pm »
Hi Alex,

Thank you for your help thus far. However, I'm unable to acquire data from my sensor. The code does compile. I am using a max30105 particle sensor connected as follows: SDA --> PA22 and SCL --> PA23. I am uploading the code via the Arduino Zero (and therefore EDBG debugger), I don't believe this is causing the problem. The sensor is supposed to have a light turn on when it is on, this is currently not happening./ I have also checked that the sensor is functional. I have included my code below. One issue I was having was that the slave address entered in i2c_init is an int; does this mean that the integer representation of the address needs to be entered rather than the hexadecimal? I have tried hexadecimal, binary and integer and none have worked. The address is 0x57, or 87 in decimal. I just wanted to make sure I was implementing your code correctly:

i2c_master.c
Code: [Select]
/*- Includes ----------------------------------------------------------------*/
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "samd21.h"
#include "hal_gpio.h"
#include "i2c_master.h"

/*- Definitions -------------------------------------------------------------*/
HAL_GPIO_PIN(SDA,             A, 22);
HAL_GPIO_PIN(SCL,             A, 23);
#define I2C_SERCOM            SERCOM2
#define I2C_SERCOM_PMUX       PORT_PMUX_PMUXE_D_Val
#define I2C_SERCOM_GCLK_ID    SERCOM2_GCLK_ID_CORE
#define I2C_SERCOM_CLK_GEN    0
#define I2C_SERCOM_APBCMASK   PM_APBCMASK_SERCOM2

#define T_RISE                215e-9 // Depends on the board, actually

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

enum
{
  I2C_PINS_SDA = (1 << 0),
  I2C_PINS_SCL = (1 << 1),
};

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

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

//-----------------------------------------------------------------------------
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);
  else
    I2C_SERCOM->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;

  *byte = I2C_SERCOM->I2CM.DATA.reg;

  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_busy(int addr)
{
  bool busy;

  I2C_SERCOM->I2CM.ADDR.reg = addr | 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;
}

//-----------------------------------------------------------------------------
void i2c_pins(int mask, int value)
{
  if (mask & I2C_PINS_SDA)
  {
    HAL_GPIO_SDA_out();
    HAL_GPIO_SDA_write(value & I2C_PINS_SDA);
  }
  else
  {
    HAL_GPIO_SDA_in();
    HAL_GPIO_SDA_clr();
  }

  if (mask & I2C_PINS_SCL)
  {
    HAL_GPIO_SCL_out();
    HAL_GPIO_SCL_write(value & I2C_PINS_SCL);
  }
  else
  {
    HAL_GPIO_SCL_in();
    HAL_GPIO_SCL_clr();
  }

  HAL_GPIO_SDA_pmuxdis();
  HAL_GPIO_SCL_pmuxdis();
}



main.c
Code: [Select]
//-----------------------------------------------------------------------------
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "samd21.h"
#include "hal_gpio.h"
#include "i2c_master.h"

//-----------------------------------------------------------------------------
#define PERIOD_FAST     100
#define PERIOD_SLOW     500

uint8_t data;

//-----------------------------------------------------------------------------
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_DIV256 | TC_CTRLA_PRESCSYNC_RESYNC;

  TC3->COUNT16.COUNT.reg = 0;

  timer_set_period(PERIOD_SLOW);

  TC3->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;

  TC3->COUNT16.INTENSET.reg = TC_INTENSET_MC(1);
  NVIC_EnableIRQ(TC3_IRQn);
}

static void sys_init(void)
{
  // Switch to 8MHz clock (disable prescaler)
  SYSCTRL->OSC8M.bit.PRESC = 0;

  // Enable interrupts
  asm volatile ("cpsie i");
}

//-----------------------------------------------------------------------------
int main(void)
{
  sys_init();
  i2c_init(100);
  i2c_start(0x57);

  while (1)
  {
i2c_read_byte(&data, true);
  }

  return 0;
}


Thanks again for your help. I'm hoping I can figure this out soon.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11258
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #38 on: October 03, 2017, 06:32:48 pm »
You need to specify full 8-bit address, so it would be 0xAE.

And I'm not sure what you expect from a simple read. This sensors requires configuration. It has a register map, so you need to write a register index you want to read before you can read the value. There are pseudo-code examples in the datasheet. Active != transaction on the bus.

And you are specifying the last byte flag, but still continue to read in a loop. That will create some unknown condition on the bus.

And there can be a lot of other problems. You need a scope/logic analyzer to see what's going on on the lines.
Alex
 

Offline d-jokic

  • Contributor
  • Posts: 25
  • Country: us
Re: I2C Atmel
« Reply #39 on: October 03, 2017, 07:30:17 pm »
Thanks for the info. This is more complicated than I anticipated, due to the complexity of the sensor.

This sensor has an Arduino library in which they assign names to all the registers; e.g:

Code: [Select]
static const uint8_t MAX30105_FIFOWRITEPTR = 0x04;
static const uint8_t MAX30105_FIFOOVERFLOW = 0x05;
static const uint8_t MAX30105_FIFOREADPTR = 0x06;
static const uint8_t MAX30105_FIFODATA = 0x07;

I know you can't just copy paste arduino library code and expect it to work on a mcu (since most libraries make use of other arduino libraries, aka they're written half in C and half in Arduino) but for something like this, I can copy this into my code right? Rather than combing through the datasheet looking for register addresses
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11258
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #40 on: October 03, 2017, 07:38:41 pm »
You can obviously copy all definitions like this. You can't copy code, but you can look at how that code is structured.

No matter what, any I2C stuff will be built from basic blocks: START, READ/WRITE, STOP. There is not a whole lot you can expect from I2C. Just see how they do it.

This is not really complex, it is pretty typical for I2C devices have a number of registers. The demo application by the same link reads and writes temperature from an I2C device, and it does setup some configuration registers. Yours will be similar.

Alex
 

Offline d-jokic

  • Contributor
  • Posts: 25
  • Country: us
Re: I2C Atmel
« Reply #41 on: October 04, 2017, 01:46:47 am »
So to clarify, the sensor requires configuration: from the data sheet I've gotten the following pseudo code. I'm having trouble understanding what needs to be done and am not sure if my interpretations are correct. Your input would really be appreciated!

The FIFO Write Pointer points to the location where the MAX30105 writes the next sample & advances for each sample pushed on to the FIFO; FIFO_WR_PTR is the bits within this register than can be accessed
The FIFO Read Pointer points to the location from where the processor gets the next sample from the FIFO through the I2C interface; advances each time a sample is popped from the FIFO. FIFO_RD_PTR is the bits within this register than can be accessed

This is the first part of the pseudo code:
Code: [Select]
(1) START; 
(2) Send device address + write mode
*slave acknowledges*
(3) Send address of FIFO_WR_PTR;
*slave acknowledges*
(4) REPEATED_START;
(5) Send device address + read mode
(6) Read FIFO_WR_PTR;
(7) STOP;

My interpretations:
Preliminary step: recommended to first clear the FIFO_WR_PTR, OVF_COUNTER, and FIFO_RD_PTR registers to all zeroes (0x00) to ensure FIFO is empty before every new sensor reading
(1)--> send a start condition
(2)--> send the slave address and set master to write mode
*slave acknowledges*
(3)--> send the address 0x04, which is the address of the FIFO write pointer
*slave acknowledges*
(4)--> send another start condition
(5)--> send the slave address and set the master to read mode
(6)--> master reads data stored; what data would be here though?
(7)--> send stop command

Then is goes on to say:

Code: [Select]

The central processor evaluates the number of samples to be read from the FIFO:
NUM_AVAILABLE_SAMPLES = FIFO_WR_PTR – FIFO_RD_PTR

NUM_SAMPLES_TO_READ = < less than or equal to NUM_AVAILABLE_SAMPLES >

My interpretation:
FIFO_WR_PTR points to where the next byte of data can be stored; and FIFO_RD_PTR points to the next byte of data to be read. This gives us how many samples can be read, but I'm not sure how.
 
Code: [Select]
Second transaction: Read NUM_SAMPLES_TO_READ samples from the FIFO:
(1)START;
(2)Send device address + write mode
(3)Send address of FIFO_DATA;
(4)REPEATED_START;
(5)Send device address + read mode
(6)for (i = 0; i < NUM_SAMPLES_TO_READ; i++) {
    Read FIFO_DATA;}

My interpretation
(1) start command
(2) send the slave address and set master to write mode
(3) send address of FIFO DATA (where we want to store data)
(4) start command again
(5) send slave address and set master to read
(6) read NUM_SAMPLES_TO_READ worth of data

My question is a dumb one, but how to I actually go about writing to these registers? I'm also not sure to which register to actually write the commands. The data sheet says " in practice only the FIFO_RD_PTR register should be written to in operation". But that doesn't make sense to me.

Datasheet I've been referencing: https://datasheets.maximintegrated.com/en/ds/MAX30105.pdf
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11258
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #42 on: October 04, 2017, 02:58:54 am »
You will have too figure out how to use that device yourself, or ask on Maxim forum.

As far as I2C goes, your understanding is correct. The data that is read will be the value of the register indexed by the address specified in the write command. This is pretty typical for I2C devices that need to report more than one value, or need configuration.

Basic approach when designing things like this is to first of all read a register with a known value, like ID, configuration, whatever. This will basically ensure that you can talk to the device and it understands you. Then start actually writing hard code, and you will probably understand how to do that based on your experience from the first basic step.
Alex
 

Offline funkathustra

  • Regular Contributor
  • *
  • Posts: 150
  • Country: us
Re: I2C Atmel
« Reply #43 on: October 04, 2017, 05:19:15 pm »
Agree with @ataradov

Few things to add: basically every i2C device these days uses SMBus-style registers to interact with the device. When I first started, it was confusing. Official Linux documentation actually helped me understand what needed to happen:
https://www.kernel.org/doc/Documentation/i2c/smbus-protocol

There's absolutely no reason you can't just manipulate the Arduino library for it, too. I had a look at it, and it's very typical I2C stuff.

Notice how everything calls into writeRegister8() and readRegister8()? Those functions could easily be implemented from the code @ataradov was nice enough to provide.

This is good stuff to learn, as 95% of sensors these days use I2C, and use it in a register-based way. Once you port a few libraries over, you'll get comfortable with this stuff enough that you'll be able to find random chips on DigiKey that hobbyist libraries don't exist for, and integrate them into your design easily.

Very powerful stuff! Keep going!
 

Offline d-jokic

  • Contributor
  • Posts: 25
  • Country: us
Re: I2C Atmel
« Reply #44 on: October 04, 2017, 10:08:14 pm »
Thanks to both of you. I took your advice and ported over readregister8 and writeregister8. However, it's still not functional; using a multimeter I found that both SDA and SCL are high, meaning that communication doesn't start. One thing I wasn't sure about in Alex's code was what to set int freq to in i2c_init(). Google says that the most widely used clock rate is 100kHz, so I put that.

This is the Arduino code; it's part of a class (MAX30105) and _i2cPort is an i2c instance through the wire library. I don't have an equivalent for _i2cPort because Alex's code is set up to run on certain pins (PA8 and PA9) ans I'm only using 1 i2c right now.

Code: [Select]
uint8_t MAX30105::readRegister8(uint8_t address, uint8_t reg) {
  _i2cPort->beginTransmission(address);
  _i2cPort->write(reg);
  _i2cPort->endTransmission(false);
  _i2cPort->requestFrom(address, 1);   // Request 1 byte
  return (_i2cPort->read());
}

void MAX30105::writeRegister8(uint8_t address, uint8_t reg, uint8_t value) {
  _i2cPort->beginTransmission(address);
  _i2cPort->write(reg);
  _i2cPort->write(value);
  _i2cPort->endTransmission();
}


This is my port over using Alex's code:

Code: [Select]
uint8_t readRegister8(uint8_t reg) {
i2c_init(100000); //initialize i2c
i2c_start(SLAVE_ADDRESS_WRITE); //start isc; send slave address and set to write
i2c_write_byte(reg); //write register of interest to slave
i2c_start(SLAVE_ADDRESS_READ); //restart command; resend slave address + read mode
i2c_read_byte(&read_register_value,true);         //read 1 bit from the slave
i2c_stop();

return (read_register_value);
}

void writeRegister8(uint8_t reg, uint8_t value) {
i2c_init(100000); //initialize i2c
i2c_start(SLAVE_ADDRESS_WRITE); //start isc; send slave address and set to write
i2c_write_byte(reg); //write register of interest to slave
i2c_write_byte(value);                 //read 1 bit from the slave
i2c_stop();
}

The readregister8 and writeregister8 functions are also used for a function called bitMask, which is used for configuring the sensor (setting up some registers regarding adc range, sampling speed, etc)
Arduino code:
Code: [Select]
void MAX30105::bitMask(uint8_t reg, uint8_t mask, uint8_t thing)
{
  // Grab current register context
  uint8_t originalContents = readRegister8(_i2caddr, reg);

  // Zero-out the portions of the register we're interested in
  originalContents = originalContents & mask;

  // Change contents
  writeRegister8(_i2caddr, reg, originalContents | thing);
}

Ported:
Code: [Select]
void bitMask(uint8_t reg, uint8_t mask, uint8_t thing)
{
// Grab current register context
uint8_t originalContents = readRegister8(reg);

// Zero-out the portions of the register we're interested in
originalContents = originalContents & mask;

// Change contents
writeRegister8(reg, originalContents | thing);
}

I probably made a mistake somewhere along the way (probably over simplified or forgot a step). I'll keep combing over it and I'll see if I can find it. If you spy anything that looks odd, please let me know!



-----------------Overall code----------------------------------------
MAIN.C

Code: [Select]
int i = 0;
uint8_t array[32];
uint8_t activeLEDs;
uint8_t read_register_value;

uint8_t MAX30105_FIFOWRITEPTR = 0x04;
uint8_t MAX30105_FIFOOVERFLOW = 0x05;
uint8_t MAX30105_FIFOREADPTR = 0x06;
uint8_t MAX30105_FIFODATA = 0x07;

uint8_t MAX30105_FIFOCONFIG = 0x08;
uint8_t MAX30105_RESET_MASK = 0xBF;
uint8_t MAX30105_RESET = 0x40;
uint8_t MAX30105_MODECONFIG = 0x09;
uint8_t MAX30105_PARTICLECONFIG = 0x0A;    // Note, sometimes listed as "SPO2" config in datasheet (pg. 11)
uint8_t MAX30105_LED1_PULSEAMP = 0x0C;
uint8_t MAX30105_MULTILEDCONFIG1 = 0x11;
uint8_t MAX30105_SAMPLEAVG_MASK = 0xE0;
uint8_t MAX30105_SAMPLEAVG_1 = 0x00;
uint8_t MAX30105_MODE_MASK = 0xF8;
uint8_t MAX30105_MODE_REDONLY = 0x02;
uint8_t MAX30105_ADCRANGE_MASK = 0x9F;
uint8_t MAX30105_ADCRANGE_4096 = 0x20;
uint8_t MAX30105_SAMPLERATE_MASK = 0xE3;
uint8_t MAX30105_SAMPLERATE_1000 = 0x14;
uint8_t MAX30105_PULSEWIDTH_MASK = 0xFC;
uint8_t MAX30105_PULSEWIDTH_411 = 0x03;
uint8_t MAX30105_SLOT1_MASK = 0xF8;
uint8_t SLOT_RED_LED = 0x01;

uint8_t SLAVE_ADDRESS_WRITE =      0xAE; //10101110
uint8_t SLAVE_ADDRESS_READ  =      0xAF; //10101111

static void sys_init(void)
{
  // Switch to 8MHz clock (disable prescaler)
  SYSCTRL->OSC8M.bit.PRESC = 0;

  // Enable interrupts
  asm volatile ("cpsie i");
}
uint8_t readRegister8(uint8_t reg) {
i2c_init(100000); //initialize i2c
i2c_start(SLAVE_ADDRESS_WRITE); //start isc; send slave address and set to write
i2c_write_byte(reg); //write register of interest to slave
i2c_start(SLAVE_ADDRESS_READ); //restart command; resend slave address + read mode
i2c_read_byte(&read_register_value,true); //read 1 bit from the slave
i2c_stop();

return (read_register_value);
}

void writeRegister8(uint8_t reg, uint8_t value) {
i2c_init(100000); //initialize i2c
i2c_start(SLAVE_ADDRESS_WRITE); //start isc; send slave address and set to write
i2c_write_byte(reg); //write register of interest to slave
i2c_write_byte(value); //read 1 bit from the slave
i2c_stop();
}
//Given a register, read it, mask it, and then set the thing
void bitMask(uint8_t reg, uint8_t mask, uint8_t thing)
{
// Grab current register context
uint8_t originalContents = readRegister8(reg);

// Zero-out the portions of the register we're interested in
originalContents = originalContents & mask;

// Change contents
writeRegister8(reg, originalContents | thing);
}



//-------------------------------------------------------------------------------------------------------------------------------------------------
int main(void)
{
sys_init();

//Initializing particle sensor-----------------------------------------------------------------------------------------------------------------

bitMask(MAX30105_MODECONFIG, MAX30105_RESET_MASK, MAX30105_RESET); //soft reset
bitMask(MAX30105_FIFOCONFIG, MAX30105_SAMPLEAVG_MASK, MAX30105_SAMPLEAVG_1); //setFIFOAverage - 1
bitMask(MAX30105_MODECONFIG, MAX30105_MODE_MASK, MAX30105_MODE_REDONLY); //setLEDMode - red only
activeLEDs = 1;
bitMask(MAX30105_PARTICLECONFIG, MAX30105_ADCRANGE_MASK, MAX30105_ADCRANGE_4096); //setADCRange - 4096
bitMask(MAX30105_PARTICLECONFIG, MAX30105_SAMPLERATE_MASK, MAX30105_SAMPLERATE_1000); //setSampleRate - 1000
bitMask(MAX30105_PARTICLECONFIG, MAX30105_PULSEWIDTH_MASK, MAX30105_PULSEWIDTH_411); //setPulseWidth - 411
writeRegister8(MAX30105_LED1_PULSEAMP, 0x1F); //setPulseAmplitudeRed(0x1F);
bitMask(MAX30105_MULTILEDCONFIG1, MAX30105_SLOT1_MASK, SLOT_RED_LED); //enableSlot(1, SLOT_RED_LED);

//clearFIFO
writeRegister8(MAX30105_FIFOWRITEPTR, 0);
writeRegister8(MAX30105_FIFOOVERFLOW, 0);
writeRegister8(MAX30105_FIFOREADPTR, 0);

//TRANSACTION 1 -------------------------------------------------------------------------------------------------------------
//Send start command to salve address 0x57 and set to write mode (master writes to slave)
i2c_init(100000);
i2c_start(SLAVE_ADDRESS_WRITE);
//Send FIFO address to slave; tells slave we want to read from this address. Saves data to FIFO
i2c_write_byte(MAX30105_FIFOWRITEPTR);
//repeated start; send start command + slave address + read mode. Now master reads data from slave
i2c_start(SLAVE_ADDRESS_READ);
//read byte from FIFO write pointer
i2c_read_byte(&MAX30105_FIFOWRITEPTR, true);
i2c_stop();
//TRANSACTION 2 -------------------------------------------------------------------------------------------------------------
//number of samples is difference between MAX30105_FIFOWRITEPTR (stopped at last sample loaded into FIFO in transaction1
int num_available_samples = MAX30105_FIFOWRITEPTR - MAX30105_FIFOREADPTR;
int num_to_read = num_available_samples;
//Send start command to salve address 0x57 and set to write mode (master writes to slave)
i2c_start(SLAVE_ADDRESS_WRITE);
//Send FIFO address to slave; tells slave we want to read from this address. Extract data from FIFO to master
i2c_write_byte(MAX30105_FIFODATA);
i2c_start(SLAVE_ADDRESS_READ);
for (i = 0; i < num_to_read; i++) {
array[i] = i2c_read_byte(&MAX30105_FIFODATA, false);
}
i2c_stop();

  while (1)
  {
  }

  return 0;
}


-------------------------------------------------------------------
I2C_MASTER.C
Code: [Select]
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "samd21.h"
#include "hal_gpio.h"
#include "i2c_master.h"

/*- Definitions -------------------------------------------------------------*/
HAL_GPIO_PIN(SDA,             A, 22);
HAL_GPIO_PIN(SCL,             A, 23);
#define I2C_SERCOM            SERCOM3
#define I2C_SERCOM_PMUX       PORT_PMUX_PMUXE_D_Val
#define I2C_SERCOM_GCLK_ID    SERCOM3_GCLK_ID_CORE
#define I2C_SERCOM_CLK_GEN    0
#define I2C_SERCOM_APBCMASK   PM_APBCMASK_SERCOM3

#define T_RISE                215e-9 // Depends on the board, actually

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

enum
{
  I2C_PINS_SDA = (1 << 0),
  I2C_PINS_SCL = (1 << 1),
};

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

//-----------------------------------------------------------------------------
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 = 9600;

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

//-----------------------------------------------------------------------------
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);
  else
    I2C_SERCOM->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;

  *byte = I2C_SERCOM->I2CM.DATA.reg;

  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_busy(int addr)
{
  bool busy;

  I2C_SERCOM->I2CM.ADDR.reg = addr | 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;
}

//-----------------------------------------------------------------------------
void i2c_pins(int mask, int value)
{
  if (mask & I2C_PINS_SDA)
  {
    HAL_GPIO_SDA_out();
    HAL_GPIO_SDA_write(value & I2C_PINS_SDA);
  }
  else
  {
    HAL_GPIO_SDA_in();
    HAL_GPIO_SDA_clr();
  }

  if (mask & I2C_PINS_SCL)
  {
    HAL_GPIO_SCL_out();
    HAL_GPIO_SCL_write(value & I2C_PINS_SCL);
  }
  else
  {
    HAL_GPIO_SCL_in();
    HAL_GPIO_SCL_clr();
  }

  HAL_GPIO_SDA_pmuxdis();
  HAL_GPIO_SCL_pmuxdis();
}


 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11258
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #45 on: October 04, 2017, 10:17:04 pm »
SDA and SCL are high, meaning that communication doesn't start.
High is the default bus state. How do you expect multimeter to see a quick transfer at 100 kHz?

One thing I wasn't sure about in Alex's code was what to set int freq to in i2c_init(). Google says that the most widely used clock rate is 100kHz, so I put that.
You've put a 100 Hz originally.

This is my port over using Alex's code:
You only need to do i2c_init() once at the very beginning of your program, but that's not a problem here.

All those functions return status codes - check them.

Again, no need for all this hardcore stuff. Pick a register you can write and read back, and make a simple code that reads a writes it first.

There is no need to post the whole code in the message - just attach files.
Alex
 

Offline d-jokic

  • Contributor
  • Posts: 25
  • Country: us
Re: I2C Atmel
« Reply #46 on: October 04, 2017, 11:22:44 pm »
So I took your advice and tried reading the Part_ID register ( 0xFF ). I did the following:

Code: [Select]
        uint8_t read_register_value;

        i2c_init(100000); //initialize i2c
i2c_start(SLAVE_ADDRESS_WRITE); //start isc; send slave address and set to write
i2c_write_byte(0xFF); //write register of interest to slave
i2c_start(SLAVE_ADDRESS_READ); //restart command; resend slave address + read mode
i2c_read_byte(&read_register_value, true);                 //read 1 bit from the slave
i2c_stop();


I placed a watchpoint on read_register_value and keep getting 0, even though the part ID is supposed to be 0x15. One thing that is definitely an issue is i2c_init. I placed a watchpoint timer on freq and am getting "could not evaluate".

« Last Edit: October 04, 2017, 11:27:42 pm by d-jokic »
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11258
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #47 on: October 04, 2017, 11:25:44 pm »
You should do the other thing I suggested, and actually check return values from all those functions. I bet some of them will return errors.
Alex
 

Offline d-jokic

  • Contributor
  • Posts: 25
  • Country: us
Re: I2C Atmel
« Reply #48 on: October 05, 2017, 12:36:14 am »
Is this also done with watches? I haven't worked much with C before, and never before with Atmel Studio so I'm not sure of the proper way. each function returns either a bool or an int, I just forget how to check what the output is. Adding a watch to the function itself (e.g. i2c_start) gives a random value. I2C start should return true or false and the value through the watch is 0x288 which means nothing i dont think. This makes me skeptical of what the "value " of the function is (aka whether its meaningful given what I'm trying to see).
If any of the functions return an error, what is the progress for debugging them? I'm not sure what could be the issue since the code you wrote is for the samd21, which is what Im using.

Thanks again.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11258
  • Country: us
    • Personal site
Re: I2C Atmel
« Reply #49 on: October 05, 2017, 12:55:08 am »
Is this also done with watches?
You do this by writing code that checks return value of functions.

Code: [Select]
if (!i2c_init(100000))
 {
    oops something bad happened there is no point going on.
 }

This makes me skeptical of what the "value " of the function is (aka whether its meaningful given what I'm trying to see).
The "value" of a function is its address in memory. Return value is a thing that is only known at the moment of a call.

If any of the functions return an error, what is the progress for debugging them?
Look at what exactly is wrong and fix it.

I'm not sure what could be the issue since the code you wrote is for the samd21, which is what Im using.
You may have connected sensor the wrong way, it may be dead, you may have wrong pull-up resistors on the bus. Many reasons.
Alex
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf