Колесная робоплатформа. Часть 2 — Контроллер периферии и связь.


Вот и добрался, наконец, до следующего этапа. Тут диплом, заморочки всякие страшные, жизненные трудности и перемены. Времени все не хватало на любимое хобби.
Зато у робота теперь есть имя:) Тимми, как у персонажа Южного парка. Итак, вот, что у меня пока получилось:
Робоплатформа

Как можно понять из заголовка, сегодня буду рассказывать про контроллер периферии.

В точности, как и планировалось, всю рутинную работу по дерганью ШИМом, регистрами, по опросу датчиков, измерению заряда батарей я возложил на отдельный маленький контроллер. Взял для этого AtMega8. Всего у этого контроллера хватает: и памяти полно, и периферии достаточно. Только портов ввода-вывода совсем мало. Этот недостаток легко исправляется сдвиговыми регистрами. А наличие аппаратного модуля SPI позволяет работать с ними в несколько строк кода.
Для управления мощными нагрузками поставил на плату два полевых транзистора. С их помощью можно, к примеру, отключить какой-нибудь блок на время, чтобы он не мешался.

Подключение регистров.

Очень важный момент! Линии SPI, идущие к регистрам и программатору нужно разделять. Если их не разделить, сожжете однозначно либо регистры, либо программатор. Вполне логично. Программатор щелкает тактирующей линией SCK, дабы отправить/считать что-то из МК, а в это время регистры гадят мусором по этим линиям. Секунды не пройдет, как программатор захочет установить высокий уровень в тот момент, когда регистр будет в низком… и

Микросхемы работают на волшебном дыму. Если он выйдет, чуда не будет.

Подробнее об этом можно прочитать в апноте AVR042:Hardware Design.

Схемы, платы…

Тут все стандартно, как всегда и как везде. AtMega8 + обвяз. Кварц взял на 14,7456 МГц. Частота эта выбрана не просто так: на этой частоте погрешность битрейта USART равна нулю, при этом мы близки к максимальной тактовой частоте в 16 МГц. Как говорилось выше, линии SPI развязаны резисторами на 4.7 кОм. Все порты с функцией АЦП выведены отдельно, на 6м напряжение с аккумулятора через делитель 56К-150К. Опорное напряжение 5В берется с AVCC и сглаживается конденсатором. Вход AREF в этом случае не подключается.
PB0 и PB1 щелкают входным и выходным регистрами. PD2 и PD3 управляют транзисторами, PD4:7 — дергают ШИМом драйвер двигателей.

Сдвиговые регистры скаскадированы по 2 каждый, т.е. доступно 16 входных и 16 выходных линий.

Может я туговато соображаю, но я еще ни разу не смог развести плату со сдвиговыми регистрами без перемычек на одной стороне. А вот давно я на двух сторонах ничего не делал:)

С лицевой стороны решил не лудить покрытие там, где нет дорожек. Отпечатал надписи и чтобы медь была как спина Бендера покрыл плату акриловым лаком. Красота вышла!

Маленькая хитрость еще с годов работы на заводе. Все, что не нужно залачить, плотно в два слоя заклеиваится малярным скотчем. Потом лак как немного подсохнет, пинцетом скотч отрывается. Если оторвать позже, скотч сильно прилипнет к лаку и останутся маленькие кусочки, что не красиво. Лак брал тот же, что и использовал раньше на производстве. Там у нас светодиодные экраны и в дождь по всему городу стояли. Без лака их чинить ездить просто бы не успевали, с лаком всего навсего замучились:) Как и рекомендуется, лак сушил феном при 70 градусах пол часа. Можно и сутки на балконе, но раз есть такая возможность.

Неясные проблемы и почему горим?

