Проблема с тахометром с использованием AttachInterrupt и сервопривода
Итак, я создаю тахометр, используя черно-белый детекторный датчик. Вот код.
unsigned long WaktuOLD;
unsigned long WaktuNOW;
int Detection = HIGH;
int Counter = HIGH;
float HitungWaktu;
float HitungRPM;
void setup() {
Serial.begin(9600);
attachInterrupt(digitalPinToInterrupt(3), rpm, RISING);
WaktuNOW = millis();
}
void rpm() {
Detection = digitalRead(3);
if (Detection == HIGH && Counter == LOW) {
WaktuNOW = millis();
HitungWaktu = WaktuNOW - WaktuOLD;
HitungRPM = 60 / (HitungWaktu / 1000);
Counter = HIGH;
} else if (Detection == HIGH && Counter == HIGH) {
Counter = LOW;
WaktuOLD = millis();
}
}
void loop() {
Serial.println(HitungRPM);
delay(5000);
}
Код работает хорошо и правильно считывает RPM. А затем с того же Arduino я управляю серводвигателем, в данном случае я использую GWS03N/STD/F с этим кодом:
#include <Servo.h>
Servo myservo;
int pos = 0;
void setup() {
myservo.attach(9);
}
void loop() {
myservo.write(90);
}
И этот код тоже работает хорошо.
Но когда я пытаюсь объединить оба кода, хорошо работает только сервокод. Показания тахометра неправильные (показания должны быть в районе 1000-1500 об/мин, а сейчас показывают 3000-30 об/мин). Я уже пытаюсь изменить прерывание тахометра на контакт 2 или 3, а сервопривода на 3, 5, 6 и 9, но все то же самое.
Так что же здесь не так? Спасибо вам большое.
Это мой код при объединении обоих кодов.
#include <Servo.h>
Servo myservo;
int pos = 0;
unsigned long WaktuOLD;
unsigned long WaktuNOW;
int Detection = HIGH;
int Counter = HIGH;
float HitungWaktu;
float HitungRPM;
void setup() {
Serial.begin(9600);
attachInterrupt(digitalPinToInterrupt(3), rpm, RISING);
WaktuNOW = millis();
myservo.attach(9);
}
void rpm() {
Detection = digitalRead(3);
if (Detection == HIGH && Counter == LOW) {
WaktuNOW = millis();
HitungWaktu = WaktuNOW - WaktuOLD;
HitungRPM = 60 / (HitungWaktu / 1000);
Counter = HIGH;
} else if (Detection == HIGH && Counter == HIGH) {
Counter = LOW;
WaktuOLD = millis();
}
}
void loop() {
Serial.println(HitungRPM);
myservo.write(90);
delay(5000);
}
@Naufal B, 👍2
2 ответа
Все переменные, к которым осуществляется доступ внутри ISR, необходимо объявить как изменчивые.
Вы используете прерывание RISING, поэтому нет необходимости проверять вывод в ISR. Вы можете быть уверены, что он перешел в HIGH для запуска прерывания.
Возможно, ваша проблема связана с конфликтами прерываний.
В 8-битных Ардуино нет концепции приоритетов прерываний. Когда выполняется одна программа прерывания, невозможно запустить любую другую процедуру прерывания, пока не завершится первая.
Прерывания имеют «естественный приоритет вызова»: ожидающие прерывания с меньшими номерами будут вызываться предпочтительнее, чем прерывания с более высокими номерами, однако это не означает, что они могут прерывать друг друга.
Проблема здесь в том, что сервобиблиотека использует таймер (и связанное с ним прерывание) для генерации сигнала. При этом используется множество постоянно выполняемых вызовов прерываний - и пока они выполняются, ваше внешнее прерывание не может быть вызвано - по крайней мере, до тех пор, пока не завершится выполняющееся в данный момент прерывание таймера, после чего будет вызван ваш внешний обработчик прерываний, поскольку он высший приоритет.
Поэтому результаты, которые вы получите при всех этих задержках, совершенно бессмысленны.
И что же вы можете сделать? Что ж, есть несколько вариантов, каждый из которых предполагает не использование прерываний.
- Вы не можете использовать библиотеку Servo.h и вместо этого перенастроить аппаратную ШИМ на правильную частоту и использовать ее для сервопривода.
- Вы можете использовать один из таймеров с внешним источником синхронизации для подсчета импульсов тахометра (я думаю, что таймер 1 имеет такую возможность на Uno)
Или еще лучше: сделайте и то, и другое. Это даст вам наиболее эффективную систему, в которой основной процессор будет участвовать только в периодическом получении счетчика от таймера и настройке значения ШИМ для вашего сервопривода. Остальное выполняется аппаратно полностью асинхронно.
Конечно, для этого вам понадобится изучить техническое описание, чтобы вы знали, как настраивать различные аппаратные модули.
Существуют ли другие библиотеки вместо «Servo.h», которые могут решить эту проблему?, @Tharindu Sathischandra
Не то чтобы я об этом знал, но существует множество руководств по изменению частоты ШИМ., @Majenko
[Это](http://arduiniana.org/libraries/pwmservo/) может вас заинтересовать..., @Majenko
Можем ли мы использовать PWMServo так же, как Servo.h?, @Tharindu Sathischandra
Это зависит от того, какие таймеры оба используют. Если у них разные таймеры, то конечно. Если нет, то нет., @Majenko
Можем ли мы решить проблему, используя две отдельные платы Arduino Uno: одну для датчика и одну для сервопривода?, @Tharindu Sathischandra
Конечно, если на связь между ними это тоже не влияет, и поскольку при этом обычно также используются прерывания, вы, вероятно, столкнетесь с некоторыми проблемами., @Majenko
Я должен прокомментировать :-). Этот ответ идеален. Сначала, когда я увидел награду, я подумал, что Таринду выбросил свои очки за эту награду. Я никогда не работал с прерываниями сервопривода или энкодера, но я знал, что 328p пропускает прерывания RPM, поскольку библиотека сервоприводов использует прерывания для генерации импульсов. Но у меня не было решения. И возможность заменить оба элемента управления внутренней периферией MCU идеальна., @Juraj
@Юрай Да, я очень близко понял ответ. Но до сих пор я понятия не имею, как решить мою проблему. Можете ли вы предложить путь для начала?, @Tharindu Sathischandra
Я бы начал с тахогенератора. Прочтите техническое описание и узнайте, как настроить таймер 1 для использования вывода T1 в качестве тактового входа и подсчета тактовых импульсов., @Majenko
- Arduino использует задержку в I2C ReceiveEvent
- Как сгенерировать аппаратное прерывание в mpu6050 для пробуждения Arduino из режима SLEEP_MODE_PWR_DOWN?
- Как заставить сервопривод вращаться на угол больше 180°
- Arduino непрерывно считывает значение АЦП с помощью прерывания
- Как правильно использовать volatile переменные в Arduino?
- Подключение Arduino к сервоприводу с внешним источником питания
- Мой сервопривод не работает плавно
- Бесполезная проводка коробки и код
Я поправился. Я, должно быть, неправильно прочитал номер пина. Тем не менее, переменные должны быть изменчивыми., @Delta_G
Это опечатка в одном из номеров пинов в Detection var, я уже исправил ее. И я уже пытаюсь сделать volatile WaktuOLD, WaktuNOW, Detection, Counter, HitungWaktu и HitungRPM, но это все еще не помогает. Я не пробовал удалить Detection var., @Naufal B