Управление сервоприводом MG996R с помощью Arduino LCD Keypad Shield



Arduino Uno c LCD Keypad Shield (LCD Shield) удобно использовать для управления сервоприводом по нескольким причинам: на нем есть кнопки для управления и экран для отображения статуса привода, через него можно подключиться к цифровым пинам и портам питания Arduino, его использование сводит к минимуму количество соединений и избавляет от необходимости задействовать монтажную плату.

Существует несколько версий LCD Shield. Насколько я понимаю, они являются клонами плат от DFRobot: версия 1.1, версия 1.0. В этих платах все кнопки через резисторы R2 - R6 подключены к аналоговому входу А0. 

У меня оказался клон версии 1.0, у которого уровни сигнала на входе А0 при нажатии кнопок заметно отличались от тех, которые указаны в примерах использования LCD Shield, поэтому пришлось определить их самостоятельно.

Для этого можно воспользоваться встроенным примером AnalogReadSerial.



Достаточно загрузить его в Arduino Uno с установленным LCD Shield и открыть монитор порта: в нем отображается уровень сигнала со входа А0 - как раз то, что, нужно. Остается только нажать кнопки по очереди.


В моем случае кнопкам соответствуют следующие уровни сигнала на пине А0:
  • SELECT - 640;
  • LEFT - 410;
  • DOWN - 256;
  • UP - 100;
  • RIGHT - 0;
  • Кнопка не нажата - 1023.
Еще одно замечание по поводу LCD Shield. С помощью пина D10 можно управлять яркостью подсветки дисплея - для этого нужен PWM сигнал. При использовании библиотеки Servo такая возможность пропадает (PWM на пине D10 перестает работать).

Теперь можно переходить к управлению сервоприводом. Пример подключения привода к связке Arduino Uno - LCD Shield с использованием внешнего блока питания привода:


В общем случае, питание сервопривода можно подавать напрямую от платы Arduino через LCD Shield, подключив красный провод привода к пину 5V. Так как сервопривод MG996R достаточно мощный и в режиме удержания потребляет ток до 1.4 А, чтобы полностью раскрыть его потенциал я использую внешний источник для его питания. В этом случае провода VCC (красный) и GND (коричневый) сервопривода подключаются к выходу внешнего блока питания. Управляющий провод сервопривода (желтый) подключается к пину D2 LCD Shield (можно использовать любой другой цифровой пин). Для согласования уровня управляющего сигнала провод GND также должен быть подключен к LCD Shield

В качестве примера я использую захват робо-руки Moveo - в этом случае работа сервопривода видна очень наглядно.  



Для работы с захватом в зависимости от ситуации можно использовать два режима: 
1. "захватил/отпустил" - в этом случае сервопривод перемещается между двумя заранее заданными положениями;
2. управление расстоянием между пальцами захвата - поворот сервопривода на произвольный угол.

Режим "захватил/отпустил". Скетч для данного режима.

****************************************************************************
#include <Servo.h>
#include <LiquidCrystal.h>

// Создание объекта для управления дисплеем
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

// Определение переменных и идентификаторов для работы с кнопками
int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

// Функция для определения нажатия кнопки
int read_LCD_buttons()
{
 adc_key_in = analogRead(0);                // чтение данных со входа А0
 // уровни для кнопок на моей плате: 0, 100, 256, 410, 640
 // добавляю к этим значениям около 50 и проверяю какому из этих уровней соответствует входящий сигнал
 if (adc_key_in > 1000) return btnNONE;     // не нажата ни одна из кнопок, для ускорения работы проверка этого условия стоит на первом месте
 if (adc_key_in < 50)   return btnRIGHT;
 if (adc_key_in < 150)  return btnUP;
 if (adc_key_in < 300)  return btnDOWN;
 if (adc_key_in < 460)  return btnLEFT;
 if (adc_key_in < 690)  return btnSELECT;
 return btnNONE;                            // если нет совпадений, возвращается отсутсвие нажатия
}

// Создание объекта для управления сервоприводом
Servo myservo;

// Переменная статуса сервопривода
bool Servo_state;

// Функция для освобождения захвата
void Servo_release() {
  myservo.write(60);          // перемещение сервопривода в позицию 60 градусов 
  Servo_state = false;        
}

