Два счетчика оборотов с прерываниями

Можно ли использовать два прерывания для подсчета прямоугольных сигналов об/мин, поступающих от двух модулей датчика Холла (со встроенными компараторами для генерации прямоугольных сигналов)?

Что делать, если два сигнала объединяются? Будут ли они конфликтовать друг с другом?

Будет ли это работать на attiny85?

Раньше я использовал два прерывания для чтения энкодеров, где два сигнала никогда не конфликтовали друг с другом

Заранее спасибо

, 👍0

Обсуждение

Какую максимальную частоту имеют прямоугольные волны? Если вы действительно не рискуете потерять какой-либо фронт сигналов, вы можете использовать их в качестве тактового входа для 2 таймеров ATTiny., @chrisl

Спасибо за ваш комментарий, Крисл ... У меня максимальный ввод 40 Гц, но на самом деле я работаю с языком программирования Arduino, поэтому мне не пришла в голову идея использовать 2 таймера ..., @Elias Taalab

О, во время написания ответа я обнаружил, что только 1 из двух таймеров имеет входной контакт счетчика, поэтому он не будет работать на ATtiny85. Я добавлю, что мой ответ., @chrisl

Вы можете использовать INT0 и PCINT. Аппаратное обеспечение выполнит проверку сигнала и установит флаг. Когда любой из этих флагов установлен, ЦП вызывает ISR. Если оба сигнала происходят одновременно, будут установлены оба флага, и ЦП будет вызывать их ISR один за другим. Вы не потеряете никаких сигналов, если только ISR не займет больше времени, чем время между сигналами. Единственная небольшая проблема заключается в том, что ISR не обязательно вызывается в точное время события., @Gerben


2 ответа


Лучший ответ:

0

Существует более 1 варианта, как это можно сделать. Частота 40 Гц относительно медленная, поэтому вы не ограничены этим.

В своем комментарии я сначала предложил использовать таймеры/счетчики с входным сигналом, но обнаружил, что это возможно только с одним из двух таймеров ATtiny. Несмотря на это, я добавлю это к ответу для информации (и потому, что это возможно на других платах с большим количеством входных контактов таймера).

  • Использование прерывания PinChange и программный подсчет: Поскольку ATtiny85 имеет только 1 внешнее прерывание, для этого необходимо использовать прерывание PinChange. Если активировано, это прерывание сработает, когда один из контактов от 0 до 5 изменит свое значение. Вы можете замаскировать любой контакт в этом диапазоне, чтобы он не учитывался. Вы бы использовали 2 контакта и замаскировали остальные. В процедуре прерывания вы должны проверить состояние двух контактов и увеличить соответствующий счетчик:

    volatile unsigned int counter1=0;
    volatile unsigned int counter2=0;
    
    ISR(PCINT0){
        if(digitalRead(input_pin_1)) counter1++;
        if(digitalRead(input_pin_2)) counter2++;
    }
    

    (Обратите внимание на объявление переменных volatile, поскольку они изменяются внутри ISR). Затем переменные counterX могут считываться через определенные промежутки времени (например, каждую секунду) для расчета RPM. Конечно, выполнение кода в ISR может означать, что вы пропустите другое прерывание. В течение короткого периода времени между первым прерыванием и завершением выполнения ISR никакое другое прерывание PinChange не будет зарегистрировано. Если это проблема, то она зависит от того, как часто импульсы будут находиться в этом небольшом диапазоне. Если это происходит очень редко, вы можете игнорировать это, так как это приводит к очень небольшим ошибкам. Вы даже можете ускорить ISR, не используя digitalRead(), выполнение которого занимает немного времени, а используя прямой Управление Prt. Это делает ошибку маловероятной.

    Для этого должны быть настроены прерывания, поэтому вам нужен такой код настройки (это будет использовать прямое управление регистрами состояния ATtiny):

    PCMSK = 0b000110; // Маскируем все выводы для этого прерывания, кроме PCINT1 и PCINT2
    GIFR &= ~(1 << PCIF); // Сброс флага прерывания PinChange
    GIMSK |= (1 << PCIE); //Включить прерывание PinChange
    

    В функции цикла вы можете использовать стиль неблокирующего кода из примера BlinkWithoutDelay, чтобы регулярно вычислять RPM на основе значений счетчика;

    void loop(){
        if(millis()-timestamp > INTERVAL){
            GIMSK &= ~(1 << PCIE); // Отключаем прерывание PinChange, чтобы нас не прерывало ti
            unsigned int countervalue1 = counter1;
            unsigned int countervalue2 = counter2;
            counter1=0;
            counter2=0;
            IMSK |= (1 << PCIE); //Повторно разрешаем прерывание PinChange
            // Выполняем расчет здесь с помощью countervalueX и показываем где-нибудь результат
        }
    }
    

    Важно отключить соответствующее прерывание, когда вы обращаетесь к переменным counterX, потому что вы должны предотвратить их изменение в процессе обработки. Короче говоря, они просто копируются в локальные переменные, а затем снова освобождаются.


