Post Go back to editing

Maximizing sample speed with CN0565

Category: Software
Product Number: CN0565

I am working with the CN0565 for a project using a wrist-mounted EIT device for hand pose estimation, similar to https://github.com/SPICExLAB/EITPose. I am currently working with the NO-OS Driver for the CN0565, but I'm struggling to get frames (full adjacent pair measurements) on an 8-electrode set past the 0.7-second mark. I've modified AD5940BiaStructInit() to attempt to max out pBiaCfg->ODR set at 2000hz and decrease the dftNum to 1024. I'm wondering if there's any precedent with this chip for continuous streaming, and any recommendations you may have to keep bringing this time down. I've attached my app.c, main.c, and parameters.h for reference.  

ADDITIONAL INFORMATION

Microcontroller board: adicup3029

Connection Type: Serial UART (not IIO)

 

/***************************************************************************//**
 *   @file   main.c
 *   @brief  EIT main.
 *   @author Darius Berghe (darius.berghe@analog.com)
********************************************************************************
 * Copyright 2022(c) Analog Devices, Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. Neither the name of Analog Devices, Inc. nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES, INC. “AS IS” AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL ANALOG DEVICES, INC. BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include "parameters.h"
#include "no_os_uart.h"
#include "no_os_gpio.h"
#include "no_os_i2c.h"
#include "no_os_spi.h"
#include "no_os_irq.h"
#include "no_os_delay.h"
#include "no_os_util.h"

#if defined(STM32_PLATFORM)
#include "stm32_spi.h"
#include "stm32_gpio.h"
#include "stm32_uart.h"
#include "stm32_i2c.h"
#include "stm32_irq.h"
#include "stm32_gpio_irq.h"
#include "stm32_uart_stdio.h"
#elif defined(ADUCM_PLATFORM)
#include "platform_init.h"
#include "aducm3029_spi.h"
#include "aducm3029_gpio.h"
#include "aducm3029_uart.h"
#include "aducm3029_i2c.h"
#include "aducm3029_irq.h"
#include "aducm3029_uart_stdio.h"
#endif

#include "app.h"
#include "ad5940.h"

#ifdef IIO_SUPPORT
#include "iio_ad5940.h"
#include "iio_adg2128.h"
#include "iio_app.h"
#endif

struct no_os_spi_desc *spi;
struct no_os_i2c_desc *i2c;
struct no_os_uart_desc *uart;

#if defined(STM32_PLATFORM)
extern UART_HandleTypeDef huart5;
#endif

void ad5940_int_callback(void *ctx)
{
	ucInterrupted = 1;
}

int main(void)
{
	int ret;

#if defined(STM32_PLATFORM)
	stm32_init();
#elif defined(ADUCM_PLATFORM)
	ret = platform_init();
	if (ret)
		return ret;
#endif

#ifndef IIO_SUPPORT
#if defined(STM32_PLATFORM)
	struct stm32_uart_init_param xuip = {
		.huart = &huart5,
		.timeout = 10,
	};
#endif
	struct no_os_uart_init_param uip = {
		.device_id = UART_DEVICE_ID,
		.baud_rate = UART_BAUDRATE,
		.size = NO_OS_UART_CS_8,
		.parity = NO_OS_UART_PAR_NO,
		.stop = NO_OS_UART_STOP_1_BIT,
#if defined(STM32_PLATFORM)
		.extra = &xuip,
#endif
		.asynchronous_rx = true,
		.irq_id = UART_IRQ_ID,
#if defined(STM32_PLATFORM)
		.platform_ops = &stm32_uart_ops,
#elif defined(ADUCM_PLATFORM)
		.platform_ops = &aducm_uart_ops,
#endif
	};

	ret = no_os_uart_init(&uart, &uip);
	if (ret < 0)
		return ret;

	no_os_uart_stdio(uart);

	printf("Hello!\n");
#endif
	struct no_os_i2c_init_param i2cip = {
		.device_id = I2C_DEVICE_ID,
		.max_speed_hz = I2C_BAUDRATE,
		.slave_address = 0x70,
#if defined(STM32_PLATFORM)
		.platform_ops = &stm32_i2c_ops,
#elif defined(ADUCM_PLATFORM)
		.platform_ops = &aducm_i2c_ops,
#endif
	};
	ret = no_os_i2c_init(&i2c, &i2cip);
	if (ret)
		goto error;

#if defined(STM32_PLATFORM)
	struct stm32_spi_init_param xsip  = {
		.chip_select_port = SPI_CS_PORT,
		.get_input_clock = HAL_RCC_GetPCLK1Freq,
	};
#elif defined(ADUCM_PLATFORM)
	struct aducm_spi_init_param xsip = {
		.continuous_mode = true,
		.dma = false,
		.half_duplex = false,
		.master_mode = MASTER
	};
#endif
	struct no_os_spi_init_param sip = {
		.device_id = SPI_DEVICE_ID,
		.max_speed_hz = 3000000,
		.bit_order = NO_OS_SPI_BIT_ORDER_MSB_FIRST,
		.mode = NO_OS_SPI_MODE_0,
		.extra = &xsip,
#if defined(STM32_PLATFORM)
		.platform_ops = &stm32_spi_ops,
#elif defined(ADUCM_PLATFORM)
		.platform_ops = &aducm_spi_ops,
#endif
		.chip_select = SPI_CS,
	};

#if defined(STM32_PLATFORM)
	struct stm32_gpio_init_param reset_xgip = {
		.mode = GPIO_MODE_OUTPUT_PP,
		.speed = GPIO_SPEED_FREQ_VERY_HIGH,
	};
#endif
	struct no_os_gpio_init_param reset_gip = {
		.port = 3,
		.number = RESET_PIN,
		.pull = NO_OS_PULL_NONE,
#if defined(STM32_PLATFORM)
		.platform_ops = &stm32_gpio_ops,
		.extra = &reset_xgip,
#elif defined(ADUCM_PLATFORM)
		.platform_ops = &aducm_gpio_ops,
#endif
	};

#if defined(STM32_PLATFORM)
	struct stm32_gpio_init_param gp0_xgip = {
		.mode = GPIO_MODE_INPUT,
		.speed = GPIO_SPEED_FREQ_VERY_HIGH,
	};
#endif

	struct no_os_gpio_init_param gp0_gip = {
		.port = 6,
		.number = GP0_PIN,
		.pull = NO_OS_PULL_NONE,
#if defined(STM32_PLATFORM)
		.platform_ops = &stm32_gpio_ops,
		.extra = &gp0_xgip,
#elif defined(ADUCM_PLATFORM)
		.platform_ops = &aducm_gpio_ops,
#endif
	};

	// aducm3029 hack, need to initialize gpio before irq, otherwise it doesn't work
#if defined(ADUCM_PLATFORM)
	struct no_os_gpio_desc *gp0;
	no_os_gpio_get(&gp0, &gp0_gip);
#endif

	struct ad5940_init_param ad5940_ip = {
		.spi_init = sip,
		.reset_gpio_init = reset_gip,
		.gp0_gpio_init = gp0_gip,
	};
#ifndef IIO_SUPPORT
	/* interrupt controller  */
	struct no_os_irq_init_param nvic_ip = {
		.irq_ctrl_id = INTC_DEVICE_ID,
		.platform_ops = IRQ_OPS,
	};
	struct no_os_irq_ctrl_desc *nvic;
	ret = no_os_irq_ctrl_init(&nvic, &nvic_ip);
	if (ret < 0)
		return ret;

	/* gpio interrupt controller  */
