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
