Настройка таймера для генерирования импульсов и их подсчета

Тема в разделе "Arduino & Shields", создана пользователем Sergei K, 11 фев 2018.

  1. Sergei K

    Sergei K Нерд

    Здравствуйте.
    Прошу помощи в критике кода и пинке в нужном направлении.
    Суть проекта заключается в следующем. Генератор низкочастотных импульсов (Ардуино Про Мини, либо Искра Нео) запускается при нажатии кнопки, при повторном нажатии - импульсы отключаются. Две другие кнопки либо увеличивают/уменьшают частоту следования импульсов, либо изменяют их длительность. Код скорее всего корявый, но работает. Далее возникло две проблемы:
    1. Не могу догнать, каким образом считать импульсы, идущие с выхода 9 при нажатии кнопки (без использования входа для внешнего прерывания)? При повторном нажатии (т.е. импульсы не идут) - счетчик останавливается.
    2. Импульсы поступают для управления полевым транзистором, а нагрузка является низкоомной (катушка с малым количеством витков). Для уменьшения тока, импульс хочу разбить на пачку импульсов (наподобие ШИМ). При этом счетчик его должен видеть, как единичный.
    Пните пожалуйста в нужном направлении, какие действия нужно выполнить, чтоб настроить таймер 1 для выполнения данных задач?
    Критика кода также приветствуется

    Код (C++):
    const int pulsePin = 9; // выход генератора
    const int buttonPin = A2;     // the number of the pushbutton pin
    const int  buttonPin2 = A3;
    const int buttonPin3 = A4;
    int buttonState = 0;         // variable for reading the pushbutton status
    int buttonState2 = 0;
    int buttonState3 = 0;
    uint8_t mode = 0; // режим
    volatile uint32_t cnt=0; // счетчик
    volatile unsigned long x=0; // время
    void setup() {
      // put your setup code here, to run once:
      Serial.begin(9600);
      pinMode(pulsePin, OUTPUT);
      TCCR1A=(1<<COM1A1)|(1<<WGM11);
      TCCR1B=(1<<WGM13)|(1<<WGM12)|(1<<CS11)|(1<<CS10); //режим СТС, делитель 64 (1<<ICES1)|
      OCR1A=250; // начальная длительность
      ICR1=15000; // начальный период
    }

    void loop() {
      // put your main code here, to run repeatedly:
      //cnt = TCNT1;
      float  freq = F_CPU/64.0/(ICR1-1); // частота импульсов
      cnt = freq*x;
      if (x!=(millis()/200)) {
         buttonState2 = digitalRead(buttonPin2);
         buttonState3 = digitalRead(buttonPin3);
       
       if (digitalRead(buttonPin) == HIGH)   {   mode++;  }
        if (mode>=2) {mode=0;}
          if (mode==0)
       {
          TCCR1A=0; // выключение импульсов??
         
          if (buttonState2 == HIGH) { ICR1+=150; } // частота
          if (buttonState3 == HIGH) { ICR1-=150;}
       }
          else
       {
           TCCR1A=(1<<COM1A1)|(1<<WGM11); // включение импульсов
         // cnt = freq*x; //cnt++;
           if (buttonState2 == HIGH) { OCR1A+=25; } // длительность
           if (buttonState3 == HIGH) { OCR1A-=25;}
         }
       x=millis()/200;
       Serial.println(cnt);
      }
    }
     
  2. Sergei K

    Sergei K Нерд

    Для подсчета импульсов применил функцию-обработчик ISR( TIMER1_COMPA_vect )
    Код (C++):
    const int pulsePin = 9; // выход генератора
    const int buttonPin = A2;     // the number of the pushbutton pin
    const int  buttonPin2 = A3;
    const int buttonPin3 = A4;
    int buttonState = 0;         // variable for reading the pushbutton status
    int buttonState2 = 0;
    int buttonState3 = 0;
    uint8_t mode = 0; // режим
    volatile uint32_t cnt=0; // счетчик
    volatile unsigned long x=0; // время
    volatile byte time2 = 0; //старший разряд времени, два младших - регистры таймера
    void setup() {
      // put your setup code here, to run once:
      Serial.begin(9600);
      pinMode(pulsePin, OUTPUT);
      //разрешаем прерывание по переполнению и захвата Timer1
      TIMSK1 = 1<<TOIE1 | 1<<ICIE1;
    //Обнуляем счетные регистры
      TCNT1 = 0;
      TCCR1A=0; //(1<<COM1A1)|(1<<WGM11);
      TCCR1B=(1<<WGM13)|(1<<WGM12)|(1<<CS11)|(1<<CS10); //режим СТС, делитель 64 (1<<ICES1)|
      OCR1A=250; // начальная длительность
      ICR1=15000; // начальный период
      TIMSK1 = 1<<OCIE1A;
    }

      ISR( TIMER1_COMPA_vect ) //прерывание счета времени
    {
      cnt++; //увеличиваем на единицу
    }


    void loop() {
      // put your main code here, to run repeatedly:
      //cnt = TCNT1;
      float  freq = F_CPU/64.0/(ICR1-1); // частота импульсов
    // cnt = freq*x;
      if (x!=(millis()/200)) {
         buttonState2 = digitalRead(buttonPin2);
         buttonState3 = digitalRead(buttonPin3);
     
       if (digitalRead(buttonPin) == HIGH)   {   mode++;  }
        if (mode>=2) {mode=0;}
          if (mode==0)
       {
         TCNT1 = 0;
         TCCR1A=0; // выключение импульсов??
       
          if (buttonState2 == HIGH) { ICR1+=150; } // частота
          if (buttonState3 == HIGH) { ICR1-=150;}
       }
          else
       {
         TCCR1A=(1<<COM1A1)|(1<<WGM11); // включение импульсов
         // cnt = freq*x; //cnt++;
           if (buttonState2 == HIGH) { OCR1A+=25; } // длительность
           if (buttonState3 == HIGH) { OCR1A-=25;}
         }
       x=millis()/200;
       Serial.println(cnt);
      }
    }
    При таком способе импульсы считает постоянно (сразу после включения, точность еще не проверил). Значит, отключение импульсов кнопкой выполнено неверно, хотя и работает.
     
  3. Sergei K

    Sergei K Нерд

    В общем не знаю, правильно ли я сделал. Первая задача решена.
    Ввел дополнительно две переменные (volatile unsigned long cnt1=0 и volatile uint32_t cnt2=0)
    и считаю их в функции-обработчике. Теперь отсчет идет при нажатии кнопки, после повторного - прекращается.
    Код (C++):
    const int pulsePin = 9; // выход генератора
    const int buttonPin = A2;     // the number of the pushbutton pin
    const int  buttonPin2 = A3;
    const int buttonPin3 = A4;
    int buttonState = 0;         // variable for reading the pushbutton status
    int buttonState2 = 0;
    int buttonState3 = 0;
    uint8_t mode = 0; // режим
    volatile unsigned long cnt=0; // счетчик
     volatile unsigned long cnt1=0;
    volatile uint32_t cnt2=0;
    volatile unsigned long x=0; // время

    void setup() {
      // put your setup code here, to run once:
      Serial.begin(9600);
      pinMode(pulsePin, OUTPUT);
      //разрешаем прерывание по переполнению и захвата Timer1
    // TIMSK1 = 1<<TOIE1 | 1<<ICIE1;
    //Обнуляем счетные регистры
      TCNT1 = 0;
      TCCR1A=0; //(1<<COM1A1)|(1<<WGM11);
      TCCR1B=(1<<WGM13)|(1<<WGM12)|(1<<CS11)|(1<<CS10); //режим СТС, делитель 64 (1<<ICES1)|
      OCR1A=250; // начальная длительность
      ICR1=15000; // начальный период
      TIMSK1 = 1<<OCIE1A;
    }

       ISR( TIMER1_COMPA_vect ) //прерывание счета времени
    {
      cnt++; //увеличиваем на единицу
     if (mode==0) {  cnt1++; }
    else { cnt1=cnt1; } // не изменяется
    cnt2=cnt-cnt1; // подсчет импульсов
    }

    void loop() {
      // put your main code here, to run repeatedly:
      //cnt = TCNT1;
      float  freq = F_CPU/64.0/(ICR1-1); // частота импульсов
    // cnt = freq*x;
      // cnt2=cnt-cnt1-4;
      if (x!=(millis()/200)) {
         buttonState2 = digitalRead(buttonPin2);
         buttonState3 = digitalRead(buttonPin3);
     
       if (digitalRead(buttonPin) == HIGH)   {   mode++;  }
        if (mode>=2) {mode=0;}
          if (mode==0)
       {
        // cnt1=cnt;
         TCNT1 = 0;
         TCCR1A=0; // выключение импульсов??
         //TCCR1B=0;
          if (buttonState2 == HIGH) { ICR1+=300; } // частота
          if (buttonState3 == HIGH) { ICR1-=300;}
       }
          if (mode==1)
       {
       // cnt1=cnt1;
         TCCR1A=(1<<COM1A1)|(1<<WGM11);
         //TCCR1B=(1<<WGM13)|(1<<WGM12)|(1<<CS11)|(1<<CS10);// включение импульсов
         // cnt = freq*x; //cnt++;
           if (buttonState2 == HIGH) { OCR1A+=25; } // длительность
           if (buttonState3 == HIGH) { OCR1A-=25;}
         }
       x=millis()/200;
       Serial.println(cnt2);
      }
    }
    Так в монитор сериал порта выдает более-менее правдоподобные данные.
    Остался вопрос - это правильно или можно как-то проще?
    По поводу разбивки одиночного импульса на пачку, буду благодарен хотя бы на намек.
     
  4. mcureenab

    mcureenab Гуру

    Хм. ШИМ сам по себе ток не регулирует. Разве что с индуктивной нагрузкой.
     
  5. Sergei K

    Sergei K Нерд

    Абсолютно точно. Нагрузка индуктивная
     
  6. AlexU

    AlexU Гуру

    Чуток перемудрили. Если правильно понимаю цель, то нужно считать импульсы пока они генерируются:
    Обработчик прерывания 'TIMER1_COMPA_vect' будет вызываться только пока "идут" импульсы. Поэтому ни каких проверок в обработчике делать не нужно и ни каких дополнительных переменных не нужно.
    Это не много странная проверка. Условие почти всегда будет истиной. Зачем такая проверка нужна?
    F_CPU лучше делить на целое 64 (а не с плавающей точкой) -- погрешности не будет -- т.к. результатом такого деления будет целое число. Вот если бы делили на 63, тогда, да, надо переводить в числа с плавающей точкой. Ну и так понимаю, больше Вам это деление не нужно.
    Самое первое, что приходит на ум -- это использование модулятора. Нужен генератор ШИМ с частотой, которую хотите подавать на полевик. Этот генератор ШИМ и вывод 9 контроллера (выход текущего генератора ШИМ) подключаются к логическому элементу "И". На выходе элемента "И" будет ШИМ с частотой генератора только тогда, когда на выходе 9 будет в наличии высокий уровень.
    Или можно на 555 реализовать аналогичный функционал.