#if defined(STM32_PLATFORM)
	struct stm32_gpio_irq_init_param gic_xip = {
		.port_nb = 6, /* port G */
	};
#endif
	struct no_os_irq_init_param gic_ip = {
#if defined(STM32_PLATFORM)
		.irq_ctrl_id = GP0_PIN,
#elif defined(ADUCM_PLATFORM)
		.irq_ctrl_id = INTC_DEVICE_ID,
#endif
		.platform_ops = GPIO_IRQ_OPS,
#if defined(STM32_PLATFORM)
		.extra = &gic_xip,
#endif
	};
	struct no_os_irq_ctrl_desc *gic;
	ret = no_os_irq_ctrl_init(&gic, &gic_ip);
	if (ret < 0)
		return ret;

	/* callback */
	struct no_os_callback_desc int_cb = {
		.callback = ad5940_int_callback,
		.event = NO_OS_EVT_GPIO,
		.peripheral = NO_OS_GPIO_IRQ,
	};

	ret = no_os_irq_register_callback(gic, INT_IRQn, &int_cb);
	if (ret < 0)
		return ret;

	ret = no_os_irq_trigger_level_set(gic, INT_IRQn, NO_OS_IRQ_EDGE_FALLING);
	if (ret < 0)
		return ret;

	ret = no_os_irq_enable(gic, INT_IRQn);
	if (ret < 0)
		return ret;

	ret = app_main(i2c, &ad5940_ip);
	if (ret < 0)
		goto error;
#else
#if defined(STM32_PLATFORM)
	struct stm32_uart_init_param uart_extra_ip = {
		.huart = &huart5,
	};
