ADC 1KHz over NUS with ADC Zephyr API - Crash

Hi everybody

I'm trying to implement a reading on a nRF5340 an ADC to sample a sensor every 1ms, pack datas and send them over NUS to a central.

I'm using NCS 2.1.0 and Zephyr ADC API

The problem is how to implement a reading of ADC in a faster way... What I implement until now is this:

An init function, to setup ADC (channels are defined into devTree, this code has been taken from an example)
and set up timers........

struct adc_sequence sequence = {
	/* individual channels will be added below */
	.channels = 0,
	.buffer	  = &sample_buffer,
	/* buffer size in bytes, not number of samples */
	.buffer_size = sizeof(sample_buffer),
	.resolution	 = ADC_RESOLUTION,
	.calibrate	 = true,
	.options	 = &adcOptions,
};

/****************************************************************/


static void AppFuncInput_AnalogIn(void)
{
	m_pAdcDevice = DEVICE_DT_GET(ADC_NODE);

	if (!device_is_ready(m_pAdcDevice))
	{
		LOG_ERROR("ADC device not found\n");
		return ERR_INVALID_PARAM;
	}
	
	/*
	 * Configure channels individually prior to sampling
	 */
	for (uint8_t i = 0; i < ADC_NUM_CHANNELS; i++)
	{
		channel_cfg.channel_id = channel_ids[i];
		LOG_INFO("Configured ADC%d", channel_ids[i]);

#ifdef CONFIG_ADC_NRFX_SAADC
		channel_cfg.input_positive = SAADC_CH_PSELP_PSELP_AnalogInput0 + channel_ids[i];
#endif

		adc_channel_setup(m_pAdcDevice, &channel_cfg);

		sequence.channels |= BIT(channel_ids[i]);
	}

	m_adcVref = adc_ref_internal(m_pAdcDevice);


    /* Initialize a Timer to run the first time after 10s and then every 1ms */
    k_timer_init(&sFunctionTimer, AppFuncInput_AnalogIn, NULL);
    k_timer_start(&sFunctionTimer, K_MSEC(10000), K_MSEC(1));

/*
.....
*/

}

Then, the callback function to read the ADC is this:

static void AppFuncInput_AnalogIn(void)
{
	int16_t err = NO_ERROR;  ///< return code for the function

	/* Read sequence of channels (fails if not supported by MCU) */
	err = adc_read(m_pAdcDevice, &sequence);
	if (err != 0)
	{
		LOG_ERROR("ADC reading failed with error %d.\n", err);
		return err;
	}

	/* if there's an extra sample number please insert these values into filter calculation */
	for (uint8_t jj = 0; jj <= ADC_EXTRASAMPLE_NUMBER; jj += ADC_NUM_CHANNELS)
	{
		for (uint8_t i = 0; i < ADC_NUM_CHANNELS; i++)
		{
			calculateMean(&m_analogInputFlt[i], sample_buffer[i + jj]);
		/* 
		....
		*/
		}
	};

	AppFuncInput_HandleBleSendingData();
};

Now, if I use the 

AppFuncInput_AnalogIn( ) 
in the normal execution, calling the function in a separate thread, it works properly but I was not able to say: "Hey, sample every 1ms".
I put a k_msleep(1) but it depends on execution timing of the function...... That's why I introduced the K_Timer element.
BUT it fails to work.
When the timer ends, and launch callback, reached the adc_read( ), it says
ASSERTION FAIL [((arch_is_in_isr() == 0) || ((timeout).ticks == (((k_timeout_t) {0})).ticks))] @ WEST_TOPDIR/zephyr/kernel/sem.c:121
This could be due to interaction between BLE stack and ADC ?

Is there a more simple method to get data every 1ms from an ADC and then send it to BLE Nus central?

