Tool/software: Code Composer Studio
I working on a project to communicate with Max30101 sensor to read SP02 data. However using the scope. I realised that 1byte from the write data goes missing during the I2CSend . The code looks as below.
klib1.h is the modified version of https://github.com/sparkfun/SparkFun_MAX3010x_Sensor_Library/blob/master/src/MAX30105.cpp. For instance when I attempted "I2CSend(I2CDEVICE_ADDR, 2,MAX30105_LED1_PULSEAMP , 0x01);", at times I am able to turn on the Red Led as expected. But this is not always the case. Majority of the time, "MAX30105_LED1_PULSEAMP" will go missing and hence the Red Led is not turned on.
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "inc/hw_i2c.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "driverlib/i2c.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"
#include "sensorlib/klib1.h"
uint32_t ui32SysClock;
uint32_t RxData;
uint32_t delay = 25000000/3; //1 sec delay. Divide 3 cz 3 clk cycles.
static unsigned long WaitI2CDone( unsigned int long ulBase){
// Wait until done transmitting
while( I2CMasterBusy(I2C0_BASE));
// Return I2C error code
return I2CMasterErr(I2C0_BASE);
}
//I2C SEND function
//sends an I2C command to the specified slave
//eg: I2CSend(LCD_SLAVE_ADDR, 3, LCD_CMD, 0x50, contrast);
void I2CSend(uint8_t slave_addr, uint8_t num_of_args, ...)
{
uint8_t i = 0;
// Tell the master module what address it will place on the bus when
// communicating with the slave.
I2CMasterSlaveAddrSet(I2C0_BASE, slave_addr, false);
//stores list of variable number of arguments
va_list vargs;
//specifies the va_list to "open" and the last fixed argument
//so vargs knows where to start looking
va_start(vargs, num_of_args);
//put data to be sent into FIFO
I2CMasterDataPut(I2C0_BASE, va_arg(vargs, uint32_t));
//if there is only one argument, we only need to use the
//single send I2C function
if(num_of_args == 1)
{
//Initiate send of data from the MCU
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);
// Wait until MCU is done transferring.
while(I2CMasterBusy(I2C0_BASE));
//"close" variable argument list
va_end(vargs);
}
//otherwise, we start transmission of multiple bytes on the
//I2C bus
else
{
//Initiate send of data from the MCU
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
// Wait until MCU is done transferring.
while(I2CMasterBusy(I2C0_BASE));
//send num_of_args-2 pieces of data, using the
//BURST_SEND_CONT command of the I2C module
for(i = 1; i < (num_of_args - 1); i++)
{
//put next piece of data into I2C FIFO
I2CMasterDataPut(I2C0_BASE, va_arg(vargs, uint32_t));
//send next data that was just placed into FIFO
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);
// Wait until MCU is done transferring.
while(I2CMasterBusy(I2C0_BASE));
}
//put last piece of data into I2C FIFO
I2CMasterDataPut(I2C0_BASE, va_arg(vargs, uint32_t));
//send next data that was just placed into FIFO
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
// Wait until MCU is done transferring.
while(I2CMasterBusy(I2C0_BASE));
//"close" variable args list
va_end(vargs);
}
}
//I2C RECEIVE function
//read specified register on slave device
uint32_t I2CReceive(uint32_t slave_addr, uint8_t reg)
{
uint8_t data = 0;
int16_t i2c_err = I2C_MASTER_ERR_NONE;
//specify that we are writing (a register address) to the
//slave device
I2CMasterSlaveAddrSet(I2C0_BASE, slave_addr, false);
//specify register to be read
I2CMasterDataPut(I2C0_BASE, reg);
//send control byte and register address byte to slave device
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
//wait for MCU to finish transaction
while(I2CMasterBusy(I2C0_BASE));
//specify that we are going to read from slave device
I2CMasterSlaveAddrSet(I2C0_BASE, slave_addr, true);
//send control byte and read from the register we
//specified
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
if(i2c_err != WaitI2CDone( I2C0_BASE)){
data = I2CMasterDataGet(I2C0_BASE);
UARTprintf("Read error in data: %x\n", data);
}
//wait for MCU to finish transaction
while(I2CMasterBusy(I2C0_BASE));
//return data pulled from the specified register
return I2CMasterDataGet(I2C0_BASE);
}
void bitMask(uint8_t reg, uint8_t mask, uint8_t thing)
{
// Grab current register context
uint8_t originalContents = I2CReceive(I2CDEVICE_ADDR, reg);
// Zero-out the portions of the register we're interested in
originalContents = originalContents & mask;
// Change contents
I2CSend(I2CDEVICE_ADDR, 2, reg, originalContents | thing);
}
void setFIFOAverage(uint8_t numberOfSamples) {
bitMask(MAX30105_FIFOCONFIG, MAX30105_SAMPLEAVG_MASK, numberOfSamples);
}
void enableFIFORollover(void) {
bitMask(MAX30105_FIFOCONFIG, MAX30105_ROLLOVER_MASK, MAX30105_ROLLOVER_ENABLE);
}
void setLEDMode(uint8_t mode) {
// Set which LEDs are used for sampling -- Red only, RED+IR only, or custom.
// See datasheet, page 19
bitMask(MAX30105_MODECONFIG, MAX30105_MODE_MASK, mode);
}
void setADCRange(uint8_t adcRange) {
// adcRange: one of MAX30105_ADCRANGE_2048, _4096, _8192, _16384
bitMask(MAX30105_PARTICLECONFIG, MAX30105_ADCRANGE_MASK, adcRange);
}
void setSampleRate(uint8_t sampleRate) {
// sampleRate: one of MAX30105_SAMPLERATE_50, _100, _200, _400, _800, _1000, _1600, _3200
bitMask(MAX30105_PARTICLECONFIG, MAX30105_SAMPLERATE_MASK, sampleRate);
}
void setPulseWidth(uint8_t pulseWidth) {
// pulseWidth: one of MAX30105_PULSEWIDTH_69, _188, _215, _411
bitMask(MAX30105_PARTICLECONFIG, MAX30105_PULSEWIDTH_MASK, pulseWidth);
}
// NOTE: Amplitude values: 0x00 = 0mA, 0x7F = 25.4mA, 0xFF = 50mA (typical)
// See datasheet, page 21
void setPulseAmplitudeRed(uint8_t amplitude) {
I2CSend(I2CDEVICE_ADDR, 2, MAX30105_LED1_PULSEAMP, amplitude);
}
void setPulseAmplitudeIR(uint8_t amplitude) {
I2CSend(I2CDEVICE_ADDR, 2, MAX30105_LED2_PULSEAMP, amplitude);
}
void setPulseAmplitudeGreen(uint8_t amplitude) {
I2CSend(I2CDEVICE_ADDR, 2, MAX30105_LED3_PULSEAMP, amplitude);
}
void setPulseAmplitudeProximity(uint8_t amplitude) {
I2CSend(I2CDEVICE_ADDR, 2, MAX30105_LED_PROX_AMP, amplitude);
}
void setProximityThreshold(uint8_t threshMSB) {
// Set the IR ADC count that will trigger the beginning of particle-sensing mode.
// The threshMSB signifies only the 8 most significant-bits of the ADC count.
// See datasheet, page 24.
I2CSend(I2CDEVICE_ADDR, 2, MAX30105_PROXINTTHRESH, threshMSB);
}
//Given a slot number assign a thing to it
//Devices are SLOT_RED_LED or SLOT_RED_PILOT (proximity)
//Assigning a SLOT_RED_LED will pulse LED
//Assigning a SLOT_RED_PILOT will ??
void enableSlot(uint8_t slotNumber, uint8_t device) {
switch (slotNumber) {
case (1):
bitMask(MAX30105_MULTILEDCONFIG1, MAX30105_SLOT1_MASK, device);
break;
case (2):
bitMask(MAX30105_MULTILEDCONFIG1, MAX30105_SLOT2_MASK, device << 4);
break;
case (3):
bitMask(MAX30105_MULTILEDCONFIG2, MAX30105_SLOT3_MASK, device);
break;
case (4):
bitMask(MAX30105_MULTILEDCONFIG2, MAX30105_SLOT4_MASK, device << 4);
break;
default:
//Shouldn't be here!
break;
}
}
void clearFIFO(void) {
I2CSend(I2CDEVICE_ADDR, 2, MAX30105_FIFOWRITEPTR, 0);
I2CSend(I2CDEVICE_ADDR, 2, MAX30105_FIFOOVERFLOW, 0);
I2CSend(I2CDEVICE_ADDR, 2, MAX30105_FIFOREADPTR, 0);
}
void softReset(void) {
bitMask(MAX30105_MODECONFIG, MAX30105_RESET_MASK, MAX30105_RESET);
while (1)
{
uint8_t response = I2CReceive(I2CDEVICE_ADDR, MAX30105_MODECONFIG);
if ((response & MAX30105_RESET) == 0) break; //We're done!
SysCtlDelay(delay); //Let's not over burden the I2C bus
}
// Put some delay here
}
void Sensorsetup(uint8_t powerLevel, uint8_t sampleAverage, uint8_t ledMode, int sampleRate, int pulseWidth, int adcRange) {
softReset(); //Reset all configuration, threshold, and data registers to POR values
//FIFO Configuration
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//The chip will average multiple samples of same type together if you wish
if (sampleAverage == 1) setFIFOAverage(MAX30105_SAMPLEAVG_1); //No averaging per FIFO record
else if (sampleAverage == 2) setFIFOAverage(MAX30105_SAMPLEAVG_2);
else if (sampleAverage == 4) setFIFOAverage(MAX30105_SAMPLEAVG_4);
else if (sampleAverage == 8) setFIFOAverage(MAX30105_SAMPLEAVG_8);
else if (sampleAverage == 16) setFIFOAverage(MAX30105_SAMPLEAVG_16);
else if (sampleAverage == 32) setFIFOAverage(MAX30105_SAMPLEAVG_32);
else setFIFOAverage(MAX30105_SAMPLEAVG_4);
//setFIFOAlmostFull(2); //Set to 30 samples to trigger an 'Almost Full' interrupt
enableFIFORollover(); //Allow FIFO to wrap/roll over
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//Mode Configuration
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (ledMode == 3) setLEDMode(MAX30105_MODE_MULTILED); //Watch all three LED channels
else if (ledMode == 2) setLEDMode(MAX30105_MODE_REDIRONLY); //Red and IR
else setLEDMode(MAX30105_MODE_REDONLY); //Red only
//activeLEDs = ledMode; //Used to control how many bytes to read from FIFO buffer
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//Particle Sensing Configuration
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if(adcRange < 4096) setADCRange(MAX30105_ADCRANGE_2048); //7.81pA per LSB
else if(adcRange < 8192) setADCRange(MAX30105_ADCRANGE_4096); //15.63pA per LSB
else if(adcRange < 16384) setADCRange(MAX30105_ADCRANGE_8192); //31.25pA per LSB
else if(adcRange == 16384) setADCRange(MAX30105_ADCRANGE_16384); //62.5pA per LSB
else setADCRange(MAX30105_ADCRANGE_2048);
if (sampleRate < 100) setSampleRate(MAX30105_SAMPLERATE_50); //Take 50 samples per second
else if (sampleRate < 200) setSampleRate(MAX30105_SAMPLERATE_100);
else if (sampleRate < 400) setSampleRate(MAX30105_SAMPLERATE_200);
else if (sampleRate < 800) setSampleRate(MAX30105_SAMPLERATE_400);
else if (sampleRate < 1000) setSampleRate(MAX30105_SAMPLERATE_800);
else if (sampleRate < 1600) setSampleRate(MAX30105_SAMPLERATE_1000);
else if (sampleRate < 3200) setSampleRate(MAX30105_SAMPLERATE_1600);
else if (sampleRate == 3200) setSampleRate(MAX30105_SAMPLERATE_3200);
else setSampleRate(MAX30105_SAMPLERATE_50);
//The longer the pulse width the longer range of detection you'll have
//At 69us and 0.4mA it's about 2 inches
//At 411us and 0.4mA it's about 6 inches
if (pulseWidth < 118) setPulseWidth(MAX30105_PULSEWIDTH_69); //Page 26, Gets us 15 bit resolution
else if (pulseWidth < 215) setPulseWidth(MAX30105_PULSEWIDTH_118); //16 bit resolution
else if (pulseWidth < 411) setPulseWidth(MAX30105_PULSEWIDTH_215); //17 bit resolution
else if (pulseWidth == 411) setPulseWidth(MAX30105_PULSEWIDTH_411); //18 bit resolution
else setPulseWidth(MAX30105_PULSEWIDTH_69);
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//LED Pulse Amplitude Configuration
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//Default is 0x1F which gets us 6.4mA
//powerLevel = 0x02, 0.4mA - Presence detection of ~4 inch
//powerLevel = 0x1F, 6.4mA - Presence detection of ~8 inch
//powerLevel = 0x7F, 25.4mA - Presence detection of ~8 inch
//powerLevel = 0xFF, 50.0mA - Presence detection of ~12 inch
setPulseAmplitudeRed(powerLevel);
setPulseAmplitudeIR(powerLevel);
setPulseAmplitudeGreen(powerLevel);
setPulseAmplitudeProximity(powerLevel);
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//Multi-LED Mode Configuration, Enable the reading of the three LEDs
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
enableSlot(1, SLOT_RED_LED);
if (ledMode > 1) enableSlot(2, SLOT_IR_LED);
if (ledMode > 2) enableSlot(3, SLOT_GREEN_LED);
//enableSlot(1, SLOT_RED_PILOT);
//enableSlot(2, SLOT_IR_PILOT);
//enableSlot(3, SLOT_GREEN_PILOT);
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
clearFIFO(); //Reset the FIFO before we begin checking the sensor
}
// convert an hexa or number to binary. Read frm last digit.
void binary(n){
while (n) {
if (n & 1)
UARTprintf("1");
else
UARTprintf("0");
n >>= 1;
}
UARTprintf("\n");
}
//initialize I2C module 0
//Slightly modified version of TI's example code
void InitI2C0(void)
{
//enable I2C module 0
SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
//reset module
//SysCtlPeripheralReset(SYSCTL_PERIPH_I2C0);
//enable GPIO peripheral that contains I2C 0
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
//Added-wait for peripherals to be ready for programming
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB));
// Configure the pin muxing for I2C0 functions on port B2 and B3.
GPIOPinConfigure(GPIO_PB2_I2C0SCL);
GPIOPinConfigure(GPIO_PB3_I2C0SDA);
// Select the I2C function for these pins.
GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2); //I2C0 SCL
GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3); //I2C0 SCL
// Enable and initialize the I2C0 master module. Use the system clock for
// the I2C0 module. The last parameter sets the I2C data transfer rate.
// If false the data rate is set to 100kbps and if true the data rate will
// be set to 400kbps.
#if defined(TARGET_IS_TM4C129_RA0) || \
defined(TARGET_IS_TM4C129_RA1) || \
defined(TARGET_IS_TM4C129_RA2)
I2CMasterInitExpClk(I2C0_BASE, ui32SysClock, false);
#else
I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);
#endif
//clear I2C FIFOs
//HWREG(I2C0_BASE + I2C_O_FIFOCTL) = 80008000;
}
void InitConsole(void){
// Enable GPIO port A which is used for UART0 pins.
// TODO: change this to whichever GPIO port you are using.
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
// Configure the pin muxing for UART0 functions on port A0 and A1.
// This step is not necessary if your part does not support pin muxing.
// TODO: change this to select the port/pin you are using.
GPIOPinConfigure(GPIO_PA0_U0RX);
GPIOPinConfigure(GPIO_PA1_U0TX);
// Enable UART0 so that we can configure the clock.
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
// Use the internal 16MHz oscillator as the UART clock source.
UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
// Select the alternate (UART) function for these pins.
// TODO: change this to select the port/pin you are using.
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
// Initialize the UART for console I/O.
UARTStdioConfig(0, 115200, 16000000);
}
void main(){
ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
SYSCTL_OSC_MAIN | SYSCTL_USE_PLL |
SYSCTL_CFG_VCO_480), 120000000);
InitConsole();
InitI2C0();
//Sensorsetup(0xFF,4,2,100,411,4096);
softReset();
I2CSend(I2CDEVICE_ADDR, 2, MAX30105_MODECONFIG , 0x02); // Heart rate
SysCtlDelay(delay);
I2CSend(I2CDEVICE_ADDR, 2,MAX30105_LED1_PULSEAMP , 0x01); // turn on red led with 118us.
while(1){
RxData = I2CReceive(I2CDEVICE_ADDR , MAX30105_PARTID );
UARTprintf("Part id is: %x\n", RxData);
SysCtlDelay(5*delay);
}
}
Attached is the scope view for I2CSend(I2CDEVICE_ADDR, 2,MAX30105_LED1_PULSEAMP , 0x01)