#endif

	struct no_os_uart_init_param iio_uart_ip = {
		.device_id = UART_DEVICE_ID,
		.irq_id = UART_IRQ_ID,
		.baud_rate = UART_BAUDRATE,
		.size = NO_OS_UART_CS_8,
		.parity = NO_OS_UART_PAR_NO,
		.stop = NO_OS_UART_STOP_1_BIT,
#if defined(STM32_PLATFORM)
		.extra = &uart_extra_ip,
#else
		.extra = NULL,
#endif
		.platform_ops = UART_OPS
	};
	struct iio_app_desc *app;
	struct iio_app_init_param app_init_param = { 0 };
	struct ad5940_iio_dev *ad5940_iio = NULL;
	struct ad5940_iio_init_param ad5940_iio_ip = {
		.ad5940_init = &ad5940_ip,
	};
	ret = ad5940_iio_init(&ad5940_iio, &ad5940_iio_ip);
	if (ret < 0)
		goto error;

	struct adg2128_iio_dev *adg2128_iio = NULL;
	ret = adg2128_iio_init(&adg2128_iio, i2c);
	if (ret < 0)
		goto error;

	struct iio_app_device devices[] = {
		{
			.name = "ad5940",
			.dev = ad5940_iio,
			.dev_descriptor = ad5940_iio->iio,
			.read_buff = NULL,
			.write_buff = NULL
		},
		{
			.name = "adg2128",
			.dev = adg2128_iio,
			.dev_descriptor = adg2128_iio->iio_dev,
		},
	};

	app_init_param.devices = devices;
	app_init_param.nb_devices = NO_OS_ARRAY_SIZE(devices);
	app_init_param.uart_init_params = iio_uart_ip;

	ret = iio_app_init(&app, app_init_param);
	if (ret)
		return ret;

	return iio_app_run(app);
#endif

	printf("Bye!\n");
	return 0;
error:
	printf("Bye! (%d)\n", ret);
	return ret;
}

/***************************************************************************//**
 *   @file   app.c
 *   @brief  EIT application source.
 *   @author Kister Jimenez (kister.jimenez@analog.com)
 *   @author Darius Berghe (darius.berghe@analog.com)
********************************************************************************
 * Copyright 2022(c) Analog Devices, Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. Neither the name of Analog Devices, Inc. nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES, INC. “AS IS” AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL ANALOG DEVICES, INC. BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include "no_os_delay.h"
#include "no_os_uart.h"
#include "no_os_spi.h"
#include "no_os_gpio.h"
#include "no_os_timer.h"
#include "bia_measurement.h"
#include "mux_board.h"
#include "app.h"
#include "parameters.h"

#if defined(ADUCM_PLATFORM)
#include "aducm3029_timer.h"
#endif

extern struct no_os_uart_desc *uart;

#define APPBUFF_SIZE 100
uint32_t AppBuff[APPBUFF_SIZE];
struct electrode_combo swComboSeq[256]; // TODO review when nElCount is 32

float SinFreqVal = 0.0;
unsigned int SinFreqValUINT = 0;
unsigned int runningCmd = 0;

#if defined(ADUCM_PLATFORM)
static struct no_os_timer_desc *q_timer;
static struct no_os_timer_desc *v_total_timer;
#endif
static uint32_t q_switch_us;
static uint32_t q_meas_us;
static bool q_timing_valid;
static uint64_t v_switch_us_accum;
static uint32_t v_switch_count;
static uint32_t v_total_us;
static bool v_timing_active;

volatile uint32_t ucInterrupted = 0; /* Flag to indicate interrupt occurred */
extern volatile unsigned char szInSring[32];

/* Since the reset pin is mapped to GPIO of AD5940, Access it through
 * AD5940 GPIO Register. */
int ADG2128_SwRst(struct ad5940_dev *dev)
{
	int ret = ad5940_WriteReg(dev, REG_AGPIO_GP0OUT, 0);
	if (ret < 0)
		return ret;
	no_os_udelay(1);
	return ad5940_WriteReg(dev, REG_AGPIO_GP0OUT, AGPIO_Pin1);
}

uint32_t GetMCUIntFlag(void)
{
	return ucInterrupted;
}

uint32_t ClrMCUIntFlag(void)
{
	ucInterrupted = 0;
	return 1;
}

#if defined(ADUCM_PLATFORM)
static int32_t q_timer_init(struct no_os_timer_desc **timer)
{
	if (*timer)
		return 0;

	struct no_os_timer_init_param tip = {
		.id = 0,
		.freq_hz = 1000000u,
		.ticks_count = 0,
		.platform_ops = &aducm_timer_ops,
		.extra = NULL,
	};

	return no_os_timer_init(timer, &tip);
}

