Колесная робоплатформа. Часть 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а связывать несколько устройств.

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



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

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

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

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

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

Комментарии (9)

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

able

  • 18 апреля 2012, 04:38
+
0
А мелкая осьминогая таракашка в уголке платы — это и есть мосфеты? Как называется?
avatar

able

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

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

Ozze

  • 18 апреля 2012, 11:06
+
0
Отличная работа!
avatar

admin

  • 18 апреля 2012, 06:49
+
0
Спасибо!
avatar

Ozze

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

aivanov

  • 19 апреля 2012, 11:00
+
+2
Когда мы едины, мы не победимы!

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

Ozze

  • 19 апреля 2012, 11:46
+
+1
Ну вот команда ROBOCRAFT… чем не вариант, очень толковые и с человеческими качествами все слава богу! Идеология у нас одна, цели те же.

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

aivanov

  • 19 апреля 2012, 11:54
+
0
собственно, здесь и можно попробовать что-то организовать ;)
avatar

admin

  • 20 апреля 2012, 07:17

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.