Измерение времени выполнения команды (в цикле)

// определение времени выполнения программного блока Ардуино

unsigned int  timerValue; // значение таймера
 
void setup() {
  Serial.begin(9600);  // инициализируем последовательный порт, скорость 9600
  // установки таймера 1
  TCCR1A = 0;
  TCCR1B = 0; 
}

void loop() {
  noInterrupts(); // запрет прерываний
  TCNT1H = 0; // сброс таймера
  TCNT1L = 0;
  TCCR1B = 1; // разрешение работы таймера

  // ---------- исследуемый программный блок ---------

  int s = analogRead(A0);
  // -------------------------------------------------

  TCCR1B = 0; // остановка таймера
  timerValue = (unsigned int)TCNT1L | ((unsigned int)TCNT1H << 8); // чтение таймера
  interrupts(); // разрешение прерываний
     
  // вывод на компьютер
  Serial.print( (float)(timerValue - 2) * 0.0625);
  Serial.println(" mks");
  delay(500);
}

Результат wokwi:

Спойлер


105.00 mks
105.00 mks
105.00 mks
105.00 mks
105.00 mks

Реальная Ардуино Нано:

Спойлер
106.25 mks
106.87 mks
108.44 mks
109.37 mks
109.06 mks
106.56 mks
109.69 mks
108.44 mks
111.56 mks
109.06 mks
106.25 mks
108.75 mks
110.94 mks
111.56 mks
108.75 mks
109.06 mks
107.81 mks
108.44 mks
110.31 mks
109.37 mks
108.12 mks
112.81 mks
108.75 mks
110.00 mks
105.62 mks



LGT8F328 частота 32 делитель ADCSRA 16

А код где?

Код из комментария # 21. Я ничего не менял.

А как Вы делитель в 16 установили? Не в коде? А где?

Извините я все уже перепутал, сейчас просто с attiny85 начал заниматься т.к. это проект пока зашел в тупик, т.к. 2кб ОЗУ мне не хватает что бы загрузить туда достаточно данных для анализа частот от 50Гц до 100Кгц.

Вот актуальные сприншоты сейчас сделал



Абсолютно не понял что Вы хотели сказать.

А скриншоты мне не по глазам. Хотите показать код – выкладывайте нормально.

Но смотреть буду только завтра. Сегодня я уже кончился.

Как ты собираешься их анализировать? Что хочешь получить после анализа?

Я хочу отобразить RMS (среднеквадратичное отклонение за период измерений) PkPk(среднее и максимальное значение пик ту пик), среднее напряжение на выходе и частоту пульсаций (буду определять по переходу ноля). Это нужно для мониторинга напряжения и пульсаций на выходе блока питания. В общем такой мини осцилограф.

Для этого я буду запихивать в массив результаты замеров и потом их анализировать.
Онид раз в секунду будет выполнена непрерывная серия измерений и результаты записаны в буфер, затем обработаны и выведены на экран.

При постоянной частоте замеров 1\40us:

Минимальная измеряемая частота будет определяться размером буфера, нужно что бы в буфер влезло хотя бы 2 периода, тесть если мне нужна минимальная измеряемая частота пульсаций 25гц то при периоде замеров 40us нужен буфер на 0.4 сек, а это 975 элементов uint16_t = 1951 байт, а памяти всего 2048, причем половину занимают библиотеки вывода на экран.

Максимальная частота ограничена периодом измерений и будет 1/0.00004 = всего 25Кгц. Используя работу с регистрами вместо analogRead() удалось получить период замеров около 2us а это уже 500Кгц… но тгда минимальная частота вырастет…


[details="Спойлер"]
//#include <GyverLBUF.h>
#include <TimerMs.h>
#include <GyverOLED.h>

uint32_t t1;
uint32_t t2;
uint16_t t3;

TimerMs tmr_v_capture(128, 1, 0);
TimerMs tmr_heartbeat_13(1000, 1, 0);
TimerMs tmr_display_v_now(100, 1, 0);
TimerMs tmr_display_v_rms(1000, 1, 0);


//GyverOLED<SSD1306_128x32> oled(0x3C);

uint16_t V_buf_length = 300;
uint16_t V_buf[300];
float analogReference_k = 4.096;
uint16_t analogReadResolution_k = 4095;
float v_divider_k = 7.657;                  // R1=1811 R2=12170  k = R1+R2/R1 = 7.720  (эксперементально = 7.652)
float v_divider_Vpkpk_k = 1.0;                // Vpk-pk correction
float v_divider_Vrms_k= 1.0;