static void q_timer_start(void)
{
	if (q_timer_init(&q_timer) != 0)
		return;

	no_os_timer_counter_set(q_timer, 0);
	no_os_timer_start(q_timer);
}

static uint32_t q_timer_stop_get_us(void)
{
	uint32_t count = 0;

	if (!q_timer)
		return 0;

	no_os_timer_counter_get(q_timer, &count);
	no_os_timer_stop(q_timer);

	return count;
}

static void v_total_timer_start(void)
{
	if (q_timer_init(&v_total_timer) != 0)
		return;

	no_os_timer_counter_set(v_total_timer, 0);
	no_os_timer_start(v_total_timer);
}

static uint32_t v_total_timer_stop_get_us(void)
{
	uint32_t count = 0;

	if (!v_total_timer)
		return 0;

	no_os_timer_counter_get(v_total_timer, &count);
	no_os_timer_stop(v_total_timer);

	return count;
}
#else
static void q_timer_start(void) { }
static uint32_t q_timer_stop_get_us(void) { return 0; }
static void v_total_timer_start(void) { }
static uint32_t v_total_timer_stop_get_us(void) { return 0; }
#endif

void configMeasurement(struct measurement_config *oldCfg,
		       struct measurement_config newCfg)
{
	AppBiaCfg_Type *pBiaCfg;
	AppBiaGetCfg(&pBiaCfg);
	if (oldCfg->nFrequency != newCfg.nFrequency) {
		pBiaCfg->bParamsChanged = true;
		oldCfg->nFrequency = newCfg.nFrequency;
		pBiaCfg->SinFreq = ((float)(newCfg.nFrequency)) * 1000.0;
	}

	if (oldCfg->nAmplitudePP != newCfg.nAmplitudePP) {
		pBiaCfg->bParamsChanged = true;
		oldCfg->nAmplitudePP = newCfg.nAmplitudePP;
		pBiaCfg->DacVoltPP = newCfg.nAmplitudePP * 1.0;
	}
	pBiaCfg->bImpedanceReadMode = newCfg.bImpedanceReadMode;
	pBiaCfg->SweepCfg.SweepEn = newCfg.bSweepEn;
	if (newCfg.bImpedanceReadMode) { // Impedance mode
		pBiaCfg->FifoThresh = 4;
	} else {
		pBiaCfg->FifoThresh = 2;
	}
	// Set newCfg as old for next command execution;
	*oldCfg = newCfg;
}

void ParseResultMode(struct measurement_config *pMeasCfg)
{
	char *cmd_ptr;
	char cTmp;
	uint8_t cmd_ok = 0;
	char hex_string_byte_param[10];
	cmd_ptr = strtok(NULL, ",");
	pMeasCfg->bImpedanceReadMode = false; //default
	if (cmd_ptr) { // If last parameter exists read it
		strcpy(hex_string_byte_param, cmd_ptr);
		cmd_ok = sscanf(hex_string_byte_param, "%c", &cTmp);
		if (cmd_ok) {
			if (cTmp == 'Z')
				pMeasCfg->bImpedanceReadMode = true;
		}
	}

	cmd_ptr = strtok(NULL, ",");
	pMeasCfg->bMagnitudeMode = false;
	if (cmd_ptr) { // If parameter exists read it
		strcpy(hex_string_byte_param, cmd_ptr);
		cmd_ok = sscanf(hex_string_byte_param, "%c", &cTmp);
		if (cmd_ok) {
			if (cTmp == 'M')
				pMeasCfg->bMagnitudeMode = true;
		}
	}
}

int32_t ParseConfig(char  *pStr,
		    struct eit_config *pEitCfg,
		    struct measurement_config *pMeasCfg)
{
	char *cmd_ptr;
	uint8_t cmd_ok = 0;
	char hex_string_byte_param[10];
	*pStr = 0;
	cmd_ptr = strtok(pStr + 1, ",");
	strcpy(hex_string_byte_param, cmd_ptr);
	cmd_ok = sscanf(hex_string_byte_param, "%hu", &pMeasCfg->nFrequency);
	if (cmd_ok) {
		cmd_ptr = strtok(NULL, ",");
		strcpy(hex_string_byte_param, cmd_ptr);
		cmd_ok = sscanf(hex_string_byte_param, "%hu",
				&pEitCfg->nElectrodeCnt);
	}
	if (cmd_ok) {
		cmd_ptr = strtok(NULL, ",");
		strcpy(hex_string_byte_param, cmd_ptr);
		cmd_ok = sscanf(hex_string_byte_param, "%hu", &pEitCfg->nForceDist);
	}
	if (cmd_ok) {
		cmd_ptr = strtok(NULL, ",");
		strcpy(hex_string_byte_param, cmd_ptr);
		cmd_ok = sscanf(hex_string_byte_param, "%hu", &pEitCfg->nSenseDist);
	}
	if (cmd_ok) {
		cmd_ptr = strtok(NULL, ",");
		strcpy(hex_string_byte_param, cmd_ptr);
		cmd_ok = sscanf(hex_string_byte_param, "%hu", &pEitCfg->nRefEl);
	}
	if (cmd_ok)
		ParseResultMode(pMeasCfg);

