Ошибка прерывания проекта Arduino?

У меня есть проект, который нужно сделать, например, система выставления счетов за воду с использованием RFID (Mfrc522) на Arduino NANO. Есть одна трудность, которую я не смог преодолеть. Я надеюсь, что кто-то может мне помочь. Ход проекта:

  1. вода или любая другая жидкость проходит через датчик, и регистрируется количество воды
  2. теперь в зависимости от количества пролитой воды определяется цена
  3. эта цена должна быть вычтена путем считывания RFID-карты проблема:
  4. я не могу управлять прерываниями
  5. моя программа вычисляет расход воды требуется:
  6. отредактируйте код таким образом, чтобы, как только через него проходит вода, он вычислял и предлагал пользователю провести карту, а когда пользователь проводит пальцем, сумма вычитается, и процесс продолжается.

если непонятно, спросите и помогите мне

#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN         9          // Настраиваемый, см. типичное расположение выводов выше
#define SS_PIN          10         // Настраиваемый, см. типичное расположение выводов выше
MFRC522 mfrc522(SS_PIN, RST_PIN);
String tagUID = "10 90 70 38";  // Строка для хранения UID тега. Измените его на UID вашего тега
int bal =1000;

byte statusLed    = 13;
byte sensorInterrupt = 0;  // 0 = цифровой контакт 2
byte sensorPin       = 2;

// Датчик расхода на эффекте Холла выдает примерно 4,5 импульса в секунду за
// литр/минута расхода.
float calibrationFactor = 4.5;

volatile byte pulseCount;  

float flowRate;
unsigned int flowMilliLitres;
unsigned long totalMilliLitres;

unsigned long oldTime;


void setup()
{

  // Инициализировать последовательное соединение для передачи значений хосту
  Serial.begin(9600);
  pinMode(sensorPin, INPUT);
  digitalWrite(sensorPin, HIGH);

  pulseCount        = 0;
  flowRate          = 0.0;
  flowMilliLitres   = 0;
  totalMilliLitres  = 0;
  oldTime           = 0;
  // Датчик Холла подключен к контакту 2, который использует прерывание 0.
  // Настроен на срабатывание при изменении состояния FALLING (переход из HIGH
  // состояние в состояние LOW)
  attachInterrupt(sensorInterrupt, pulseCounter, FALLING);
  Serial.begin(9600);    // Инициализируем последовательную связь с ПК
  while (!Serial);
  SPI.begin();      // Инициируем шину SPI
  mfrc522.PCD_Init();   // Инициализация MFRC522
}

/**
 * Main program loop
 */
void loop()
{

   if((millis() - oldTime) > 2000)    // Обрабатывать счетчики только один раз в секунду
  { 
    // Отключить прерывание при расчете расхода и отправке значения в
    // гостья
    detachInterrupt(sensorInterrupt);

    // Поскольку этот цикл может не завершиться ровно через 1 секунду, мы вычисляем
    // количество миллисекунд, прошедших с момента последнего выполнения и использования
    // это для масштабирования вывода. Мы также применяем коэффициент калибровки для масштабирования выходных данных.
    // исходя из количества импульсов в секунду в единицах измерения (литров/минуту в
    // в данном случае) поступающие от датчика.
    flowRate = ((1000.0 / (millis() - oldTime)) * pulseCount) / calibrationFactor;

    // Обратите внимание на время выполнения этого прохода обработки. Обратите внимание, что поскольку мы
    // отключенные прерывания, функция millis() на самом деле не будет правильно увеличиваться
    // в этот момент, но он все равно вернет значение, которое было установлено непосредственно перед
    // прерывания исчезли.
    oldTime = millis();

    // Разделите скорость потока в литрах/минуту на 60, чтобы определить, сколько литров было
    // прошел через датчик за этот 1-секундный интервал, затем умножить на 1000, чтобы
    // конвертировать в миллилитры.
    flowMilliLitres = (flowRate / 60) * 1000;

    // Добавляем миллилитры, пройденные за эту секунду, к общему итогу
    totalMilliLitres += flowMilliLitres;

    unsigned int frac;

    // Вывести скорость потока за эту секунду в литрах/минуту
    Serial.print("Flow rate: ");
    Serial.print(int(flowRate));  // Выводим целую часть переменной
    Serial.print("L/min");
    Serial.print("\t");       // Печать табуляции

    // Вывести общее количество литров, пролитых с момента запуска
    Serial.print("Output Liquid Quantity: ");        
    Serial.print(totalMilliLitres);
    Serial.println("mL"); 
    Serial.print("\t");       // Печать табуляции
    Serial.print(totalMilliLitres/1000);
    Serial.print("L");
    rfid();


    // Сброс счетчика импульсов, чтобы мы могли снова начать приращение
    pulseCount = 0;

    // Разрешить прерывание снова теперь, когда мы закончили отправку вывода
    attachInterrupt(sensorInterrupt, pulseCounter, FALLING);
  }
}

