Hackster is hosting Hackster Holidays, Ep. 5: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Friday!Stream Hackster Holidays, Ep. 5 on Friday!
Nghia Nguyen
Published © GPL3+

Freedom K82F Sport Kit Companion

A complete sport kit to support physical exercise, including counting your jumping, basketball practices, monitoring pushup and posture.

AdvancedFull instructions provided2 days1,050
Freedom K82F Sport Kit Companion

Things used in this project

Hardware components

Kinetis Freedom Board with FlexIO
NXP Kinetis Freedom Board with FlexIO
×1
Grove starter kit plus for Intel Edison
Seeed Studio Grove starter kit plus for Intel Edison
×1
SparkFun Lidar lite
×1

Software apps and online services

NXP KDS v3 IDE
Microsoft Visual Studio Code
NXP Online Kinetis Expert

Hand tools and fabrication machines

Hot glue gun (generic)
Hot glue gun (generic)

Story

Read more

Schematics

Heart rate sensor schematic

Circuit to monitor heart rate by PPG methodology.

Code

K82F Main Program

C/C++
This code contain everything you need to let this system works.
/*
 * Sport Kit Companion v1.0
 * Author: Nghia Nguyen
 */

#include <stdarg.h>
#include <stdint.h>
#include <stdbool.h>

#include "fsl_debug_console.h"
#include "board.h"
#include "fsl_adc16.h"
#include "fsl_i2c.h"

#include "pin_mux.h"
#include "clock_config.h"
/*******************************************************************************
 * Definitions
 ******************************************************************************/

//========== ADC =================================================================
#define DEMO_ADC16_BASE ADC0
#define DEMO_ADC16_CHANNEL_GROUP 0U

#define ADC_CH0 8U			//
#define ADC_CH1 9U			// Light sensor
#define ADC_CH2 15U
//#define ADC_CH3 4U

//========== I2C =================================================================
#define ACCEL_I2C_CLK_SRC I2C3_CLK_SRC
#define I2C_BAUDRATE 400000U
#define FXOS8700_WHOAMI 0xC7U
#define MMA8451_WHOAMI 0x1AU
#define ACCEL_STATUS 0x00U
#define ACCEL_XYZ_DATA_CFG 0x0EU
#define ACCEL_CTRL_REG1 0x2AU
/* FXOS8700 and MMA8451 have the same who_am_i register address. */
#define ACCEL_WHOAMI_REG 0x0DU
#define ACCEL_READ_TIMES 2

/*
 * @ ADC0 Channels (SE0-31)
 * 23: DAC0_OUT
 * 8: PTB0 - Arduino A0
 * 9: PTB1 - Arduino A1
 * 15:PTC1 - Arduino A2
 * 4b:PTC2 - Arduino A3
 * 12:PTB2
 * 13: PTB3
 *
 * @ Resolution: 12bits
 *
 */
/*******************************************************************************
 * Prototypes
 ******************************************************************************/
void delayms(uint16_t milisecs);

static bool I2C_ReadAccelWhoAmI(void);
static bool I2C_initLidar(void);
static bool I2C_WriteAccelReg(I2C_Type *base, uint8_t device_addr, uint8_t reg_addr, uint8_t value);
static bool I2C_ReadAccelRegs(I2C_Type *base, uint8_t device_addr, uint8_t reg_addr, uint8_t *rxBuff, uint32_t rxSize);



/*******************************************************************************
 * Variables
 ******************************************************************************/
uint16_t adc_raw;
uint16_t count=0;

uint16_t distance;
uint8_t lidar_buff[2];

/*  FXOS8700 and MMA8451 device address */
const uint8_t g_accel_address[] = {0x1CU, 0x1DU, 0x1EU, 0x1FU};

/* Lidar lite device address: 0x62 */
const uint8_t lidar_address = 0x62U;

i2c_master_handle_t g_m_handle;

uint8_t g_accel_addr_found = 0x00;

volatile bool completionFlag = false;
volatile bool nakFlag = false;
/*******************************************************************************
 * Code
 ******************************************************************************/
void delayms(uint16_t milisecs)
{
	// 8000000 ~= 1sec
	volatile uint32_t i = 0;
	for (i = 0; i < milisecs*8000; ++i)
	{
		__asm("NOP"); /* delay */
	}
}