	if (!cmd_ok) //Error Parsing
		return 1;
	else
		return 0;
}

int32_t ParseQuery(char *pStr,
		   struct electrode_combo *pElCmb,
		   struct measurement_config *pMeasCfg)
{
	char *cmd_ptr;
	uint8_t cmd_ok = 0;
	char hex_string_byte_param[10];
	*pStr = 0;
	cmd_ptr = strtok(pStr + 1, ",");
	strcpy(hex_string_byte_param, cmd_ptr);
	cmd_ok = sscanf(hex_string_byte_param, "%hu", &pMeasCfg->nFrequency);
	if (cmd_ok) {
		cmd_ptr = strtok(NULL, ",");
		strcpy(hex_string_byte_param, cmd_ptr);
		cmd_ok = sscanf(hex_string_byte_param, "%hu", &pElCmb->F_plus);
	}
	if (cmd_ok) {
		cmd_ptr = strtok(NULL, ",");
		strcpy(hex_string_byte_param, cmd_ptr);
		cmd_ok = sscanf(hex_string_byte_param, "%hu", &pElCmb->F_minus);
	}
	if (cmd_ok) {
		cmd_ptr = strtok(NULL, ",");
		strcpy(hex_string_byte_param, cmd_ptr);
		cmd_ok = sscanf(hex_string_byte_param, "%hu", &pElCmb->S_plus);
	}
	if (cmd_ok) {
		cmd_ptr = strtok(NULL, ",");
		strcpy(hex_string_byte_param, cmd_ptr);
		cmd_ok = sscanf(hex_string_byte_param, "%hu", &pElCmb->S_minus);
	}
	if (cmd_ok)
		ParseResultMode(pMeasCfg);

	if (!cmd_ok) //Error Parsing
		return 1;
	else
		return 0;
}

void SendResultUint32(uint32_t *pData, uint32_t nDataCount)
{
	uint32_t i;

	for (i = 0; i < nDataCount - 1; i++) {
		printf("%lx,", pData[i]);
	}

	printf("%lx", pData[i]);
}

void SendResultFloat32(float *data, uint32_t DataCount)
{
	uint32_t i;

	for (i = 0; i < DataCount - 1; i++) {
		printf("%0.4f,", data[i]);
	}

	printf("%0.4f", data[i]);
}

void SendResultIeee754(float *data, uint32_t DataCount)
{

	uint32_t *pVal = (uint32_t *)((void *)data);
	uint32_t i;

	for (i = 0; i < DataCount - 1; i++) {
		printf("%lx,", pVal[i]);
	}

	printf("%lx", pVal[i]);
}

void SendResult(uint32_t *pData, uint16_t len,
		bool bImpedanceReadMode, bool bMagnitudeMode)
{
	float fMagVal = 0;
	fImpCar_Type fCarZval;
	iImpCar_Type iCarVval;
	signExtend18To32(pData, len);
	if (bImpedanceReadMode && (len == 4)) { // Send Impedance
		fCarZval = computeImpedance(pData);
		if (bMagnitudeMode) { // Complex to Magnitude
			fMagVal = sqrt(fCarZval.Real * fCarZval.Real +
				       fCarZval.Image * fCarZval.Image);
			SendResultIeee754(&fMagVal, 1); // Impedance Magnitude only. Float
		} else { // Complex Impedance in IEE754 uint32 hex string.
			SendResultIeee754((float *)&fCarZval, 2);
		}
	} else if ((!bImpedanceReadMode) && (len == 2)) { // Send Voltage
		if (bMagnitudeMode) { // Complex to Magnitude
			iCarVval = *((iImpCar_Type *)pData);
			fMagVal = sqrt((iCarVval.Real * 1.0) * (iCarVval.Real * 1.0) +
				       (iCarVval.Image * 1.0) * (iCarVval.Image * 1.0));
			SendResultIeee754(&fMagVal, 1); // Voltage Magnitude only. Float
		} else { // Complex Voltage in uint32 hex string.
			SendResultUint32(pData, 2);
		}
	}
}