// Функция для активации захвата
void Servo_hold() {
  myservo.write(170);         // перемещение сервопривода в позицию 170 градусов
  Servo_state = true;
}

void setup() {

  // Инициализация сервопривода на пине D2 в позиции "отпустить"
  myservo.attach(2); 
  Servo_release();

  // Инициализация дисплея с указанием кол-ва символов и строк
  lcd.begin(16, 2);
  // Вывод сообщения на дисплей
  lcd.setCursor(0,0);
  lcd.print("Press SELECT");
  lcd.setCursor(0,1);
  lcd.print("Servo: FREE");
}

void loop() {
    
  lcd_key = read_LCD_buttons();     // определение нажатия кнопки

  if (lcd_key == btnSELECT) {      // если нажата кнопка SELECT
    lcd.setCursor(7,1);             // установка курсора на 8 символ 2 строки
    if (!Servo_state) {             // если привод в позиции "отпустить" - переводим в позицию "захватить" и выводим соответствующее сообщение
      Servo_hold(); 
      lcd.print("HOLD");
    }
    else {                          // если привод в позиции "захватить" - переводим в позицию "отпустить"
      Servo_release();
      lcd.print("FREE");
    }
    delay(200);                     // 200 мс на обработку команды
  }
}
****************************************************************************

Видео с демонстрацией работы скетча. 


Вне зависимости от размеров объекта захват будет их удерживать, стремясь сомкнуть пальцы. В этом режиме привод перемещается с максимальной скоростью.

Второй режим - управление расстояния между пальцами захвата. Скетч для этого режима.

****************************************************************************
#include <Servo.h>
#include <LiquidCrystal.h>

// Создание объекта для управления дисплеем
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

// Определение переменных и идентификаторов для работы с кнопками
int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

// Функция для определения нажатия кнопки
int read_LCD_buttons()
{
 adc_key_in = analogRead(0);                // чтение данных со входа А0
 // уровни для кнопок на моей плате: 0, 100, 256, 410, 640
 // добавляю к этим значениям около 50 и проверяю какому из этих уровней соответствует входящий сигнал
 if (adc_key_in > 1000) return btnNONE;     // не нажата ни одна из кнопок, для ускорения работы проверка этого условия стоит на первом месте
 if (adc_key_in < 50)   return btnRIGHT;
 if (adc_key_in < 150)  return btnUP;
 if (adc_key_in < 300)  return btnDOWN;
 if (adc_key_in < 460)  return btnLEFT;
 if (adc_key_in < 690)  return btnSELECT;
 return btnNONE;                            // если нет совпадений, возвращается отсутсвие нажатия
}

// Создание объекта для управления сервоприводом
Servo myservo;

// Переменная для хранения позиции сервопривода
int pos = 60;

void setup() {
  // Инициализация сервопривода на пине D2 и перевод в начальную позицию 
  myservo.attach(2); 
  myservo.write(pos);

  // Инициализация дисплея с указанием кол-ва символов и строк
  lcd.begin(16, 2);
  // Вывод сообщения на дисплей
  lcd.setCursor(0,0);
  lcd.print("Press UP/DOWN");
  lcd.setCursor(0,1);
  lcd.print("Position: 60");
}

void loop() {    
  lcd_key = read_LCD_buttons();     // определение нажатия кнопки

  if (lcd_key == btnUP)            // если нажата кнопка UP
    if (pos < 180) pos++;          // если текущий угол меньше 180 градусов, увеличиваем его
  if (lcd_key == btnDOWN)          // если нажата кнопка DOWN 
    if (pos > 0) pos--;            // если текущий угол больше 0 градусов, уменьшаем его
  
  myservo.write(pos);              // перемещение привода в новую позицию

  // вывод данных на дисплей
  lcd.setCursor(10,1);              
  lcd.print(pos);
  if (pos < 100) {
    lcd.setCursor(12,1);
    lcd.print(" ");
  }
  if (pos < 10) {
    lcd.setCursor(11,1);
    lcd.print(" ");
  }
  
  delay(15);                       // время на перевод привода в заданную позицию
}

****************************************************************************

Последняя команда delay в цикле loop определяет скорость работы привода.

Видео с демонстрацией работы скетча (задержка 15 мс).



А это с задержкой 45 мс.



Спасибо за внимание! Let`s go design!



Комментарии