static void i2c_master_callback(I2C_Type *base, i2c_master_handle_t *handle, status_t status, void *userData)
{
	/* Signal transfer success when received success status. */
	if (status == kStatus_Success)
	{
		completionFlag = true;
	}
	/* Signal transfer success when received success status. */
	if (status == kStatus_I2C_Nak)
	{
		nakFlag = true;
	}
}

static bool I2C_ReadAccelWhoAmI(void)
{
	/*
    How to read the device who_am_I value ?
    Start + Device_address_Write , who_am_I_register;
    Repeart_Start + Device_address_Read , who_am_I_value.
	 */
	uint8_t who_am_i_reg = ACCEL_WHOAMI_REG;
	uint8_t who_am_i_value = 0x00;
	uint8_t accel_addr_array_size = 0x00;
	bool find_device = false;
	uint8_t i = 0;
	uint32_t sourceClock = 0;

	i2c_master_config_t masterConfig;

	/*
	 * masterConfig.baudRate_Bps = 100000U;
	 * masterConfig.enableHighDrive = false;
	 * masterConfig.enableStopHold = false;
	 * masterConfig.glitchFilterWidth = 0U;
	 * masterConfig.enableMaster = true;
	 */
	I2C_MasterGetDefaultConfig(&masterConfig);

	masterConfig.baudRate_Bps = I2C_BAUDRATE;

	sourceClock = CLOCK_GetFreq(ACCEL_I2C_CLK_SRC);

	I2C_MasterInit(BOARD_ACCEL_I2C_BASEADDR, &masterConfig, sourceClock);

	i2c_master_transfer_t masterXfer;
	memset(&masterXfer, 0, sizeof(masterXfer));

	masterXfer.slaveAddress = g_accel_address[0];
	masterXfer.direction = kI2C_Write;
	masterXfer.subaddress = 0;
	masterXfer.subaddressSize = 0;
	masterXfer.data = &who_am_i_reg;
	masterXfer.dataSize = 1;
	masterXfer.flags = kI2C_TransferNoStopFlag;

	accel_addr_array_size = sizeof(g_accel_address) / sizeof(g_accel_address[0]);

	for (i = 0; i < accel_addr_array_size; i++)
	{
		masterXfer.slaveAddress = g_accel_address[i];

		I2C_MasterTransferNonBlocking(BOARD_ACCEL_I2C_BASEADDR, &g_m_handle, &masterXfer);

		/*  wait for transfer completed. */
		while ((!nakFlag) && (!completionFlag))
		{
		}

		nakFlag = false;

		if (completionFlag == true)
		{
			completionFlag = false;
			find_device = true;
			g_accel_addr_found = masterXfer.slaveAddress;
			break;
		}
	}

	if (find_device == true)
	{
		masterXfer.direction = kI2C_Read;
		masterXfer.subaddress = 0;
		masterXfer.subaddressSize = 0;
		masterXfer.data = &who_am_i_value;
		masterXfer.dataSize = 1;
		masterXfer.flags = kI2C_TransferRepeatedStartFlag;

		I2C_MasterTransferNonBlocking(BOARD_ACCEL_I2C_BASEADDR, &g_m_handle, &masterXfer);

		/*  wait for transfer completed. */
		while ((!nakFlag) && (!completionFlag))
		{
		}

		nakFlag = false;

		if (completionFlag == true)
		{
			completionFlag = false;
			return true;
		}
		else
		{
			PRINTF("Not a successful i2c communication \r\n");

		}
	}
	return false;
}

static bool I2C_initLidar(void)
{
	uint32_t sourceClock = 0;
	i2c_master_config_t masterConfig;
	/*
	 * masterConfig.baudRate_Bps = 100000U;
	 * masterConfig.enableHighDrive = false;
	 * masterConfig.enableStopHold = false;
	 * masterConfig.glitchFilterWidth = 0U;
	 * masterConfig.enableMaster = true;
	 */
	I2C_MasterGetDefaultConfig(&masterConfig);

	masterConfig.baudRate_Bps = I2C_BAUDRATE;
	sourceClock = CLOCK_GetFreq(ACCEL_I2C_CLK_SRC);
	I2C_MasterInit(BOARD_ACCEL_I2C_BASEADDR, &masterConfig, sourceClock);

	return true;
}

