2 аналоговых датчика, датчик пульса и проблема LM35

sensors temperature

Я делаю проект, в котором использую два аналоговых датчика. Датчик пульса и датчик температуры LM35. Когда я использую их отдельно, все нормально, коды в порядке, когда я их комбинирую, lm35 не точен, имеет погрешность около 10 градусов, но датчик пульса, кажется, в порядке. Я читал что-то об аналого-цифровом преобразователе, который есть у Arduino, и ему нужна задержка или что-то еще, чтобы обмануть АЦП. Буду признателен, если кто-нибудь поможет мне изменить код. Большое спасибо!

#include <LiquidCrystal.h>
#include <TonePlayer.h>

const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

int pulsePin = A0;                 // Фиолетовый провод датчика пульса, подключенный к аналоговому контакту A0
int blinkPin = 13;                // контакт для мигания светодиода при каждом ударе

const int sensor=A5; // Назначаем аналоговый контакт A5 на переменную «датчик» температуры
float tempc;
float vout; //временная переменная для хранения показаний датчика

TonePlayer tone1 (TCCR1A, TCCR1B, OCR1AH, OCR1AL, TCNT1H, TCNT1L);  // контакт D9 (Уно), D11 (Мега)

// Изменчивые переменные, используемые в подпрограмме обслуживания прерывания!
volatile int BPM;                   // целое число, которое содержит необработанный аналог в 0. обновляется каждые 2 мс
volatile int Signal;                // содержит входящие необработанные данные
volatile int IBI = 600;             // int, который содержит временной интервал между ударами! Сеять надо!
volatile boolean Pulse = false;     // «Истинно», когда обнаружено живое сердцебиение пользователя. «Ложь», если это не «живой бит».
volatile boolean QS = false;        // становится истинным, когда Arduoino находит бит.

static boolean serialVisual = true;   // По умолчанию установлено значение «false». Снова установите значение «true», чтобы увидеть визуальный пульс Arduino Serial Monitor ASCII.

volatile int rate[10];                      // массив для хранения последних десяти значений IBI
volatile unsigned long sampleCounter = 0;          // используется для определения времени импульса
volatile unsigned long lastBeatTime = 0;           // используется для поиска IBI
volatile int P = 512;                      // используется для нахождения пика в пульсовой волне, с затравкой
volatile int T = 512;                     // используется для поиска впадины в пульсовой волне, засеянной
volatile int thresh = 525;                // используется для нахождения мгновенного момента сердцебиения, засеяно
volatile int amp = 100;                   // используется для хранения амплитуды импульсного сигнала, заполненного
volatile boolean firstBeat = true;        // используется для заполнения массива скорости, поэтому мы запускаем с разумным BPM
volatile boolean secondBeat = false;      // используется для заполнения массива скорости, поэтому мы запускаем с разумным BPM


void setup() {

  pinMode(blinkPin,OUTPUT);         // контакт, который будет мигать в такт вашему сердцу!
  pinMode (9, OUTPUT);  // выходной контакт фиксирован (OC1A)

  Serial.begin(115200);             // мы согласны говорить быстро!
  interruptSetup();                 // настраивает чтение сигнала датчика пульса каждые 2 мс
                                    // ЕСЛИ ВЫ ПОДАЕТЕ ПИТАНИЕ НА ДАТЧИК ИМПУЛЬСОВ ПРИ НАПРЯЖЕНИИ МЕНЬШЕ, ЧЕМ НАПРЯЖЕНИЕ НА ПЛАТЕ,
                                    // ОТКОММЕНТИРУЕМ СЛЕДУЮЩУЮ СТРОКУ И ПОДАЕМ ЭТО НАПРЯЖЕНИЕ НА ВЫВОД A-REF

  pinMode(sensor,INPUT); // Конфигурация контакта A1 в качестве входа // AnalogReference(EXTERNAL);

  lcd.begin(16, 2);
  lcd.clear();
}


// Где происходят чудеса
void loop() {
  serialOutput();  
  if (QS == true) { // Было найдено сердцебиение
    // BPM и IBI определены
    // Quantified Self "QS" true, когда arduino находит сердцебиение
    serialOutputWhenBeatHappens(); // Случился бит, выведите его в последовательный порт.
    QS = false; // сброс флага Quantified Self для следующего раза

    vout=analogRead(sensor);
    vout=(vout*500)/1023;
    tempc=vout; // Сохранение значения в градусах Цельсия

    Serial.print ("Temperatura: ");
    Serial.print(tempc);
    Serial.println(" C ");

  }
  delay(20); // сделать перерыв
}


