Проблема с подключением датчика сердцебиения и температуры на arduino uno, но работает по отдельности

Таким образом, код работает отдельно для датчика температуры и датчика сердцебиения, но когда я объединяю его вместе, он показывает неверные значения. для датчика температуры

Я использую датчик температуры lm35 и питание от USB

#include <LiquidCrystal.h>


const int rs = 13, en = 12, d4 = 11, d5 = 10, d6 = 9, d7 = 8;
LiquidCrystal lcd(13, 12, 11, 10, 9, 8);

int pulsePin = A0;


int blinkPin = 5;                // вывод для мигания светодиода при каждом ударе

// Изменчивые переменные, используемые в подпрограмме обслуживания прерывания!
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

const int sensor = A5;
int tempc ;
float vout;

void setup()
{
  vout =analogRead(sensor); 
  pinMode(sensor,INPUT);
  pinMode(blinkPin,OUTPUT);         // вывод, который будет мигать в такт вашему сердцу!
  Serial.begin(9600);             // мы согласны говорить быстро!
  interruptSetup();                 // настраивает чтение сигнала датчика пульса каждые 2 мс
                                  // ЕСЛИ ВЫ ПОДАЕТЕ ПИТАНИЕ НА ДАТЧИК ИМПУЛЬСОВ ПРИ НАПРЯЖЕНИИ МЕНЬШЕ, ЧЕМ НАПРЯЖЕНИЕ НА ПЛАТЕ,
                                    // ОТКОММЕНТИРУЕМ СЛЕДУЮЩУЮ СТРОКУ И ПОДАЕМ ЭТО НАПРЯЖЕНИЕ НА ВЫВОД A-REF
                                    // аналоговая ссылка (ВНЕШНЯЯ);
 lcd.begin(16, 4);
 lcd.clear();
}


