This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nRF52832 SPI tagging for multi-channel ADC?

Hi,

I have successfully connected an MCP3004 to the nRF52832 and can assign the ADC channel 0 output to a characteristic value. Works beautifully.

However, there are three other channels I want to use on the ADC for other sensors and the method to query the ADC over the SPI bus uses a bitmap to select the channel in the initial query.

The problem is the SPI event that occurs.

I presently will not know which channel is being returned in a given event.

Is there a method to tag the initial transfer with some indicator of which channel is being requested? Perhaps some other clever way?

Maybe having different rx buffers and a map of which buffer pertains to which channel?

Thanks!

  • Hi,

    Are you using the SPI master driver in the SDK? Your solution of using multiple rx buffers might work, but you will need some way to know which buffer contain valid data. If you are OK with reading "old data", you can setup a rx buffer for each channel and update the characteristics from all channels on each SPI callback, but this will generate additional work for the CPU.

    An alternative is to use the p_context pointer to signalize to the event handler which channel is in use. Unfortunately this pointer is set on the interface initialization, and not on the execution of a transfer. You will then have to uninit and initialize the SPI interface for each transfer. In SDK 13 you can also use the SPI transaction manager, which can take a pointer, p_user_data as argument, and pass this to the event handler. This takes advantage of the p_context pointer in the SPI master driver, and will automatically uninit and init the SPI interface if the parameters change.

    Are you starting multiple transaction simultaneously, or use EasyDMA with the SPI peripheral? If not, you can simply set a flag indicating which channel is read and check this in the SPI event handler.

    Best regards,

    Jørgen

  • Thanks for the response. I was able to get a solution that I will put below for others to maybe use if they have a similar problem.

    I am using SPIM and Easy-DMA. I have some instability in the read ADC values and I am not sure if that's my poor electronics or something else. I was, however, able to use an oscilloscope on the four SPI channels and witness proper SPI correspondence between master and slave. Thus, this is fabulous.

    image description

    I am using SDK 11, not sure why actually, but it's working for me, so I'll stick to it.

    The design I am using makes an array of uint8_t elements that is the (number of channels being used * length of channels). I have two functions (rx_adc_buffer_channel and rx_adc_buffer_address) that can give the array offset given a channel number and its inverse function: given an address return the channel number. This allows me to pass the proper channel rx_buffer to the timer event for the SPI MOSI event and then get the channel after the SPI transfer event or MOSI data.

    I found I couldn't make two writes back-to-back in one timer interval, but this could just be confusion on my part and I should revisit it. However, I don't require an aggressive sample rate so I interleave the channels during ADC read events and this is working.

    I'm just going to paste the code fragments here: (Formatting is terrible; sorry)

    #define ADC_CHANNEL_COUNT               2
    #define ADC_RX_BUFFER_LENGTH            3
    
    static uint8_t*                         rx_adc_channels;
    static uint8_t                          m_channel_toggle;
    static uint16_t                         adc_char_handle_arr[ADC_CHANNEL_COUNT];
    
    
    static uint8_t* rx_adc_buffer_address(uint8_t channel_id) {
        int offset;
        uint8_t* return_addr;
    
        offset = sizeof(uint8_t);
        offset *= ADC_RX_BUFFER_LENGTH;
        offset *= channel_id;
    
        return_addr = rx_adc_channels + offset;
    
        return return_addr;
    }
    
    
    static uint8_t rx_adc_buffer_channel(uint8_t* address) {
        uint8_t channel;
    
        channel = (address - rx_adc_channels) / ADC_RX_BUFFER_LENGTH;
    
        return channel;
    }
    
    
    /* ADC MCP3004 */
    
    static void read_adc(uint8_t* counter) {
        uint8_t tx_length = 3;
        uint8_t rx_length = ADC_RX_BUFFER_LENGTH;
        uint8_t channel;
        uint8_t channel_bitmap;
    
        m_channel_toggle++;
    
        channel = m_channel_toggle % ADC_CHANNEL_COUNT;
       /* Sorry I don't know the escape characters for less than so bitshift left is two lt signs.*/
        channel_bitmap = 128 | (channel (bitshift left) 4);
    
        /* Set SPI Channel */
        m_tx_buf[0] = 1;
        m_tx_buf[1] = channel_bitmap;
        m_tx_buf[2] = 0;
    
        APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, m_tx_buf, tx_length,
            rx_adc_buffer_address(channel), rx_length));
    }
    
    
    void spi_event_handler(nrf_drv_spi_evt_t const * p_event)
    {
        int _size;
        uint8_t _channel;
        uint16_t _handle;
    
        if (p_event->data.done.rx_length >= 3 ) {
    
            _channel = rx_adc_buffer_channel(p_event->data.done.p_rx_buffer);
            _handle = adc_char_handle_arr[_channel];
    
            rx_adc_buffer_channel(p_event->data.done.p_rx_buffer);
            m_adc_channel0 = (*(p_event->data.done.p_rx_buffer + 2) * 256 ) +
                *(p_event->data.done.p_rx_buffer + 1);
    
            if (m_conn_handle != BLE_CONN_HANDLE_INVALID) {
                counter_attr_value.len = 0x02;
                counter_attr_value.offset = 0x00;
                counter_attr_value.p_value = (uint8_t*)&m_adc_channel0;
                sd_ble_gatts_value_set(m_conn_handle, _handle, &counter_attr_value);
             }
        }
    }
    
    
    static void rx_adc_channels_init(void) {
        int size;
    
        size = sizeof(uint8_t) * ADC_CHANNEL_COUNT * ADC_RX_BUFFER_LENGTH;
        rx_adc_channels = (uint8_t*)malloc(size);
        memset(rx_adc_channels, 0, size);
    }
    
  • Hi,

    Is it possible to put a link to the entire code. I am trying to communicate with the MCP3008 in a similar way but it still doesn't seem to work.

    Kind regards

Related