Interfacing with MAX11198 dual simultaneous ADC over SPI

Hi,

I am trying to interface with the MAX11198 simultaneous dual ADC over SPI. The ADC has two channels and I am able to read values from one channel at a time using the nRF52832 DK as a SPI master. The ADC has one CLK pin, one CNVST pin (conversion start, acts as chip select) and 2 DOUT pins. I have set the conversion rate to 100kHz using a timer and PPI to toggle the CNVST at this rate. I am reading 2000 samples from the ADC then stoppping sampling to do some processing of the data. To do the repeated SPI master read I use:

ret_code_t ret_0 = nrf_drv_spi_xfer(&spi0, &xfer_0, NRF_DRV_SPI_FLAG_HOLD_XFER | NRF_DRV_SPI_FLAG_REPEATED_XFER ); 

To read from both DOUT pins at once I am trying to use a SPI slave instance with its CLK and CS pins physically tied to CLK and CNVST of the SPI master instance. However, I cannot figure out how to set up repeated slave SPI reads like can be done with nrf_drv_spi_xfer() function and the NRF_DRV_SPI_FLAG_REPEATED_XFER flag.

I'm open to any suggestions on better ways to accomplish this or if anyone has used this part before with a Nordic MCU.

Here are relevant functions from my project:

void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
                       void *                    p_context)
{
    if(count0==ADC_QUE_SIZE)
    {
      spi_sampling_event_disable();
      spi_xfer_done = true;
    }
    else
    {
      adc0[count0++] = ((uint16_t)(m_rx_buf_0[0]<<8) | m_rx_buf_0[1]);
    }
}

void spis_event_handler(nrf_drv_spis_event_t event)
{
    if(event.evt_type == NRF_DRV_SPIS_XFER_DONE)
    {
        adc1[count1++] = ((uint16_t)(m_rx_buf_1[0]<<8) | m_rx_buf_1[1]);
      
        memset(m_rx_buf_1, 0, m_length);

        nrf_drv_spis_buffers_set(&spis, m_tx_buf, m_length, m_rx_buf_1, m_length);
    }
}

void spi_config(void)
{
    // Instance 0 config
    nrf_drv_spi_config_t spi_config_0 = NRF_DRV_SPI_DEFAULT_CONFIG;

    spi_config_0.mode      = NRF_DRV_SPI_MODE_1;
    spi_config_0.frequency = NRF_DRV_SPI_FREQ_8M;
    spi_config_0.ss_pin    = NRF_DRV_SPI_PIN_NOT_USED;
    spi_config_0.miso_pin  = SPI_MISO_PIN;
    spi_config_0.mosi_pin  = NRF_DRV_SPI_PIN_NOT_USED;
    spi_config_0.sck_pin   = SPI_SCK_PIN;

    APP_ERROR_CHECK(nrf_drv_spi_init(&spi0, &spi_config_0, spi_event_handler, NULL)); 
      
    memset(m_rx_buf_0, 0, m_length);

    nrf_drv_spi_xfer_desc_t xfer_0 = NRF_DRV_SPI_XFER_TRX(m_tx_buf, m_length, m_rx_buf_0, m_length);

    ret_code_t ret_0 = nrf_drv_spi_xfer(&spi0, &xfer_0, NRF_DRV_SPI_FLAG_HOLD_XFER | NRF_DRV_SPI_FLAG_REPEATED_XFER ); 

  
    // Instance 1 config    
    nrf_drv_spis_config_t spis_config = NRF_DRV_SPIS_DEFAULT_CONFIG;
   
    spis_config.mode      = NRF_DRV_SPI_MODE_1;
    spis_config.csn_pin   = APP_SPIS_CS_PIN;
    spis_config.miso_pin  = NRFX_SPIS_PIN_NOT_USED;
    spis_config.mosi_pin  = APP_SPIS_MOSI_PIN;
    spis_config.sck_pin   = APP_SPIS_SCK_PIN;
    
    APP_ERROR_CHECK(nrf_drv_spis_init(&spis, &spis_config, spis_event_handler));
      
    memset(m_rx_buf_1, 0, m_length);

    nrf_drv_spis_buffers_set(&spis, m_tx_buf_1, m_length, m_rx_buf_1, m_length);
}

void spi_sampling_event_init(void)
{
    ret_code_t err_code;
    
    nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);  // Initialize high
    
    err_code = nrf_drv_gpiote_out_init(GPIO_OUTPUT_PIN_NUMBER, &config); //CNVST
    
    // 159 = 100kHz
    nrf_drv_timer_extended_compare(&timer, NRF_TIMER_CC_CHANNEL0, 159, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);

    uint32_t gpiote_task_addr = nrf_drv_gpiote_out_task_addr_get(GPIO_OUTPUT_PIN_NUMBER); 

    uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&timer,NRF_TIMER_CC_CHANNEL0);
    
    uint32_t spi_start_task_addr   = nrf_drv_spi_start_task_get(&spi0);
    uint32_t spi_end_evt_addr = nrf_drv_spi_end_event_get(&spi0);

    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);
}

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);
}

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);
}

I am developing on the nRF52832 dev kit with Segger Embedded Studio v6.30 using SDK v17.1.0.

Thanks,

Daragh

Parents Reply Children
  • I like the idea, but don't have any example that can help you (I couldn't find any similar api for spis).

    It's not quite clear to me how this looks like on a physical interface, how you toggle CSN and how many bytes are sent between each CSN toggle. If it's only 1byte read, then I am afraid that this may be missed by SPIS due it require some time between CSN and first clock (especially if you are also in sleep using WFE at any time here).

    Kenneth

Related