const uint8_t bitmap_32x32[] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xC0, 0xE0, 0xF0, 0x70, 0x70, 0x30, 0x30, 0x30, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF0, 0x70, 0x30, 0x30, 0x20, 0x00, 0x00,
  0x00, 0x30, 0x78, 0xFC, 0x7F, 0x3F, 0x0F, 0x0F, 0x1F, 0x3C, 0x78, 0xF0, 0xE0, 0xC0, 0x80, 0x80, 0x80, 0x40, 0xE0, 0xF0, 0xF8, 0xFC, 0xFF, 0x7F, 0x33, 0x13, 0x1E, 0x1C, 0x1C, 0x0E, 0x07, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF9, 0xF7, 0xEF, 0x5F, 0x3F, 0x7F, 0xFE, 0xFD, 0xFB, 0xF1, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x1E, 0x33, 0x33, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x1F, 0x0E, 0x04, 0x00, 0x00, 0x00, 0x00,
};

    uint32_t V_buf_calibrate_value = 0;
    uint16_t V_buf_calibrate_cicles = 10;
    float    V_buf_calibrate_desc_period = 0;
    uint32_t V_buf_summ = 0;
    int16_t  V_buf_medium = 0;
    int16_t  V_buf_element_deviation = 0; // при int16_t  V_buf_element_deviation_sq = V_buf_element_deviation * V_buf_element_deviation; дает переполнение
    float    V_buf_element_deviation_sq = 0;
    uint16_t V_buf_element_deviation_max = 0;
    uint32_t V_buf_element_deviation_max_sq = 0;
    float    V_buf_elements_deviation_sqr_sum = 0;
    int16_t  V_buf_element_deviation_pre = 0;
    float    V_buf_rms = 0.0;
    uint16_t V_buf_zero_cross = 0;
    uint16_t V_buf_half_period_start = 0;
    uint16_t V_buf_half_period_end = 0;
    uint16_t V_buf_period_i = 0; // период в количестве тактов дисткретизации
    uint16_t V_buf_period_i_meas_count = 0; // кол-во замеров периода (сколько раз измерялся период в буфере)
    float    V_buf_period_1_meas = 0.0;
    float    V_buf_period_sum = 0.0;
    float    V_buf_freq_median = 0.0;
    float    V_buf_medium_volt = 0.0;

/*
  //  TYPE - указанный при инициализации тип данных
      void write(TYPE newVal);        // добавить в буфер
      void write(int num, TYPE val);  // запись в буфер по номеру num
      TYPE read(int num);             // чтение из буфера
      int size();                     // размер буфера
  */

void meas_start(){
  t1 = micros();
}
void meas_stop(){
  t2 = micros();
   Serial.print(" / ");
   Serial.print(t2-t1);
   Serial.println(" / ");
}

void setup() {
  analogReference(INTERNAL4V096);     // внктренне опорное АЦП
  analogReadResolution(12);           // DAC resolution
  DDRB = 0b00100000;                  // pinMode(PIN_A13, OUTPUT);
  PORTB ^= (1<<PORTB5);               // led bit inverter
  ADCSRA = ADCSRA & 0b11111000;       // установить делитель ADC. Очичтить младшие 3 бита в регистре
  ADCSRA = ADCSRA | 0b00000100;       // Установить младшие 3 бита = x8 (011) x16 (100) x32 (101) x64 (110)
  pinMode(PIN_A0, INPUT);
  tmr_v_capture.setPeriodMode();
  //tmr_v_capture.setMicros(true);
  tmr_display_v_now.setPeriodMode();
  tmr_display_v_rms.setPeriodMode();
  Serial.begin(9600);
  /*
  oled.init();
  oled.setContrast(180);
  oled.setScale(3);
  oled.clear();
  oled.update();
  */
  //////////////////// Calibrate //////////////////////
    Serial.println("Calibrate...");
    for (int j=0; j < V_buf_calibrate_cicles; j++) {
    t1 = micros();  
      for (int i=0; i < V_buf_length; i++){
      V_buf[i] = analogRead(A0);
      }
    t2 = micros();
    V_buf_calibrate_value += t2-t1;
    Serial.print(" Время заполнени буфера нарастающим итогом по тестовым запускам (мкс)");
    Serial.println(V_buf_calibrate_value);
    }
    V_buf_calibrate_value  = V_buf_calibrate_value / V_buf_calibrate_cicles; // Ширина окна выборки (мкс) из V_buf_length выборок - это время заполнения массива данных
    V_buf_calibrate_desc_period = (float)V_buf_calibrate_value / V_buf_length; // Время между измерениями АЦП в мкс это период дискретизации
    Serial.print(" Среднее время заполнени буфера (мкс) ");
    Serial.println(V_buf_calibrate_value);
    Serial.print(" Период дискретизации (мкс) ");
    Serial.println(V_buf_calibrate_desc_period);
    Serial.print(" Длинна буфера (ед) ");
    Serial.println(V_buf_length);
    Serial.print(" Размер буфера (байт) ");
    Serial.println(sizeof(V_buf));
    Serial.print(" Минимальная частота измерения (Гц) ");
    Serial.println((float)1 / V_buf_calibrate_value * 1000000 * 2);
    Serial.print(" Максимальная частота измерения (Гц) ");
    Serial.println((float)1 / V_buf_calibrate_desc_period * 1000000 / 2);
  //////////////////////////////////////////////////////

  Serial.println();
  Serial.println("Starting...");
}

