… или добро пожаловать в ад, ардуинщик!
Любой человек, который использует в своих проекта arduino, рано или поздно сталкивается с тем, что ему перестает хватать возможностей имеющегося микроконтроллера. Те, кому просто не хватает ножек, пытаются мигрировать на mega, остальные ищут шилды с необходимым функционалом или начинают изобретать свои. Но любому дрыгоножеству рано или поздно наступает предел: не хватает! Наиболее малодушные спрыгивают на raspberry или другие подобные платы, а считающие что они сильны духом начинают смотреть на другие платформы.
У меня есть и ардуинки и “малинки” и кучка других плат, поэтому я вполне резонно считал, что в микроконтроллерах я далеко не чайник и знаю, с какой стороны пины дергать … В общем, через друзей ко мне поступило предложение сделать довольно простенькую (на фоне уже сделанных проектов) систему. Но на stm32. В памяти сразу всплыла отладочная плата stm32f3discovery, купленная “на сдачу” при очередной закупке и пылящаяся в закромах. Даже не обсудив условия, соглашаюсь: форсированное обучение новой платформе, да еще и на живом проекте, всегда лучше “ну вот посижу вечерком, побалуюсь проектиками …”. Дополнительным бонусом стало изучение микроконтроллеров, на которых построено управление раздаточной коробкой и климатом в моем УАЗе.
Лирическая завлекалка: (из описания) … плата на базе микроконтроллера STM32F303VCT6 включает в себя встроенный отладчик ST-LINK/V2, акселерометр, гироскоп, электронный компас STMEMS, разъем MiniUSB, светодиоды и пользовательские кнопки. Процессор работает на частоте до 72МГц и предоставляет пользователю 90 каналов ввода-вывода. И все это продается за 600 рублей. В одном из самых дорогих магазинов. Аналогичный функционал (ну почти аналогичный: таких скоростей и объемов памяти нет) на ардуине будет стоить около 7-8 тысяч рублей. А отладчиков с аналогичными возможностями на ардуинках вообще нет как класса.
И тут я совершил первую ошибку, впрочем вполне понятную в моем положении: не посмотрел на поддержку микроконтроллера производителем. Ну в самом деле, ведь они делают микропроцессор и плату на нем, а значит должна быть и документация и примеры … Как же я ошибался … В общем, выбрали микроконтроллер и плату просто по необходимому функционалу. Ей оказалась плата STM32L100C-DISCO.
Пока собирали макет, я, уверенный в своих знаниях, полез по интернету почитать про то, как же все-таки программируются эти микроконтроллеры. И тут я немного припух.
Судите сами. Вот кусочек кода:
RCC->APB2ENR |= RCC_APB2Periph_GPIOC;
GPIOC->CRH |=0x33;
GPIOC->CRH &= ~0xCC;
GPIOC->ODR |= (GPIO_ODR_ODR9 | GPIO_ODR_ODR8);
GPIOC->BSRR=(GPIO_BSRR_BS8|GPIO_BSRR_BS9);
Это всего-лишь простейшее зажигание двух светодиодиков.
Нет, каждая по отдельности конструкция мне понятна, но вот смысл этих конструкций не понятен. Ситуацию усугубляло то, что рядом обычно приводились разные таблички с какими-то регистрами, в которые зачем-то надо писать какие-то кракозябры и в результате происходило волшебство. Вспомнив, что в ардуинке тоже можно такими же кракозябрами писать, я начал усиленно продираться через эту мешанину.
Второе отступление: в отличии от ардуинки, для stm32 есть много вариантов сред для разработки. В результате любая тема “а что выбрать?” довольно быстро превращается в обычный срач. Лично я выбрал бесплатную CooCox. Не без глюков, но все что надо есть.
Вторая ошибка: я считал, что раз микроконтроллеры принадлежат к одной архитектуре, то и программироваться они должны одинаково. Ну грубо говоря если на SMT32F0 какой-то код включает ножку А0 на чтение, то тот же код должен включать ту же ножку и на STM32L1. Хренушки. Все разное даже для микроконтроллеров одной серии, но различающихся разными буковками на конце. Особенно для дополнительного функционала типа таймеров. В результате примеры из интернета в лучшем случае просто не работали (вы когда-нибудь видели неработающий blink на ардуинке? А тут легко!), а в худшем просто не компилировались.
Я было впал в печаль, но решил что я не первый в таком положении, а значит должен быть какой-то выход. И выход был найден практически сразу: SPL. Фирменная библиотека от STmicrosystems.
Еще одно отступление: для других процессоров у ST есть более навороченные и новые библиотеки. Но вот для L – нету. Смотреть на мою первую ошибку.
Радости моей не было предела. Код сразу преобразился в почти читаемый:
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC , ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOC , &GPIO_InitStructure);
GPIO_SetBits(GPIOC, GPIO_Pin_8|GPIO_Pin_9);
Это уже было прорывом. Хоть и были проблемы со всякими константами, но они были вполне понятными: не стоит ожидать скорости работы ножки в 50МГц от контроллера работающего на 32х.
Описаний, как включать-выключать порты в интернете куча с маленькой тележкой, поэтому я довольно быстро научился мигать светодиодиками и читать состояние переключателей. Уверенность в себе росла как на дрожжах, добавляя радости от обладания таким быстрым, функциональным и дешевым микроконтроллером.
На одном из шагов потребовалось прицепиться к контроллеру по обычному ком-порту (USART по импортному). Уже вполне привычные заклинания, аналогичные вышеприведенным и честно спертым из интернета и … и ничего. В смысле вообще ничего.
Попробовал один пример: пусто. Второй: пусто. Начитался про DMA (в stm32 можно сделать так, что бы внутренности микроконтроллера сами отсылали данные, не требуя постоянного присмотра из программы), написал: пусто. Я начал метаться в разные стороны: то пример попробую, то документацию почитаю. Ничего. Не работает.
Тогда я вспомнил, что я вообще-то работаю с железной штукой, а не с обычной программой. Сажусь логическим пробником на порты микроконтроллера и вижу, что что-то он там все-таки передает. Обрадованно кричу “это проблемы на другой стороне, железка по rs232 не хочет работать!”. Тут же сам себя урезониваю: с другими штуками эта железка вполне себе работает. А с моей нет. Значит проблема все-таки у меня …
Достаю осциллограф и смотрю на ножки. Сигналы есть, всякие нолики-единички я вижу очень отчетливо.
Нахожу переходник для компьютера и подключаю. Запускаю терминалку. С контроллера передаю 00, в компьютер приходит 00. Передаю с контроллера 55, в компьютер приходит два байта. Передаю АА, компьютер получает аж три байта! Офигеваю. Даже мрачно.
Еще одно отступление: в принципе всем необходимым арсеналом настоящий ардуинщик обзаводится еще в процессе, но многие обходятся дешевым китайским мультиметром (а то и вовсе живут без него). Тут же очень желательно заиметь осциллограф, даже самый простенький. Более-менее приличные, подключаемые к компу стоят 7-8 тысяч и очень сильно облегчат отладку устройств. Даже хвалимый многими Keil (он умеет рисовать рисуночки происходящего на портах) в некоторых случаях очень сильно лажает.
В результате я сижу, обложенный перемигивающимися устройствами, с кучей открытых окон с документацией (от описаний платы и процессора и заканчивая спецификацией протокола rs232) и ничего не понимаю. Все выглядит как надо, но не работает. На самом деле, судя по форумам, это вполне обыденная ситуация.
Наконец после череды матов и сломанной клавиатуры я решаю сделать перерыв. Пойти поесть, прогуляться, попить пива … поспать наконец. После перерыва все так же ничего не работает. Через некоторое время я уже даже начинаю понимать сущность значения некоторых констант, но это не продвигает меня ни на шаг.
Через некоторое время я обнаружил себя тупо играющимся частотой развертки осциллографа. Рисуночек передачи раздвигается-сжимается … а внизу бегают циферки разрешения. Постепенно в мой мозг прямо-таки просачивается мысль “какого хрена этот пик шириной 20мс? он же должен быть 10!”. Лезу в программу, ставлю вместо положенных 115200 скорость 230400, заливаю и вуаля! Все работает! Попрыгав горным козлом по комнате, я стал разбираться, в чем же причина такого поведения.
Тут вскрылась очередная моя ошибка: я был уверен, что часто используемые функции вылизаны. Но в библиотеках от производителя есть ошибки. И это даже не индусские ошибки, а какие-то пакистанские или уругвайские. И в документации тоже есть ошибки. И там и там они глупейшие. Но зарыты так, что новичок ни в жизнь не догадается.
Кратко говоря, все дело было в неправильном указании частоты процессора. Библиотекописатели были уверены, что он работает на 32МГц, а в реальности он работает на 16МГц. А так как в STM32 все (в смысле вообще все) рассчитывается исходя из этого значения, то и usart в реальности работал на скорости 57600 вместо требуемых мне 115200.
Уже потом я пообщался с матерыми стмщиками. Все они с усмешкой выслушали мои причитания и поделились страшным корпоративным секретом: они работают на старых библиотеках, в которых все ошибки уже известны на перечет и держат свою коллекцию функций-оберток, которые просто кочуют из проекта в проект.
Прослезившись, я пошел реализовывать следующий по плану функционал: простой АЦП. Программе надо замерить уровень сигнала на одной из ножек и потом согласно этому уровню начать делать свое черное дело. И тут я опять встрял: программа либо висла, либо в ответ возвращала 0. Уже наученный горьким опытом, я посмотрел реальные данные на ножке. Как легко догадаться, они отличались от 0 …
В общем, я мучался. Я страдал и рыдал. Я даже помощи на форумах попросил. Но судя по обилию аналогичных вопросов, я был не одинок. Я даже чуть не смалодушничал и не решил попробовать бэйсик для stm (есть закрытый проект, который позволяет программировать на бейсике, С или паскале. Со своими библиотеками и ни с чем не совместимый).
И снова я решил поразмыслить. Раз я не один такой, кому не нравится шариться вместо документации по заголовочным файлам в поиске ответов, то наверняка же уже есть решения? В принципе да, есть. Любой производитель софта по stm имеет по своей библиотечке, но как-то они вяло их поддерживают …
И тут я натыкаюсь на библиотеку opencm3. С открытыми исходниками, поддерживает мой процессор, как и кучку других и даже примеры есть. Уже наученный горьким опытом, полез в исходники. Библиотека сделана по принципу SPL, но как-то более изящней что ли. То есть библиотека по сути является набором функций-оберток вокруг трахтибидохов с регистрами. Но с документацией. Пусть скудной, пусть не всегда полной, но весь есть что почитать! На фоне остального бардака это выглядело просто изумительно.
В итоге код практически приблизился по внятности к ардуинному:
rcc_periph_clock_enable(RCC_GPIOС);
gpio_mode_setup(GPIOС, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO8 | GPIO9);
gpio_set(GPIOС, GPIO8|GPIO9);
Попробовал на этой библиотеке сделать АЦП – работает. ЦАП – работает. USART – работает. Даже таймеры удалось сконфигурировать без шаманства!
В общем, пока я категорически рекомендую эту библиотеку. Стало сильно проще работать.
Прочитавший это “ардуинщик” легко может ужаснуться и подумать “нафиг-нафиг. лучше я уж потихоньку, полегоньку на старом добром avr переживу”. Зря. Да, ST еще те редиски и специально делают все так, что бы их продукция была доступна только профессионалам, имеющим возможность потратить кучу времени и денег на изучение платформы. Зато в ответ вы получаете доступ к дешевым и мощным микроконтроллерам, которые могут делать столько интересных вещей, которых на avr попросту нет. И поверьте, даже прочитав описание какого-нибудь контроллера типа “имеет 5 таймеров”, вы никогда не узнаете, что с этими таймерами можно делать, пока не попробуете и не залезете в документацию. Ведь архитектура stm позволяет скрещивать функционал внутри самым причудливым образом. И к примеру, можно сделать так, что бы один и тот же таймер в одной прошивке мигал светодиодиком, а в другой читал показания енкодера моторчика …
Как говорится, продолжение следует.