/*
Insterrupt Service Routine
 */
void pulseCounter()
{
  // Увеличиваем счетчик импульсов
  pulseCount++;
}

void rfid()
{
   // поместите сюда ваш основной код для многократного запуска:


  Serial.println("In Rfid Loop");
  delay(3000);
  if ( ! mfrc522.PICC_IsNewCardPresent()) {
        return;
      }
      // Выбираем одну из карт
      if ( ! mfrc522.PICC_ReadCardSerial()) {
        return;
      }
  //Чтение с карты
      String tag = "";
      for (byte j = 0; j < mfrc522.uid.size; j++)
      {
        tag.concat(String(mfrc522.uid.uidByte[j] < 0x10 ? " 0" : " "));
        tag.concat(String(mfrc522.uid.uidByte[j], HEX));
      }
      tag.toUpperCase();
       if (tag.substring(1) == tagUID)
      {
        bal=bal-20;
        delay(100);
        if(bal>=30)
        {
          Serial.println(bal);
          Serial.println("YOUR BALANCE");
          delay(400);

          // Сброс счетчика импульсов, чтобы мы могли снова начать приращение
          //число импульсов = 0;

          // Разрешить прерывание снова теперь, когда мы закончили отправку вывода
          //attachInterrupt(sensorInterrupt, pulseCounter, FALLING);
        }
       else
        {
          Serial.println("INSUFFICIENT BALANCE PLEASE RECHARGE");
         }
        delay(100);

      }
      else
      {
        Serial.println("WRONG CARD ENTERED");

       }
}

, 👍2

Обсуждение

А в чем у вас проблема с прерываниями? «не удалось управлять прерываниями» — не очень хорошее описание проблемы. Ожидали ли вы, что ваш код будет работать, и что он на самом деле сделал вместо этого?, @chrisl

У вас отключены прерывания большую часть времени. Если вы хотите использовать прерывание, вам нужно его подключить. Вместо того, чтобы отключаться на весь цикл, отсоединитесь только на очень короткое время, сделайте копию переменной pulseCount, а затем снова включите прерывания. Затем используйте копию, которую вы сделали в расчетах., @Delta_G


1 ответ


1

Вы слишком долго отключаете прерывания. Прерывания предназначены для обработка критичных ко времени задач, которые не могут дождаться полной итерации цикла. Если вы когда-нибудь отключите их, это должно быть как можно короче. В вашем случае вам нужно только отключить прерывания при доступе к pulseCount, так как эта переменная совместно используется ISR и обычный код. Это можно сделать в очень коротком «критическом разделе», т.к. следует:

noInterrupts();
uint8_t pulseCountCopy = pulseCount;
pulseCount = 0;
interrupts();

В остальной части цикла вы получаете доступ только к pulseCountCopy. Обратите внимание, что вам не нужно сбрасывать pulseCount на ноль. Так как это увеличивается с использованием правил модульной арифметики, вы можете просто вычислить различия между последовательными показаниями. Вы получите правильный результат, даже когда он переворачивается. Это делает критическую секцию даже короче:

static byte oldPulseCount;
noInterrupts();
uint8_t pulseCountCopy = pulseCount;
interrupts();
uint8_t pulseIncrement = pulseCountCopy - oldPulseCount;
oldPulseCount = pulseCountCopy;

Затем обратите внимание, что, поскольку чтение одного байта является атомарной операцией (одна машинная инструкция), вам даже не нужно отключать прерывает всех! Однако, если поток когда-либо может быть больше, чем 0,47 л/с, то 8 бит будет недостаточно для pulseCount. Ты придется использовать 16-битную переменную (uint16_t) и сохранить критическую раздел.

Кроме того, вы вызываете millis() несколько раз за цикл. итерация. Это плохая практика, так как нет гарантии, что вы всегда получайте одно и то же значение, поэтому вы в конечном итоге вычисляете время быть несколько неряшливым. Вам лучше сделать что-то вроде

uint32_t now = millis();

в начале loop() и использовать одно и то же значение для всего итерация.

Еще одно замечание: вы дифференцируете количество импульсов, чтобы получить скорость потока, а затем интегрировать скорость потока, чтобы получить общий объем. Это совершенно бесполезно. Подсчет пульса уже является мерой общий объем. Базовые арифметические действия показывают, что калибровочный коэффициент равен

4,5 (импульсов/с) / (л/мин) = 270 импульсов/л

Ваши расчеты будут менее небрежными (с меньшим накоплением ошибок), если Вы просто делите общее количество импульсов на 270, чтобы получить общий объем в литров.

,