void interruptSetup() {     
  // Инициализирует Timer2 для создания прерывания каждые 2 мс.
  TCCR2A = 0x02;     // ОТКЛЮЧИТЬ ШИМ НА ЦИФРОВЫХ ВЫВОДАХ 3 И 11 И ПЕРЕХОДИТЬ В РЕЖИМ CTC
  TCCR2B = 0x06;     // НЕ ПРИНИМАТЬ СРАВНЕНИЕ, 256 PRESCALER
  OCR2A = 0X7C;      // УСТАНОВИМ НАЧАЛЬНУЮ ЧАСТЬ СЧЁТЧИКА НА 124 ДЛЯ ЧАСТОТЫ ВЫБОРКИ 500 Гц
  TIMSK2 = 0x02;     // ВКЛЮЧАЕМ ПРЕРЫВАНИЕ ПРИ СОВПАДЕНИИ МЕЖДУ TIMER2 И OCR2A
  sei();             // УБЕДИТЕСЬ, ЧТО ГЛОБАЛЬНЫЕ ПРЕРЫВАНИЯ РАЗРЕШЕНЫ
} 

void serialOutput() {   // Решаем, как выводить серийный номер.
 if (serialVisual == true){  
     arduinoSerialMonitorVisual('-', Signal);   // переходит к функции, которая делает Serial Monitor Visualizer
  } else{
      sendDataToSerial('S', Signal);     // переходит к функции sendDataToSerial
   }        
}

void serialOutputWhenBeatHappens() {    
 if (serialVisual == true) { // Код для работы визуализатора Serial Monitor

     Serial.print("Batai ");  // Безумие искусства ASCII
     Serial.print("BPM: ");
     Serial.println(BPM);
     lcd.print("Batai ");
     lcd.setCursor(6,0);
     lcd.print("BPM: ");
     lcd.setCursor(10,0);
     lcd.print(BPM);


     lcd.setCursor(0,1);
     lcd.print("Temp: ");
     lcd.setCursor(6,1);
     lcd.print(tempc);
     lcd.setCursor(11,1);
     lcd.print("C ");

     delay(300);
     lcd.clear();
   } else {
     sendDataToSerial('B',BPM);   // отправляем частоту сердечных сокращений с префиксом «B»
     sendDataToSerial('Q',IBI);   // отправляем время между ударами с префиксом 'Q'
   }   
}

void arduinoSerialMonitorVisual(char symbol, int data ) {    
  const int sensorMin = 0;      // минимум сенсора, обнаруженный экспериментально
  const int sensorMax = 1024;    // максимум сенсора, обнаруженный экспериментально
  int sensorReading = data; // сопоставляем диапазон датчика с диапазоном из 12 вариантов:
  int range = map(sensorReading, sensorMin, sensorMax, 0, 11);
  // делаем что-то другое в зависимости от
  // значение диапазона:
}


void sendDataToSerial(char symbol, int data ) {
   Serial.print(symbol);
   Serial.println(data);                
}

