4M: Multi channel ADC + DMA, is this the most efficient way to do it?

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
lock attach
Attachments are accessible only for community members.
cadi_1014291
Level 6
Level 6
25 likes received 10 likes received 10 likes given

Hi,

I have a test project were i use the ADC SAR Seq available in the 4200M, i need to read three inputs, i decided to transfer the results of the conversions from the ADC to memory with DMA.

Each ADC channel have a specific register to save the conversion result, so as i have 3 inputs i needed three DMA channels to transfer the result of each input to memory, then trigger an interrupt on one DMA channel and do the conversion from counts to voltage, like so:

adc_dma.png

This is the main loop:

#include "project.h"

#include <stdio.h>

#include <stdbool.h>

volatile bool data_ready = false;

enum {

    ADC_CHANNEL_0 = 0,

    ADC_CHANNEL_1 = 1,

    ADC_CHANNEL_2 = 2,

};

enum {

    FORM_FEED_CMD = 0x0C,

};

struct adc_result {

    char    string[20];

    float32 volts;

    int16_t counts;

} adc_result;

CY_ISR_PROTO(DMA_Handler);

int main(void)

{

   

    struct adc_result ADC_Data[ADC_SEQUENCED_CHANNELS_NUM];

    size_t string_size = sizeof(ADC_Data[ADC_CHANNEL_0]) / sizeof(char);

    DMA_Chn0_SetInterruptCallback(&DMA_Handler);

    CyIntEnable(CYDMA_INTR_NUMBER);

   

    CyGlobalIntEnable;

    UART_Start();

    UART_PutString("Test ADC -> DMA -> memory\r\n");

   

    PWM_Start();

    ADC_Start();

    DMA_Chn0_Start((void*) ADC_SAR_CHAN0_RESULT_PTR, &ADC_Data[ADC_CHANNEL_0].counts);

    DMA_Chn1_Start((void*) ADC_SAR_CHAN1_RESULT_PTR, &ADC_Data[ADC_CHANNEL_1].counts);

    DMA_Chn2_Start((void*) ADC_SAR_CHAN2_RESULT_PTR, &ADC_Data[ADC_CHANNEL_2].counts);

   

    ADC_StartConvert();

   

    while (1) {

       

        if ( true == data_ready ) {

           

            UART_PutChar(FORM_FEED_CMD);

           

            for (uint8_t channel = 0; channel < ADC_SEQUENCED_CHANNELS_NUM; channel++) {

                ADC_Data[channel].volts = ADC_CountsTo_Volts(channel, ADC_Data[channel].counts);

                snprintf(ADC_Data[channel].string, string_size, "CH %d: %.3f Volts\r\n", channel, ADC_Data[channel].volts);

                UART_PutString(ADC_Data[channel].string);

            }

           

            data_ready = false;

        }

       

    }

}

CY_ISR(DMA_Handler)

{

    data_ready = true;

    CyDmaClearInterruptSource(DMA_Chn0_CHANNEL_MASK);

}

/* [] END OF FILE */

The test project seems to work but is it the most efficient way to implement it?

May be a waste of DMA channels but i will try to port this project to the 4L family and send the ADC conversion results to the PC via USB, so i need to keep the infinite loop of the main function almost empty for USB transactions i guess (haven't did a USB project before ).

Any advice or feedback?

The project is attached, i used Creator 4.1

Carlos

PD: The new forum seems great

0 Likes
1 Solution
RodolfoGL
Employee
Employee
250 solutions authored 250 sign-ins 5 comments on KBA

Hi,

You could use a single DMA channel. The ADC results are sequentially placed in the memory. If you create an array with all the results, you can configure your DMA to increment the source and destination addresses after every transfer. Here is the summary:

- Source Address = SAR_CHAN_RESULT0;

- Destination Address = &MyResults[0]; // Declared as uint32 MyResults[3];

Regards,

Rodolfo Lossio

View solution in original post

6 Replies
Vasanth
Moderator
Moderator
Moderator
250 sign-ins 500 solutions authored First question asked

Hey,

Using 3 channels out of the 32 available channels does not look like much of a wastage. As each channel has only 2 TD, this looks like a more elegant solution. Do check the example codes available on Creator before using USB, File -> Code Examples.

Best Regards,
VSRS

0 Likes
RodolfoGL
Employee
Employee
250 solutions authored 250 sign-ins 5 comments on KBA

Hi,

You could use a single DMA channel. The ADC results are sequentially placed in the memory. If you create an array with all the results, you can configure your DMA to increment the source and destination addresses after every transfer. Here is the summary:

- Source Address = SAR_CHAN_RESULT0;

- Destination Address = &MyResults[0]; // Declared as uint32 MyResults[3];

Regards,

Rodolfo Lossio

Hi,

Thanks for the reply, i will try that and upload the project when i got it working.

I guess the destination address can be declared as uint16_t and configure the DMA to transfer from Word -> HalfWord.

Carlos

0 Likes

Hi Carlos,

Yes, you can. Note that the the most significant bits from the SAR_CHAN_RESULT contains some useful information (maybe not for your application). It can tell:

- If the result is valid

- If the result is in range (based on the ADC limits defined in the component customizer)

- If the result is saturated

If you transfer from Word -> Halfword, you would loose that information.

Regards,

Rodolfo Lossio

lock attach
Attachments are accessible only for community members.

rlos wrote:

Hi Carlos,

Yes, you can. Note that the the most significant bits from the SAR_CHAN_RESULT contains some useful information (maybe not for your application). It can tell:

- If the result is valid

- If the result is in range (based on the ADC limits defined in the component customizer)

- If the result is saturated

If you transfer from Word -> Halfword, you would loose that information.

Regards,

Rodolfo Lossio

Hi,

Actually i didn't knew that, i will need to take that in mind. I will update the project taking that "flags" to check for valid conversions.

Here's what i have so far:

adc_dma.png

The DMA Configuration:

adc_dma_config.png

And the main.c:

#include "project.h"

#include <stdio.h>

#include <stdbool.h>

volatile bool data_ready = false;

enum {

    FORM_FEED_CMD = 0x0C,

};

struct adc_result {

    char    string[20];

    float32 volts[ADC_SEQUENCED_CHANNELS_NUM];

    int16_t counts[ADC_SEQUENCED_CHANNELS_NUM];

} adc_result;

CY_ISR_PROTO(DMA_Handler);

int main(void)

{

   

    struct adc_result ADC_Data;

    size_t string_size = sizeof(ADC_Data.string) / sizeof(char);

    DMA_ADC_SetInterruptCallback(&DMA_Handler);

    CyIntEnable(CYDMA_INTR_NUMBER);

   

    CyGlobalIntEnable;

    UART_Start();

    UART_PutString("Test ADC -> DMA -> memory\r\n");

   

    PWM_Start();

    ADC_Start();

    DMA_ADC_Start((void*) ADC_SAR_CHAN0_RESULT_PTR, &ADC_Data.counts);

   

    ADC_StartConvert();

   

    while (1) {

       

        if ( true == data_ready ) {

           

            UART_PutChar(FORM_FEED_CMD);

           

            for (uint8_t channel = 0; channel < ADC_SEQUENCED_CHANNELS_NUM; channel++) {

                ADC_Data.volts[channel] = ADC_CountsTo_Volts(channel, ADC_Data.counts[channel]);

                snprintf(ADC_Data.string, string_size, "CH %d: %.3f Volts\r\n", channel, ADC_Data.volts[channel]);

                UART_PutString(ADC_Data.string);

            }

           

            data_ready = false;

        }

       

    }

}

CY_ISR(DMA_Handler)

{

    data_ready = true;

    CyDmaClearInterruptSource(DMA_ADC_CHANNEL_MASK);

}

The project is attached, will update it with the "latest" info about the ADC conversion flags

Regards

Carlos

0 Likes

rlos wrote:

Hi Carlos,

Yes, you can. Note that the the most significant bits from the SAR_CHAN_RESULT contains some useful information (maybe not for your application). It can tell:

- If the result is valid

- If the result is in range (based on the ADC limits defined in the component customizer)

- If the result is saturated

If you transfer from Word -> Halfword, you would loose that information.

Regards,

Rodolfo Lossio

Just found the documentation about that on the ADC SAR Seq Datasheet 

adc_result_register.png

Just one more question, i was saving the ADC conversion results in a signed 16bits variable (int16_t), if i wanted to include the flag bits should i use a signed or unsigned 32bits variable? i think an unsigned 32bit variable should be the right one, but i'm not sure.

Regards

Carlos