В ходе сборки столкнулся с парой трудностей. Сперва как подал напряжение на АЦП… МК сгорел. Я удивился такому повороту, потому как с АЦП уже несколько раз работал. Десять раз все перепроверил, пожал плечами, выпаял МК, поставил новый… и все чудесным образом сразу заработало. Похоже, попался брак.
На последнем этапе, написании прошивки, столкнулся с непонятными зависаниями через каждые 2-15 минут. Источник зависаний нашел: while(!(SPSR & (1 << SPIF))); — ожидание флага завершения работы SPI. Пришлось заменить на _delay_us(500). Теперь не зависает, но и задержка оказывается больше, чем необходимо. Странно это очень. Но пляски с бубном с этим изделием прошли успешно. Еще очень удивился тому, что левый мотор заметно отстает от правого. Даже откопал заказы на почте и проверил. Оба 250:1. Отпаивал проверял отдельно. Отстает все равно. Это просто выправил ШИМом. Хотя на этом проблемы не закончились, про остальные дальше. Связь.

Для связи с ПК, как и планировалось, использую модуль HC-05. Про него уже писалось достаточно и тут и на других ресурсах, поэтому я повторяться не буду, а лучше расскажу с чем столкнулся сам. А поплясать пришлось. Во первых с некоторыми версиями BlueSoleil модуль сильно тормозит на прием. Данные не теряются, но могут где-то зависнуть на минуту. Но это еще пол беды. GUI на С программировать я не умею, а кнопочками поуправлять хочется. Взялся написать скетчик на скриптовой примоченьке Processing. И тут с досадой узнал, что в нем есть баг, не позволяющий использовать COM-порт через блютус в виндоус. Печалька. Но что-же… если гора не идет к Мухаммеду, гора идет нафиг. Не стал я заморачиваться с тем, как убрать тормоза на приеме и как реализовать GUI без Processing, а просто взял второй модуль и сопряг их друг с другом. Про стопряжение достаточно написано тут, хочется еще только обратить внимание на один важный момент, который нигде не был упомянут: чтобы модули могли подключаться друг к другу, у них должны стоять одинаковые пароли. Я пол часа из-за этого убил впустую, но потом догадался.
А дальше все просто: аппаратный порт ПК -> преобразователь уровней -> HC-05 ~~~~~~> HC-05 -> наш контроллер. Все просто, прозрачно и без заморочек.

Пара слов про питание.

Как и собирался, питаю все это от трех Li-Ion аккумуляторов. Взял KeepPower на 2600мАч типоразмера А защищенные. Всем они хороши, кроме цены. 500р за штуку. Аккумулятор внутри имеет промышленный элемент 18650 фирмы Sanyo и схемку защиты.

Для них смастерил отсек из текстолита. Элементы прижимаются пружинами, взятыми из какого-то другого старого раскуроченного отсека. Вообще пружины при больших токах применять не рекомендуется: из-за плохой проводимости металла из которого их обычно делают будут большие потери и пружины могут нагреваться. У меня вся конструкция пока потребляет меньше полампера, и никаких нагревов я не заметил.

Припаять стальные пружины к меди — дело не из легких. Пришлось воспользоваться ортофосфорной кислотой. Химически активна и оставляет за собой пленку, превращающую все, что ей смажешь в огромный резистор. Отмывал ее после пайки сперва водой с содой, потом, как перестало шипеть, просто водой.

Софт.

На этом железная эпопея заканчивается и пора переходить с программам. Общий принцип прост: контроллер принимает команду по UART, выполняет и, если нужно, отвечает. Поскольку UART устройств у нас по плану несколько, для избежания коллизий никто не заикается до тех пор, пока основной контроллер не попросит. Поскольку это внутренний протокол, особой человекопонятности ему и не нужно, зато нужны надежность и краткость. В общих чертах решил делать так: первый байт — команда, несколько следующих за ним — параметры. Например, нужно установить левый двигатель на половину скорости в прямом направлении. Шлем контроллеру L0x7F и DLF. L — команда установки скорости левого двигателя, 0x7F — половинная скорость, D — команда установки направления, LF = left forward. Аналогично и со всем остальным. Таким образом можно реализовать до 255 команд с разветвленной системой параметров. С этим проблем на удивление не возникло.
На стороне ПК написал временный тестовый скетчик в Processing. Тупо жмем кнопки «вперед-назад-лево-право» и вспоминаем начальную школу, когда все перлись с RC машинок. Запустив терминалку, можно еще пощелкать выходами, опросить входы, попереключать транзисторы, спросить заряд аккумулятора и узнать напряжение на аналоговых входах. Раз у нас будет основной контроллер, то пока и не стоит делать интерфейс для всех функций.