void loop() {

  if (tmr_heartbeat_13.tick()) {
    PORTB ^= (1<<PORTB5);
  }

  if (tmr_v_capture.tick()) {
    noInterrupts();
    for (int i=0; i < V_buf_length; i++){
    V_buf[i] = analogRead(A0);
    }
    interrupts();
  }

  if (tmr_display_v_now.tick()) {
    //noInterrupts();
    V_buf_summ = 0;
    for (int i=0; i < V_buf_length; i++) {
      V_buf_summ = V_buf_summ + V_buf[i];
    }
    V_buf_medium = V_buf_summ / V_buf_length;
    V_buf_medium_volt = (V_buf_medium * analogReference_k) / analogReadResolution_k * v_divider_k;
    //interrupts();
    /*
    oled.setScale(1);
    oled.home();
    oled.print("V=");
    oled.print(V_buf_medium_volt, 3);
    oled.update();
    */

    /*
    Serial.print(V_buf_medium_volt, 3);
    Serial.print(" === ");
    Serial.println(V_buf_medium);
    */


  }

  if (tmr_display_v_rms.tick()) {

    Serial.println("===========================");
    

    V_buf_summ = 0;
    V_buf_rms = 0;
    V_buf_element_deviation = 0;
    V_buf_element_deviation_pre = 0;
    V_buf_element_deviation_sq = 0;
    V_buf_element_deviation_max = 0;
    V_buf_element_deviation_max_sq = 0;
    V_buf_elements_deviation_sqr_sum = 0;
    V_buf_period_i_meas_count = 0;
    V_buf_period_sum= 0;
    V_buf_half_period_start = 0;
    V_buf_half_period_end = 0;

    bool V_buf_start_is_found = 0; // флаг нахождения начала периода, что бы не найти первым окончание

    //noInterrupts();
  
    for (int i=0; i < V_buf_length; i++) {
      //Serial.println(V_buf[i]);
      
      V_buf_element_deviation = V_buf_medium - V_buf[i]; // отклонение от срднего текущей точки

       
      
      if (V_buf_element_deviation < 0 && V_buf_element_deviation_pre > 0) {
        //Serial.println("Period START");
        V_buf_half_period_start = i; // пересечение ноля на подъеме из - в + по оси Х
        V_buf_start_is_found = 1; // флаг нахождения начала периода, что бы не найти первым окончание
        }

      if (V_buf_element_deviation > 0 && V_buf_element_deviation_pre < 0 && V_buf_start_is_found) {
        //Serial.println("Period END");
        V_buf_half_period_end = i; // пересечение ноля на спаде из + в - по оси Х
        V_buf_period_i = (V_buf_half_period_end - V_buf_half_period_start) * 2; // период в количестве тактов дисткретизации
        V_buf_period_i_meas_count++; // кол-во событий пересечения (полуволна) это кол-во измерений периода которое получилось в буфере
        V_buf_period_1_meas = V_buf_period_i * V_buf_calibrate_desc_period; // Период по результатам одного события пересечения (одна полуволна) в мкс = период в тактах дискретизации * время одного такта дискретизации
        V_buf_period_sum += V_buf_period_1_meas;               // Сумма значений замеров частоты (мкс)
        V_buf_freq_median = 1.0 / ((V_buf_period_sum/V_buf_period_i_meas_count) / 1000000);
        //V_buf_freq_median = V_buf_period_sum/V_buf_period_i_meas_count;
        }


            
      V_buf_element_deviation_pre = V_buf_element_deviation;
      V_buf_element_deviation_sq = V_buf_element_deviation * V_buf_element_deviation;      // квадрат отклонения элемента
      V_buf_elements_deviation_sqr_sum = V_buf_elements_deviation_sqr_sum + V_buf_element_deviation_sq;  // сумма квадратов отклонения элементов

      /*
      Serial.print(V_buf_element_deviation_sq);
      Serial.print("   ");
      Serial.print(V_buf_element_deviation);
      Serial.print("   ");
      Serial.print(V_buf_medium);
      Serial.print("   ");
      Serial.println(V_buf[i]);
      */
  
  
      
      
      if (V_buf_element_deviation_sq > V_buf_element_deviation_max_sq){
          V_buf_element_deviation_max_sq = V_buf_element_deviation_sq;    // нахождение максимума квадрата отклонения
        } 
      
      /*
      Serial.print(i);
      Serial.print(" = ");
      Serial.print(V_buf[i]);
      Serial.print(" = ");
      Serial.println(V_buf_freq_median);
      
      /*
      Serial.print(" = ");
      Serial.print(V_buf_period_i);
      Serial.print(" = ");
      Serial.print(V_buf_half_period_start);
      Serial.print(" = ");
      Serial.println(V_buf_half_period_end);
      */
    
    }
    //interrupts();
    
    V_buf_element_deviation_max = sqrt(V_buf_element_deviation_max_sq);
    V_buf_rms = sqrt(V_buf_elements_deviation_sqr_sum / (float)V_buf_length);
    float V_buf_rms_volt = (V_buf_rms * analogReference_k) / analogReadResolution_k * v_divider_Vrms_k;
    float V_buf_element_deviation_max_volt = ((float)V_buf_element_deviation_max * analogReference_k) * 2 / analogReadResolution_k * v_divider_Vpkpk_k;



    Serial.print(V_buf_medium_volt, 3);
    Serial.print("    ");
    Serial.print(V_buf_rms_volt, 3);
    Serial.print("    ");
    Serial.print(V_buf_element_deviation_max_volt, 3);
    Serial.print("    ");
    Serial.println(V_buf_freq_median, 3);

    
/*

    oled.setScale(1);
    oled.setCursor(0, 1);
    oled.print("Vrms=");
    oled.print(V_buf_rms_volt, 3);
    oled.setCursor(0, 2);
    oled.print("Vpkpk=");
    oled.print(V_buf_element_deviation_max_volt, 3);
    oled.update();
*/
  }
}
[/details]