static bool I2C_WriteAccelReg(I2C_Type *base, uint8_t device_addr, uint8_t reg_addr, uint8_t value)
{
	i2c_master_transfer_t masterXfer;
	memset(&masterXfer, 0, sizeof(masterXfer));

	masterXfer.slaveAddress = device_addr;
	masterXfer.direction = kI2C_Write;
	masterXfer.subaddress = reg_addr;
	masterXfer.subaddressSize = 1;
	masterXfer.data = &value;
	masterXfer.dataSize = 1;
	masterXfer.flags = kI2C_TransferDefaultFlag;

	/*  direction=write : start+device_write;cmdbuff;xBuff; */
	/*  direction=recive : start+device_write;cmdbuff;repeatStart+device_read;xBuff; */

	I2C_MasterTransferNonBlocking(BOARD_ACCEL_I2C_BASEADDR, &g_m_handle, &masterXfer);

	/*  wait for transfer completed. */
	while ((!nakFlag) && (!completionFlag))
	{
	}

	nakFlag = false;

	if (completionFlag == true)
	{
		completionFlag = false;
		return true;
	}
	else
	{
		return false;
	}
}

static bool I2C_ReadAccelRegs(I2C_Type *base, uint8_t device_addr, uint8_t reg_addr, uint8_t *rxBuff, uint32_t rxSize)
{
	i2c_master_transfer_t masterXfer;
	memset(&masterXfer, 0, sizeof(masterXfer));
	masterXfer.slaveAddress = device_addr;
	masterXfer.direction = kI2C_Read;
	masterXfer.subaddress = reg_addr;
	masterXfer.subaddressSize = 1;
	masterXfer.data = rxBuff;
	masterXfer.dataSize = rxSize;
	masterXfer.flags = kI2C_TransferDefaultFlag;

	/*  direction=write : start+device_write;cmdbuff;xBuff; */
	/*  direction=recive : start+device_write;cmdbuff;repeatStart+device_read;xBuff; */

	I2C_MasterTransferNonBlocking(BOARD_ACCEL_I2C_BASEADDR, &g_m_handle, &masterXfer);

	/*  wait for transfer completed. */
	while ((!nakFlag) && (!completionFlag))
	{
	}

	nakFlag = false;

	if (completionFlag == true)
	{
		completionFlag = false;
		return true;
	}
	else
	{
		return false;
	}
}

/*!
 * @brief Main function
 */
