2 аналоговых датчика, датчик пульса и проблема LM35
Я делаю проект, в котором использую два аналоговых датчика. Датчик пульса и датчик температуры 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
@Lucian Petcu, 👍-1
Обсуждение1 ответ
У меня есть несколько предложений:
Комбинированный код сильно отличается от работающего простого кода 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;
}
- Значения NaN в инкубаторе для яиц
- Проблема с условием if
- Какова работа pulseIn?
- Сколько датчиков может поддерживать один модуль Arduino?
- Получение BPM из данного кода
- Как получить данные о весе с датчиков стеклянных электронных весов для ванной?
- DS18B20 дает высокие показания. Как заставить его вернуть правильную температуру?
- Какой тип разъема использует система GROVE?
Когда вы используете датчики отдельно, вы все еще используете этот код, который вы опубликовали? Если нет, то чем отличается отдельный код, особенно для LM35, от приведенного выше?, @JRobert
Я до сих пор использую этот код, моя проблема в том, что я не знаю, как правильно их комбинировать. код только для LM35 следующий.
const int sensor=A5; плавающая температура; плавающий выход; недействительная установка () { Серийный.начать(115200); } недействительный цикл () { vout = аналоговое чтение (датчик); vвых=(vвых*500)/1023; темп=выход; // Сохранение значения в градусах Цельсия Serial.print("Температура: "); Serial.print(tempc); Serial.println("С"); задержка(1000); }
, @Lucian Petcu