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.
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:
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:
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:
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
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
#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();
}