int main(void)
{
	adc16_config_t adc16ConfigStruct;
	adc16_channel_config_t adc16ChannelConfigStruct;

	bool isThereAccel = false;

	BOARD_InitPins();
	BOARD_BootClockRUN();
	BOARD_InitDebugConsole();

	PRINTF("\r\nFreedom Sport Companion Demonstration:\r\n");
	PRINTF("- Read sound sensor to detect motion\r\n");
	PRINTF("- Use Lidar Lite to measure distance\r\n");
	PRINTF("- Monitor Heartbeat \r\n");

	/*
	 * adc16ConfigStruct.referenceVoltageSource = kADC16_ReferenceVoltageSourceVref;
	 * adc16ConfigStruct.clockSource = kADC16_ClockSourceAsynchronousClock;
	 * adc16ConfigStruct.enableAsynchronousClock = true;
	 * adc16ConfigStruct.clockDivider = kADC16_ClockDivider8;
	 * adc16ConfigStruct.resolution = kADC16_ResolutionSE12Bit;
	 * adc16ConfigStruct.longSampleMode = kADC16_LongSampleDisabled;
	 * adc16ConfigStruct.enableHighSpeed = false;
	 * adc16ConfigStruct.enableLowPower = false;
	 * adc16ConfigStruct.enableContinuousConversion = false;
	 */
	ADC16_GetDefaultConfig(&adc16ConfigStruct);
	ADC16_Init(DEMO_ADC16_BASE, &adc16ConfigStruct);
	ADC16_EnableHardwareTrigger(DEMO_ADC16_BASE, false); /* Make sure the software trigger is used. */
#if defined(FSL_FEATURE_ADC16_HAS_CALIBRATION) && FSL_FEATURE_ADC16_HAS_CALIBRATION
	if (kStatus_Success == ADC16_DoAutoCalibration(DEMO_ADC16_BASE))
	{
		PRINTF("ADC16_DoAutoCalibration() Done.\r\n");
	}
	else
	{
		PRINTF("ADC16_DoAutoCalibration() Failed.\r\n");
	}
#endif /* FSL_FEATURE_ADC16_HAS_CALIBRATION */
	PRINTF("Press any key to get user channel's ADC value ...\r\n");

	adc16ChannelConfigStruct.channelNumber = ADC_CH0;
	adc16ChannelConfigStruct.enableInterruptOnConversionCompleted = false;
#if defined(FSL_FEATURE_ADC16_HAS_DIFF_MODE) && FSL_FEATURE_ADC16_HAS_DIFF_MODE
	adc16ChannelConfigStruct.enableDifferentialConversion = false;
#endif /* FSL_FEATURE_ADC16_HAS_DIFF_MODE */

	GETCHAR();

	while (1)
	{
		I2C_MasterTransferCreateHandle(BOARD_ACCEL_I2C_BASEADDR, &g_m_handle, i2c_master_callback, NULL);
		isThereAccel = I2C_ReadAccelWhoAmI();

		/*  read the accel xyz value if there is accel device on board */
		if (true == isThereAccel)
		{
			uint8_t databyte = 0;
			uint8_t write_reg = 0;
			uint8_t readBuff[7];
			int16_t x, y, z;
			uint8_t status0_value = 0;
			uint32_t i = 0U;

			/*  please refer to the "example FXOS8700CQ Driver Code" in FXOS8700 datasheet. */
			/*  write 0000 0000 = 0x00 to accelerometer control register 1 */
			/*  standby */
			/*  [7-1] = 0000 000 */
			/*  [0]: active=0 */
			write_reg = ACCEL_CTRL_REG1;
			databyte = 0;
			I2C_WriteAccelReg(BOARD_ACCEL_I2C_BASEADDR, g_accel_addr_found, write_reg, databyte);

			/*  write 0000 0001= 0x01 to XYZ_DATA_CFG register */
			/*  [7]: reserved */
			/*  [6]: reserved */
			/*  [5]: reserved */
			/*  [4]: hpf_out=0 */
			/*  [3]: reserved */
			/*  [2]: reserved */
			/*  [1-0]: fs=01 for accelerometer range of +/-4g range with 0.488mg/LSB */
			/*  databyte = 0x01; */
			write_reg = ACCEL_XYZ_DATA_CFG;
			databyte = 0x01;
			I2C_WriteAccelReg(BOARD_ACCEL_I2C_BASEADDR, g_accel_addr_found, write_reg, databyte);

			/*  write 0000 1101 = 0x0D to accelerometer control register 1 */
			/*  [7-6]: aslp_rate=00 */
			/*  [5-3]: dr=001 for 200Hz data rate (when in hybrid mode) */
			/*  [2]: lnoise=1 for low noise mode */
			/*  [1]: f_read=0 for normal 16 bit reads */
			/*  [0]: active=1 to take the part out of standby and enable sampling */
			/*   databyte = 0x0D; */
			write_reg = ACCEL_CTRL_REG1;
			databyte = 0x0d;
			I2C_WriteAccelReg(BOARD_ACCEL_I2C_BASEADDR, g_accel_addr_found, write_reg, databyte);
			for (i = 0; i < ACCEL_READ_TIMES; i++)
			{
				status0_value = 0;
				/*  wait for new data are ready. */
				while (status0_value != 0xff)
				{
					I2C_ReadAccelRegs(BOARD_ACCEL_I2C_BASEADDR, g_accel_addr_found, ACCEL_STATUS, &status0_value, 1);
				}

				/*  Multiple-byte Read from STATUS (0x00) register */
				I2C_ReadAccelRegs(BOARD_ACCEL_I2C_BASEADDR, g_accel_addr_found, ACCEL_STATUS, readBuff, 7);

				status0_value = readBuff[0];
				x = ((int16_t)(((readBuff[1] * 256U) | readBuff[2]))) / 4U;
				y = ((int16_t)(((readBuff[3] * 256U) | readBuff[4]))) / 4U;
				z = ((int16_t)(((readBuff[5] * 256U) | readBuff[6]))) / 4U;

				PRINTF("I2C: x = %5d , y = %5d , z = %5d \r\n", x, y, z);
			}
		}

		/*
		 * read Lidar lite
		 */

		I2C_initLidar();

		//		I2C_WriteAccelReg(BOARD_ACCEL_I2C_BASEADDR, lidar_address, 0x00, 0x04);
		I2C_WriteAccelReg(BOARD_ACCEL_I2C_BASEADDR, lidar_address, 0x45, 0xC8);	//10Hz
		I2C_WriteAccelReg(BOARD_ACCEL_I2C_BASEADDR, lidar_address, 0x04, 0x21);
		I2C_WriteAccelReg(BOARD_ACCEL_I2C_BASEADDR, lidar_address, 0x11, 0xFF); //continuos
		I2C_WriteAccelReg(BOARD_ACCEL_I2C_BASEADDR, lidar_address, 0x00, 0x04);	//start

		delayms(500);
		/*  Multiple-byte Read from distance register */
		I2C_ReadAccelRegs(BOARD_ACCEL_I2C_BASEADDR, lidar_address, 0x8f, lidar_buff, 2);
		//		I2C_ReadAccelRegs(BOARD_ACCEL_I2C_BASEADDR, lidar_address, 0x0f, &lidar_buff[0], 1);
		//		I2C_ReadAccelRegs(BOARD_ACCEL_I2C_BASEADDR, lidar_address, 0x10, &lidar_buff[1], 1);

		distance = (lidar_buff[0] << 8) + lidar_buff[1];

		PRINTF("distance: %4d \r\n",lidar_buff[0],lidar_buff[1],distance);



		/*
             When in software trigger mode, each conversion would be launched once calling the "ADC16_ChannelConfigure()"
             function, which works like writing a conversion command and executing it. For another channel's conversion,
             just to change the "channelNumber" field in channel's configuration structure, and call the
             "ADC16_ChannelConfigure() again.
		 */

		/*
		 * Get ADC value and print counter in console
		 */
		// CH0: potentiometer
		adc16ChannelConfigStruct.channelNumber = ADC_CH0;
		ADC16_SetChannelConfig(DEMO_ADC16_BASE, DEMO_ADC16_CHANNEL_GROUP, &adc16ChannelConfigStruct);
		while (0U == (kADC16_ChannelConversionDoneFlag &
				ADC16_GetChannelStatusFlags(DEMO_ADC16_BASE, DEMO_ADC16_CHANNEL_GROUP)))
		{}
		adc_raw = ADC16_GetChannelConversionValue(DEMO_ADC16_BASE, DEMO_ADC16_CHANNEL_GROUP);

		if(adc_raw > 40000){
			count++;
			PRINTF("Counter: %d\r\n",count);
			delayms(200);
			// Clear counter
			if(count == 20)
				count=0;
		}
		else{
			PRINTF("ADCval0: %4d   ",adc_raw);
		}

		// CH1: sound sensor
		adc16ChannelConfigStruct.channelNumber = ADC_CH1;
		ADC16_SetChannelConfig(DEMO_ADC16_BASE, DEMO_ADC16_CHANNEL_GROUP, &adc16ChannelConfigStruct);
		while (0U == (kADC16_ChannelConversionDoneFlag &
				ADC16_GetChannelStatusFlags(DEMO_ADC16_BASE, DEMO_ADC16_CHANNEL_GROUP)))
		{}
		adc_raw = ADC16_GetChannelConversionValue(DEMO_ADC16_BASE, DEMO_ADC16_CHANNEL_GROUP);
		PRINTF("ADCval1: %4d   ",adc_raw);

		// CH2: light sensor
		adc16ChannelConfigStruct.channelNumber = ADC_CH2;
		ADC16_SetChannelConfig(DEMO_ADC16_BASE, DEMO_ADC16_CHANNEL_GROUP, &adc16ChannelConfigStruct);
		while (0U == (kADC16_ChannelConversionDoneFlag &
				ADC16_GetChannelStatusFlags(DEMO_ADC16_BASE, DEMO_ADC16_CHANNEL_GROUP)))
		{}
		adc_raw = ADC16_GetChannelConversionValue(DEMO_ADC16_BASE, DEMO_ADC16_CHANNEL_GROUP);
		PRINTF("ADCval2: %4d\r\n",adc_raw);

		delayms(50);
	}
}

Credits

Nghia Nguyen

Nghia Nguyen

2 projects • 5 followers
Robotics and Drone Developer

Comments