Помогите вычислить ширину импульса!

Тема в разделе "Arduino & Shields", создана пользователем mib, 17 фев 2017.

  1. mcureenab

    mcureenab Гуру

    Скетч для генерации импульсов фиксированной длительности.
    Использовал для тестирования библиотеки.

    Код (C++):
    // pulse 1
    const int outPin = 3;                 // digital pin 3

    void setup()
    {
      pinMode(outPin, OUTPUT);      // sets the digital pin as output
    }

    void loop()
    {
      uint8_t oldSREG(SREG);
      cli();
      digitalWrite(outPin, HIGH);   // sets the pin on
      delayMicroseconds(1250);        // pauses for 1250 microseconds    
      digitalWrite(outPin, LOW);    // sets the pin off
      SREG = oldSREG;
      delay(500);
    }
     
     
    mib нравится это.
  2. ostrov

    ostrov Гуру

    Что это:
    Код (C++):
    uint8_t oldSREG(SREG);
    Зачем это в лупе:
    Код (C++):
    cli();
    Для чего это:
    Код (C++):
    SREG = oldSREG;
     
  3. mcureenab

    mcureenab Гуру

    Измерение времени в тиках таймера без масштабирования с захватом времени по прерыванию.

    Код (C++):
    // micros 2

    class Timer1 {
    public:
      enum CS : uint8_t { // Clock Select
        CS_STOP = 0,                              // No clock source. (Timer/Counter stopped), Initial
        CS_1 =                        _BV(CS10),  // clkI/O/1 (No prescaling)
        CS_8 =              _BV(CS11),            // clkI/O/8 (From prescaler)
        CS_64 =             _BV(CS11)|_BV(CS10),  // clkI/O/64 (From prescaler)
        CS_256 =  _BV(CS12),                      // clkI/O/256 (From prescaler)
        CS_1024 = _BV(CS12)          |_BV(CS10),  // clkI/O/1024 (From prescaler)
        CS_FALL = _BV(CS12)|_BV(CS11),            // External clock source on T1 pin. Clock on falling edge (PD6 (T1/OC4D/ADC9))
        CS_RISE = _BV(CS12)|_BV(CS11)|_BV(CS10)   // External clock source on T1 pin. Clock on rising edge  (PD6 (T1/OC4D/ADC9))
      };
      enum _WGM : uint8_t { // WGM internal bit mask
        _WGM__ = 0b0000,
        _WGM13 = 0b1000,
        _WGM12 = 0b0100,
        _WGM11 = 0b0010,
        _WGM10 = 0b0001,
      };
      enum WGM : uint8_t { // Waveform Generation Mode
        WGM_0  = _WGM__,                            // Normal, Initial
        WGM_1  =                            _WGM10, // PWM, Phase Correct, 8-bit
        WGM_2  =                   _WGM11,          // PWM, Phase Correct, 9-bit
        WGM_3  =                   _WGM11 | _WGM10, // PWM, Phase Correct, 10-bit
        WGM_4  =          _WGM12,                   // CTC
        WGM_5  =          _WGM12          | _WGM10, // Fast PWM, 8-bit
        WGM_6  =          _WGM12 | _WGM11,          // Fast PWM, 9-bit
        WGM_7  =          _WGM12 | _WGM11 | _WGM10, // Fast PWM, 10-bit
        WGM_8  = _WGM13,                            // PWM, Phase and Frequency Correct
        WGM_9  = _WGM13                   | _WGM10, // PWM, Phase and Frequency Correct
        WGM_10 = _WGM13          | _WGM11,          // PWM, Phase Correct
        WGM_11 = _WGM13          | _WGM11 | _WGM10, // PWM, Phase Correct
        WGM_12 = _WGM13 | _WGM12,                   // CTC
        WGM_14 = _WGM13 | _WGM12 | _WGM11,          // Fast PWMFast PWM
        WGM_15 = _WGM13 | _WGM12 | _WGM11 | _WGM10, // Fast PWMFast PWM
      };
      static Timer1& instance(){
        static Timer1 i;
        return i;
      }
      unsigned long timer_tiks(unsigned int *hi) const;
      bool get_ICR(unsigned long *tiks); // Возвращает результат последнего захвата
      void set_ICR_duty()  { ICR_edge = 1; }; // swap edge on each captire.
      void set_ICR_fall()  { ICR_edge = 2; }; // a falling (negative) edge is used as trigger. Вступает в силу после очередного срабатывания триггера
      void set_ICR_raise() { ICR_edge = 3; }; // a rising (positive) edge will trigger the capture. Вступает в силу после очередного срабатывания триггера
      void start_timer()  { PRR0   &= ~(_BV(PRTIM1)); } // Initial
      void stop_timer()   { PRR0   |=   _BV(PRTIM1); }
      void set_CS(CS mode);    // Clock Select
      void set_WGM(WGM mode);  // Waveform Generation Mode
      void int_overflow()  {     // TIMER1_OVF_vect handler
        overflow_count++;
      };
      void int_capture() {     // TIMER1_CAPT_vect Pin A6
        uint16_t t(ICR1);
        ICR = t; // TCNT1
        switch(ICR_edge){
          case 0: break;
          case 1: TCCR1B ^= _BV(ICES1); break;
          case 2: TCCR1B &= ~(_BV(ICES1)); ICR_edge = 0; break;
          case 3: TCCR1B |=   _BV(ICES1);  ICR_edge = 0; break;
        }
        ICR_overflow_count = overflow_count + ( ( TIFR1 & _BV(TOV1) ) && ( t < 2048 ) ? 0 : 1);
        is_ICR = true;
      };
    protected:
    private:
      Timer1();
      ~Timer1();
      Timer1(Timer1 const&) = delete;
      Timer1& operator= (Timer1 const&) = delete;
      volatile unsigned long overflow_count;
      volatile bool ICR_duty;
      volatile uint8_t ICR_edge;
      volatile bool is_ICR;
      volatile uint16_t ICR;
      volatile unsigned long ICR_overflow_count;
    };

    ISR(TIMER1_OVF_vect){
        Timer1::instance().int_overflow();
    }

    ISR(TIMER1_CAPT_vect){ // PD4 (ICP1/ADC8)
        Timer1::instance().int_capture();
    }

    Timer1::Timer1()
    :overflow_count(0)
    ,is_ICR(false)
    ,ICR(0)
    ,ICR_overflow_count(0)
    ,ICR_duty(false)
    ,ICR_edge(0)
    {
        TIMSK1 |= _BV(TOIE1); // разрешаем прерывание по переполнению таймера
        TIMSK1 |= _BV(ICIE1);
    };

    Timer1::~Timer1(){
        TIMSK1 &= ~(_BV(ICIE1));
        TIMSK1 &= ~(_BV(TOIE1)); // запрещаем прерывание по переполнению таймера
    };

    void Timer1::set_CS(
      CS mode
    ){
        TCCR1B = ( TCCR1B & ~( _BV(CS12)|_BV(CS11)|_BV(CS10) ) ) | mode;
    }

    void Timer1::set_WGM(
      WGM mode
    ){
        TCCR1A = TCCR1A & ~( _BV(WGM11)|_BV(WGM10) ) | (_WGM11 & mode ? _BV(WGM11) : 0) | (_WGM10 & mode ? _BV(WGM10) : 0) ;
        TCCR1B = TCCR1B & ~( _BV(WGM13)|_BV(WGM12) ) | (_WGM13 & mode ? _BV(WGM13) : 0) | (_WGM12 & mode ? _BV(WGM12) : 0) ;
    }

    unsigned long Timer1::timer_tiks(unsigned int *hi) const {
        uint8_t oldSREG(SREG);
        cli();
        unsigned long m(overflow_count);
        uint16_t t(TCNT1);
        if( (TIFR1 & _BV(TOV1)) && ( t < 2048 ) ) m++;
        SREG = oldSREG;
        if( hi ) *hi = m >> 16;
        return (m << 16) + t;
    }

    bool Timer1::get_ICR(unsigned long *tiks) {
        if( !is_ICR ) return false;
        uint8_t oldSREG(SREG);
        cli();
        *tiks = (ICR_overflow_count << 16) | ICR ;
        is_ICR = false;
        SREG = oldSREG;
        return true;
    }

    Timer1& tr1(Timer1::instance());

    void setup() {
      // put your setup code here, to run once:
        pinMode(A6, INPUT_PULLUP); // Пин A6 аппаратно связан с таймером 1.
        tr1.set_ICR_raise();
        tr1.set_WGM(Timer1::WGM_0);
        tr1.set_CS(Timer1::CS_1);
        Serial.begin(250000);
        while(!Serial){};
        Serial.println("Staring...");
        tr1.start_timer();
        unsigned long t1;
        while( ! tr1.get_ICR(&t1) ){ delay(100); };
    }

    void loop() {
      // put your main code here, to run repeatedly:
        unsigned long t1, t2;
        tr1.set_ICR_fall();
        if( tr1.get_ICR(&t1) ) {
            tr1.set_ICR_raise();
            while( ! tr1.get_ICR(&t2) ){ delay(100); };  // между тестовыми импульсами интервал 500 мс.
            Serial.println( t2 - t1 );
            delay(350);  // между тестовыми импульсами интервал 500 мс.
        }
    }
     

    Вполне стабильная работа.
    20062 = 1253,875 мкс
    20063 = 1253,9375 мкс

    Код (Text):
    Staring...
    20062
    20062
    20063
    20062
    20062
    20062
    20062
    20063
    20062
    20062
    20062
    20062
    20063
    20062
    20062
    20062
    20062
    20063
    20062
    20062
    20062
    20062
    20062
    20062
    20063
    20062
     
    Скетч // pulse 1 запускал на Arduino micro. Скетч // micros 2 запускал на Arduino Leonardo.
    На MICRO питание дано с Leonardo. На Leonardo по USB с ноутбука. Тот же USB на консоль.

    Провод от MICRO пин 3 -> Leonardo пин 4 (он же A6).
     
    Последнее редактирование: 21 фев 2017
    mib нравится это.
  4. mcureenab

    mcureenab Гуру

    Это запрещает прерывания на время импульса, а затем разрешает. Точнее возвращает, как было.
    По другому не получалось генерировать импульсы стабильной длительности. На несколько мкс длительность плавала. А так джиттер в десятые доли мкс укладывается.
     
  5. serg_admin

    serg_admin Гик

    Serial.print - генерирует прерывание окончания передачи по RS232 оно и вклинивается.
     
  6. ostrov

    ostrov Гуру

    cli - вот запрет прерываний, если нужно потом включить, то ниже добавить sei, если нет, то достаточного одного cli в сетапе. Все остальное, что я выше скопировал - ненужный и неправильный мусор.
     
  7. mcureenab

    mcureenab Гуру

    Этот "мусор" взят из ядра Arduino и прекрасно работает у миллионов пользователей.
    Согласен, что конкретно для этого скетча можно съэкономить переменную и одну строчку кода. однако, ограничив возможность повторного использования.
     
  8. mcureenab

    mcureenab Гуру

    Скетч // pulse 1 не использует Serial.

    Если замерять время между спадом и подъемом, то тоже время дрожит.

    Я сначала пробовал analogWrite использовать. Но тоже как то не стабильно импульсы шли.
     
  9. mib

    mib Нуб

    При компиляции ругается на 48, 49, 151 строки

    Blink:48: error: 'PRR0' was not declared in this scope
    void start_timer() { PRR0 &= ~(_BV(PRTIM1)); } // Initial
    Blink:49: error: 'PRR0' was not declared in this scope
    void stop_timer() { PRR0 |= _BV(PRTIM1); }
    Blink:151: error: 'class Timer1' has no member named 'start_timer'
    tr1.start_timer();
    Версия ардуино 1.8.1
     
    Последнее редактирование: 21 фев 2017
  10. mcureenab

    mcureenab Гуру

    Удалите все строки с функциями start_timer и stop_timer. Видимо это фичи Leonardo. По умолчанию таймер должен работать
     
  11. mib

    mib Нуб

    порт пишет только
    Staring...
    и больше ничего
    А6 у меня нет, можно ли использовать другие и нужно ли править что-то, кроме 144 строчки тогда?
     
  12. mcureenab

    mcureenab Гуру

    Ищите. На Leonardo пин А6 с обратной стороны платы подписан. С лицевой стороны это пин 4. Раз скетч скомпилировался, значит A6 определен.
    Заменить пин нельзя. Он аппаратно связан с таймером1.
     
  13. serg_admin

    serg_admin Гик

    Скорее всего выбрана не та плата перед компиляцией. Эти регистры есть в Arduino mega например
     
  14. serg_admin

    serg_admin Гик

    Каждый таймер обычно связан с двумя пинами. Соответственно из них можно выбрать.
     
  15. mcureenab

    mcureenab Гуру

    Arduino UNO->
    ATmega328/P->
    (PCINT0/CLKO/ICP1) PB0->

    (D 8) PB0 14

    Выходит на UNO ICP1 это Цифровой пин 8.
     
  16. serg_admin

    serg_admin Гик

    Насколько я могу судить Вы правы это 8-й пин.
     
  17. mcureenab

    mcureenab Гуру

    Как бы это в библиотеке оформить...
    Предупреждением компилятора выводить, что-ли? Мол пин такой то под ICP занят.