void MuxSupportedElectrodeCounts()
{
	//uint8_t outBuff[20]={0};
	printf("%s", "08,10,20");
}

/* !!Change the application parameters here if you want to change it to
 * none-default value */
void AD5940BiaStructInit(void)
{
	AppBiaCfg_Type *pBiaCfg;

	AppBiaGetCfg(&pBiaCfg);

	pBiaCfg->SeqStartAddr = 0;
	pBiaCfg->MaxSeqLen = 512; /** @todo add checker in function */

	pBiaCfg->RcalVal = 1000.0;
	// pBiaCfg->DftNum = DFTNUM_8192;
	// pBiaCfg->DftNum = DFTNUM_2048;
	pBiaCfg->DftNum = DFTNUM_1024;

	pBiaCfg->NumOfData = -1; /* Never stop until you stop it manually by
							  * AppBiaCtrl(dev, ) function */
	pBiaCfg->BiaODR = 2000;	 /* ODR(Sample Rate) 20Hz */
	pBiaCfg->FifoThresh = 2; /* 4 */
	pBiaCfg->ADCSinc3Osr = ADCSINC3OSR_2;

	pBiaCfg->DacVoltPP = 500.0; //800.0
	pBiaCfg->SinFreq = 10000.0; /* 10000Hz */
	pBiaCfg->SweepCfg.SweepEn = false;
	pBiaCfg->SweepCfg.SweepStart = 10000;
	pBiaCfg->SweepCfg.SweepStop = 80000.0;
	pBiaCfg->SweepCfg.SweepPoints = 20;
	pBiaCfg->SweepCfg.SweepLog = true;
	pBiaCfg->SweepCfg.SweepIndex = 0;
}

uint16_t generateSwitchCombination(struct eit_config eitCfg,
				   struct electrode_combo *swSeq)
{
	uint16_t i = 0;
	uint16_t j = 0;
	uint8_t F_plus;
	uint8_t F_minus;
	uint8_t S_plus;
	uint8_t S_minus;
	uint16_t seqCtr = 0;
	for (i = 0; i < eitCfg.nElectrodeCnt; i++) {
		F_plus = i;
		F_minus = (i + eitCfg.nForceDist) % eitCfg.nElectrodeCnt;
		for (j = 0; j < eitCfg.nElectrodeCnt; j++) {
			S_plus = j % eitCfg.nElectrodeCnt;
			if (S_plus == F_plus || S_plus == F_minus)
				continue;
			S_minus = (S_plus + eitCfg.nSenseDist) % eitCfg.nElectrodeCnt;
			if (S_minus == F_plus || S_minus == F_minus)
				continue;

			swSeq[seqCtr].F_plus = F_plus;
			swSeq[seqCtr].F_minus = F_minus;
			swSeq[seqCtr].S_plus = S_plus;
			swSeq[seqCtr++].S_minus = S_minus;
		}
	}
	return seqCtr;
}