ISR(TIMER2_COMPA_vect) { // срабатывает, когда Timer2 считает до 124

  cli();                                      // отключаем прерывания, пока делаем это
  Signal = analogRead(pulsePin);              // чтение датчика пульса
  sampleCounter += 2;                         // отслеживать время в миллисекундах с помощью этой переменной
  int N = sampleCounter - lastBeatTime;       // отслеживаем время с момента последнего удара, чтобы избежать шума
                                              // найти пик и впадину пульсовой волны
  if(Signal < thresh && N > (IBI/5)*3) { // избегаем дихротического шума, ожидая 3/5 последнего IBI
    if (Signal < T) { // T — впадина
      T = Signal; // отслеживаем самую низкую точку пульсовой волны
    }
  }

  if(Signal > thresh && Signal > P) {       // условие thresh помогает избежать шума
    P = Signal;                             // P — пик
  }                                        // отслеживаем наивысшую точку пульсовой волны

  // ТЕПЕРЬ ПРИШЛО ВРЕМЯ УЗНАТЬ БИЕНИЕ СЕРДЦА
  // значение сигнала возрастает каждый раз, когда есть импульс
  if (N > 250) {                                   // избегаем высокочастотного шума
    if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) ) {        
        Pulse = true;                               // устанавливаем флаг Pulse, когда мы думаем, что есть пульс
        digitalWrite(blinkPin,HIGH);                // включить светодиод на контакте 13
        tone1.tone (1000);  // 1000 Гц

        IBI = sampleCounter - lastBeatTime;         // измеряем время между ударами в мс
        lastBeatTime = sampleCounter;               // отслеживать время следующего импульса

        if(secondBeat) {                        // если это вторая доля, if secondBeat == TRUE
          secondBeat = false;                   // очистить флаг secondBeat
          for(int i=0; i<=9; i++) { // начальное значение промежуточного итога для получения реалистичного BPM при запуске
            rate[i] = IBI;                      
          }
        }

        if(firstBeat) { // если бит найден впервые, if firstBeat == TRUE
          firstBeat = false;                   // очистить флаг firstBeat
          secondBeat = true;                   // устанавливаем второй флаг долей
          sei();                               // снова разрешаем прерывания
          return;                              // Значение IBI ненадежно, поэтому отбрасываем его
        }   
      // сохраняем промежуточный итог последних 10 значений IBI
      word runningTotal = 0;                    // очищаем переменную runningTotal

      for(int i=0; i<=8; i++) {                 // сдвигаем данные в массиве ставок
          rate[i] = rate[i+1];                  // и удалить самое старое значение IBI
          runningTotal += rate[i];              // суммируем 9 самых старых значений IBI
        }

      rate[9] = IBI;                          // добавляем последний IBI в массив ставок
      runningTotal += rate[9];                // добавить последний IBI в runningTotal
      runningTotal /= 10;                     // среднее значение последних 10 значений IBI
      BPM = 60000/runningTotal;               // сколько ударов может уместиться в минуту? это БПМ!
      QS = true;                              // установить флаг Quantified Self
      // ФЛАГ QS НЕ СНЯТ ВНУТРИ ЭТОЙ ISR
    }                       
  }

  if (Signal < thresh && Pulse == true) {    // когда значения падают, бит закончен
      digitalWrite(blinkPin,LOW);            // выключаем светодиод на контакте 13
      tone1.noTone ();
      Pulse = false;                         // сбрасываем флаг Pulse, чтобы мы могли сделать это снова
      amp = P - T;                           // получаем амплитуду пульсовой волны
      thresh = amp/2 + T;                    // устанавливаем порог на 50% от амплитуды
      P = thresh;                            // сбросить их для следующего раза
      T = thresh;
    }

  if (N > 2500) {                          // если 2,5 секунды проходят без такта
    thresh = 512;                          // устанавливаем порог по умолчанию
    P = 512;                               // установить P по умолчанию
    T = 512;                               // устанавливаем Т по умолчанию
    lastBeatTime = sampleCounter;          // обновить lastBeatTime
    firstBeat = true;                      // установите их, чтобы избежать шума
    secondBeat = false;                    // когда мы получим сердцебиение
  }
  sei();                                   // разрешить прерывания, когда закончите!
}// конец isr

, 👍-1

Обсуждение

Когда вы используете датчики отдельно, вы все еще используете этот код, который вы опубликовали? Если нет, то чем отличается отдельный код, особенно для LM35, от приведенного выше?, @JRobert

Я до сих пор использую этот код, моя проблема в том, что я не знаю, как правильно их комбинировать. код только для LM35 следующий. const int sensor=A5; плавающая температура; плавающий выход; недействительная установка () { Серийный.начать(115200); } недействительный цикл () { vout = аналоговое чтение (датчик); vвых=(vвых*500)/1023; темп=выход; // Сохранение значения в градусах Цельсия Serial.print("Температура: "); Serial.print(tempc); Serial.println("С"); задержка(1000); } , @Lucian Petcu


1 ответ


2

У меня есть несколько предложений:

Комбинированный код сильно отличается от работающего простого кода LM35. Я предлагаю вам начать с комбинированного кода, но закомментировать все, что напрямую не относится к чтению LM35, и заставить его работать правильно.

Подпрограмма обслуживания прерываний (ISR) пытается сделать слишком много и оставляет прерывания выключенными во время всех этих вычислений. Кроме того, трудно отлаживать код внутри ISR! ISR должны выполнять минимальную работу, которую они должны выполнять. В этом случае похоже, что ему нужно читать & сохраните значение датчика пульса и выполните вычисления N, lastBeatTime и sampleCounter. Пусть он также устанавливает флаг каждый раз, когда он запускается.

Основной цикл должен проверить этот флаг и, если он установлен, снять его и вызвать другую функцию, чтобы сделать все, что вы удалили из ISR. Отладьте этот код, возможно, закомментировав LM35.

В идеале ваша функция loop() должна быть такой простой, как:

void loop(){
   Timers.run();
   maybeCalcHeartbeat();
   maybeDoOtherSubtask();
   ...
}

Затем остальная часть вашего кода переходит в функции "может быть". «ВозможноСделать» просто решает, подходят ли время или условия для того, чтобы сделать конкретную вещь; если да, то функция это делает; но в любом случае возвращается быстро:

void maybeCalcHeartbeat(void){
   if( !isrFlag )
      return;
   else {
      // все, что вы вынули из ISR, идет сюда //

      isrFlag = false;
   }
,