Внимание: следующее предложение не работает на ATtiny85, так как у него есть только 1 входной контакт таймера.

  • Использование как доступных таймеров/счетчиков, так и сигналов в качестве тактового входа: Если вы действительно не хотите пропускать импульсы, вы можете использовать два таймера/счетчика. Таймеры и счетчики одинаковы. Счетчик увеличивает значение регистра состояния на каждый спадающий или нарастающий фронт (в зависимости от конфигурации) сигнала. Если этот сигнал поступает от часов микросхемы, это называется таймером. Вы можете использовать его как счетчик, подавая каждую прямоугольную волну на один входной контакт счетчика. Чтобы фактически получить RPM, вы можете либо проверить значение счетчиков (TCNT0) через определенный интервал времени, либо вы можете активировать прерывание по переполнению обоих таймеров и измерить время между переполнениями.

    Поскольку определение края осуществляется аппаратно, ни один импульс не может быть пропущен. С другой стороны, этот способ требует дополнительной настройки и блокирует 2 драгоценных таймера.


При работе с регистрами состояния таким прямым способом техническое описание ATtiny85 обязательно к прочтению.

,

Хорошо, я думаю, что использование Arduino pro mini будет достаточно дешевым, если я захочу использовать второй метод (метод двух таймеров), и он будет точным ... Как вы думаете, если я хочу добавить i2c или последовательный порт в код?, @Elias Taalab

Это не должно быть проблемой для Pro mini, так как у него есть аппаратный последовательный порт и аппаратный I2C. ATtiny нет, поэтому Pro mini действительно была бы лучшей платой., @chrisl

Если я использую два прерывания, будет ли это конфликтовать с кодом Arduino (например, если у меня есть три тензодатчика, которые мне нужно прочитать или записать значение в сервопривод) ?? .. мой первоначальный пост был об использовании attiny85 (или atmega328 в arduino pro mini) для отправки значений оборотов в мастер arduino, который соберет все значения и сделает некоторые выводы .., @Elias Taalab

Насколько я помню, библиотека сервоприводов использует Timer1, а Timer 0 обычно используется для функций синхронизации Arduino (задержка, миллисекунды и братья и сестры). Также было бы неплохо использовать это ведомое устройство Arduino для чтения оборотов в минуту. Или более крупный Arduino с большим количеством таймеров., @chrisl


0

Если вы можете не использовать прерывания, ваш Tiny все равно может выполнять свою работу и не пропускать ни одного импульса, если функция loop() может выполняться с частотой >40 Гц, а импульсы не являются чрезвычайно короткими "пиками", а имеют разумный рабочий цикл (или, если он короткий, вы можете быстрее сэмплировать):

// данные вывода
static struct {
   uint8_t pinNum;
   bool wasHi;       // последнее состояние
   unsigned count;   // количество импульсов
} pinsDat[2] = {
   {input_pin_1, false, 0},
   {input_pin_2, false, 0},
};


void setup(){
   ; // ваша установка здесь
}


void loop(){
   countPulses();
   // ...
}

void countPulses(){
   for( uint8_t i = 0; i < 2; ++i){
      if( !digitalRead(pinsDat[i].pinNum) )
         isHigh1 = false;       // pin lo, просто сохраните его состояние
      else if( !pinsDat[i].wasHi ){
         ++pinsDat[i].count;    // прикрепить привет; считать это
         ++pinsDat[i].wasHi;    // сохраняем его состояние
      }
   }
}
,