int app_main(struct no_os_i2c_desc *i2c, struct ad5940_init_param *ad5940_ip)
{
	int ret;
	static uint32_t IntCount;
	struct eit_config oldEitCfg;
	struct electrode_combo oldElCfg;
	struct measurement_config oldMeasCfg;

	struct eit_config newEitCfg;
	struct electrode_combo newElCfg;
	struct measurement_config newMeasCfg;

	char *buffStr = 0;
	uint32_t temp;
	uint16_t switchSeqCnt = 0;
	uint16_t switchSeqNum = 0;

	int32_t cmd_err = 0;
	uint8_t lastConfig = 'C';

	struct ad5940_dev *ad5940;
	ret = ad5940_init(&ad5940, ad5940_ip);
	if (ret)
		return ret;

	AD5940BiaStructInit(); /* Configure your parameters in this function */

	oldMeasCfg.bImpedanceReadMode = true;
	oldMeasCfg.bMagnitudeMode = false;
	oldMeasCfg.nFrequency = 10;	// default 10 Khz Excitation
	oldMeasCfg.nAmplitudePP = 300; // default 300mV peak to peak excitation
	oldMeasCfg.bSweepEn = false;

	oldElCfg.F_plus = 0;
	oldElCfg.F_minus = 3;
	oldElCfg.S_plus = 1;
	oldElCfg.S_minus = 2;

	oldEitCfg.nElectrodeCnt = 16;
	oldEitCfg.nForceDist = 1;
	oldEitCfg.nSenseDist = 1;
	oldEitCfg.nRefEl = 0;

	switchSeqNum = 0;
	switchSeqCnt = generateSwitchCombination(oldEitCfg, swComboSeq);

	uint8_t cmd[32];
	uint8_t cmdi = 0;

	while (1) {
		ret = no_os_uart_read(uart, &cmd[cmdi], 1);
		if (ret == 1)
			cmdi++;
		else if (!ret)
			continue;

		if (cmd[cmdi - 1] == '\n') {
			if (cmd[0] == 'O') { // STOP
				ADG2128_SwRst(ad5940);
				AppBiaCtrl(ad5940, BIACTRL_STOPNOW, 0);
				printf("%s", "\n!O ");
				runningCmd = 0;
				fflush(stdin);
				printf("%s", "\n");
			}
			if (cmd[0] == 'R') { // RESET
				ADG2128_SwRst(ad5940);
				printf("%s", "!R \n");
				runningCmd = 0;
				fflush(stdin);
			}

			if (!runningCmd) {
				if (cmd[0] == 'B') { //
					ADG2128_SwRst(ad5940);
					printf("%s", "!B ");
					runningCmd = 0;
					fflush(stdin);
					MuxSupportedElectrodeCounts();
					putchar('\n');
				}

				if (cmd[0] == 'Q') { //Run Specific combination
					AppBiaCtrl(ad5940, BIACTRL_STOPNOW, 0);
					cmd[0] = 0;
					newElCfg = oldElCfg;
					newMeasCfg = oldMeasCfg;
					buffStr = (char *)(cmd + 1);
					cmd_err = ParseQuery(buffStr,
							     &newElCfg,
							     &newMeasCfg);
					fflush(stdin);
					// No errors during parsing.
					if (!cmd_err) {
						runningCmd = 'Q';
						printf("%s", "!CMD Q OK\n");
						configMeasurement(&oldMeasCfg, newMeasCfg);
						AppBiaInit(ad5940, AppBuff, APPBUFF_SIZE);
						no_os_udelay(10);
						printf("%s", "!Q ");
						q_timer_start();
						setMuxSwitch(i2c, ad5940, newElCfg, MUXBOARD_SIZE);
						no_os_udelay(3);
						q_switch_us = q_timer_stop_get_us();
						q_timer_start();
						q_timing_valid = true;
						AppBiaCtrl(ad5940, BIACTRL_START, 0);
						lastConfig = 'Q';
					} else {
						printf("%s", "!CMD Q ERROR\n");
					}
				}

				if (cmd[0] == 'C') { //Configure
					AppBiaCtrl(ad5940, BIACTRL_STOPNOW, 0);
					cmd[0] = 0;
					newEitCfg = oldEitCfg;
					newMeasCfg = oldMeasCfg;
					buffStr = (char *)(cmd + 1);
					cmd_err = ParseConfig(buffStr,
							      &newEitCfg,
							      &newMeasCfg);
					fflush(stdin);
					//all command params are valid, execute command
					if (!cmd_err) {
						runningCmd = 0;
						switchSeqNum = 0;
						printf("%s", "!CMD C OK\n");
						switchSeqCnt = generateSwitchCombination(newEitCfg,
								swComboSeq);
						configMeasurement(&oldMeasCfg, newMeasCfg);
						AppBiaInit(ad5940, AppBuff, APPBUFF_SIZE);
						no_os_udelay(10);
						printf("%s", "!C OK\n");
						lastConfig = 'C';
					} else {
						printf("%s", "!CMD C ERROR\n");
					}
				}

				if (cmd[0] == 'V') { //Start boundary voltage query sequence
					fflush(stdin);
					if (lastConfig == 'C') {
						printf("%s", "!CMD V OK\n");
						runningCmd = 'V';
						switchSeqNum = 0;
							v_switch_us_accum = 0;
							v_switch_count = 0;
							v_total_timer_start();
							q_timer_start();
							setMuxSwitch(i2c, ad5940, swComboSeq[switchSeqNum++], newEitCfg.nElectrodeCnt);
						AppBiaInit(ad5940, AppBuff, APPBUFF_SIZE);
						no_os_udelay(10);
							v_switch_us_accum += q_timer_stop_get_us();
							v_switch_count++;
							q_timer_start();
							v_timing_active = true;
						AppBiaCtrl(ad5940, BIACTRL_START, 0);
						printf("%s", "!V ");
					} else
						printf("%s", "!Send C Command first to configure!\n");
				}
			}

			memset(cmd, 0, sizeof(cmd));
			cmdi = 0;
		}

		/* Check if interrupt flag which will be set when interrupt occured. */
		if (GetMCUIntFlag()) {
			IntCount++;
			ClrMCUIntFlag(); /* Clear this flag */
			temp = APPBUFF_SIZE;
			AppBiaISR(ad5940, AppBuff,
				  &temp); /* Deal with it and provide a buffer to store data we got */
			AppBiaCtrl(ad5940, BIACTRL_STOPNOW, 0);
			if (runningCmd == 'V' || runningCmd == 'Q') {
				//If Q command is being ran return result
				if (runningCmd == 'Q') {
					if (q_timing_valid) {
						q_meas_us = q_timer_stop_get_us();
						printf("!QT %lu,%lu\n", (unsigned long)q_switch_us,
						       (unsigned long)q_meas_us);
						q_timing_valid = false;
					}
					SendResult(AppBuff, temp, newMeasCfg.bImpedanceReadMode,
						   newMeasCfg.bMagnitudeMode);
					putchar('\n');
					runningCmd = 0;
				}
				//If V or Z command is being ran and this is the last set of ADC, send a terminator character
				if ((runningCmd == 'V') && switchSeqNum >= switchSeqCnt) {
					SendResult(AppBuff, temp, newMeasCfg.bImpedanceReadMode,
						   newMeasCfg.bMagnitudeMode);
					putchar('\n');;
					if (v_timing_active) {
						v_total_us = v_total_timer_stop_get_us();
						printf("!VT %lu,%lu\n",
						       (unsigned long)(v_switch_count ?
							(v_switch_us_accum / v_switch_count) : 0),
						       (unsigned long)v_total_us);
						v_timing_active = false;
					}
					runningCmd = 0;
				}

				//if V is still running and switch combinations are not exhausted, restart AFE Seq with new switch combo
				if ((runningCmd == 'V') && switchSeqNum < switchSeqCnt) {
					SendResult(AppBuff, temp, newMeasCfg.bImpedanceReadMode,
						   newMeasCfg.bMagnitudeMode);
					putchar(',');
					q_timer_start();
					setMuxSwitch(i2c, ad5940, swComboSeq[switchSeqNum++], newEitCfg.nElectrodeCnt);
					no_os_udelay(3);
					v_switch_us_accum += q_timer_stop_get_us();
					v_switch_count++;
					AppBiaCtrl(ad5940, BIACTRL_START, 0);
				}
			}
		}
	}
}