// Где происходят чудеса
void loop()
{

lcd.setCursor(0,0);
lcd.print(" T in c = ");
lcd.print(tempc);
lcd.setCursor(0,1); 

delay(100);
   serialOutput();  

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

    }

  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) // Код для работы визуализатора последовательного монитора
   {   

     int din ;       
      din = analogRead(4);

     Serial.print(BPM);
     Serial.print(", ");
     Serial.print(tempc);
     Serial.print(", ");
     Serial.print(din);
     Serial.print(", ");

     Serial.println();
     lcd.setCursor(0,2);
     lcd.print("Heart-Beat Found ");
     lcd.setCursor(6,3);
     lcd.print(BPM);
     delay(100);
     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
        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
      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

, 👍2


3 ответа


2

Кажется, в коде отсутствует повторяющееся обновление tempc через

vout =analogRead(sensor);

Альтернативой может быть изменение строки для установки tempc на

tempc=(int) (analogRead(sensor) * 500.0f/1023.0f);
,

спасибо за ваш ответ, я все еще новичок, не могли бы вы объяснить больше?, @Aref Ben Lazrek

Добавьте, что vout = строка между QS = false; и tempc = и дайте мне знать, если проблема не устранена. По сути, вам нужно обновить vout, прежде чем вы вычислите из него tempc. AnalogRead — это однократное чтение на контакте., @Abel

Спасибо, постараюсь держать вас в курсе, @Aref Ben Lazrek

извините за поздний ответ, я попробовал ваши предложения, но, к сожалению, не получил никакого результата. Я думаю, что проблема связана с инструкциями interrupts() или timer2, потому что когда я добавляю cli() (отключить прерывание) перед vout= аналоговое чтение (датчик); температура дает мне правильное значение, но датчик пульса перестает работать. это то, что я сейчас получаю https://www.youtube.com/watch?v=dBPKmYd08U8 спасибо, @Aref Ben Lazrek


1

Почему вы вычисляете tempc только после того, как распечатаете значение в serialOutputWhenBeatHappens`? Вы также забыли постоянно считывать температуру.

Я бы сделал это следующим образом:

void loop()
{
  // считываем датчик до того, как он будет напечатан на ЖК-дисплее
  vout  = analogRead( sensor ); 

  // здесь я не уверен, что вы вычисляете
  // вы сопоставляете напряжение датчика с диапазоном от 0°C до 500°C
  // вы измеряете сердцебиение и температуру дракона? ;-)
  // эта формула такая же, как у вас
  tempc = ( vout / 1023.0 ) * 500.0;

  // Я бы также очистил ЖК-дисплей, прежде чем писать на него
  lcd.clear();

  lcd.setCursor( 0, 0 );
  lcd.print( "T in c = " );
  lcd.print( tempc );
  lcd.setCursor( 0,1 ); 

  // Здесь нет необходимости замедлять цикл.
  // пусть работает...
  // delay(100);

  serialOutput();  

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

     // сброс флага Quantified Self для следующего раза
     QS = false; 
  }

  // сделать перерыв (почему?)
  // delay(20);
}
,

1

Если предоставленный источник урезан до строк, связанных с температурой, то это именно так. Если вы попробуете этот скетч, он должен вести себя так же "неправильно":

const int sensor = A5;
int tempc;
float vout;

void setup()
{
    vout = analogRead(sensor); 
    pinMode(sensor, INPUT);
}

void loop()
{
    lcd.print(tempc);
    tempc = (vout * 500) / 1023;
}

В loop() значение tempc печатается до его пересчета из vout.

Но vout записывается только один раз в setup().

Таким образом, "неправильные" значения, о которых вы говорите, всегда являются одним и тем же значением, которое было измерено во время запуска. (Вы не упомянули, что не так.)


Примечание. Как описано в справке по библиотеке Arduino, нет необходимости устанавливать вывод в режим INPUT, поскольку он используется по умолчанию.


Я прочитал ваш комментарий к ответу @Abel и даже посмотрел ваше видео. Поскольку непонятно, что вы сделали, я ничего не могу сказать, почему у вас сейчас прыгающие значения. (Пожалуйста, напишите немного прозы и покажите новый код в своем вопросе в следующий раз.)

Если мы применим его совет, исходник будет выглядеть так. Я удалил настройку режима вывода (см. выше) и сделал последовательность инструкций в loop() более логичной.

const int sensor = A5;
int tempc;
float vout;

void setup()
{
}

void loop()
{
    vout = analogRead(sensor); 
    tempc = (vout * 500) / 1023;
    lcd.print(tempc);
}

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


Дополнительные примечания:

  1. analogRead() возвращает int. Вы присваиваете его float, так что это неявное преобразование.
  2. Этот float удобен, потому что сопоставление выполняется "вручную". Если бы вы использовали map(), это мог бы быть int.
  3. Результат выражения сопоставления имеет тип float, и вы присваиваете его int. Вот еще одно неявное преобразование, которое удаляет дробную часть. Это нормально из-за точности измерения.

Теперь к вашему предположению, что прерывания мешают измерению температуры.

Из источника я не вижу причин. Итак, вы можете продолжить следующим образом:

  1. Прокомментируйте все, что связано с получением пульса, включая прерывание таймера. Вы можете сделать это с помощью пар /* … */ или #if 0-#endif. Первые не могут быть объединены друг с другом, а для вторых нужны отдельные строки.
  2. Проверьте, правильно ли отслеживается температура.
    • Если да, продолжайте.
    • Если нет, то ваша схема измерения температуры каким-то образом неисправна. Схема и фотография (отредактируйте свой вопрос для этого) могут быть нам полезны. Не продолжайте, пока проблема не будет решена.
  3. Таймер и его программа обработки прерываний снова активируются, но ничего не выполняются.
  4. Проверьте, в норме ли температура.
    • Если да, продолжайте.
    • Если нет, то пока у меня нет идей.
  5. Повторно включите получение пульса.
  6. Проверьте, в норме ли температура.
    • Если да, продолжайте.
    • Если нет, то пока у меня нет идей.
  7. Проблема решена.
,