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