Hello,
I need to sample 2 saadc channels at 6Khz, and another saadc channel at approx 1/sec for a battery measurement. Which are configured for channels 0,1 and 6 respectively.
Ive looked around the dev zone, and the following posts( here and here), indicate that the best way to do this is to disable the battery channel (chan 6) and enable it after a certain number if intervals. I did notice however, that these posts were from over 6 years ago on different hardware.
See my implementation for below questions:
/* API for reading MFTM ADC Channels - battery is measured 1/sec - Force sensors are measured 6000Hz */ #include <logging/log.h> #include "mftm_adc_init.h" #include "msg_nrf_sensors.h" #include "msg_npb.h" #include "msg_pkt.h" #include "mftm_adc_init.h" #include "mftm_gpio_init.h" #include <zephyr.h> #include <helpers/nrfx_gppi.h> #include <nrfx_dppi.h> #include <nrfx_saadc.h> #include <nrfx_timer.h> #include <logging/log.h> #define SAMPLE_RATE 33300UL //33ms sample rate for 3 samples in buffer will give us approx 100ms interrupt frequency #define BUFFER_SIZE (3) // we will set this to 9 for now to get an idea of what is going on // we want to sample the battery 1/sec. at 100ms interrupts 10 iterations gives us 1hz sampling for battery #define BATTERY_ADC_NUM_ITERATIONS (10) int16_t saadc_buf1[BUFFER_SIZE]; int16_t saadc_buf2[BUFFER_SIZE]; nrfx_timer_t timer1 = NRFX_TIMER_INSTANCE(1); //timer instance static uint32_t chan_bit_mask = 0; LOG_MODULE_DECLARE(nrf_sensors); uint8_t channel; static int channels_to_read = ADC_NUM_CHANNELS; // actual number of channles we detect static bool send_force_raw = false; //guard for sending force measurments once stop command has been sent to fsm static int8_t channel_ids[ADC_NUM_CHANNELS] = { CFG0, CFG1, CFG2, CFG3, CFG4, CFG5, CFG6, CFG7}; static int8_t chan_inputs[ADC_NUM_CHANNELS] = { ADC_0TH_CHANNEL_INPUT, ADC_1ST_CHANNEL_INPUT, ADC_2ND_CHANNEL_INPUT, ADC_3RD_CHANNEL_INPUT, ADC_4TH_CHANNEL_INPUT, ADC_5TH_CHANNEL_INPUT, ADC_6TH_CHANNEL_INPUT, ADC_7TH_CHANNEL_INPUT}; void init_bat_chan(){ nrfx_err_t err; nrfx_saadc_channel_t saadc_channel = NRFX_SAADC_DEFAULT_CHANNEL_SE(chan_inputs[6], 6); err = nrfx_saadc_channel_config(&saadc_channel); if(err != NRFX_SUCCESS) { LOG_ERR("init_chan_cfgs(). Could not configure SAADC channels: %d", err); } } void disable_bat_chan(){ nrfx_err_t err; // chan_bit_mask ^= (1 << 6); err = nrfx_saadc_channels_deconfig((1 <<6)); if(err!=NRFX_SUCCESS){ LOG_ERR("unit_bat_chan(). not able to unitalize bat channel: %d ",err); } } void saadc_evt_handler(nrfx_saadc_evt_t const * p_event) { static int32_t sample_adc_count = 0; nrfx_err_t err = NRFX_SUCCESS; static uint8_t counter = 0; uint16_t raw_val =0; if(p_event->type == NRFX_SAADC_EVT_DONE) { if (!ready_to_send) { return; } sample_adc_count++; //display data in buffer for (uint8_t i = 0; i < p_event->data.done.size; i++) { raw_val = p_event->data.done.p_buffer[i]; // should be a singe 16 bit adc value if(raw_val > 0){ LOG_INF("samp = %d, idx =%d ",raw_val, i); } } //*** send adc buffer to fifo from &p_event->data.done.p_buffer*** //initalize bat channel if(sample_adc_count == BATTERY_ADC_NUM_ITERATIONS) { //reset adc access count sample_adc_count = 0; //*** send bat messages from p_event->data.done.p_buffer[2] //disable channel 2 NRF_SAADC->CH[6].PSELP = 0; // method 1 // disable_bat_chan(); // method 2 LOG_INF("Disabling channel 6"); } if(sample_adc_count == (BATTERY_ADC_NUM_ITERATIONS -1)){ //enable channmel 2 NRF_SAADC->CH[(6].PSELP = 1; // method 1 // enable_bat_chan(); // method 2 LOG_INF("Enabling channel 6"); } } else if(p_event->type == NRFX_SAADC_EVT_CALIBRATEDONE) { LOG_INF("SAADC calibrated."); } else if(p_event->type == NRFX_SAADC_EVT_BUF_REQ) { counter++; if(counter%2) { err = nrfx_saadc_buffer_set(saadc_buf1, BUFFER_SIZE); if(err != NRFX_SUCCESS) { LOG_ERR("Error! Could not set buffer2: %d", err); } } else { err = nrfx_saadc_buffer_set(saadc_buf2, BUFFER_SIZE); if(err != NRFX_SUCCESS) { LOG_ERR("Error! Could not set buffer1: %d", err); } } } else if(p_event->type == NRFX_SAADC_EVT_FINISHED) { LOG_INF("SAADC finished sampling"); } } void timer1_evt_handler(nrf_timer_event_t event_type, void * p_context) //called when timer is triggered { // Nothing to do here, the timer's IRQ is not enabled } void timer_init(void) { nrfx_err_t err = NRFX_SUCCESS; nrfx_timer_config_t timer_cfg = NRFX_TIMER_DEFAULT_CONFIG; timer_cfg.frequency = NRF_TIMER_FREQ_16MHz; timer_cfg.mode = NRF_TIMER_MODE_TIMER; timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32; timer_cfg.interrupt_priority = NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY; err = nrfx_timer_init(&timer1, &timer_cfg, timer1_evt_handler); uint32_t time_ticks; time_ticks = nrfx_timer_us_to_ticks(&timer1, SAMPLE_RATE); if(err != NRFX_SUCCESS) { LOG_ERR("Error! Could not initialize TIMER1: %d", err); } nrfx_timer_extended_compare(&timer1, NRF_TIMER_CC_CHANNEL0, time_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false); } uint32_t init_chan_cfgs(){ int8_t chan_idx =0; int err; for (uint8_t i = 0; i <= ADC_NUM_CHANNELS; i++) { chan_idx = channel_ids[i]; if (chan_idx >= 0){ nrfx_saadc_channel_t saadc_channel = NRFX_SAADC_DEFAULT_CHANNEL_SE(chan_inputs[chan_idx], chan_idx); err = nrfx_saadc_channel_config(&saadc_channel); if(err != NRFX_SUCCESS) { LOG_ERR("init_chan_cfgs(). Could not configure SAADC channels: %d", err); } chan_bit_mask |= (1 << chan_idx); }else{ channels_to_read--; // decrement number of channels to read with every failure of this if statment } } return chan_bit_mask; } void saadc_init(void) { nrfx_err_t err = NRFX_SUCCESS; nrfx_saadc_adv_config_t saadc_adv_cfg = NRFX_SAADC_DEFAULT_ADV_CONFIG; saadc_adv_cfg.start_on_end = true; // saadc_adv_cfg.low_power_mode =true; err = nrfx_saadc_init(IRQ_PRIO_LOWEST); // err = nrfx_saadc_init(4); if(err != NRFX_SUCCESS) { LOG_ERR("Error! Could not initialize SAADC: %d", err); } err = nrfx_saadc_offset_calibrate(NULL); if(err != NRFX_SUCCESS) { LOG_ERR("Error! Could not calibrate offset: %d", err); } // set up ADC channels chan_bit_mask = init_chan_cfgs(); err = nrfx_saadc_advanced_mode_set(chan_bit_mask, NRF_SAADC_RESOLUTION_12BIT, &saadc_adv_cfg, saadc_evt_handler); // err = nrfx_saadc_advanced_mode_set((1<<6), NRF_SAADC_RESOLUTION_12BIT, &saadc_adv_cfg, saadc_evt_handler); if(err != NRFX_SUCCESS) { LOG_ERR("Error! Could not set advanced SAADC mode: %d", err); } err = nrfx_saadc_buffer_set(saadc_buf1, BUFFER_SIZE); if(err != NRFX_SUCCESS) { LOG_ERR("Error! Could not set buffer1: %d", err); } err = nrfx_saadc_buffer_set(saadc_buf2, BUFFER_SIZE); if(err != NRFX_SUCCESS) { LOG_ERR("Error! Could not set buffer2: %d", err); } err = nrfx_saadc_mode_trigger(); if(err != NRFX_SUCCESS) { LOG_ERR("Error! Could trigger mode: %d", err); } } int mftm_adc_channels_init(void) { LOG_INF("nrfx_saadc sample on %s", CONFIG_BOARD); nrfx_err_t err; /* Connect SAADC IRQ to nrfx_saadc_irq_handler */ IRQ_CONNECT(SAADC_IRQn, IRQ_PRIO_LOWEST, nrfx_isr, nrfx_saadc_irq_handler, 0); // IRQ_CONNECT(SAADC_IRQn, 4, nrfx_isr, nrfx_saadc_irq_handler, 0); timer_init(); saadc_init(); // Allocate a DPPI channel // uint8_t channel; err = nrfx_dppi_channel_alloc(&channel); if (err != NRFX_SUCCESS) { LOG_ERR("(D)PPI channel allocation error: %08x", err); return; } /* Configure endpoints of the channel so that the TIMER1 CAPTURE0 * event is connected with the SAADC SAMPLE task. This means that each time * TIMER1 reaches it's set compare value, the SAADC will sample all * enabled channel once. */ nrfx_gppi_channel_endpoints_setup(channel, nrfx_timer_event_address_get(&timer1, NRF_TIMER_EVENT_COMPARE0), nrf_saadc_task_address_get(NRF_SAADC, NRF_SAADC_TASK_SAMPLE)); return channels_to_read; } void start_mftm_adc(bool send_force){ /* Trigger offset calibration * As this generates a _DONE and _RESULT event * the first result will be incorrect. */ send_force_raw = send_force; int err; // NRF_SAADC->TASKS_CALIBRATEOFFSET = 1; if(!timer_running){ NRF_SAADC->ENABLE = 1; nrfx_timer_enable(&timer1); /* Enable DPPI channel. */ err = nrfx_dppi_channel_enable(channel); if (err != NRFX_SUCCESS) { LOG_ERR("Failed to enable (D)PPI channel, error: %08x", err); return; } timer_running = true; } } void stop_mftm_adc(){ //kill the timer to stop sample measurments if(timer_running){ nrfx_timer_disable(&timer1); nrfx_dppi_channel_disable(channel); NRF_SAADC->ENABLE = 0; timer_running = false; } }
Is it possible to disable the channel measurement in-between saadc event calls as the above posts imply?
To test the different output data rate functionality, I slowed down the sample rate to 33ms, and set the samples in buffer to 3 with a counter up to 10 (that is incremented ever NRFX_SAADC_EVT_DONE event) and then set to zero once the battery channel has been enabled (see line 158). So with this test I would expect the channel 6 to only be printed out into the buffer 1/sec. Where channels 0,1 should be printed out very time.
My problem is, channel 6 is never being disabled. Demonstrated by the samples in the buffer never changing their values ( which I tested by logging the buffer values over rtt).
If tried the following methods which were recommended in the above posts to disable and enable the channel on the fly (assuming this is possible):
Method 1: toggling the PSELP register once the counter reaches 10:
void disable_bat_chan(){ nrfx_err_t err; // chan_bit_mask ^= (1 << 6); err = nrfx_saadc_channels_deconfig((1 <<6)); if(err!=NRFX_SUCCESS){ LOG_ERR("unit_bat_chan(). not able to unitalize bat channel: %d ",err); } }
Any help would be appreciated.
Kind regards,
Matt