Arduino Mega 2560 + DRV8871: двигатель не вращается при управлении с помощью ШИМ
Я использую Arduino Mega 2560 для управления драйвером двигателя DRV8871, подключенным к коллекторному двигателю постоянного тока. В комплект входят:
- Три кнопки: ВВЕРХ, ВНИЗ и БЫСТРО
- Один вход концевого выключателя, где верхний и нижний концевые выключатели соединены последовательно
- Управление скоростью ШИМ с использованием медленного затухания, как описано в этой публикации на форуме TI
Цели:
- Управление скоростью: запуск двигателя на скорости ~30% при нажатии кнопок UP или DOWN и ~70% при нажатии кнопок UP или DOWN одновременно с кнопкой FAST.
- Ускорение: Реализуйте настраиваемую кривую ускорения.
- Неблокирующий код: убедитесь, что программа не использует
delay()и не блокирует выполнение.
Проблема:
Я написал следующий код: ссылка на GitHub. Однако при нажатии кнопок «Вверх» или «Вниз» двигатель издаёт шум, но не вращается (возможно, глохнет, даже при ШИМ = 100).
Интересно, что если я обойду управление ШИМ и использую:
digitalWrite(in1Head, HIGH);
analogWrite(in2Head, LOW);
двигатель работает правильно.
Вопросы:
- Может ли эта проблема быть связана с неправильным управлением ШИМ?
- Происходит ли остановка двигателя из-за недостаточного пускового тока?
- Есть ли какие-нибудь предложения по отладке или улучшению кода?
Мы будем очень признательны за любые идеи! ###обновлять: Я обновил код, следуя рекомендациям. Пожалуйста, сообщите, правильно ли я понял.
- Я добавил цикл while к концевому выключателю. Хочу, чтобы при срабатывании концевого выключателя двигатель останавливался, а затем менял направление вращения и отключал концевой выключатель, двигаясь в течение времени
disengage_ls.
//Мотор головки
int in1Head = 44; //на ARDUINO MEGA
int in2Head = 45;
int limSwHead = 19; //2 нормально закрытых концевых выключателя, соединенных последовательно
int FAST = 16; //нажать кнопку
int UP = 17; //нажать кнопку
int DOWN = 15; //нажать кнопку
volatile bool limSwHeadTriggered = false;
// Направление перечисления и флага
enum Direction { FORWARD, BACKWARD };
Direction currentDirection = FORWARD; // Инициализируем с направлением по умолчанию
uint8_t PWM_fast =70; //значение в % от скорости двигателя
uint8_t PWM_slow =30; //значение в % от скорости двигателя
uint8_t PWM = 20; //минимальное значение ШИМ (также будет изменено ниже)
unsigned long zero = 0;
uint16_t accel_interval = 10;//время в миллисекундах для изменения скорости при ускорении
uint8_t speedIncrease = 5; //% увеличения скорости для каждого accel_interval
uint16_t disengage_ls = 500; //время в миллисекундах для отключения Limitswitch при срабатывании
boolean fastState = false;
boolean upState = false;
boolean downState = false;
//----ФУНКЦИИ----------------------------------------------------------------------------------------
//ПРЕРЫВАНИЯ
void headISR() {
limSwHeadTriggered = true;
}
void HeadForward(uint8_t PWM) {
analogWrite(in1Head, 255);
analogWrite(in2Head,(255 * (100 - PWM)) / 100); //обратный ШИМ в диапазоне 0-255
currentDirection = FORWARD; // Установить флаг направления
}
void HeadBackward(uint8_t PWM) {
analogWrite(in1Head,(255 * (100 - PWM))/ 100); //обратный ШИМ в диапазоне 0-255
analogWrite(in2Head, 255);
currentDirection = BACKWARD; // Установить флаг направления
}
void HeadStop() {
analogWrite(in1Head,255);
analogWrite(in2Head, 255);
PWM=20;
}
void HeadCoast() {
analogWrite(in1Head,0);
analogWrite(in2Head, 0);
PWM=20;
}
void limSwHit() {
HeadStop();
if(currentDirection == FORWARD){
HeadBackward(PWM_slow);
}
else if (currentDirection == BACKWARD){
HeadForward(PWM_slow);
}
}
void setup() {
Serial.begin(9600);
//мотор головки
pinMode(in1Head, OUTPUT);
pinMode(in2Head, OUTPUT);
//концевой выключатель
pinMode(limSwHead, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(limSwHead), headISR, RISING); // Триггер по ВЫСОКОМУ сигналу
//Кнопки
pinMode(FAST,INPUT_PULLUP);
pinMode(UP,INPUT_PULLUP);
pinMode(DOWN,INPUT_PULLUP);
}
void loop() {
if(limSwHeadTriggered){
unsigned long initialMillis = millis();
while(millis()- initialMillis<= disengage_ls){
limSwHit();
}
HeadCoast()
limSwHeadTriggered=digitalRead(limSwHead);
}
// считываем состояние значения кнопки, инвертированное из-за подтягивающих резисторов:
upState = !digitalRead(UP);
downState = !digitalRead(DOWN);
fastState = !digitalRead(FAST);
if (upState && !downState && (millis()- zero >= accel_interval)){
if (PWM < (fastState ? PWM_fast : PWM_slow)) {
PWM += speedIncrease;
}
HeadForward(PWM);
Serial.println("moving up");
zero = millis();
}
else if(!upState && downState && (millis()- zero >= accel_interval)) {
if (PWM < (fastState ? PWM_fast : PWM_slow)) {
PWM += speedIncrease;
}
HeadBackward(PWM);
Serial.println("moving down:");
zero = millis();
}
else{
HeadCoast();
}
}
@Luigi, 👍1
Обсуждение1 ответ
Лучший ответ:
Если у вас есть вольтметр, показания напряжения на контактах, которые вы пытаетесь использовать, будут очень полезны. Используйте их, чтобы убедиться, что вы правильно настраиваете ШИМ. Создайте новый скрипт и добавьте в него только самый простой код, то есть настройте контакты, а затем попробуйте запустить двигатель с помощью ШИМ в одном направлении в течение 5 секунд с перерывом в 5 секунд. Поэкспериментируйте со значениями ШИМ и обратите внимание на потребление тока — это может быть вашей проблемой. Как только вы заставите двигатель двигаться, вы можете начать интегрировать его в свою основную программу.
Отдельно от движений двигателя проверьте часть вашего основного цикла, которая отвечает за концевой выключатель. Вы постоянно присваиваете новое время вашей переменной initialMillis, поэтому вы достигнете этого 500-мс бенчмарка только в том случае, если вызываемая функция занимает 500 мс, чего на самом деле не происходит. Цикл while исправит это. Неясно, что именно вы хотите, чтобы делал концевой выключатель, но сейчас он фактически ничего не делает — на мгновение останавливает двигатель и выводит сообщение после задержки, но в остальном не мешает вам продолжать движение в том же направлении или что-то ещё. Так что взгляните на это и определите, какая функциональность вам нужна. Возможно, вам придётся переключиться на отслеживание изменения состояний кнопок, а не на то, нажаты ли они в данный момент. Таким образом, вы сможете останавливаться и двигаться в другую сторону после срабатывания концевого выключателя, даже если пользователь не отпустит кнопку, что, я полагаю, и задумано.
В вашей логической оценке для движения upState есть «!fastState», что означает, что если вы попытаетесь двигаться ВВЕРХ и БЫСТРО, вы вообще не сдвинетесь. Вы правильно реализовали это для логики downState, поэтому я предполагаю, что это пережиток предыдущей итерации кода, который вы забыли изменить.
Спасибо за подробный анализ. Я собираюсь провести эксперименты с ШИМ, а пока обновлю код, следуя вашим рекомендациям. Пожалуйста, сообщите, правильно ли я понял. У меня есть некоторые сомнения по поводу комментария о вычитании нуля: я обнуляю время после каждого увеличения скорости, чтобы сбросить счётчик для следующего увеличения скорости (где accel_interval — это время между двумя разными наборами скорости). Не понимаю, почему вы предлагаете его убрать., @Luigi
@Luigi, а, ладно, я пропустил, где ты присваиваешь значение millis() в этих циклах. Да, всё в порядке, хотя имя переменной вроде *zeroRef* может быть более понятным., @InBedded16
@Luigi, ещё два небольших замечания: в цикле while для ожидания 500 мс после нажатия LS вы непрерывно вызываете limSwHit(), а затем HeadCoast(). Это будет постоянно останавливать и запускать мотор. Вам нужно вынести эти два вызова функций за пределы цикла while, чтобы он выполнился один раз, завершился в состоянии COAST, а затем перешёл в цикл while для ожидания этих 500 мс. Во-вторых, HeadCoast() находится внутри оператора *else*, поэтому каждый раз, когда accel_interval не достигнут, вы переходите в состояние COAST. Перенесите это в оператор *else if*, который срабатывает, когда ни одна из кнопок не нажата., @InBedded16
@Luigi, и всё это нормально итерировать, поэтому я рекомендую сначала простой скрипт для работы движения. Иначе как узнать, в чём проблема — в ШИМ или в логике кода? Начните с базовой функциональности, затем постепенно внедряйте всё остальное и убедитесь, что всё работает, прежде чем переходить к следующему., @InBedded16
Спасибо. Я попробовал простой скрипт, и он работает, значит, проблемы связаны с моим кодом. Благодаря вашим подсказкам, я совершенствуюсь. Насчёт цикла while при достижении LS: то же самое, если вместо этого написать delay(disengage_ls)?, @Luigi
Если вы имеете в виду замену цикла while функцией delay(), то нет, это не совсем то же самое. Вам стоит изучить последствия использования delay(), чтобы лучше понять, но, по сути, она приостанавливает вашу программу на указанное время, что влияет на прерывания и другие процессы. Возможно, это не так уж важно для вашей программы, но лучше всего использовать задержки на основе таймера., @InBedded16
- Управление скоростью вентилятора с помощью библиотеки Arduino PID
- Выводы прерываний Arduino Mega 2560 и отображение портов с помощью поворотного энкодера
- Почему мой код прерывания не работает?
- Измерение скорости двигателя постоянного тока с помощью поворотного энкодера
- Можно ли использовать цифровые контакты в качестве выхода ШИМ?
- Изменение ШИМ на Arduino Mega, контакты 9 и 10 на 20–25 кГц
- Сообщение об ошибке: "exit status 1 expected initializer before 'void'."
- Корректный по фазе и частоте режим PWM Arduino Mega 2560
хорошо, вопрос обновлен, @Luigi