Работа с регистрами.

Очень красиво получается работа с регистрами по SPI. Когда МК в режиме ведущего, запись в SPDR инициирует передачу, при этом все принятые данные попадают в приемный буфер, и из того же SPDR их можно считать. Тактирующую линию SPI при этом подключаем к тактирующим входам обоих регистров, выход входного забрасываем на MISO, а вход выходного на MOSI. Достаточно дернуть защелку входного регистра, дважды записать-считать и щелкнуть выходным. И всеми этими приемами-отправками занимается аппаратный модуль SPI, не нагружая нас, память и процессор.

Заводим структуру портов:

struct IO_Port {
   unsigned char input_h;
   unsigned char input_l;
   unsigned char output_h;
   unsigned char output_l;
} IO_Port;

И функцию обновления:

void Shift_SPI(void) {
// Щелкаем входом
PORTB &= 0b11111110;
PORTB |= 1 << PB0;

// Передаем-принимаем
SPI_MasterTransmit(IO_Port.output_h);
IO_Port.input_l = SPI_Receive();
SPI_MasterTransmit(IO_Port.output_l);
IO_Port.input_h = SPI_Receive();

// Щелкаем выходными регистрами
PORTB |= 1 << PB1;
PORTB &= 0b11111101;
}

ШИМ двигателей.

L293DNE не любит высоких частот по входу, что нам и на руку. Реже придется занимать МК переключениями. Поскольку управлять придется двумя двигателями с четырех портов, я решил остановиться на программном ШИМе. Заводим таймер и два вектора прерывания: по переполнению — инициализация и забивание регистра сравнения следующим интервалом, по совпадению — установка следующего интервала и отключение вывода.

ISR (TIMER2_OVF_vect) {
   if (motors_needs_update) {
     motors_needs_update = 0;
	 motor.left_speed = motor_left_temp;
     motor.right_speed = motor_right_temp;
   }

   if ((motor.left_speed || motor.right_speed) == 0) return; // Моторы остановлены

   if (motor.left_speed != 0) {
     motor_control(LEFT, motor.left_direction, TRUE); // левый включить
   } else {
     motor_control(LEFT, 0, 0);
   }
   if (motor.right_speed != 0) {
     motor_control(RIGHT, motor.right_direction, TRUE);  // правый включить
   } else {
     motor_control(RIGHT, 0, 0);
   }

   if (motor.left_speed == 0 && motor.right_speed != 0) {
     OCR2 = motor.right_speed;
     pwm_state = 1;
	 return;
   }

   if (motor.right_speed == 0 && motor.left_speed != 0) {
     OCR2 = motor.left_speed;
     pwm_state = 1;
	 return;
   }

   if (motor.left_speed == motor.right_speed) {
     OCR2 = motor.left_speed;
     pwm_state = 1;
	 return;
   }

   if ((motor.left_speed && motor.right_speed) != 0 &&
       (motor.left_speed < motor.right_speed))
   {
     OCR2 = motor.left_speed;
	 pwm_state = 0;
   } else {
     OCR2 = motor.right_speed;
	 pwm_state = 0;
   }
}

ISR (TIMER2_COMP_vect) {
   switch (pwm_state) {
   case 0: // Если разные скорости - кладем следующий интервал в регистр стравнения
     if (motor.left_speed < motor.right_speed) {
       motor_control(LEFT, 0, 0);
       OCR2 = motor.right_speed;
	 } else {
       motor_control(RIGHT, 0, 0);
       OCR2 = motor.left_speed;
	 }
	 pwm_state = 1;
   break;
   case 1: // Все импульсы закончились, пора отключать порт
	 motor_control(LEFT, 0, 0);
	 motor_control(RIGHT, 0, 0);
   break;

   }
}

Для читабельности сделал функцию, щелкающую портами для моторов:

void motor_control(char motor, char direct, char action) {
   // Левый мотор
   if (motor == LEFT) {
     if (action == FALSE) {
	    PORTD &= 0b00111111;
		return;
	 } else {
	    if (direct == FORWARD) {
		  PORTD |= 0b01000000;
		  return;
		} else {
          PORTD |= 0b10000000;
		  return;
		}
	 }

   }
   // Правый мотор
   if (motor == RIGHT) {
     if (action == FALSE) {
	    PORTD &= 0b11001111;
		return;
	 } else {
	    if (direct == FORWARD) {
		  PORTD |= 0b00100000;
		  return;
		} else {
          PORTD |= 0b00010000;
		  return;
		}
	 }

   }
}

