6 Replies Latest reply on Aug 22, 2017 11:17 AM by user_365962704

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

    user_365962704

      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

        • 1. Re: 4M: Multi channel ADC + DMA, is this the most efficient way to do it?
          vsrs

          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

          • 2. Re: 4M: Multi channel ADC + DMA, is this the most efficient way to do it?
            rlos

            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

            1 of 1 people found this helpful
            • 3. Re: 4M: Multi channel ADC + DMA, is this the most efficient way to do it?
              user_365962704

              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

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

                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

                1 of 1 people found this helpful
                • 5. Re: 4M: Multi channel ADC + DMA, is this the most efficient way to do it?
                  user_365962704

                  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

                  • 6. Re: 4M: Multi channel ADC + DMA, is this the most efficient way to do it?
                    user_365962704

                    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