Частотомер на arduino

Тема в разделе "Arduino & Shields", создана пользователем Nekto_nikto, 1 авг 2020.

  1. Nekto_nikto

    Nekto_nikto Гик

    Всем привет.
    Возникла необходимость измерить частоту сигнала генератора, чтоб убедиться что она соответствует требуемой. Т.к. понимаю что бывает такое явление, что частота гуляет со временем, решил сохранять измеренные значения в массиве из 200 элементов, после чего выводить полученные значения в порт. А т.к. не уверен в уровне напряжения сигнала и есть возможность превышения уровня, то решил этот самый массив сохранять в EEPROM ардуины, откуда потом его выковыривать, путем ввода Go в мониторе порта, после чего собственно смотреть весь массив полученных значений.
    написал программу
    Код (C++):
    #include <EEPROM.h>
    /*
      Чтоб не насиловать себе мозг с ассемблером, можно использовать следующий код для измерения времери работы нашего кода считывающего значение таймера
      код брал здесь http://mypractic-forum.ru/viewtopic.php?t=17
      // определение времени выполнения программного блока Ардуино
    */

    /*
    unsigned int  timerValue;   // значение таймера

    // мои переменные
    unsigned int Period[200];  // Массив, в котором будут храниться отловленные значения
    unsigned int TimerWas=0;   // Предидущее отловленное значение таймера
    unsigned int TimerVal=0;   // Значение таймера
    unsigned int Decrement=0;  // Количество циклов, тратящееся на обработку кода по извлечению значения таймера
    unsigned int tmp_TCNT1=0;   // Временная переменная, где будет храниться считанное значение TCNT1
    int i=0;
    void setup() {
      Serial.begin(9600);  // инициализируем последовательный порт, скорость 9600
      // установки таймера 1
      TCCR1A = 0;
      TCCR1B = 0;
    }

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

      // ---------- исследуемый программный блок ---------
     
    tmp_TCNT1=TCNT1;
    TimerVal=65534-(tmp_TCNT1+TimerWas);  // Если мы просто напишем TimerVal=tmp_TCNT1-TimerWas; то как минимум сразу после переполнения таймера при расчетах будут возникать неточности
                                          // а если использовать ветвление при расчетах, то это увеличит время исполнения данного куска кода и как следствие уменьшит точность
    TimerWas=tmp_TCNT1;
    TimerVal=TimerVal-Decrement;          // вычитаем из значения таймера, время исполнения этого кода
    Period[i]=TimerVal;
    i++;
    if(i>199)
      {
      asm("cli"); // Запрещаем прерывания
      TCCR1B=0;   // Выключаем таймер1
      // Пишем весь массив в энергонезависимую память EEPROM, т.к. при непосредственном подключении к ПК есть вероятность повредить компьютер
      // поэтому доставать значения будем после отключения Arduino, посылая через COM порт команду на чтение
      for(i=0; i<=398; i=i+2)                 //пишем массив в EEPROM
        {
        EEPROM.put((int)i/2, Period[(int)i/2]);
        }

      // сигналим светодиодом на плате, что запись произведена и можно подключать к ПК и смотреть что получилось
      while(true)
        {
        digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
        delay(500);                       // wait for a second
        digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
        delay(500);
        }
      }

      // -------------------------------------------------

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

    */

    ///*
    // Тестирую на частоте 40КГц
    unsigned int Period[200];  // Массив, в котором будут храниться отловленные значения
    unsigned int TimerWas=0;   // Предидущее отловленное значение таймера
    unsigned int TimerVal=0;   // Значение таймера
    unsigned int Decrement=50; // Количество циклов, тратящееся на обработку кода по извлечению значения таймера
    unsigned int tmp_TCNT1=0;   // Временная переменная, где будет храниться считанное значение TCNT1
    int i=0;
    String S="";               // Переменная передаваемая через COM порт
    void Int0() // Функция прерывания для ножки МК D2
    {
    // Как читать регистр таймера TCNT1 показано на 116 странице ДШ
    tmp_TCNT1=TCNT1;
    TimerVal=65534-(tmp_TCNT1+TimerWas);  // Если мы просто напишем TimerVal=tmp_TCNT1-TimerWas; то как минимум сразу после переполнения таймера при расчетах будут возникать неточности
                                          // а если использовать ветвление при расчетах, то это увеличит время исполнения данного куска кода и как следствие уменьшит точность
    TimerWas=tmp_TCNT1;
    TimerVal=TimerVal-Decrement;          // вычитаем из значения таймера, время исполнения этого кода
    Period[i]=TimerVal;
    i++;
    if(i>199)
      {
      asm("cli"); // Запрещаем прерывания
      TCCR1B=0;   // Выключаем таймер1
      // Пишем весь массив в энергонезависимую память EEPROM, т.к. при непосредственном подключении к ПК есть вероятность повредить компьютер
      // поэтому доставать значения будем после отключения Arduino, посылая через COM порт команду на чтение
      for(i=0; i<=398; i=i+2)                 //пишем массив в EEPROM
        {
        EEPROM.put((int)i/2, Period[(int)i/2]);
        }

      // сигналим светодиодом на плате, что запись произведена и можно подключать к ПК и смотреть что получилось
      while(true)
        {
        digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
        delay(500);                       // wait for a second
        digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
        delay(500);
        }
      }
    }

    void setup()
    {
      // Конфигурируем нужные ножки МК
      Serial.begin(9600);                 // подключаем последовательный порт
      pinMode(2, INPUT_PULLUP);           // конфигурируем ножку D2
      attachInterrupt(0, Int0, RISING);   // назначаем обработчик прерывания для ножки D2
      pinMode(3, OUTPUT);                 // конфигурируем ножку D3
      digitalWrite(3, HIGH);              // подаем на ножку D3 высокий логический уровень, чтоб открыть транзистор на оптореле
      pinMode(LED_BUILTIN, OUTPUT);       // конфигурируем ножку, отвечающую за встроенный светодиод на плате

      for(i=0; i<=199; i++)                 // Инициализируем массив значений
        {
        Period[i]=0;
        }
      i=0;

      // инициализируем Timer1
      noInterrupts(); // запрещаем прерывания
      TCNT1H = 0;
      TCNT1L = 0;
      TCCR1A = 0;
      TCNT1  = 0;
      TCCR1B |= (1 << CS10); // Prescaler=1 No prescaling частота 16MHz
    interrupts(); // Разрешаем прерывания

    }


    void loop()
    {
      if (Serial.available()) // проверяем, поступают ли какие-то команды на мониторе порта
      {
        S = Serial.readString();  // !!! Читаем строку из терминала
        if(S=="Go")
          {
          asm("cli"); // Запрещаем прерывания
          TCCR1B=0;   // Выключаем таймер1
          for(i=0; i<=398; i=i+2)   //читаем массив из EEPROOM
            {
            EEPROM.get((int)i/2, Period[(int)i/2]);
            }
         
          for(i=0;i<=199;i++)     // выводим массив в терминал через COM порт
            {
            Serial.println((String)i+"\t"+(String)((float)(Period[i] * 0.0625))+" mks  => F="+(String)((float)(1000/(Period[i] * 0.0625)))+" KHz");
            }
          }
      }
    }
    //*/
     
     
  2. Nekto_nikto

    Nekto_nikto Гик

    После чего решил проверить свой "частотомер" с помощью другой arduino в качестве источника сигнала с достаточно простым кодом
    Код (C++):
    const int PWMPin = 2;    // ножка D2

    void setup()
    {
      // Конфигурируем нужные ножки МК
      pinMode(PWMPin, OUTPUT);

      // Инициализируем Timer1
      noInterrupts(); // запрещаем прерывания
      TCCR1A = 0;
      TCCR1B = 0;
      TCNT1  = 0;
      OCR1A = 200; // (16MHz / 200 = 80kHz которые дадут -> 40kHz в результате, т.к. задействуется только половина периода )
      TCCR1B |= (1 << WGM12); // CTC mode
      TCCR1B |= (1 << CS10); // Prescaler 1
      TIMSK1 |= (1 << OCIE1A); // Compare Timer Interrupt
    interrupts(); // Разрешаем прерывания

      PORTD=PORTD||0b00011100;
      PORTD=PORTD&&0b11101111;
    }


    // обработчик прерывания по переполнению таймера1
    ISR(TIMER1_COMPA_vect)      
    {
      //cli();  // Запрещаем прерывания
      PORTD=PORTD^0b00001100;
      //sei();  // Разрешаем прерывания и возвращаемся обратно в основной цикл программы
    }


    void loop()
    {
         
    }
    В результате в массиве значений вместо примерено искомых 1/40000=0,000025c=25mks +/-
    получил такие значения:
    0 407.06 mks => F=2.46 KHz
    1 193.56 mks => F=5.17 KHz
    2 0.75 mks => F=1333.33 KHz
    3 3888.00 mks => F=0.26 KHz
    4 3695.19 mks => F=0.27 KHz
    5 3502.38 mks => F=0.29 KHz
    6 3309.63 mks => F=0.30 KHz
    7 3100.88 mks => F=0.32 KHz
    8 2892.06 mks => F=0.35 KHz
    9 2699.25 mks => F=0.37 KHz
    10 2490.50 mks => F=0.40 KHz
    11 2281.69 mks => F=0.44 KHz
    12 2088.88 mks => F=0.48 KHz
    13 1896.13 mks => F=0.53 KHz
    14 1687.38 mks => F=0.59 KHz
    15 1478.56 mks => F=0.68 KHz
    16 1285.75 mks => F=0.78 KHz
    17 1077.00 mks => F=0.93 KHz
    18 868.19 mks => F=1.15 KHz
    19 675.38 mks => F=1.48 KHz
    20 482.63 mks => F=2.07 KHz
    21 273.88 mks => F=3.65 KHz
    22 65.06 mks => F=15.37 KHz
    23 3968.25 mks => F=0.25 KHz
    24 3775.50 mks => F=0.26 KHz
    25 3566.69 mks => F=0.28 KHz
    26 3373.88 mks => F=0.30 KHz
    27 3181.13 mks => F=0.31 KHz
    28 2972.38 mks => F=0.34 KHz
    29 2763.56 mks => F=0.36 KHz
    30 2570.75 mks => F=0.39 KHz
    31 2362.00 mks => F=0.42 KHz
    32 2153.19 mks => F=0.46 KHz
    33 1960.38 mks => F=0.51 KHz
    34 1767.63 mks => F=0.57 KHz
    35 1558.88 mks => F=0.64 KHz
    36 1350.06 mks => F=0.74 KHz
    37 1157.25 mks => F=0.86 KHz
    38 948.50 mks => F=1.05 KHz
    39 739.69 mks => F=1.35 KHz
    40 546.88 mks => F=1.83 KHz
    41 354.13 mks => F=2.82 KHz
    42 145.38 mks => F=6.88 KHz
    43 4032.56 mks => F=0.25 KHz
    44 3855.75 mks => F=0.26 KHz
    45 3663.00 mks => F=0.27 KHz
    46 3454.25 mks => F=0.29 KHz
    47 3245.44 mks => F=0.31 KHz
    48 3052.63 mks => F=0.33 KHz
    49 2843.88 mks => F=0.35 KHz
    50 2635.06 mks => F=0.38 KHz
    51 2442.25 mks => F=0.41 KHz
    52 2249.50 mks => F=0.44 KHz
    53 2040.75 mks => F=0.49 KHz
    54 1831.94 mks => F=0.55 KHz
    55 1639.13 mks => F=0.61 KHz
    56 1430.38 mks => F=0.70 KHz
    57 1221.56 mks => F=0.82 KHz
    58 1012.75 mks => F=0.99 KHz
    59 819.94 mks => F=1.22 KHz
    60 627.19 mks => F=1.59 KHz
    61 418.44 mks => F=2.39 KHz
    62 225.63 mks => F=4.43 KHz
    63 16.88 mks => F=59.26 KHz
    64 3904.06 mks => F=0.26 KHz
    65 3727.25 mks => F=0.27 KHz
    66 3534.50 mks => F=0.28 KHz
    67 3325.75 mks => F=0.30 KHz
    68 3116.94 mks => F=0.32 KHz
    69 2924.13 mks => F=0.34 KHz
    70 2715.38 mks => F=0.37 KHz
    71 2506.56 mks => F=0.40 KHz
    72 2313.75 mks => F=0.43 KHz
    73 2121.00 mks => F=0.47 KHz
    74 1912.25 mks => F=0.52 KHz
    75 1703.44 mks => F=0.59 KHz
    76 1510.63 mks => F=0.66 KHz
    77 1301.88 mks => F=0.77 KHz
    78 1093.06 mks => F=0.91 KHz
    79 884.25 mks => F=1.13 KHz
    80 691.44 mks => F=1.45 KHz
    81 498.69 mks => F=2.01 KHz
    82 289.94 mks => F=3.45 KHz
    83 97.13 mks => F=10.30 KHz
    84 3984.38 mks => F=0.25 KHz
    85 3791.56 mks => F=0.26 KHz
    86 3598.75 mks => F=0.28 KHz
    87 3406.00 mks => F=0.29 KHz
    88 3197.25 mks => F=0.31 KHz
    89 2988.44 mks => F=0.33 KHz
    90 2795.63 mks => F=0.36 KHz
    91 2586.88 mks => F=0.39 KHz
    92 2378.06 mks => F=0.42 KHz
    93 2185.25 mks => F=0.46 KHz
    94 1992.50 mks => F=0.50 KHz
    95 1783.75 mks => F=0.56 KHz
    96 1574.94 mks => F=0.63 KHz
    97 1382.13 mks => F=0.72 KHz
    98 1189.38 mks => F=0.84 KHz
    99 980.63 mks => F=1.02 KHz
    100 771.81 mks => F=1.30 KHz
    101 579.00 mks => F=1.73 KHz
    102 370.25 mks => F=2.70 KHz
    103 161.44 mks => F=6.19 KHz
    104 4064.63 mks => F=0.25 KHz
    105 3887.88 mks => F=0.26 KHz
    106 3679.13 mks => F=0.27 KHz
    107 3470.31 mks => F=0.29 KHz
    108 3277.50 mks => F=0.31 KHz
    109 3068.75 mks => F=0.33 KHz
    110 2859.94 mks => F=0.35 KHz
    111 2667.13 mks => F=0.37 KHz
    112 2474.38 mks => F=0.40 KHz
    113 2265.63 mks => F=0.44 KHz
    114 2056.81 mks => F=0.49 KHz
    115 1864.00 mks => F=0.54 KHz
    116 1655.25 mks => F=0.60 KHz
    117 1446.44 mks => F=0.69 KHz
    118 1253.63 mks => F=0.80 KHz
    119 1060.88 mks => F=0.94 KHz
    120 852.13 mks => F=1.17 KHz
    121 643.31 mks => F=1.55 KHz
    122 450.50 mks => F=2.22 KHz
    123 241.75 mks => F=4.14 KHz
    124 32.94 mks => F=30.36 KHz
    125 3936.13 mks => F=0.25 KHz
    126 3759.38 mks => F=0.27 KHz
    127 3550.63 mks => F=0.28 KHz
    128 3341.81 mks => F=0.30 KHz
    129 3149.00 mks => F=0.32 KHz
    130 2956.25 mks => F=0.34 KHz
    131 2747.50 mks => F=0.36 KHz
    132 2538.69 mks => F=0.39 KHz
    133 2345.88 mks => F=0.43 KHz
    134 2137.13 mks => F=0.47 KHz
    135 1928.31 mks => F=0.52 KHz
    136 1735.50 mks => F=0.58 KHz
    137 1542.75 mks => F=0.65 KHz
    138 1334.00 mks => F=0.75 KHz
    139 1125.19 mks => F=0.89 KHz
    140 932.38 mks => F=1.07 KHz
    141 723.63 mks => F=1.38 KHz
    142 514.81 mks => F=1.94 KHz
    143 322.00 mks => F=3.11 KHz
    144 129.25 mks => F=7.74 KHz
    145 4016.50 mks => F=0.25 KHz
    146 3823.69 mks => F=0.26 KHz
    147 3630.88 mks => F=0.28 KHz
    148 3422.13 mks => F=0.29 KHz
    149 3213.31 mks => F=0.31 KHz
    150 3020.50 mks => F=0.33 KHz
    151 2827.75 mks => F=0.35 KHz
    152 2619.00 mks => F=0.38 KHz
    153 2410.19 mks => F=0.41 KHz
    154 2217.38 mks => F=0.45 KHz
    155 2008.63 mks => F=0.50 KHz
    156 1799.81 mks => F=0.56 KHz
    157 1607.00 mks => F=0.62 KHz
    158 1414.25 mks => F=0.71 KHz
    159 1205.50 mks => F=0.83 KHz
    160 996.69 mks => F=1.00 KHz
    161 803.88 mks => F=1.24 KHz
    162 595.13 mks => F=1.68 KHz
    163 386.31 mks => F=2.59 KHz
    164 193.50 mks => F=5.17 KHz
    165 0.75 mks => F=1333.33 KHz
    166 3888.00 mks => F=0.26 KHz
    167 3695.19 mks => F=0.27 KHz
    168 3502.38 mks => F=0.29 KHz
    169 3293.63 mks => F=0.30 KHz
    170 3084.81 mks => F=0.32 KHz
    171 2892.00 mks => F=0.35 KHz
    172 2699.25 mks => F=0.37 KHz
    173 2490.50 mks => F=0.40 KHz
    174 2281.69 mks => F=0.44 KHz
    175 2088.88 mks => F=0.48 KHz
    176 1896.13 mks => F=0.53 KHz
    177 1687.38 mks => F=0.59 KHz
    178 1478.56 mks => F=0.68 KHz
    179 1285.75 mks => F=0.78 KHz
    180 1077.00 mks => F=0.93 KHz
    181 868.19 mks => F=1.15 KHz
    182 675.38 mks => F=1.48 KHz
    183 466.63 mks => F=2.14 KHz
    184 257.81 mks => F=3.88 KHz
    185 65.00 mks => F=15.38 KHz
    186 3968.25 mks => F=0.25 KHz
    187 3775.50 mks => F=0.26 KHz
    188 3566.69 mks => F=0.28 KHz
    189 3373.88 mks => F=0.30 KHz
    190 3181.13 mks => F=0.31 KHz
    191 2972.38 mks => F=0.34 KHz
    192 2763.56 mks => F=0.36 KHz
    193 2570.75 mks => F=0.39 KHz
    194 2362.00 mks => F=0.42 KHz
    195 2153.19 mks => F=0.46 KHz
    196 1960.38 mks => F=0.51 KHz
    197 1767.63 mks => F=0.57 KHz
    которые к искомым никакого отношения не имеют.
    Как я понимаю Arduino с которого снимаю сигнал при всем желании ТАКУЮ погрешность не даст...
    Поэтому возникает вопрос где же я мог профакаться в исходной программе измерений.
    и сразу думаю нужно написать по поводу кусочка
    Код (C++):
    tmp_TCNT1=TCNT1;
    TimerVal=65534-(tmp_TCNT1+TimerWas);
    когда так делал измерения исходил из следующих условий:

    TCNT1>TimerWas 65535>40000 когда таймер растет в первый проход разница 25535
    TCNT1<TimerWas 40000<65535 когда таймер только что переполнился разница 25535
    TCNT1=TimerWas не бывает

    65535+40000 при переполнении дают 39999
    65535-39999=25536 что на 1 больше разницы
     
  3. parovoZZ

    parovoZZ Гуру

    Чтобы измерить частоту, есть два варианта: на низких частотах и больших периодах логичнее считать время периода. На высоких частотах и малых временах периода логичнее считать количество периодов за единицу времени. Какой способ применён у автора, он об этом не сказал, а разбираться в коде мне банально лень.
     
  4. Daniil

    Daniil Гуру

    Любопытная задачка. Оговорюсь, что я с регистрами давно не играл.
    1. У вас в комментариях отсылка на 116 стр. - это таймер2, а вы работаете с таймером1. Я не знаю одинаковы ли они (8бит/16бит, понятно). Не могли ли вы что-то напутать читая не про нужный таймер?
    2. Удалил.
    3. Я понял, что ардуина ждет когда ее ногу дернут задним (возрастающим) (не суть) фронтом и засекается таймер. Но что за магия с "предыдущим" значением?
    4. Я не знаю как ардуина пишет в еепром. Выход из функции еепром.put означает "запись завершена" или "данные отосланы и записывают, нужно подождать"?
    Если 2-е то еепром у вас каша.
    5. Вы пытаетесь собрать "лог. анализатор? Т.е. Таймер молотит постоянно, а вы в нужный момент вытаскиваете и преобразуете во время периода? Почему бы не обнулять счетчик таймера?
    6. Меня в си приучили вещественную константу обозначать с десятичным нулем. Может у вас счет при выводе ломается?
    7. В передатчике ocr1a лучше сделать 199, чтобы точно 80кГц было.
    8. Далее мои познания начинают плыть. В передатчике вы конфигурируете окр1а, а пытаете работать с канало В? Так можно?
    2. Вы уверены в передатчике? Может лучше на 555 таймере сделать мигалку, а частотомеру поделить тактовую частоту, чтобы измерять периоды ~500ms?
     
  5. parovoZZ

    parovoZZ Гуру

    я вот здесь показываю, как делать генератор частоты:
    https://breadboard.ru/607/реализовать-алгоритм-плавного-увеличения-яркости-светодиода

    Можно. Но работать будет именно так, как написан код, а не так, как хочет программист.
     
  6. Nekto_nikto

    Nekto_nikto Гик

    Daniil:
    1.Перепроверил, все правильно на 116 странице показывают как работать с 16 битным регистром Timer1.
    У меня все это реализуется на Arduino nano, которая работает на ATMega328, там все именно так.
    3.5. Смысл программы вы поняли верно именно анализатор я и собираю, просто не знал как правильно эта штука называется, вот и обозвал ее частотомером. Там действительно 16 битный таймер молотит без остановки и когда приходит высокий логический уровень на ножку D2, то запускается обработчик аппаратного прерывания, т.к. насколько я знаю аппаратные прерывания в МК это самое быстрое из всего что можно использовать. Затем в обработчике прерывания МК считает период входящего сигнала, руководствуясь тем, что каждый период заключен между двумя нарастающими фронтами. Поэтому для расчетов программа использует считанное значение таймера tmp_TCNT1 и предидущее TimerWas, но если просто вычесть TimerWas из tmp_TCNT1, то как минимум когда таймер переполнится мы получим отрицательное неверное значение, именно об этом я и писал тут
    Поэтому для того чтоб получать вменяемые значения при измерениях придется использовать ветвления if/case, но это утяжелит саму программу и по размеру, что не критично и по времени выполнения учатка кода для обработчика прерывания, а вот это уже критично, т.к. как думаю вы сами понимаете это прерывание должно обрабатываться максимально быстро. Поэтому я и сделал такой финт ушами использовав особенности двоичного счисления, чтобы просто избежать ветвлений.
    Дальше программа просто сохраняет считанное значение регистра в переменной TimerWas для использования в следующем прерывании и вносит поправку в переменную TimerVal, на величину переменной Decrement, которая равна количеству тактов тратящихся на код обработчика прерывания при расчетах и записи значения количества тактов в массив из 200 ячеек. Кстати да в массив и как следствие в EEPROM я пишу не вещественную переменную а рассчитанное количество тактов кварца в периоде сигнала. А уже потом, когда извлекаю из EEPROM и вывожу в терминал, программа рассчитывает и выводит вещественные числа.
    4. Мысль интересная, я в эту сторону даже не думал, НО исходя из того что это функция из стандартных библиотек Arduino
    https://www.arduino.cc/en/Reference/EEPROMPut
    думаю что ждать тут все же не требуется, т.к. логично было бы предположить, что их форум бы завалили вопросами связанными с некорректной работой этой функции, поэтому думаю что всетаки если и нужно подождать, функция это делает сама.
    6.:oops: может быть и так, я давно не программировал :/
    дело в том, что этот "частотомер" делается изначально для измерения частоты меандра с выхода 555 таймера, но и там в рассчитанных значениях получается лабуда, правда более стабильная, поэтому в качестве источника для отладки и решил взять другую ардуину, видимо зря.

    parovoZZ:
    Я уже находил целых два рецепта того как делать частотомеры, но меня реализации смущают, т.к. там измерение как я понимаю идет всего одно по прерыванию переполнения таймера.
     
  7. Nekto_nikto

    Nekto_nikto Гик

    По пункту 6: значения ардуинка пересчитывает и выводи верно
    0 3294.44 mks => F=0.30 KHz
    1 2972.81 mks => F=0.34 KHz
    2 2523.56 mks => F=0.40 KHz
    3 2153.81 mks => F=0.46 KHz
    4 1816.38 mks => F=0.55 KHz
    5 1399.06 mks => F=0.71 KHz
    6 1045.44 mks => F=0.96 KHz
    7 692.06 mks => F=1.44 KHz
    8 258.69 mks => F=3.87 KHz
    9 3985.00 mks => F=0.25 KHz
    10 3647.56 mks => F=0.27 KHz
    11 3214.19 mks => F=0.31 KHz
    12 2844.50 mks => F=0.35 KHz
    13 2491.06 mks => F=0.40 KHz
    14 2057.69 mks => F=0.49 KHz
    15 1672.00 mks => F=0.60 KHz
    16 1318.50 mks => F=0.76 KHz
    17 917.13 mks => F=1.09 KHz
    18 483.56 mks => F=2.07 KHz
    19 97.88 mks => F=10.22 KHz
    20 3840.38 mks => F=0.26 KHz
    21 3439.00 mks => F=0.29 KHz
    22 3053.38 mks => F=0.33 KHz
    23 2715.88 mks => F=0.37 KHz
    24 2314.56 mks => F=0.43 KHz
    25 1849.00 mks => F=0.54 KHz
    26 1495.19 mks => F=0.67 KHz
    27 1173.81 mks => F=0.85 KHz
    28 724.56 mks => F=1.38 KHz
    29 354.81 mks => F=2.82 KHz
    30 17.38 mks => F=57.55 KHz
    31 3664.06 mks => F=0.27 KHz
    32 3294.31 mks => F=0.30 KHz
    33 2956.81 mks => F=0.34 KHz
    34 2539.50 mks => F=0.39 KHz
    35 2153.88 mks => F=0.46 KHz
    36 1800.38 mks => F=0.56 KHz
    37 1399.00 mks => F=0.71 KHz
    38 1013.44 mks => F=0.99 KHz
    39 643.94 mks => F=1.55 KHz
    40 242.50 mks => F=4.12 KHz
    41 3952.94 mks => F=0.25 KHz
    42 3599.44 mks => F=0.28 KHz
    43 3214.00 mks => F=0.31 KHz
    44 2796.50 mks => F=0.36 KHz
    45 2378.88 mks => F=0.42 KHz
    46 1993.25 mks => F=0.50 KHz
    47 1591.75 mks => F=0.63 KHz
    48 1206.19 mks => F=0.83 KHz
    49 836.69 mks => F=1.20 KHz
    50 451.25 mks => F=2.22 KHz
    51 33.75 mks => F=29.63 KHz
    52 3760.13 mks => F=0.27 KHz
    53 3406.69 mks => F=0.29 KHz
    54 2941.25 mks => F=0.34 KHz
    55 2571.44 mks => F=0.39 KHz
    56 2250.00 mks => F=0.44 KHz
    57 1816.75 mks => F=0.55 KHz
    58 1383.06 mks => F=0.72 KHz
    59 1045.38 mks => F=0.96 KHz
    60 676.06 mks => F=1.48 KHz
    61 242.63 mks => F=4.12 KHz
    62 3984.94 mks => F=0.25 KHz
    63 3615.56 mks => F=0.28 KHz
    64 3198.06 mks => F=0.31 KHz
    65 2796.44 mks => F=0.36 KHz
    66 2426.88 mks => F=0.41 KHz
    67 2073.44 mks => F=0.48 KHz
    68 1640.06 mks => F=0.61 KHz
    69 1270.38 mks => F=0.79 KHz
    70 884.94 mks => F=1.13 KHz
    71 467.44 mks => F=2.14 KHz
    72 113.81 mks => F=8.79 KHz
    73 3840.44 mks => F=0.26 KHz
    74 3455.00 mks => F=0.29 KHz
    75 3021.44 mks => F=0.33 KHz
    76 2619.75 mks => F=0.38 KHz
    77 2266.19 mks => F=0.44 KHz
    78 1864.81 mks => F=0.54 KHz
    79 1415.25 mks => F=0.71 KHz
    80 1061.50 mks => F=0.94 KHz
    81 724.13 mks => F=1.38 KHz
    82 338.81 mks => F=2.95 KHz
    83 4017.31 mks => F=0.25 KHz
    84 3631.69 mks => F=0.28 KHz
    85 3246.13 mks => F=0.31 KHz
    86 2796.63 mks => F=0.36 KHz
    87 2426.88 mks => F=0.41 KHz
    88 2073.44 mks => F=0.48 KHz
    89 1640.06 mks => F=0.61 KHz
    90 1270.38 mks => F=0.79 KHz
    91 932.94 mks => F=1.07 KHz
    92 499.63 mks => F=2.00 KHz
    93 97.94 mks => F=10.21 KHz
    94 3840.38 mks => F=0.26 KHz
    95 3455.00 mks => F=0.29 KHz
    96 3021.44 mks => F=0.33 KHz
    97 2619.75 mks => F=0.38 KHz
    98 2282.19 mks => F=0.44 KHz
    99 1880.88 mks => F=0.53 KHz
    100 1479.31 mks => F=0.68 KHz
    ...
    0 52711 cycles
    1 47565 cycles
    2 40377 cycles
    3 34461 cycles
    4 29062 cycles
    5 22385 cycles
    6 16727 cycles
    7 11073 cycles
    8 4139 cycles
    9 63760 cycles
    10 58361 cycles
    11 51427 cycles
    12 45512 cycles
    13 39857 cycles
    14 32923 cycles
    15 26752 cycles
    16 21096 cycles
    17 14674 cycles
    18 7737 cycles
    19 1566 cycles
    20 61446 cycles
    21 55024 cycles
    22 48854 cycles
    23 43454 cycles
    24 37033 cycles
    25 29584 cycles
    26 23923 cycles
    27 18781 cycles
    28 11593 cycles
    29 5677 cycles
    30 278 cycles
    31 58625 cycles
    32 52709 cycles
    33 47309 cycles
    34 40632 cycles
    35 34462 cycles
    36 28806 cycles
    37 22384 cycles
    38 16215 cycles
    39 10303 cycles
    40 3880 cycles
    41 63247 cycles
    42 57591 cycles
    43 51424 cycles
    44 44744 cycles
    45 38062 cycles
    46 31892 cycles
    47 25468 cycles
    48 19299 cycles
    49 13387 cycles
    50 7220 cycles
    51 540 cycles
    52 60162 cycles
    53 54507 cycles
    54 47060 cycles
    55 41143 cycles
    56 36000 cycles
    57 29068 cycles
    58 22129 cycles
    59 16726 cycles
    60 10817 cycles
    61 3882 cycles
    62 63759 cycles
    63 57849 cycles
    64 51169 cycles
    65 44743 cycles
    66 38830 cycles
    67 33175 cycles
    68 26241 cycles
    69 20326 cycles
    70 14159 cycles
    71 7479 cycles
    72 1821 cycles
    73 61447 cycles
    74 55280 cycles
    75 48343 cycles
    76 41916 cycles
    77 36259 cycles
    78 29837 cycles
    79 22644 cycles
    80 16984 cycles
    81 11586 cycles
    82 5421 cycles
    83 64277 cycles
    84 58107 cycles
    85 51938 cycles
    86 44746 cycles
    87 38830 cycles
    88 33175 cycles
    89 26241 cycles
    90 20326 cycles
    91 14927 cycles
    92 7994 cycles
    93 1567 cycles
    94 61446 cycles
    95 55280 cycles
    96 48343 cycles
    97 41916 cycles
    98 36515 cycles
    99 30094 cycles
    100 23669 cycles
    ...
    т.к. попробывал некоторые значения пересчитать в ручную, даже округляет сама)))
    По пункту 8: закомментировал в коде передатчика строку
    собственно результат в спойлере выше.
     
  8. parovoZZ

    parovoZZ Гуру

    вот ты здесь и спалился) Т.к. такой расчет привязан к тактовой частоте ТАЙМЕРА, то совершенно естественным образом значения у тебя плавают.
     
  9. Daniil

    Daniil Гуру

    Боюсь, для дальнейшей проверки я бы начал считать вместе с ардуиной на листочке. Я бы настаивал на генераторе на 555 с периодом >500 мс. Тогда в обработчике прерываний выплёвывал отладочную информацию/промежуточные значения.
    От чего ж, если тактовая частота таймера постоянна?
     
  10. parovoZZ

    parovoZZ Гуру

    на вход в аппаратное прерывание МК затрачивает 4 такта (речь про AVR. У ARM всё грустнее). Но надо понимать, что если МК находится в прерывании и поступил следующий запрос на прерывание, то МК сперва завершит обработку текущего прерывания, выйдет из него (от 4-х до 6-ти тактов), выполнит одну инструкцию кода и только потом зайдёт в следующее прерывание. Какие прерывания выполняются в недрах дурины не знают даже те, кто бьёт себя рукой в грудь и считает себя лютым дуринщиком.
     
    Владимир_71 нравится это.
  11. Nekto_nikto

    Nekto_nikto Гик

    Я бы выразился иначе, т.к. частота у дуни 16МГц а в счетчике максимальное значение 65535, итого получается максимальный период в теории для измерений T=65535/16000000=0,0040959375 секунды.

    Кажется понял в чем дело, видимо вы Daniil когда писали догадку про функцию PUT для EEPROM оказались правы, т.к. вот тут
    https://www.arduino.cc/en/Reference/EEPROMWrite
    пишут что для того чтоб записать что то в EEPROM, требуется от 3,3ms, и почему то мне думается, что эту задержку реализовали с помощью функции delay, которая как выяснилось в прерывании не работает, о чем свидетельствуют два факта, после записи в EEPROM по коду на плате должен мигать встроенный светодиод, а он у меня просто загорается, причем почти сразу и второй факт, то что пробывал ставить задержку после
    Код (C++):

    EEPROM.put((int)i/2, Period[(int)i/2]); // для записи в EEPROM нужна пауза не менее чем 3,3ms, а у нас запрещены прерывания, вследствие чего функция Delay не работает внутри кода прерывания
    delay(500);
    встроенный светодиод точно также загорается почти сразу, хотя учитывая, то что в массиве 200 ячеек для записи при задержке в пол секунды, диод должен загореться как минимум только через 100секунд, а это уже больше чем полторы минуты, но никак не сразу.
     
  12. Nekto_nikto

    Nekto_nikto Гик

    Прописал простенький отладочный код
    Код (C++):

    #include <EEPROM.h>

     unsigned int Period[200];  // Массив, в котором будут храниться отловленные значения
     unsigned int TimerWas=0;   // Предидущее отловленное значение таймера
     unsigned int TimerVal=0;   // Значение таймера
     unsigned int tmp_TCNT1=0;   // Временная переменная, где будет храниться считанное значение TCNT1
     int i=0;
     String S="";               // Переменная передаваемая через COM порт

    void Int0();

    void setup()
    {
      // Конфигурируем нужные ножки МК
      Serial.begin(9600);                 // подключаем последовательный порт
      pinMode(2, INPUT_PULLUP);           // конфигурируем ножку D2
      attachInterrupt(0, Int0, FALLING);  // назначаем обработчик прерывания для ножки D2 по спаду фронта, т.к. если прописать RISING, то прерывание происходит само собой
      pinMode(3, OUTPUT);                 // конфигурируем ножку D3
      digitalWrite(3, LOW);              // подаем на ножку D3 высокий логический уровень, чтоб открыть транзистор на оптореле
      pinMode(LED_BUILTIN, OUTPUT);       // конфигурируем ножку, отвечающую за встроенный светодиод на плате

    }

    void loop()
    {
      if (Serial.available()) // проверяем, поступают ли какие-то команды на мониторе порта
      {
        S = Serial.readString();  // !!! Читаем строку из терминала
        if(S=="Go")
          {
          //asm("cli"); // Запрещаем прерывания
          EEPROM.get((int)0, Period[0]);
          delay(5);
          EEPROM.get((int)2, Period[1]);
          delay(5);

          Serial.println((String)Period[0]);
          Serial.println((String)Period[1]);
          }
      }
    }

    void Int0() // Функция прерывания для ножки МК D2
    {
      asm("cli"); // Запрещаем прерывания
      EEPROM.put((int)0, 65533); // для записи в EEPROM нужна пауза не менее чем 3,3ms, а у нас запрещены прерывания, вследствие чего функция Delay не работает внутри кода прерывания
      delay(5);
      EEPROM.put((int)2, 65534); // для записи в EEPROM нужна пауза не менее чем 3,3ms, а у нас запрещены прерывания, вследствие чего функция Delay не работает внутри кода прерывания
      delay(5);
      // сигналим светодиодом на плате, что запись произведена и можно подключать к ПК и смотреть что получилось
      while(true)
        {
        digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
        delay(1000);                       // wait for a second
        digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
        delay(1000);
        }
    }
     

    и выяснилось, что если ставить запрет прерываний внутри прерывания, то это никак не влияет, функция
    EEPROM.put отрабатывает как надо, в отличии от функции delay.
    А вот дальше если поставить запрет прерываний перед чтением EEPROM и выводом в монитор порта, то значения выводятся некорректно, вернее выводится только одно 65, и то не то что записывали.
    Поэтому думаю что скорее всего функция вывода в COM порт зависит от прерываний, т.к. если нет запрета прерываний, то из EEPROM нормально считываются и выводятся значения 65533 и 65534.
    А вот функция delay в прерывании нормально не обрабатывается, т.к. после записи этих значений светодиод не мигает, как должен по программе, а просто горит.
     
  13. parovoZZ

    parovoZZ Гуру

    Это лишняя операция. МК самостоятельно запрещает все прерывания.
     
  14. parovoZZ

    parovoZZ Гуру

    Я уже показывал, как работать с EEPROM. Чтобы не ждать когда освободится контроллер памяти EEPROM, достаточно проверять флаг (сейчас не скажу какой. В даташите есть. Примеры работы там тоже есть). Когда флаг свободен, тогда и заходить в функцию записи EEPROM. Просто же всё.
     
  15. b707

    b707 Гуру

    Чебурашка, вы изначально правильно начали, но потом вас куда понесло.. Все что нужно сделать - это при срабатывании прерывания запомнить значение счетчика TCNT . Потом при следующем прерывании снова считать значение TCNT . Разница между этими значениями и есть период. Это все что нужно делать в прерывании. Все вычисления, сохранения значений в массив или ЕЕПРОМ - все это делается уже в основной программе.
    Ваши попытки в прерывании писать в ЕЕПРОМ или мигать диодом - это бред собачий, это делает программу нерабочей.
    Кроме того, ваши потуги бороться с переполнением счетчика выглядят смешно. В этом нет никакой нужды. Контроллер отлично справляется с этим сам. Для проверки попробуйте вычесть из четырех два - получится конечно 2. А теперь попробуйте вычесть на ардуине изTCNT1 = 0 число TCNT1 = 65534 - вы удивитесь, но снова получится два! без всяких ваших специальных функций
     
    Daniil нравится это.
  16. parovoZZ

    parovoZZ Гуру

    это если переменная беззнаковая. На переменной со знаком будет всё так, как в учебнике по математике.
     
  17. b707

    b707 Гуру

    TCNT - беззнаковый
     
  18. parovoZZ

    parovoZZ Гуру

    Я про переменную, а не про регистр.
     
  19. Daniil

    Daniil Гуру

    всеми руками за такое решение, но проскакивала мысль об лог. анализаторе.
    В общем, задачу нужно решать по кусочкам, а не нахрапом.
    Вот хэлпы перестали (а начинали ли?) нормальные писать.
    Есть функция, что будет если подать ей аргумент1, а что если аргумент2? Как ф-я влияет на прерывания и т.д. Программист я никудышный, поэтому быдлокодю самостоятельно( конечно, использую готовые процедуры, но их и подвергаю сомнению в первую очередь.
     
  20. Nekto_nikto

    Nekto_nikto Гик

    b707:
    не совсем так, дело в том что я не уверен в амплитуде сигнала, а конкретно в том что она будет не больше 5 вольт, в связи с чем мне очень не хочется снимать показания в то время как ардуина будет подключена к ПК, т.к. можно периферию попалить. Поэтому и идут пляски со светодиодами, я так узнаю что запись завершена, а дальше отключаю ардуино от источника сигнала, после чего подключаю к ПК и снимаю показания.
    В целом конечно можно вынести занесение данных в EEPROM за пределы обработчика прерывания, используя флаг, сигнализирующий о заполнении массива. Но и те же delay можно внутри прерывания заменить просто циклами сложения и посчитать сколько тактов будет выполняться цикл, но это конечно извращение.

    В целом спасибо всем к то участвует в обсуждении, буду решать, буду думать.