/***************************************************************************//**
 *   @file   parameters.h
 *   @brief  Parameters Definitions.
 *   @author Darius Berghe (darius.berghe@analog.com)
********************************************************************************
 * Copyright 2022(c) Analog Devices, Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. Neither the name of Analog Devices, Inc. nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES, INC. “AS IS” AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL ANALOG DEVICES, INC. BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/
#ifndef __PARAMETERS_H__
#define __PARAMETERS_H__

#if defined(ADUCM_PLATFORM)
#include "aducm3029_irq.h"
#include "aducm3029_gpio_irq.h"

#define SPI_DEVICE_ID		0
#define SPI_CS			1
#define INTC_DEVICE_ID		ADUCM_GPIO_B_GROUP_SOFT_CTRL
#define UART_DEVICE_ID		0
#define UART_IRQ_ID		ADUCM_UART_INT_ID
#define UART_BAUDRATE		230400
#define I2C_DEVICE_ID		0
#define I2C_BAUDRATE		100000
#define RESET_PIN		13 // 0.13
#define GP0_PIN			15 // 0.15
#define INT_IRQn		GP0_PIN
#define UART_OPS			&aducm_uart_ops
#define IRQ_OPS			&aducm_irq_ops
#define GPIO_IRQ_OPS    	&aducm_gpio_irq_ops

#elif defined(STM32_PLATFORM)
#include "stm32_hal.h"

#define SPI_DEVICE_ID		1
#define SPI_CS			15
#define SPI_CS_PORT		0
#define INTC_DEVICE_ID		0
#define INT_IRQn		EXTI9_5_IRQn
#define UART_DEVICE_ID		5
#define UART_IRQ_ID		UART5_IRQn
#define UART_OPS			&stm32_uart_ops
#define IRQ_OPS			&stm32_irq_ops
#define GPIO_IRQ_OPS    	&stm32_gpio_irq_ops
#ifdef IIO_SUPPORT
extern UART_HandleTypeDef 	huart5;
#endif
#define IIO_APP_HUART		&huart5
#define UART_BAUDRATE		230400
#define I2C_DEVICE_ID		1
#define I2C_BAUDRATE		100000

#define RESET_PIN		12 // D.12
#define GP0_PIN			7 // G.7
#endif

#endif // __PARAMETERS_H__

Thank you,

Alex

Thread Notes