Hi,
I am trying to read samples from an external ADC at 100kHz sampling rate using the SPIM peripheral. I want to take a window of 2000 samples then can pause for processing. It is important that the sampling rate is always 100kHz and that I don't miss any samples.
The ADC samples are 2 bytes and are read after toggling the ADC's conversion start pin (CNVST) low. I am switching the CNVST pin low every 10us using PPI, Timer and GPIOTE. This works reliably and I am able to get samples periodically by reading the data in the SPI buffer in the SPI interrupt handler like below. After I reach 2000 samples (ADC_QUE_SIZE) I stop and process the data.
void spi_event_handler(nrf_drv_spi_evt_t const * p_event, void * p_context) { if(count==ADC_QUE_SIZE) { spi_sampling_event_disable(); spi_xfer_done = true; } else { adc[count++] = ((uint16_t)(m_rx_buf[0]<<8) | m_rx_buf[1]); } }
The method above requires CPU intervention in the interrupt handler, the problem is when I have an active Bluetooth connection this interrupt handler can get delayed and I miss samples by the time the SPI reads can resume.
I am trying to implement a sampling method using an array list as discussed in this this post. My idea is to have 2000* 2-byte buffers that will be sequentially filled without requiring the CPU but I am not getting any data. It is my understanding that that the max SPI buffer is 155 bytes, but can I have more than this number of buffers in the arrayList for EasyDMA?
Here is my code trying to do this, adapted from this answer
#define ADC_QUE_SIZE 2000 typedef struct ArrayList { uint8_t rx_buffer[2]; }ArrayList_type; static ArrayList_type p_rx_buffer[ADC_QUE_SIZE]; void spi_config(void) { NRF_SPIM0->RXD.MAXCNT = 2; NRF_SPIM0->RXD.PTR = (int32_t) &p_rx_buffer; NRF_SPIM0->RXD.LIST = SPIM_RXD_LIST_LIST_ArrayList; nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG; spi_config.mode = NRF_DRV_SPI_MODE_1; spi_config.frequency = NRF_DRV_SPI_FREQ_8M; spi_config.ss_pin = NRF_DRV_SPI_PIN_NOT_USED; spi_config.miso_pin = SPI_MISO_PIN; spi_config.mosi_pin = NRF_DRV_SPI_PIN_NOT_USED; spi_config.sck_pin = SPI_SCK_PIN; APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, NULL, NULL)); memset(p_rx_buffer, 0, sizeof(p_rx_buffer)); nrf_drv_spi_xfer_desc_t xfer = NRF_DRV_SPI_XFER_TRX(m_tx_buf, m_length, (uint8_t*)p_rx_buffer, 2); uint32_t flags = NRF_DRV_SPI_FLAG_HOLD_XFER | NRF_DRV_SPI_FLAG_REPEATED_XFER| NRF_DRV_SPI_FLAG_RX_POSTINC | NRF_DRV_SPI_FLAG_NO_XFER_EVT_HANDLER; ret_code_t ret = nrf_drv_spi_xfer(&spi, &xfer, flags); } void timer_count_handler(nrf_timer_event_t event_type, void * p_context) { switch (event_type) { case NRF_TIMER_EVENT_COMPARE0: spi_sampling_event_disable(); spi_xfer_done = true; break; default: //Do nothing. break; } } void spi_sampling_event_init(void) { ret_code_t err_code; nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true); err_code = nrf_drv_gpiote_out_init(GPIO_OUTPUT_PIN_NUMBER, &config); // Fs = 100kHz: cc_value = 159. With interrupt disabled (false) nrf_drv_timer_extended_compare(&timer_adc, NRF_TIMER_CC_CHANNEL0, 159, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false); // Compare event after 2000 samples with interrupt enabled (true) nrf_drv_timer_extended_compare(&timer_count, NRF_TIMER_CC_CHANNEL0, 2000, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true); uint32_t gpiote_task_addr = nrf_drv_gpiote_out_task_addr_get(GPIO_OUTPUT_PIN_NUMBER); uint32_t spi_start_task_addr = nrf_drv_spi_start_task_get(&spi); uint32_t spi_end_evt_addr = nrf_drv_spi_end_event_get(&spi); uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&timer_adc, NRF_TIMER_CC_CHANNEL0); uint32_t timer_count_task = nrf_drv_timer_task_address_get(&timer_count, NRF_TIMER_TASK_COUNT); uint32_t timer_cc_event = nrf_drv_timer_event_address_get(&timer_count, NRF_TIMER_EVENT_COMPARE0); err_code = nrf_drv_ppi_channel_alloc(&ppi_channel_1); err_code = nrf_drv_ppi_channel_assign(ppi_channel_1, timer_compare_event_addr, gpiote_task_addr); err_code = nrf_drv_ppi_channel_fork_assign(ppi_channel_1, spi_start_task_addr); err_code = nrf_drv_ppi_channel_alloc(&ppi_channel_2); err_code = nrf_drv_ppi_channel_assign(ppi_channel_2, spi_end_evt_addr, gpiote_task_addr); err_code = nrf_drv_ppi_channel_fork_assign(ppi_channel_2, timer_count_task); } void spi_sampling_event_enable(void) { ret_code_t err_code; nrf_drv_gpiote_out_task_enable(GPIO_OUTPUT_PIN_NUMBER); err_code = nrf_drv_ppi_channel_enable(ppi_channel_1); err_code = nrf_drv_ppi_channel_enable(ppi_channel_2); nrf_drv_timer_enable(&timer_adc); nrf_drv_timer_enable(&timer_count); // Configure short between spi end event and spi start task nrf_spim_shorts_enable(spi.u.spim.p_reg, NRF_SPIM_SHORT_END_START_MASK); nrf_spim_task_trigger(spi.u.spim.p_reg, NRF_SPIM_TASK_START); } void spi_sampling_event_disable(void) { ret_code_t err_code; nrf_drv_gpiote_out_task_disable(GPIO_OUTPUT_PIN_NUMBER); err_code = nrf_drv_ppi_channel_disable(ppi_channel_1); err_code = nrf_drv_ppi_channel_disable(ppi_channel_2); nrf_drv_timer_disable(&timer_adc); nrf_drv_timer_disable(&timer_count); // Configure short between spi end event and spi start task nrf_spim_shorts_disable(spi.u.spim.p_reg, NRF_SPIM_SHORT_END_START_MASK); }
The data in rx_buffer is never written to and stays 0, from the memset to 0 in the spi_config function.
It would be great if someone could point me toward some similar examples as I find the documentation on EasyDMA array lists quite cryptic.
Many thanks,
Daragh
Specs:
nRF52832QFAA on custom PCB interfacing with MAX11198 ADC (16bit)
Segger Embedded Studio v6.30 with nRF5_SDK_17.1.0