Thank you !
Parents
  • Hello,

    Is there a more simple method to get data every 1ms from an ADC and then send it to BLE Nus central?

    It might not be an easier way than what you are already doing, but there could be better ways to achieve it :) 

    how to implement a reading of ADC in a faster way...

    How fast do you need the readings?
    For periodic samplings at a high frequency I would recommend that you set up PPI between the timer CC event and the SAADC SAMPLE task, so that the CC event triggers the SAMPLE task immediately, without requiring CPU interaction. This will also lower your overall power consumption because the CPU does not have to wake up in order to trigger each sampling.

    Do you intend to only sample the 1 channel? If so, you can use the SAADC internal timer to trigger sampling, according to the Continuous sampling section of the SAADC documentation.

    I put a k_msleep(1) but it depends on execution timing of the function...... That's why I introduced the K_Timer element.
    BUT it fails to work.

    You are right that using k_sleep will be upredictable, and it is therefore not the recommended way to do periodic sampling with high accuracy.
    As long as the CPU is required to trigger the sampling the sampling could also be delayed, if the CPU is busy handling a higher-priority event/interrupt exactly when the sampling needs to be started.
    This is why we recommend implementing the continuous sampling as described in the documentation referenced above.

    Best regards,
    Karl

  • Hi  

    thank you for your answer and suggestion.

    I think that continous sampling could be perfect for me, and if you have a guide / example to start with into NCS2.1.0 it'd be great ( I found something on the forum but they are often linked to older versions).

    Secondary point, the issue about timer:
    Why using the timer, the system crashes ? 

     [!k_is_in_isr()] @ WEST_TOPDIR/zephyr/subsys/bluetooth/host/conn.c:1208
    [00:00:05.465,728] [1B][1;31m<err> os: r0/a1:  0x00000004  r1/a2:  0x000004b8  r2/a3:  0x00000000[1B][0m
    [00:00:05.465,759] [1B][1;31m<err> os: r3/a4:  0x20008496 r12/ip:  0x00000000 r14/lr:  0x0000f445[1B][0m
    [00:00:05.465,759] [1B][1;31m<err> os:  xpsr:  0x61000025[1B][0m
    [00:00:05.465,os: Faulting instruction address (r15/pc): 0x00028936[1B][0m
    [00:00:05.465,789] [1B][1;31m<err> os: >>> ZEPHYR FATAL ERROR 4: Kernel panic on CPU 0[1B][0m
    [00:00:05.465,789] [1B][1;31m<err> os: Fault during interrupt handling
    [1B][0m
    [00:00:05.465,820] [1B][1;31m<err> os: Current thread: 0x200022d0 (idle)[1B][0m
    [00:00:05.516,296] [1B][1;31m<err> fatal_error: Resetting system[1B][0m
    þ> 
    [    0.285 B] AppInit.c: --*** Booting Zephyr OS build v3.1.99-ncs1  ***

    I think that it's not normal that, calling a timer, it provide a fantastic crash of the entire app...
    I tryed to set it up in the way you see in the previous post, or even defining it in compile time:

    /**
     * @brief Handle if some inputs has to be sent over BLE
     *
     * @return tErr
     */
    static int16_t AppFuncInput_HandleBleSendingData(void);
    
    
    static K_TIMER_DEFINE(sFunctionTimer, AppFuncInput_HandleBleSendingData, NULL);
    
    
    /* [===============================================================================] */
    static tErr AppFuncInput_HandleBleSendingData(void)
    {
        /*
        ...
        */
        
    	/* If it's in a sampling state, and there's new data to send */
    	if (AppFuncBLE_isSampling() && m_bNewSampleReady)
    	{
    		/* Convert to mV and send over BLE */
    	}
    
    	return err;
    };
    
    /* [===============================================================================] */
    static tErr AppFuncInput_InitAnalog(void)
    {
    	m_pAdcDevice = DEVICE_DT_GET(ADC_NODE);
    
        /*
        ...
        */
        
    	/* Initialize a Timer to run the first time after 10s and then every 1ms */
    	k_timer_start(&sFunctionTimer, K_MSEC(5000), K_MSEC(50));
    
    	return NO_ERROR;
    };

  • Hello again,

    enpre said:
    thank you for your answer and suggestion.

    No problem at all, I am happy to help!

    enpre said:
    I think that continous sampling could be perfect for me,

    Great! There are still some questions that remain from my previous comment:

    1. What sampling frequency do you need for your application?

    2. How many channels will you be sampling each time?

    The Continuous sampling I referenced in my last reply is a feature of the SAADC peripheral, and so you could for example use it's internal timer for this, if applicable.
    Additionally, it seems that you are currently intending to have the TIMER CC event trigger a sampling by the CPU, is that correct? If so, this will not provide you the benefits I mentioned in my previous reply when I described PPI. If you are using PPI then the TIMER CC event handler could be empty, because the event itself will trigger the sampling in the SAADC.

    Lastly, could you show the full log from your application before it crashes? and could you show the full code as well? I notice that some of the included comments does not exactly match the line they are related to, which has me worried that some/all of them might be outdated.

    Best regards,
    Karl

  • Here we are!

    1. What sampling frequency do you need for your application?

    I have 12 peripherals, connected to the same central.
    Many of them has to piclk up data from an ADC every 1ms and send it over BLE.

    Obviously, I pack them in a 100sample before sending them to the central.

    2. How many channels will you be sampling each time?

    For now, 1 channel.


    Here you are the code of the Input module, which handles the sending of the data:

    /* ********************************************************************************** */
    /**	@file AppFuncInput.c
     *
     * @copyright
     * \@Copyright(c) 2022, All rights reserved.
     * @par
     * No part of this document must be reproduced in any form - including copied,
     * transcribed, printed or by any electronic means - without specific written
     * permission from the authors.
     *
     * @addtogroup AppFuncInput
     * @{
     */
    /************************************************************************************/
    
    #include "AppFuncInput.h"
    
    //====================================================================================
    //	@@	LOCAL DEFINITIONS
    //====================================================================================
    #include "AppPeripherals.h"
    #include "RTE_Bsp.h"
    #include "AppFuncBle.h"
    
    #include <zephyr.h>
    #include <zephyr/kernel.h>
    #include <drivers/adc.h>
    #include <sys/printk.h>
    
    #include <helpers/nrfx_gppi.h>
    #include <hal/nrf_gpio.h>
    #include <nrfx_comp.h>
    
    //====================================================================================
    //	@@	ADC CHANNELS
    //====================================================================================
    
    #define ADC_NUMBER_OF_SAMPLE (100)	///< Number of sample into a single ADC read. e.g. (100) means 100 samples for each read
    
    #if !DT_NODE_EXISTS(DT_PATH(zephyr_user)) || !DT_NODE_HAS_PROP(DT_PATH(zephyr_user), io_channels)
    #error "No suitable devicetree overlay specified"
    #endif
    
    #define ADC_NUM_CHANNELS DT_PROP_LEN(DT_PATH(zephyr_user), io_channels)
    
    #if ADC_NUM_CHANNELS > 2
    #error "Currently only 1 or 2 channels supported in this application"
    #endif
    
    #if ADC_NUM_CHANNELS == 2 && \
    	!DT_SAME_NODE(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), io_channels, 0), DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), io_channels, 1))
    #error "Channels have to use the same ADC."
    #endif
    
    #define ADC_NODE DT_PHANDLE(DT_PATH(zephyr_user), io_channels)
    
    
    //====================================================================================
    //	@@	ADC Configuration
    //====================================================================================
    /* Common settings supported by most ADCs */
    #define ADC_RESOLUTION		 12
    #define ADC_GAIN			 ADC_GAIN_1_6
    #define ADC_REFERENCE		 ADC_REF_INTERNAL
    #define ADC_ACQUISITION_TIME ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40)
    
    /* Get the numbers of up to two channels */
    static uint8_t channel_ids[ADC_NUM_CHANNELS] = {DT_IO_CHANNELS_INPUT_BY_IDX(DT_PATH(zephyr_user), 0),
    #if ADC_NUM_CHANNELS == 2
    												DT_IO_CHANNELS_INPUT_BY_IDX(DT_PATH(zephyr_user), 1)
    #endif
    };
    
    static int16_t m_adcBuffer[(ADC_NUMBER_OF_SAMPLE) *ADC_NUM_CHANNELS];  ///< buffer used for storing data
    
    struct adc_channel_cfg channel_cfg = {.gain				= ADC_GAIN,
    									  .reference		= ADC_REFERENCE,
    									  .acquisition_time = ADC_ACQUISITION_TIME,
    									  /* channel ID will be overwritten below */
    									  .channel_id	= 0,
    									  .differential = 0};
    
    const struct adc_sequence_options adcOptions = {
    	.extra_samplings = ADC_NUMBER_OF_SAMPLE - 1,
    	.interval_us	 = 1000,  ///< interval between samples
    };
    
    struct adc_sequence sequence = {
    	/* individual channels will be added below */
    	.channels = 0,
    	.buffer	  = &m_adcBuffer,
    	/* buffer size in bytes, not number of samples */
    	.buffer_size = sizeof(m_adcBuffer),
    	.resolution	 = ADC_RESOLUTION,
    	.calibrate	 = true,
    	.options	 = &adcOptions,
    };
    
    //====================================================================================
    //	@@	COMPARATORs
    //====================================================================================
    #define ANALOG_INPUT_USED (0)				///< select which analog input is used as comparator
    #define COMP_LPCOMP_IRQn  COMP_LPCOMP_IRQn	///< irq used for comparator setting
    
    /* ----------------------- */ /* clang-format off */ /* ----------------------- */ 
    /** @brief COMP threshold custom configuration. */
    #define NRFX_COMP_CONFIG_TH_CUSTOM                                                                      \
    	{                                                                                                  \
    		.th_down = NRFX_VOLTAGE_THRESHOLD_TO_INT(1, 3), \
    		.th_up = NRFX_VOLTAGE_THRESHOLD_TO_INT(1.5, 3) 	\
    	}
    
    #define NRFX_COMP_CUSTOM_CONFIG(_input)                         										\
    	{                                                  													\
    		.reference = NRF_COMP_REF_VDD,																	\
    		.main_mode = NRF_COMP_MAIN_MODE_SE,																\
    		.threshold = NRFX_COMP_CONFIG_TH_CUSTOM,															\
    		.speed_mode = COMP_MODE_SP_Normal,																\
    		.hyst = COMP_HYST_HYST_NoHyst,																	\
    		.input = (nrf_comp_input_t) _input, .interrupt_priority = 5,									\
    	}
    /* ----------------------- */ /* clang-format on */ /* ----------------------- */
    
    //====================================================================================
    //	@@LOCAL VARIABLE
    //====================================================================================
    const struct device *m_pAdcDevice;								///< device pointer
    static tAdcObj		 m_adcObj[ADC_ID_MAX] = {ADC_OBJ_LIST};		///< object used as a storage point for every ADC input
    static int32_t		 m_adcVref			= 0;					///< adc Vref
    static int32_t		 m_minTempDetected	= 0;					///< lower temperature detected during open/close event
    static bool			 m_bIsTapOpen		= false;				///< IF true TAP is OPEN
    static time_t		 m_upDownTimer		= 0;					///< variable used to calculate time elapsed between comparator ON/OFF
    static time_t		 m_totalOpeningTime = 0;					///< variable used to SUM all
    static tFltMean		 m_analogInputFlt[ADC_NUM_CHANNELS];		///< analog input filter
    static int16_t		 m_adcValueOld[RTE_ADC_OLD_VALUES_NUMBER];	///< ring buffer with latest sample
    static uint8_t		 m_adcValueOldCtr  = 0;						///< counter used to fill data of ADC struct
    static tBlePacket	 m_outBlePacket	   = {0};					///< pointer used to cast in/out msg
    static bool			 m_bNewSampleReady = false;					///< if true, news samples are ready
    
    static K_TIMER_DEFINE(sFunctionTimer, AppFuncInput_HandleBleSendingData, NULL);	 ///< start TIMER to get data and send them over BLE
    
    //====================================================================================
    //	@@	CONSTANTS
    //====================================================================================
    
    //====================================================================================
    //	@@	PRIVATE FUNCTIONS PROTOTYPES
    //====================================================================================
    
    /**
     * @brief Function for initializing LEDs and Buttons.
     */
    static tErr AppFuncInput_InitButton(void);
    
    /**
     * @brief Initalize comparators on analog inputs
     *
     * @return tErr
     */
    static tErr AppFuncInput_InitComparators(void);
    
    /**
     * @brief Handle if some inputs has to be sent over BLE
     *
     * @return tErr
     */
    static tErr AppFuncInput_HandleBleSendingData(void);
    
    /**
     * @brief Initalize analog inputs
     *
     * @return tErr
     */
    static tErr AppFuncInput_InitAnalog(void);
    
    /**
     * @brief
     *
     * @return tErr
     */
    static tErr AppFuncInput_CheckTemperature(void);
    
    //====================================================================================
    //	@@	PUBLIC FUNCTIONS IMPLEMENTATION
    //====================================================================================
    
    /* [===============================================================================] */
    void AppFuncInput_Init(void)
    {
    	AppFuncInput_InitAnalog();
    	AppFuncInput_InitButton();
    	AppFuncInput_InitComparators();
    }
    
    /* [===============================================================================] */
    bool AppFuncInput_Run(void)
    {
    	tErr err = NO_ERROR;  ///< return code for the function - @see BSPErrors.h
    
    	err = AppFuncInput_CheckTemperature();
    
    	return (err == NO_ERROR);
    }
    
    /* [===============================================================================] */
    time_t AppFuncInput_GetOpeningTime(void)
    {
    	time_t temp		   = m_totalOpeningTime;
    	m_totalOpeningTime = 0;
    
    	return temp;
    };
    
    //====================================================================================
    //	@@	PRIVATE FUNCTIONS PROTOTYPES
    //====================================================================================
    /* [===============================================================================] */
    static void AppFuncInput_ComparatorHandler(nrf_comp_event_t event)
    {
    	uint32_t comparedValue = nrfx_comp_sample();
    
    	if (comparedValue && (event == NRF_COMP_EVENT_UP))
    	{
    		m_upDownTimer = OSAPI_GetTime() - m_upDownTimer;
    		LOG_INFO("COMPARATOR Detect OPEN AIN[%d] Time Open[%d]ms\n", ANALOG_INPUT_USED, m_upDownTimer);
    		m_totalOpeningTime += m_upDownTimer;
    		m_bIsTapOpen = false;
    	}
    	else
    	{
    		/* Start: Event DOWN */
    		m_upDownTimer	  = OSAPI_GetTime();
    		m_bIsTapOpen	  = true;
    		m_minTempDetected = 0;
    	}
    }
    
    /* [===============================================================================] */
    static tErr AppFuncInput_CheckTemperature(void)
    {
    	int32_t nowTemperature;
    
    	if (m_bIsTapOpen)
    	{
    		nowTemperature = AppFuncInput_GetAnalog(ADC_ID_TEMPERATURE);
    		if (nowTemperature > m_minTempDetected)
    		{
    			m_minTempDetected = nowTemperature;
    		}
    	}
    };
    
    /* [===============================================================================] */
    int32_t AppFuncInput_GetLowerTemperature(void)
    {
    	int32_t lowerTemp = m_minTempDetected;
    	m_minTempDetected = 0;
    
    	return lowerTemp;
    };
    
    /* [===============================================================================] */
    static tErr AppFuncInput_InitButton(void)
    {
    	tErr err = NO_ERROR;  ///< return code for the function - @see BSPErrors.h
    
    	//	err = dk_buttons_init(AppZigbee_ButtonHandlerCB);
    
    	if (err)
    	{
    		LOG_ERROR("Cannot init buttons (err: %d)", err);
    	}
    
    	return err;
    }
    
    /* [===============================================================================] */
    static tErr AppFuncInput_InitComparators(void)
    {
    	nrfx_err_t err;
    
    	nrfx_comp_config_t comp_config = NRFX_COMP_CUSTOM_CONFIG(ANALOG_INPUT_USED);
    
    	err = nrfx_comp_init(&comp_config, AppFuncInput_ComparatorHandler);
    	if (err != NRFX_SUCCESS)
    	{
    		LOG_ERROR("nrfx_comp_init error: %08x", err);
    		return ERR_INVALID_PARAM;
    	}
    
    	/* Connect COMP_IRQ to nrfx_comp_irq_handler */
    	IRQ_CONNECT(COMP_LPCOMP_IRQn, 2, nrfx_isr, nrfx_comp_irq_handler, 0);
    
    	nrfx_comp_start(NRF_COMP_EVENT_CROSS, COMP_SHORTS_READY_SAMPLE_Msk);
    
    	return NO_ERROR;
    };
    
    /* [===============================================================================] */
    static tErr AppFuncInput_HandleBleSendingData(void)
    {
    	tErr	err	   = NO_ERROR;	///< return code for the function - @see BSPErrors.h
    	uint8_t sample = 0;			///< variable used to count
    	int32_t val_mv = 0;			///< used to convert to mV an int32 value, as adc_raw_to_millivolts works ONLY with int32 !!!
    
    	/* If it's in a sampling state, and there's new data to send */
    	if (AppFuncBLE_isSampling() && m_bNewSampleReady)
    	{
    		/* Convert to mV */
    		for (sample = 0; sample < ADC_NUMBER_OF_SAMPLE; sample++)
    		{
    			val_mv = m_adcBuffer[sample];
    			adc_raw_to_millivolts(m_adcVref, ADC_GAIN, ADC_RESOLUTION, &val_mv);
    			m_adcBuffer[sample] = val_mv;
    		}
    
    		/* Pack data and send ! */
    		m_outBlePacket.bleCmdId	 = BLE_CMD_ID_DATA_FROM_SENSOR;
    		m_outBlePacket.len		 = ADC_NUMBER_OF_SAMPLE;
    		m_outBlePacket.timestamp = AppFuncBLE_GetElapsedSamplingTime();
    		memcpy(&m_outBlePacket.dataPack, &m_adcBuffer, sizeof(m_adcBuffer));
    
    		if (AppFuncBLE_SendData(&m_outBlePacket) == NO_ERROR)
    		{
    			m_bNewSampleReady = false;
    		}
    	}
    
    	return err;
    };
    
    /* [===============================================================================] */
    /**
     * @brief function called in a separate thread, in this way:
     
      		#define ADC_THREAD_PERIOD  100; ///< period in milliseconds
     
    		start = OSAPI_GetTimeComplete();
    		AppFuncInput_AnalogIn();
    		end			  = OSAPI_GetTimeComplete();
    
    		elapsedTimeUs = ((end.tv_nsec - start.tv_nsec) / 1000);
    
    		// calculate if the analogIn has request more than ADC_THREAD_PERIOD ms, otherwise calculate remain time before next event
    		if (elapsedTimeUs > (ADC_THREAD_PERIOD +1) * 1000)
    		{
    			k_usleep(10);
    		}
    		else
    		{
    			k_usleep(ADC_THREAD_PERIOD * 1000 - elapsedTimeUs);
    		}
     *
     */
    void AppFuncInput_AnalogIn(void)
    {
    	tErr err = NO_ERROR;  ///< return code for the function - @see BSPErrors.h
    
    	/* Read sequence of channels (fails if not supported by MCU) */
    	err = adc_read(m_pAdcDevice, &sequence);
    	if (err != 0)
    	{
    		LOG_ERROR("ADC reading failed with error %d.\n", err);
    		return err;
    	}
    
    	m_bNewSampleReady = true;
    };
    
    /* [===============================================================================] */
    int16_t AppFuncInput_GetAnalog(tAdcId adcId)
    {
    	int32_t adcMv = (int32_t) *m_adcObj[adcId].adcValueFlt;
    
    	adc_raw_to_millivolts(m_adcVref, ADC_GAIN, ADC_RESOLUTION, &adcMv);
    	return (int16_t) adcMv;
    };
    
    /* [===============================================================================] */
    static tErr AppFuncInput_InitAnalog(void)
    {
    	m_pAdcDevice = DEVICE_DT_GET(ADC_NODE);
    
    	if (!device_is_ready(m_pAdcDevice))
    	{
    		LOG_ERROR("ADC device not found\n");
    		return ERR_INVALID_PARAM;
    	}
    
    	/* Configure channels individually prior to sampling */
    	for (uint8_t i = 0; i < ADC_NUM_CHANNELS; i++)
    	{
    		channel_cfg.channel_id = channel_ids[i];
    
    		LOG_INFO("Configured ADC%d", channel_ids[i]);
    
    #ifdef CONFIG_ADC_NRFX_SAADC
    		channel_cfg.input_positive = SAADC_CH_PSELP_PSELP_AnalogInput0 + channel_ids[i];
    #endif
    
    		adc_channel_setup(m_pAdcDevice, &channel_cfg);
    
    		sequence.channels |= BIT(channel_ids[i]);
    	}
    
    	/* Get vREF */
    	m_adcVref = adc_ref_internal(m_pAdcDevice);
    
    	/* Initialize a Timer to run the first time after 10s and then every 1ms */
    	k_timer_start(&sFunctionTimer, K_MSEC(5000), K_MSEC(50));
    
    	return NO_ERROR;
    };
    /** @} end of Group AppFuncInput */



    In a separate file, you can find the function which sends data over BLE.
    I can attach you the file:

    /* [===============================================================================] */
    tErr AppFuncBLE_SendData(tBlePacket *pBleMsgOut)
    {
    	tErr	err			= NO_ERROR;	 ///< return code for the function - @see BSPErrors.h
    	uint8_t idPeriph	= 0;
    	uint8_t downCounter = 0;
    
    	/* Don't know why, but Zephyr sizeof(enum) returns 4, but it allocates 8bytes.......... so fixed manually */
    	m_NusTxMsg.len =  sizeof(pBleMsgOut->bleCmdId) + sizeof(pBleMsgOut->timestamp) + sizeof(pBleMsgOut->idx) + sizeof(pBleMsgOut->len) + pBleMsgOut->len;
    	memcpy(&m_NusTxMsg.data, pBleMsgOut, m_NusTxMsg.len);
    
    	err = bt_nus_send(m_current_conn, (const uint8_t *) m_NusTxMsg.data, m_NusTxMsg.len);
    	if (err)
    	{
    		LOG_ERROR("Failed to send data to Central over BLE connection (err %d)\n", err);
    	}
    	else
    	{
    #if 1 /* @enprevo - used to enable print of outgoing messages */
    		LOG_INFO("Sent CMD[%d] Timestamp[%lld] lenPacket[%d] d0[%d] d1[%d] d%d[%d]\n",
    				 pBleMsgOut->bleCmdId,
    				 pBleMsgOut->timestamp,
    				 m_NusTxMsg.len,
    				 pBleMsgOut->dataPack[0],
    				 pBleMsgOut->dataPack[1],
    				 pBleMsgOut->len-1,
    				 pBleMsgOut->dataPack[pBleMsgOut->len-1]);
    #endif
    	}
    	return err;
    };



    ----------------------------------------------


    I usually use posix to get high compatibility between RTOS, and so I'm using GetTime syscall to handle the events in my application, without using hardware timers, if it's your answer... Smiley

    it seems that you are currently intending to have the TIMER CC event trigger a sampling by the CPU, is that correct?
  • Hello again,

    enpre said:
    2. How many channels will you be sampling each time?

    For now, 1 channel.

    In that case, you could use the internal SAADC timer to perform these measurements.
    In this case, you will not need to set up a PPI channel to perform the measurements, and you also do not have to leave the HFCLK running constantly, which will save you a lot of power.

    enpre said:
    Many of them has to piclk up data from an ADC every 1ms and send it over BLE.

    Obviously, I pack them in a 100sample before sending them to the central.

    That sounds like a good way to do it!
    Just make sure that your peripheral devices have a buffer that is big enough to store the necessary amount of data in the case of a poor connection (high packet loss) or a disconnection.

    Best regards,
    Karl

Reply
  • Hello again,

    enpre said:
    2. How many channels will you be sampling each time?

    For now, 1 channel.

    In that case, you could use the internal SAADC timer to perform these measurements.
    In this case, you will not need to set up a PPI channel to perform the measurements, and you also do not have to leave the HFCLK running constantly, which will save you a lot of power.

    enpre said:
    Many of them has to piclk up data from an ADC every 1ms and send it over BLE.

    Obviously, I pack them in a 100sample before sending them to the central.

    That sounds like a good way to do it!
    Just make sure that your peripheral devices have a buffer that is big enough to store the necessary amount of data in the case of a poor connection (high packet loss) or a disconnection.

    Best regards,
    Karl

Children
Related