Полный исходник ищите в конце статьи. Если честно, то как программист я самоучка, не считая двух семестров паскаля в техникуме и бейсика в школе. Поэтому если что-то можно сделать толковее, рад только буду услышать.

В заключении.

Ухабами, да болотами, но задача контроллера периферии решилась. Впереди основной контроллер, датчики и сенсоры, сервоконтроллер, ожидание камеры и передатчика видеосигнала. Основным контроллером теперь можно взять вообще что угодно, что может работать с двумя USARTами. Хоть AVR, хоть stm32, хоть Arduino. Даже можно попробовать сперва одно, потом другое. Склоняюсь больше к привычным ATMEGA, но летом постараюсь перекрутить на stm32.
А в следующий раз будем по одной шине UARTа связывать несколько устройств.

И напоследок видео:

http://www.youtube.com/watch?v=ezRIqPGQuWI

Тут думаю и так все ясно, сами смотрите. Радиоуправляемая машинка, детство прям:)

Файлы к статье:

Исходный код (временно без мультимодульного USARTа).
Схемы и платы

Тестовый скетч

PS
Что-то тут все со своими проектами затихли. Печально будет, если никто так и не очнется.

Содержание серии:
0 — Планирование
1 — Приводы и питание
2 — Контроллер периферии и связь
3а — Мультимодульный USART. Теория.
3б — Мультимодульный USART. Реализация.


0 комментариев на «“Колесная робоплатформа. Часть 2 — Контроллер периферии и связь.”»

  1. ЗдОрово получилось!
    Так, навскидку, пока 2 вопроса:
    1.«с некоторыми версиями BlueSoleil модуль сильно тормозит на прием» — с какими именно?
    а то я тут как раз задумался о покупке лицензии на программу.
    2. Когда я мутил свой мотор-шилд, мне пришлось затворы мосфетов притягивать к земле, ставил резюки на 10к, иначе они совсем не хотели переключаться 🙂
    У Вас так работает?

    • А мелкая осьминогая таракашка в уголке платы — это и есть мосфеты? Как называется?

    • 1. Я пробовал в 6.4.249 и 5.0.5. В 6й версии кроме проблемы с этим модулем все работает отлично. 5я у меня иногда вылетает с ошибкой при подключении гарнитуры Nokia к Skype. По моему у этой программы только один чего-то стоящий конкурент — Toshiba Bluetooth Stack, проигрывающий по всем параметрам. Так что выбор не большой. Обе версии уже устаревшие, уже есть 8я.
      2. У меня так работает. Затворы подключены к выводу МК, а когда тот стоит на выход, на нем железно или 1 или 0. Другое означает сожженный порт. Сложно рассуждать без марки транзисторов и схемы.

      Ага:) Восьминогая ктулха — как раз два мосфета в одном корпусе. Зовутся IRF7311, распространены, стоят рублей 10 и купить их не проблема. Использую часто, когда мосфетов нужно четное число:) К тому же при 5 вольтах на затворе открываются полностью, что тут и нужно.

    • Очнемся! Сейчас сам делаю проект, покажу летом, он правда онлайн. На мой взгляд нам в первую очередь нужно учиться быть вместе. Поскольку к таким делам побуждаться всегда сложнее то ощущение плеча порой ох как не хватает

    • Когда мы едины, мы не победимы!

      А вообще у меня давно вертятся мысли собрать команду любителей робототехники, и что-то делать вместе. Найти хорошего программиста, электронщика, механика… Эх, а надо бы так:) Ну или присоединится к существующим.

    • Ну вот команда ROBOCRAFT… чем не вариант, очень толковые и с человеческими качествами все слава богу! Идеология у нас одна, цели те же.

      Мое предположение, что нужно чуть подправить формат сайта добавить сюда возможности работы в команде, видеоконференции например 🙂

Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Android Arduino Bluetooth CraftDuino DIY IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение