Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A/D continuous parameter calculation (IDFGH-14459) #15233

Open
3 tasks done
zamek42 opened this issue Jan 19, 2025 · 1 comment
Open
3 tasks done

A/D continuous parameter calculation (IDFGH-14459) #15233

zamek42 opened this issue Jan 19, 2025 · 1 comment
Labels
Status: Opened Issue is new

Comments

@zamek42
Copy link

zamek42 commented Jan 19, 2025

Answers checklist.

  • I have read the documentation ESP-IDF Programming Guide and the issue is not addressed there.
  • I have updated my IDF branch (master or release) to the latest version and checked that the issue is present there.
  • I have searched the issue tracker for a similar issue and not found a similar issue.

General issue report

Hi All,
I need help to understand continuous ad configuration of ESP IDF.

I want to digitize 6 analog inputs with esp idf. Each channel should take a sample for 20ms in every 100us, it is 200 samples per channel.
The calculation process:

  1. frame size: number of channels * number of samples * size of adc_digi_output_data_t. ( 6 * 200 * 2 = 2400 )
  2. sample frequency: 10000 samples/seconds * number of channels ( 10000 * 6 = 60Khz )
    I used the adc continuous sample application for the test.
  3. I set the dma buffer size to 2 times the frame_size.
  4. I turned on the AD log level to DEBUG, but I didn't see any log messages from ad driver.
  5. I set the s_on_pool_ovf function to my own implementation, which only incremented a variable if a buffer overflow occurred.

In ad_init I print the ad parameters:
I (680) ad: CHANNEL_NUMS:6, SAMPLES_PER_CHANNEL:200, sizeof(adc_digi_output_data_t):2, FRAME_SIZE:2400, sample freq:60000

I change a gpio pin in the ad DMA interrupt, so I can check the interrupt frequency with an oscilloscope, which is strangely 25ms instead of the desired 20ms.

I haven't processed the incoming data yet, I just calculated the number of data per channel in an array in each cycle.
The result of this was very strange (elen is the read_len from adc_continuous_read ):
A/D cycles:3697, timeouts:0, size errors:0, invalid channels:0, ovf:0, elen:2400
ch0:1400 ch1:199 ch2:199 ch3:200 ch4:201 ch5:201

In total, there are 2400 samples, but 1400 of them are on the first channel, while the other channels each have 200 samples.
When I tried to set frame size to number of samples instead of bytes, I get the similar results, ch0 is 7 times larger than others:

I (679) ad: CHANNEL_NUMS:6, SAMPLES_PER_CHANNEL:200, sizeof(adc_digi_output_data_t):2, FRAME_SIZE:1200, sample freq:60000
`

A/D cycles:3911, timeouts:0, size errors:0, invalid channels:0, ovf:0, elen:1200 `

ch0:700 ch1:99 ch2:99 ch3:100 ch4:101 ch5:101
here is the code:

`

  #include <stdbool.h>
  #include <stdint.h>
  #include <string.h>
  #include <stdio.h>
  #include <strings.h>
  #include <sys/stat.h>
  #include "esp_attr.h"
  #include "esp_err.h"
  #include "esp_console.h"
  #include "argtable3/argtable3.h"
  #include "freertos/projdefs.h"
  #include "hal/adc_types.h"
  #include "portmacro.h"
  #include "esp_log.h"
  #include "freertos/task.h"
  #include "freertos/semphr.h"
  #include "esp_adc/adc_continuous.h"
  #include "driver/gpio.h"
  #include "ad.h"

  #undef LOG_LOCAL_LEVEL
  #define LOG_LOCAL_LEVEL ESP_LOG_INFO
  #define TAG "ad"

  #define ADC_ATTEN             (ADC_ATTEN_DB_12)
  #define ADC_BIT_WIDTH         (SOC_ADC_DIGI_MAX_BITWIDTH)
  #define AD_MAX_VALUE		  ((1)<<(ADC_BIT_WIDTH))
  #define MOVING_AVG_QUEUE_SIZE (10)

  #define SCOPE_PIN  (16)

  static adc_channel_t channels[] = {//       						DEVKIT
                                    ADC_CHANNEL_0,				//L1 voltage	GPIO36			PIN3
                                    ADC_CHANNEL_3,				//L1 current	GPIO39			PIN4
                                    ADC_CHANNEL_6,				//L2 voltage	GPIO34			PIN5
                                    ADC_CHANNEL_7,				//L2 current	GPIO35			PIN6
                                    ADC_CHANNEL_4,				//L3 voltage	GPIO32 			PIN7
                                    ADC_CHANNEL_5				//L3 current	GPIO33			PIN8 
                                    };

  #define SAMPLE_INTERVAL_US  (100)
  #define SAMPLES_PER_CHANNEL (200)	//200
  #define CHANNEL_NUMS (sizeof(channels)/sizeof(adc_channel_t))			//6
  #define FRAME_SIZE ((CHANNEL_NUMS)*(SAMPLES_PER_CHANNEL)*sizeof(adc_digi_output_data_t))      //6*2*200=2400
  #define SAMPLE_FREQUENCY    (1000000/(SAMPLE_INTERVAL_US))*(CHANNEL_NUMS) // 60Khz
  #define DMA_BUFFER_SIZE     ((FRAME_SIZE)*2) 

  typedef struct {
    uint32_t cycles;
    uint32_t timeouts;
    uint32_t size_errors;
    uint32_t invalid_channels;
    uint32_t overflow;
    } statistic_t;


  static TaskHandle_t s_task_handle;
  static adc_continuous_handle_t ad_handle = NULL;
  static statistic_t statistics;
  static uint32_t elen=0;
  static adc_digi_output_data_t raw_data[FRAME_SIZE];
  static uint32_t samples_per_channels[CHANNEL_NUMS];
  static bool pin_state=false;

  static void register_cmd();

  static inline int get_channel_idx(uint8_t ch) {
    for(int i=0;i<CHANNEL_NUMS;i++)
    if (channels[i]==ch)
    return i;
  statistics.invalid_channels++;
  return -1;	
  }

  static bool IRAM_ATTR s_on_pool_ovf(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *user_data) {
    BaseType_t mustYield = pdFALSE;
    statistics.overflow++;
    //Notify that ADC continuous driver has done enough number of conversions
    return (mustYield == pdTRUE);
  }

  static bool IRAM_ATTR s_conv_done_cb(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *user_data) {	
    BaseType_t mustYield = pdFALSE;
    gpio_set_level(SCOPE_PIN, pin_state=!pin_state);
    elen=edata->size;
    if (edata->size==FRAME_SIZE) {
      ++statistics.cycles;		
      vTaskNotifyGiveFromISR(s_task_handle, &mustYield);
      }
    else 
    ++statistics.size_errors;		
    return (mustYield == pdTRUE);
  }
    
    static void continuous_adc_init() { 
      adc_continuous_handle_cfg_t adc_config = {
        .conv_frame_size =FRAME_SIZE,     
        .max_store_buf_size = DMA_BUFFER_SIZE,
        .flags.flush_pool=0
        };
      
      ESP_ERROR_CHECK(adc_continuous_new_handle(&adc_config, &ad_handle));
      
      adc_digi_pattern_config_t adc_pattern[CHANNEL_NUMS];
      
      for (int i = 0; i < CHANNEL_NUMS; i++) {
        adc_pattern[i].atten = ADC_ATTEN;
        adc_pattern[i].channel = channels[i];
        adc_pattern[i].unit = ADC_UNIT_1;
        adc_pattern[i].bit_width = ADC_BIT_WIDTH;
        
        ESP_LOGI(TAG, "adc_pattern[%d].unit is :%"PRIx8", channel:%"PRIu8", atten:%"PRIu8", bitwidth:%"PRIu8, 
                i, adc_pattern[i].unit, adc_pattern[i].channel, adc_pattern[i].atten, adc_pattern[i].bit_width);
        }
        
        adc_continuous_config_t dig_cfg = {
          .sample_freq_hz = SAMPLE_FREQUENCY,
          .conv_mode = ADC_CONV_SINGLE_UNIT_1,
          .format = ADC_DIGI_OUTPUT_FORMAT_TYPE1,
          .pattern_num=CHANNEL_NUMS,
          .adc_pattern=adc_pattern
          };    
        ESP_ERROR_CHECK(adc_continuous_config(ad_handle, &dig_cfg));
        
        adc_continuous_evt_cbs_t cbs = {
          .on_conv_done = s_conv_done_cb,
          .on_pool_ovf=s_on_pool_ovf,
          };
        
        ESP_ERROR_CHECK(adc_continuous_register_event_callbacks(ad_handle, &cbs, NULL));    
        ESP_ERROR_CHECK(adc_continuous_start(ad_handle));
        ESP_LOGI(TAG, "CHANNEL_NUMS:%d, SAMPLES_PER_CHANNEL:%d, sizeof(adc_digi_output_data_t):%d, FRAME_SIZE:%d, sample freq:%d",
                CHANNEL_NUMS, SAMPLES_PER_CHANNEL, sizeof(adc_digi_output_data_t), FRAME_SIZE, SAMPLE_FREQUENCY);
        }
        
        static void fn_ad(void *p) {
          uint32_t read_len=0;
          int ch; 
          
          continuous_adc_init();
          for (;;) {
            if (ulTaskNotifyTake(pdTRUE, portMAX_DELAY)!=pdPASS) {
              continue;
            }
            
            esp_err_t res=adc_continuous_read(ad_handle, (uint8_t *)raw_data, 
                                              sizeof(raw_data), &read_len, portMAX_DELAY);
            
            if (res==ESP_OK) {
              bzero(samples_per_channels, sizeof(samples_per_channels));	  	    
              for (int i=0;i<FRAME_SIZE;i++) {
                if ((ch=get_channel_idx(raw_data[i].type1.channel))!=-1)
                ++samples_per_channels[ch];
                else
                ++statistics.invalid_channels;
                }
              }
              else 
              ++statistics.size_errors;
              
              continue;
            }
              ESP_ERROR_CHECK(adc_continuous_stop(ad_handle));
              ESP_ERROR_CHECK(adc_continuous_deinit(ad_handle));
              }
              
              BaseType_t ad_init(void) {
                esp_log_level_set(TAG, LOG_LOCAL_LEVEL);
                esp_log_level_set("ADC", ESP_LOG_DEBUG);  
                ESP_LOGD(TAG, "Enter AD init");
                
                register_cmd();
                
                gpio_config_t gpio_cfg = {
                  .intr_type=GPIO_INTR_DISABLE,
                  .mode=GPIO_MODE_OUTPUT,
                  .pin_bit_mask=(1<<SCOPE_PIN),
                  .pull_up_en=0,
                  .pull_down_en=0,
                  };
                gpio_config(&gpio_cfg);
                return (xTaskCreate(fn_ad, "ad", 4096, 
                                    NULL, uxTaskPriorityGet(NULL), &s_task_handle));   
              }
              
              BaseType_t ad_deinit() {
                return pdPASS;
              }
              
              #define CMD "ad" 
              
              struct {
                arg_int_t *phase;	
                arg_end_t *end;
                } args;
              
              static void help() {
                arg_print_syntax(stdout, (void **)&args, "\n");
                arg_print_glossary(stdout, (void **)&args, "%-25s %s\n");
                }
              
              static void print_stat() {
                printf("A/D cycles:%"PRIu32", timeouts:%"PRIu32", size errors:%"PRIu32", invalid channels:%"PRIu32", ovf:%"PRIu32", elen:%"PRIu32"\n", 
                      statistics.cycles, statistics.timeouts, statistics.size_errors, statistics.invalid_channels, statistics.overflow,elen);
                for (int i=0;i<CHANNEL_NUMS;i++) 
                printf("ch%d:%"PRIu32" ", i, samples_per_channels[i]);
                printf("\n");	
                }
                
                static int fn_cmd(int argc, char **argv) {
                  int err=arg_parse(argc, argv, (void **) &args);
                  if (err!=0) {
                    help();
                    return 0;
                  }  
                  
                  print_stat();  
                  return 0;
                }
                  
                  static void register_cmd() {
                    args.phase = arg_int0("pP", "phase", "<n>", "phase index 1 up to 3");
                    args.end=arg_end(0);
                    const esp_console_cmd_t cmd = {
                      .command=CMD,
                      .help="",
                      .hint="",
                      .func=&fn_cmd
                      };
                    
                    ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));  
                    }

`

@espressif-bot espressif-bot added the Status: Opened Issue is new label Jan 19, 2025
@github-actions github-actions bot changed the title A/D continuous parameter calculation A/D continuous parameter calculation (IDFGH-14459) Jan 19, 2025
@zamek42
Copy link
Author

zamek42 commented Jan 19, 2025

couple of hours later: when I don't use ADC_CHANNEL_0 it works well. Seems to be this channel has some special priority, or is it bug?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Opened Issue is new
Projects
None yet
Development

No branches or pull requests

2 participants