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