В процессе я понял что пока это мне не по силам, код работает но плохо, думаю он ужасен. Сейчас хочу сделать просто вольт-ампер-ватметр на attiny85 что бы в процессе получить больше опыта работы с этими штуками.

Ну вообще то среднеквадратичное отклонение - сигма - это совсем не RMS. В любом случае что бы всё что ты хочешь посчитать посчиталось, надо собрать данные в массив, возводить в квадрат, суммировать, брать квадратный корень, Бегать по массиву данных и массивам промежуточных расчётов. Это я к тому что тинька не справится. Скорости и памяти не хватит. Даже на простой 50 Гц RMS вольтметр.

Я так и подумал. Поэтому заказал у китайцев какую то штуку всего за 250 руб - ESP32-DOWDQ6-V3 dual-core 32bit MCU. По характеристикам просто зверь)).

А пока развлекаюсь c attiny85. как оказалось такому нубу как мне даже её надолго хватит поиграться.

Да вы правы RMS там ещё от формы волны зависит. Я собирался прото среднеквадратичное считать.

Кстити атмега328 высчитывает среднеквадратичное из массива uint16_t array[256] очень быстро, помоему около 1 ms, а вот вывод на экран 128х32 занимает 70ms… но я так понял я уперся в скорость I2С и нужно найти экран с каким нибудь параллельным интерфейсом.

Это абсолютно нереальная цифра. Как вам в теме про Тини ответил Евгений, реальная цифра измерения примерно в 50 раз больше

Да я совсем забыл, пока не разбирирался с этой конструкцией, как разберусь напишу реальное время.

  t1=micros();
  do{ ADCSRA |= (1 << ADSC); }
  while ((ADCSRA & (1 << ADIF)) == 0);
  V_capture = (ADCL|ADCH << 8);
  t2=micros();

Его не обязательно измерять, его можно просто вычислить. 13 циклов частоты АДС, для второго и последующих измерений, 25 для первого.

Ну тогда печаль, выходит если частота 125 КГц этим ацп маскимум 5кгц можно измерить. И то это 2 измерения за период, по переходу через ноль частоту установить не получится, только по пикам измерять.

Это есть в даташите - стоило сразу посмотреть, чтоб не было напрасных ожиданий
Да и вообще как-то нелогично пытаться померить сигнал 500 МГц с помощью АЦП, тактовая которого 125 КГц :slight_smile:

Да я вообще все это придумал потому что китайские вольтамперметры жутко тормозят, обновление экрана 1 раз в секунду. Я сначала просто хотел что бы при вращении ручки напряжения на БП на экране напяжение плавно отображалось а не скачками.

Про измерение пульсаций это я уже потом придумал.

Ну ничего страшного, значит будем измерять просто среднеквадратичное без частоты. Да и в моем линейном БП остовная частота пульсаций 50Гц или 100Гц

Мне 500Мгц не нужно, мне 100Кгц вполне хватит, я просто не знал что он на таком большом делителе только нормально работает.

Берите СТМ32, там вы даже на самых дешевых платах получите частоту АДС в мегагерцы… если осилите :slight_smile: