Теория по КМПУ Готовые элементы систем Технологии и хитрости Прочее Магазин Контакты
 


Микроконтроллер LGT8F328P: китайская ATmega328P по-русски


Начало работы


Основная цель данной вводной заметки – рассказать, каким образом можно из говна и палок собрать программатор для микроконтроллера LGT8F328P. Кроме того, коснемся вопроса софта, при помощи которого прошивку можно залить в камень. Ну и, естественно, поморгаем светодиодом, а также разберемся, как настроить тактовую частоту кирпича в условиях, когда такого понятия, как «фузы», для него просто не существует.

Сразу хочу отметить: здесь я не буду рассматривать работу с LGT8F328P в среде «Arduino», поскольку я с ней совершенно не знаком, да и прошивка в камень там, вроде как, льется тупо через загрузчик. Разговор будет идти о наиболее общем случае, т.е. о ситуации «вот у нас есть голый МК и бинарный файл для него – чё теперь делать?». Это позволит внедрить LGT8F328P в любой проект, а не только в железку, базирующуюся на плате «Uno» / «Nano» / и т.д. Ничего не имею против ардуины, даже более того – прошивка для программатора чипов LGT8F написана именно в этой среде. Но, повторюсь, хочется сделать так, чтобы с новыми камнями можно было работать на любой плате, а не только содержащей преобразователь USB<=>UART.

Надо сказать, что паренек я довольно въедливый и люблю докопаться до самой сути решаемой задачи. Поэтому данная заметка будет содержать довольно много дополнительной информации, которая большинству читателей, наверное, тупо не нужна. И если вам надо просто по-быстрому сделать программатор для LGT8F328P из доступных средств и/или попробовать залить тестовую программу в камень, вот «быстрые» ссылки:

•  программатор LGT8F328P из «Arduino» за пять минут;

•  заливка светодиодной мигалки в LGT8F328P.

Ну а тех, кому тоже интересно поглубже разобраться в вопросе, приглашаю продолжить чтение (наливайте чай/кофе/алкоголь – заметка, как обычно, получилась достаточно объемной).

Для опытов я использовал модуль BTE17-14, на борту которого присутствует сам камень LGT8F328P, стабилизатор 78L05, светодиод и кнопка сброса МК:



Вроде как по выводам он совместим с Arduino Pro Mini, но тут я не специалист, может это и не так. Для меня важно, что на данной плате есть необходимый минимум для первого знакомства с пациентом, ну а остальное со временем приложится.

От автора

Не секрет, что в настоящее время почти вся более-менее сложная электроника содержит на борту как минимум один микроконтроллер. И так же не секрет, что камни постоянно совершенствуются, усложняются, становятся всё более интересными. Поэтому львиная доля разработчиков периодически «пересаживается» на более свежую и производительную платформу, благо выбор их весьма и весьма богат. Но вот лично у меня с этим как-то не сложилось – я как начал в 2007 году использовать микроконтроллеры семейства AVR, так по сей день ими и пользуюсь, ибо они практически полностью покрывают весь спектр моих задач. При этом нельзя сказать, что я не пытался переехать на более могучие кирпичи – я моргал светодиодом на STM-ках, немного ковырялся в ESP8266, а ESP32 мне настолько понравились, что я сразу начал строчить заметку с их нормальным русским описанием и даже создал под эти камни встраиваемый модуль с USB-интерфейсом. Однако, все перечисленные платформы оказались для меня избыточны, т.к. требуемые задачи всегда удавалось решить при помощи старых добрых AVR-ок. Думаю, немалую роль в этом сыграло то, что обычно я весь проектируемый дывайс распиливаю на отдельные модули, общающиеся по стандартным шинам, а для реализации такого модуля обычно за глаза хватает камня из линейки ATmegaX8. Во всяком случае, платы для модулей ESP32 до сих пор валяются без дела:



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

Единственный момент, который меня иногда напрягал в камнях от Atmel – сравнительно низкая тактовая частота и ее зависимость от напряжения питания. И здесь я говорю не про сотню мегагерц (там успешно работают ПЛИС), а про несколько десятков. Сколько раз думал – вот бы прибавить к 20МГц-ам хотя бы 20%, и было бы вообще комфортно. Я даже запускал AVR-ки с кварцем на 25МГц, но там больше геморроя, чем счастья – камень то стартует, то нет, то в процессе работы отваливается. И это еще ладно, если МК питается от +5,0В, ибо если напряжение питания составляет +3,3В, гарантированная тактовая частота вообще не превышает 13МГц. Поэтому каждый раз приходилось как-то исхитряться, но всегда получалось сравнительно легко выйти из положения без перехода на более могучую платформу. А тут некоторое время назад как-то случился очередной затык с закупками 328-х мег – то ли самих чипов не было, то ли цену выставляли конскую, не помню. И полез я поискать альтернативные предложения, в результате чего наткнулся на китайский камень LGT8F328P.

Прочел про него пару обзоров, посмотрел характеристики и обрадовался – кирпич умеет работать на 32МГц-ах, да еще и при любом допустимом напряжении питания. Как раз то, что мне нужно! При этом «голый» чип хер где купишь, однако, есть платы BTE17-14 с LGT8F328P и стабилизатором 78L05, которые доступны во многих российских конторах (мне этот момент крайне важен, намного важнее цены́, хотя эти модули и в России не особо дорогие). Конечно, камни с этих плат придется сдувать, но в этом нет ничего страшного, ведь у меня не крупносерийное производство. Зато в случае необходимости можно будет проводка́ми к микроконтроллеру подпаяться, ну и вообще – для макетирования плата МК с минимальным обвесом самое то. В общем, заказал некоторое количество модулей BTE17-14 и стал курить тему дальше.

Простейший программатор («LGTISP»)

При переезде на незнакомую аппаратную платформу разработчика обычно интересует три момента:

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

•  где взять программатор, которым сгенеренный файл можно физически перелить из компа в кирпич;

•  где взять софт, который сможет нормально работать с выбранным программатором (зачастую программа для написания кода и программа для прошивки камня – это разные программы).

Отмечу, что с программным обеспечением проблем обычно не возникает. И если уж вы дочитали заметку до этого места, то хотя бы софт, на котором можно написать прошивку и сгенерить бинарный файл для китайского МК, у вас уже есть. Предположение это основано на том, что камни LGT8F328P вряд ли будут интересны новичкам – платформы STM и ESP намного интереснее, а сто́ят не сильно дороже. А значит, ранее вы уже работали с микроконтроллерами AVR, и ваше ПО вполне подойдет для китайских чипов, ибо они шьются теми же файлами «HEX», что и обычные AVR-ки. С софтом, через который прошивка будет заливаться в камень, тоже проблем нет – он известен, бесплатен и его можно много где скачать (см. ниже). А вот по поводу программатора, судя по различным статьям и форумам, могут возникнуть серьезные вопросы (еще раз повторю, что здесь не рассматриваются модули «Arduino», у которых на борту есть вся аппаратная часть, позволяющая просто воткнуть плату в USB-порт компа и нажать кнопку «Загрузить в МК»).

Вопрос первый – сможет ли программатор, предназначенный для прошивки AVR-ок, прошивать еще и микроконтроллеры LGT8F328P? Ответ – нет, не сможет, ибо эти камни шьются совершенно по другому интерфейсу. Вопрос второй – можно ли купить фирменный программатор для LGT8F328P? Ответ – вряд ли. По задумке изготовителей, данные кирпичи должны шиться и отлаживаться при помощи устройства «SWDICE mkII», причем там предусмотрены разные модели. Вариант «Pro» предназначен для прошивки и отладки камней «онлайн», т.е., видимо, с использованием компа. От данной разновидности мне удалось найти только фотку:



что неудивительно, ибо даже ссылка с сайта разработчика LGT8F328P ведет в никуда. И есть еще вариант «Offline», который позиционируется как «SWDISP автономный программатор». Данная модель, видимо, позволяет шить (и только шить!) камни без использования ПК загруженной ранее в программатор прошивкой. Данный товар можно купить через сайт разработчика (см. ссылку «LGTMCU Offline HV»), а также на AliExpress, но там всего два лота, стоимость дывайса получается весьма неслабая, а самое главное – не особо понятно, как этим программатором пользоваться.

Вопрос третий – можно ли купить нефирменный готовый программатор? Ответ – не знаю, если поискать «LGT8F328P Programmer» на AliExpress, наткнемся только на программер «LarduinoISP». Стоимость его на момент написания данной заметки составляет около 1500р., однако как самих лотов, так и людей, купивших товар, довольно мало. По отзывам на форумах, данный дывайс вроде как предназначен для заливки ардуиновского загрузчика в LGT8F328P, поэтому ничто не мешает использовать его для «обычного» прошивания камня. Однако, лично я в данную тему не вникал, поэтому сказать что-либо по этому поводу не могу. Ну и остался главный вопрос – а можно ли сделать программатор для LGT8F328P своими руками, да еще и из говна и палок? И ответ на него – конечно же, можно, об чем и пойдет речь далее.

Принцип работы любого программатора микроконтроллеров был мной подробно рассмотрен в заметке про FT232RL. Кратко же суть данного устройства можно описать так: программатор для камня, допускающего внутрисхемную прошивку (ISP) – это просто преобразователь какого-либо стандартного компьютерного интерфейса (LPT, COM, USB) в соответствующие TTL-сигналы, подаваемые на программирующие выводы кирпича. Микроконтроллеры LGT8F328P шьются по шине SWD (Serial Wire Debug interface) через следующие линии:

•  линия данных «SWD» («совмещенный» аналог линий «MISO» и «MOSI» у AVR);

•  линия тактовых импульсов «SWC» (аналог «SCK» у AVR);

•  линия сброса «RSTN» (аналог «nRESET» у AVR). Вообще говоря, шина SWD является двухпроводной, и линия сброса на ней не обязательна (гуглим «SWD interface»). Однако, если программер или отладчик имеет выход «RESET», то отказываться от его использования лично я не рекомендую (хотя бы из-за наличия возможности принудительно сбросить камень при помощи компа).

Таким образом, задача программатора для китайского камня – преобразовать интерфейс USB (как наиболее популярный на данный момент) в сигналы «RSTN», «SWD» и «SWC»:



Причем, в настоящее время доступен целый ворох дешевых китайских чипов типа CH340G/CH340N/и т.д., поэтому целесообразно разбить эту задачу на две: сначала посредством моста USB<=>UART преобразовать данные от компа в стандартный «последовательный» вид, и уже при помощи знакомого UART-ового формата сформировать сигналы «RSTN», «SWD» и «SWC», используя какой-нибудь микроконтроллер. При этом камень в программере, по большому счету, может быть вообще любым, лишь бы у него хватило памяти и прочих ресурсов на поставленную задачу. Но поскольку кирпичи LGT8F328P идут на смену AVR-кам, то опыт работы с последними у вас должен быть в наличии, поэтому собирать программатор будем именно на микроконтроллере от Atmel (или Microchip).

Отмечу, что из всех попадавшихся мне прошивок для камня, который будет работать в LGT-программаторе, сразу и без танцев с бубном заработала лишь одна. В оригинале это скетч для ардуины под названием «LGTISP» от автора «brother_yan», исходники которого лежат тут. При заливке в платы «Uno», «Nano» и т.д. данный скетч превращает их в USB-программатор камней LGT8Fx8P, работающий с компом по протоколу STK500 (аналогичный протокол использует популярный программер AVRISP, отсюда, видимо, и название LGTISP). Краткое описание прошивки есть в файле «readme.md», там же сказано, какой ноге ардуины какой сигнал соответствует:

•  D10 (PB2) – линия сброса «RSTN» (порт PC6 кирпича LGT8F328P);

•  D12 (PB4) – линия данных «SWD» (порт PE2 кирпича LGT8F328P);

•  D13 (PB5) – линия тактовых импульсов «SWC» (порт PE0 кирпича LGT8F328P).

Обратите внимание на фразу

«`RST` can be either connect or not»,

которая говорит, что линию «RSTN» можно подключить к LGT8F328P, а можно и не подключать. И это действительно так – как показывает практика, данные кирпичи прекрасно шьются безо всякого сброса. Однако, выше я уже отмечал, что подключение линии «RSTN» к порту PC6 камня позволит его принудительно сбрасывать при помощи компа, что в ряде случаев может оказаться весьма полезным. А вот фразу

«Short `RESET` pin and `VCC` pin of arduino board to avoid bootloader executing.»

категорически рекомендую проигнорировать, ибо для «обычной» прошивки китайских кирпичей она не нужна. Суть здесь в чем – рассматриваемый скетч, похоже, разрабатывался не как «программатор вообще», а исключительно для возможности заливки в камень LGT8F328P загрузчика, аналогичного ардуиновому (см. пункт №2 файла «readme.md»). Насколько я понял из форумов, с некоторых пор предприимчивые китайцы стали втыкать в платы «Arduino» не 328-е Меги, а собственные чипы. И если пользователь для каких-то своих целей полностью стирал в ардуине микроконтроллер, то потом заново прошить загрузчик в плату у него не получалось, ибо у LGT8F328P нет ни фузов, ни специальной области бутлоадера, да и вообще процесс прошивки идет несколько иначе. И специально для того, чтобы издохший модуль можно было оживить, люди изобрели костыль в виде скетча «LGTISP», возможность же прошивки китайских МК просто автоматически вытекает из данной цели. Так вот, в случае необходимости заливки в LGT8F328P именно загрузчика я полностью согласен – линию сброса программатора действительно нужно накоротко замкнуть на шину «+5,0В», иначе ничего не выйдет. Дело в том, что заливку бутлоадера придется производить из среды «Arduino», которая перед этим обязательно дернет вывод «DTR» преобразователя USB<=>UART, что приведет к сбросу камня на плате новоиспеченного программатора:



И как следствие, загрузчик начнет шиться не в китайца, а в ардуиновую Мегу328, что нам совсем не улыбается. Изменить это никак нельзя, среда просто работает таким образом – перед загрузкой прошивки в плату «Arduino» она сбрасывает камень на ней при помощи упомянутого выше сигнала «DTR», и пока кирпич не очухался после сброса, заливает в него требуемый скетч. Если же замкнуть точки «RESET» и «VCC» ардуинового модуля, то время перезарядки конденсатора C4 на схеме выше станет практически нулевым, и Мега328 просто не успеет сброситься. А это значит, что дальше «Arduino» продолжит работать в режиме именно программатора и начнет пересылать загрузчик именно в LGT8F328P, что нам, собственно, и было нужно. Конечно, такое решение сильно нагружает выход «DTR» USB-моста, ведь на время перезарядки емкости C4 он будет накоротко замкнут с шиной питания +5,0В. Но, как было сказано ранее, это продлится очень недолго, да еще и процесс этот разовый, так что, скорее всего, одну процедуру заливки загрузчика выход «DTR» выдержит. Главное – не забыть потом разомкнуть точки «RESET» и «VCC» на плате «Arduino», а то можно долго думать, почему же перестала шиться 328-я Мега (да и USB-преобразователь рискуете спалить). Однако, нам-то требуется не разовый прошиватель загрузчика, а программатор LGT8F328P «вообще», и заливать в камень мы будем не бутлоадер, а свою собственную программу. Поэтому использовать среду «Arduino» нам нет никакой необходимости, а остальной софт, при помощи которого можно прошить микроконтроллер, состояние линии «DTR» самостоятельно не изменяет – он просто шлет в последовательный порт требуемую прошивку и всё. А посему в нашем случае лучше не закорачивать точки «RESET» и «VCC» ардуинового модуля, дабы лишний раз не рисковать здоровьем USB-моста.

Таким образом, простейший способ получения программатора для работы с микроконтроллерами LGT8F328P выглядит так:

•  скачиваем архив со скетчем «LGTISP» (оригинал проекта находится здесь);

•  открываем скетч в среде «Arduino» (я пользуюсь версией 1.8.5), и компилируем его (кнопка «Проверить» с галочкой, либо «Ctrl+R»). Обратите внимание – чтобы всё прошло успешно, необходимо в стандартном ардуиновском файле «HardwareSerial.h» изменить значение константы SERIAL_RX_BUFFER_SIZE с 64 на 250;

ВНИМАНИЕ: если не хочется заморачиваться со средой «Arduino», то в скачанном архиве есть обычный бинарный файл «LGTISP.hex», который можно залить в ардуиновский камень привычным вам программатором;

•  берем какую-либо плату «Arduino» (дабы уменьшить гарабиты программатора, рекомендую взять «Nano», причем, без впаянных гребенок PLS);

•  припаиваем провода необходимой длины к следующим точкам платы: D12 (линия данных «SWD»), D13 (линия тактовых импульсов «SWC»), GND (общий провод). По желанию можно также вытащить во внешний мир линию сброса (точка D10) и питание +5,0В (точка VCC). К другим концам проводов припаиваем разъем, через который программатор будет подключаться к камню LGT8F328P;

•  подключаем допиленную плату «Arduino» к компу через USB-порт;

•  заливаем в плату скетч. Если используется ардуиновская среда, надо выбрать нужное название модуля («Инструменты => Плата»), после чего нажать кнопку «Загрузка» или «Ctrl+U». Если камень шьется «классическим» способом (т.е. программатором), то перед заливкой файла «LGTISP.hex» лучше сначала полностью стереть кирпич (напоминаю, работаем с ATmega328P), а затем выставить фузы 0x3F («Lock»), 0xD1 («High»), 0xFF («Low»). Состояние BOD («Ext. Fuse») можно оставить без изменений, один хер программатор не использует EEPROM. И еще одно – файл «LGTISP.hex» собирался под плату «Arduino Nano», поэтому с ней он точно будет работать нормально. Что же касается других моделей плат, ничего конкретного сказать не могу, ибо я не знаток среды «Ардуино»;

•  в финале рекомендую укутать допиленную плату «Arduino» в термоусадку или хотя бы обмотать изолентой, дабы случайно ничего не коротнуть;

•  простейший программатор для камней LGT8F328P готов!



Можно отпраздновать данное событие. Только имейте ввиду, что подключать собранный программатор к китайскому камню напрямую можно лишь тогда, когда питание у LGT8F328P составляет +5,0В. В противном случае потребуются цепи согласования логических уровней (примеры которых рассмотрены, например, здесь).

Далее пойдет речь о том, как можно привести данный программатор в более культурный вид, но если вам достаточно того, что получилось (работать-то оно точно будет), можно сразу перейти к проверке программера.

Программатор «LGTISP-nRF»

Несмотря на дешевизну и быстроту изготовления, простейший вариант программатора «LGTISP» имеет серьезный недостаток в виде крайне низкой надежности. Исходя из своего опыта могу сказать, что собранной поделки надолго не хватит – рано или поздно обязательно либо где-то отвалится провод, либо USB-шнур сломается, либо еще что-нибудь произойдет. Конечно, для первого знакомства с китайскими чипами программатора из ардуины более чем достаточно, но «на долгую» всяко нужно что-то понадежнее. Поэтому категорически рекомендую оформить программер в виде отдельного устройства с платой, заточенной именно под функцию прошивки.

Собственно говоря, спроектировать такую плату не особо сложно – по идее, это просто должна быть та же самая ардуина, только снабженная нормальными разъемами и имеющая форм-фактор «USB-свисток» (например, как у типового программатора AVR):



Однако, я бы порекомендовал принять во внимание еще несколько моментов. Во-первых, если глянуть повнимательней на код скетча «LGTISP», можно заметить, что данная прошивка поддерживает работу с тремя светодиодами: «Programming», «Error» и «Heartbeat». При этом в файле «readme.md» про лампочки почему-то нет ни слова, однако, в само́м листинге программы есть и их краткое описание (строки №№18…21), и объявление соответствующих портов (строки №№56…58). Первый из указанных светодиодов загорается при каждом доступе Ардуины к памяти программируемого кирпича, второй – если «что-то идет не так» (в частности, если пропала синхронизация), третий же просто плавно мигает, пока на программатор подано питание. Отмечу, что для использования индикаторов «Programming» и «Error» каких-то дополнительных действий выполнять не требуется (только повесить лампочки с резисторами на необходимые порты ардуины), а вот для активации «сердцебиения» нужно будет раскомментировать строки 83, 119 и 133 (на всякий случай проверьте эти номера́ путем поиска «LED_HB» и «heartbeat» по тексту программы). По умолчанию индикация процесса программирования выведена на порт D7 Ардуины, индикация наличия ошибки – на порт D8, ну а светодиод «Heartbeat» – на порт D9. Однако, пользователь может использовать и другие порты, если они ему более удобны – для этого нужно просто указать требуемые цифры в строках №№56…58 кода скетча. Только при этом следует учитывать, что «сердцебиение» будет плавным лишь в случае задействования ШИМяных выводов Ардуины, ну и, естественно, нужно, чтобы «световые» порты не совпадали с линиями интерфейса SWD (по умолчанию D10, D12 и D13):



Во-вторых, скетч «LGTISP» весит сравнительно немного, поэтому он спокойно влазит не только в камень ATmega328P, традиционно используемый в Ардуине, но и в pin-to-pin совместимые, но более дешевые кирпичи ATmega168(P/PA), ATmega88(P/PA) и ATmega8(A). Конечно, особых денег здесь не выгадать, особенно с учетом того, что программаторов для камней LGT8F328P вам вряд ли понадобится сильно больше одного, но всё же для 8-й меги в том же Китае иногда можно встретить довольно выгодные лоты. Кроме того, лично я при малейшей возможности пытаюсь воткнуть в проект именно ATmega8A, поскольку у меня имеются достаточные запасы данных кирпичей, а вот 328-е зачастую приходится покупать. В общем, если вас заинтересовала данная возможность, нужно пройти в папку с установленной средой «Arduino», далее идем в «\hardware\arduino\avr» и открываем файл «boards.txt». В данном файле описываются различные модели плат из мира ардуины, в частности, установленный на них камень, его характеристики, а также тактовая частота МК. Собственно, там вроде бы всё понятно и без комментариев, но на всякий случай приведу пример, в котором изменены куски кода для компиляции скетча «LGTISP» под кирпич ATmega8A. Изменения вносились для платы «Arduino Uno», поэтому и проект тоже должен собираться под нее (см. «Инструменты => Плата» в среде «Ардуино»). Оригинальный ардуиновский код закомментирован символом «#» в начале строки, все изменения выделены жырным:

uno.upload.tool=avrdude
uno.upload.protocol=arduino
#uno.upload.maximum_size=32256
uno.upload.maximum_size=7680
#uno.upload.maximum_data_size=2048
uno.upload.maximum_data_size=1024
uno.upload.speed=115200

#uno.build.mcu=atmega328p
uno.build.mcu=atmega8
uno.build.f_cpu=16000000L
#uno.build.f_cpu=11059200L
uno.build.board=AVR_UNO
uno.build.core=arduino
uno.build.variant=standard

В данном примере типовая модель камня («atmega328p») заменена на требуемую («atmega8»), а также скорректирован объем FLASH и SRAM (512 байт из «полных» 8192 байт флэша у 8-й меги отводится на загрузчик). Кроме того, в нем заметны мои попытки сплавить хоть куда-то давно валяющуюся кучу кварцев на 11,0592МГц (см. параметр «uno.build.f_cpu»), однако, здесь оказалось не всё так просто. Сама по себе шина SWD нормально работает и шьет камень хоть на 16МГц-ах, хоть на 11,0592МГц. Однако, если при этом на кирпич с программатора приходит еще и линия «RSTN», то система начинает глючить через раз. Замечу – не наглухо затыкается, а непредсказуемо дуркует. Например, она может запросто работать в течение часа, после чего тупо разучиться шить камни LGT8F328P. После этого надо переткнуть программатор несколько раз, затем сплясать с бубном, затем подождать, затем сделать еще что-то, после чего всё опять приходит в норму (или не приходит). Особо ковыряться с данной проблемой мне было некогда, да и не проблема это на самом деле, ведь с «родной» частотой всё работает без вопросов, а если не использовать линию сброса, то программатор перестает глючить даже на «кривой» частоте. Однако, рискну предположить, что дело в формировании длительностей и пауз у сигналов на шине SWD, ведь для этого используются обычные NOP-ы, и временами, видимо, где-то что-то перестает успевать или, наоборот, начинает сильно торопиться. В общем, если вам действительно требуется изменить тактовую частоту работы МК, то разобраться в данном вопросе, думаю, будет не особо сложно. Однако, веских причин для ее изменения лично я не вижу – снижать потребление камня в программаторе вроде бы бессмысленно, ибо он питается от USB и лишних 10мА здесь ничего не решат. Если же вам нужно прошивать LGT8F328P, питающийся от напряжения +3,3В или ниже, гораздо эффективней использовать схему согласования уровня логических сигналов, чем понижать тактовую частоту МК (напомню, что при низком питании AVR-ки на 16МГц-ах работать не будут):



Данный согласователь – это третий момент, на котором я хотел бы заострить ваше внимание. Вещь крайне полезная, и если включить ее в схему программатора, не надо будет постоянно думать о согласовании питания прошивающего и прошиваемого камней. При этом предлагаемое решение довольно просто́, сто́ит три копейки, а также широко известно в узких кругах, поэтому принцип его работы рассматривался многими (в том числе и мной). Отмечу, что мне лень было рисовать согласователь уровней с нуля, из-за чего на картинке выше приведен кусок схемы готового устройства, и на кривую нумерацию деталей обращать внимание не нужно. Кроме того, если вы зададите вопрос «а зачем на линии SWC, заведомо работающей только в одну сторону (от программатора к МК), поставлен двухсторонний преобразователь?», я вам не смогу на него ответить – видимо, в момент проектирования устройства мой разум помутил Ктулху. Поэтому резистор R1 и полевик VT1 можно смело выкинуть, поставив взамен последнего диод BAT54C (как это сделано на линии сброса), однако, в целом все номиналы деталей проверены в железе и согласователь работает без нареканий даже в приведенном выше виде. Возможно, вас могут смутить сравнительно большие номиналы резисторов на схеме, но здесь нужно учитывать относительно низкую скорость работы интерфейса SWD, реализованного в скетче «LGTISP»:



На данном рисунке приведены осциллограммы прошивки камня LGT8F328P при помощи рассматриваемого скетча. Синим цветом показан сигнал «RSTN», а желтым – сигнал «SWC». Нетрудно видеть, что частота синхроимпульсов на шине SWD составляет не более 500кГц, причем, скетч «LGTISP» позволяет сформировать только такую частоту. Время между спадом сигнала сброса и началом изменений на линии «SWC» всегда больше 2мкс, а если брать разность между спадом сброса и передним фронтом первого тактового импульса, она будет больше 3мкс. При таких временах, как было показано в моей заметке про CH340N, вполне достойно работают номиналы подтягивающих резисторов 5кОм…10кОм, поэтому излишне грузить шины питания я не стал. Конечно, лучшим доказательством здесь были бы фотографии сигналов после схемы согласования, но я забыл их сделать, так что в этом вопросе вам придется поверить мне на слово.

В общем, оптимальный программатор камней LGT8F328P, на мой взгляд, должен состоять из преобразователя USB<=>UART, прошивающего МК, а также схемы согласования уровней сигналов:



И мне очень хотелось бы представить вам здесь устройство, собранное именно по такой структуре. Однако, как оказалось, в архиве моих разработок уже был дывайс, из которого элементарно получается программатор китайских кирпичей, а главное – под него уже имеется пара десятков изготовленных печатных плат. Данный дывайс имеет форм-фактор «USB-свисток», собран на базе камня ATmegaX8, на его борту присутствует преобразователь USB<=>UART, три светодиода, управляемых МК, и даже на выходной разъем выведены порты PB2, PB4 и PB5, т.е. линии «RSTN», «SWD» и «SWC» из скетча «LGTISP». Таким образом, в нем присутствует практически всё, о чем говорилось ранее, кроме схемы согласования уровней логических сигналов, ну и еще печатная плата у дывайса двухсторонняя (что однозначный минус с точки зрения радиолюбителя). И в итоге я решил, что для обкатки различных ньюансов мне пока хватит и такого программатора



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

Переделываемый дывайс изначально был преобразователем интерфейса «USB<=>Радиоканал» на базе радиомодуля nRF24L01 (отсюда и название моего программатора – «LGTISP-nRF»). Модуль этот управляется по SPI, что в нашем случае весьма кстати – благодаря этому на выходной разъем программера попала вся шина SWD, ибо в скетче «LGTISP» она реализована исключительно на SPI-линиях. Обратите внимание на то, что в данном скетче не используются возможности интерфейса SPI как такового – все необходимые сигналы формируются тупо ногодрыгом, и если нужно, их можно взять и с других выводов кирпича (см. ниже). Однако, у меня вот так удачно сошлись звёзды, и это стало нехеровым аргументом в пользу именно переделки под программер готового проекта, а не разработки нового.

Схема программатора «LGTISP-nRF» показана на рисунке ниже:



Обратите внимание на то, что элементы с неуказанными марками или номиналами в устройство не устанавливаются, и по количеству таких элементов видно, что проект не разрабатывался с нуля, а был тупо переделан из другого. Основой программатора является камень ATmega8A, за преобразование данных из USB-формата в UART и обратно отвечает китайский мост CH340N (больно уж я люблю эти чипы за дешевизну, небольшой корпус и за минимум обвеса). Тактовая частота кирпича равна стандартным ардуиновским 16МГц (выше я уже объяснял почему), питание берется напрямую с шины USB через самовосстанавливающийся предохранитель FU1. Конечно, для удешевления предохранитель можно не ставить и замкнуть вместо него джампер JP1, однако, лишняя защита USB-порту, думаю, не повредит.

Камень может управлять тремя светодиодами (см. далее), а на разъем XP2 «LGT8FX8P» вытащены шесть его портов, в том числе, и линии интерфейса SWD. Отмечу, что для формирования сигналов «SWD» и «SWC», как и в оригинальной версии «LGTISP», здесь используются порты PB4 и PB5, а вот сигнал «RSTN» я перекинул с вывода PB2 на вывод PD7. После этого все порты ATmega8A, которые выведены на разъем XP2 и не используются для прошивки китайского кирпича, стали иметь возможность аппаратного формирования ШИМ-сигнала (OC1A, OC1B и OC2). Правда, я не знаю, зачем это надо, но на всякий случай пусть будет (один хер – альтернативная функция у PD7 совершенно убогая). Все программные изменения, касающиеся линии «RSTN», выполнялись в файле «swd_lgt8fx8p.h» ардуиновского проекта (см. ниже), все измененные строки помечены камментом с кодовым словом «Podkassetnik».

Отмечу, что помимо программирующих сигналов на разъем XP2 выведена линия сброса камня ATmega8A, а также две земли и питающая шина +5,0В. Обратите внимание – питание, берущееся с USB-разъема, вытащено во внешний мир чисто на всякий случай, и в обычной жизни я бы данной возможностью не пользовался, ибо по уму не программатор должен запитывать прошиваемую плату, а наоборот. Однако, иногда бывают ситуации, когда надо вот кровь из носу подать питание на таргет-устройство, а из доступных источников энергии – только комп с его USB-портами, и вот тогда рассматриваемая возможность будет весьма кстати. Что же касается линии сброса, то она позволяет прошивать камень в программаторе, благодаря наличию на разъеме XP2 сигналов MOSI, MISO и SCK (контакты №5, №4 и №6 соответственно). Для этого потребуется любой привычный вам AVR-программер, ну и еще пять-шесть проводов для макетных плат:



либо специально сделанный для данной цели шлейф (хотя, ради одной прошивки камня лично я не стал бы с ним заморачиваться).

Завершая описание схемы программатора «LGTISP-nRF», скажу несколько слов о световых индикаторах режима работы устройства. Всего дывайс содержит 4 лампочки, одна из которых (HL2) наглухо прикручена к шине питания, а тремя (HL1, HL3, HL4) может управлять камень. Светодиод HL2 показывает, что с USB-порта приходят +5В, и при подключении программатора к компу он будет гореть постоянно. Светодиоды HL4 и HL3 выступают в роли индикаторов «Programming» и «Error», описание работы которых было приведено в самом начале данного пункта. От лампочки же «Heartbeat» я решил отказаться, т.к. светик HL2 («PWR») фактически дублирует ее функцию, только без моргания (хотя, сама прошивка исправно выдает сигнал сердцебиения на порт PC2). А вот что действительно было бы полезно – это индикатор наличия связи с компом. Дело в том, что светодиод «Programming» зажигается только непосредственно в момент заливки прошивки в память LGT8F328P, а этому предшествуют некоторые подготовительные действия, которые связаны с общением по USB и обычно занимают значительное время (несколько секунд). И в итоге получается, что после нажатия кнопки «ПРОШИТЬ КИТАЙСКИЙ КАМЕНЬ» индикация процесса работы программатора возникает далеко не сразу, что немного подбешивает (особенно когда ты не пользуешься отлаженной поделкой, а проводишь эксперименты над программером). Поэтому я решил третий светодиод, управляемый кирпичом (HL1), назвать не «Heartbeat», а «USB» и заставить его моргать в моменты общения с компом. Делается это несложно – в основном цикле скетча «LGTISP» есть вызов функции «Serial.available()», после которого программа переходит на обработку данных, принятых от компьютера (функция «avrisp()»). Отмечу, что выполнение такого перехода само по себе сигнализирует о том, что какие-то байты программатор от компа уже принял, и если перед вызовом «avrisp()» вставить вспышку лампочки «USB» – это и будет означать факт общения программатора с ПК. Все программные изменения, касающиеся светодиода HL1, выполнялись в основном файле скетча («LGTISP-nRF.ino», см. ниже), измененные строки помечены камментами с кодовым словом «Podkassetnik». Обратите внимание на то, что вспышка светодиода HL1 увеличивает время программирования камня LGT8F328P, поскольку пока она не завершится, процесс обработки данных от компа не начнется. Поэтому я сделал длительность вспышки минимальной – всего 1мс. При сопротивлении токоограничивающего резистора R1 300 Ом ± 5% и использовании яркого светодиода HL1 вспышки светика получаются достаточно заметными.

Печатная плата программатора «LGTISP-nRF» показана на рисунке ниже:



Как и говорил выше, основной ее минус, наверное, в том, что она является двухсторонней. В остальном плата довольно «щадящая» – минимальная толщина дорожки составляет 0,55мм (да и то лишь местами), минимальный диаметр дырки 0,7мм, пояса́ на пятаках не менее 0,3мм, типоразмер элементов не меньше 0805. Конечно, фломастером такую плату не нарисуешь, но при использовании хоть ЛУТа, хоть фоторезиста проблем возникнуть не должно. Исходный файл с платой есть в общем архиве проекта, название файла – «LGTISP-nRF_PCB.dip». Нарисована плата в САПР DipTrace; у данной программы есть полностью бесплатная версия под названием Freeware.

После изготовления платы набираем для нее запчасти (полный перечень элементов есть в файле «LGTISP-nRF_BOM.pdf» из общего архива проекта) и впаиваем их на место. Дальше нужно воткнуть собранный программатор в USB-порт компа (сразу должна загореться синяя лампочка HL2) и установить дрова для микросхемы CH340N (более свежий их вариант, возможно, есть на сайте производителя). Ну а далее необходимо прошить камень в программере. И тут есть два пути – можно либо воспользоваться готовым бинарным файлом, сгенерированным мной, либо создать такой файл самостоятельно при помощи среды «Arduino». Первый путь, конечно, проще, однако, он позволяет работать только с той конфигурацией программатора, которая была описана выше. При этом вы даже длительность вспышки светодиода «USB» поменять не сможете, ибо хер знает, где она там находится в HEX-овом файле, не говоря уж о том, чтобы перекинуть те же лампочки на другие ноги кирпича. Второй путь сложнее и потребует разбираться в коде скетча «LGTISP», однако, он позволяет заточить этот код под свои конкретные нужды (что весьма полезно, если вы, например, разводите плату самостоятельно). В частности, он дает возможность выбрать наиболее подходящую модель камня, поменять его тактовую частоту, раскидать линии шины SWD и светодиоды по выводам МК на свой вкус, изменить поведение лампочек и т.д. И здесь выбор за вами: с одной стороны, использование готового решения (пусть даже вы ничего не сможете в нем изменить) позволяет существенно сэкономить время и нервы. С другой стороны, думаю, знатокам среды «Arduino», да и просто языка Си, не составит особого труда разобраться в коде скетча «LGTISP», хотя на это и потребуется дополнительное время. С третьей стороны, для того, чтобы начать ковыряться в коде программы, людям обычно нужна серьезная причина (любопытство, кстати, тоже сюда относится). В общем, лично я не готов агитировать вас за тот или иной путь получения файла прошивки для камня в программаторе, поэтому далее просто вкратце расскажу про оба способа, дабы вам было проще определиться с выбором.

Первым делом необходимо скачать архив «LGTISP-nRF_Firmware», в котором в том или ином виде содержится всё необходимое для прошивки кирпича программатора. Перечень файлов архива приведен ниже, цоколевка платы «Arduino Nano» в него включена для удобства (чтобы не надо было постоянно глядеть в Интернете, какому порту МК соответствуют точки «D0», «D1», «D2» и т.д.):

•  «Arduino_Nano_Pinout.jpg» – файл с цоколевкой платы «Arduino Nano»;

•  «LGTISP-nRF.ino» – скетч исходного проекта «LGTISP», допиленный под мою версию программатора;

•  «LGTISP-nRF_m8_16000000Hz.hex» – бинарный файл, предназначенный для заливки в камень моего программатора. Файл собран под кирпич ATmega8A и кварц с номиналом 16МГц;

•  «swd_lgt8fx8p.cpp» – библиотека функций для работы с LGT8F328P по интерфейсу SWD;

•  «swd_lgt8fx8p.h» – заголовочный файл для библиотеки «swd_lgt8fx8p».

Как видно из этого списка, архив уже содержит бинарный файл для заливки в камень, поэтому если вы решили выбрать первый путь (использование готового решения), всё достаточно просто. Берем собранный программатор для LGT8F328P и подключаем его к вашему программатору для камней AVR:



Соединить программаторы можно при помощи упоминавшихся выше проводов для макетных плат, либо специально сделанного для этой задачи шлейфа. Если AVR-программер имеет собственное питание, и оба свистка вставляются в один и тот же комп, точки «+5.0V» и «VCC» соединять не нужно. А дальше шьем фузы камня в программаторе «LGTISP-nRF» (я сделал Lock = 0x3F; High = 0xD1; Low = 0x3F), после чего заливаем в него файл «LGTISP-nRF_m8_16000000Hz.hex». На этом всё – прошивальщик кирпичей LGT8F328P готов. Если же вы решили пойти по второму пути, вам сначала потребуется открыть в среде «Arduino» файл «LGTISP-nRF.ino», после чего произвести все необходимые изменения и сгенерить бинарный файл для прошивки камня самостоятельно («Скетч => Экспорт бинарного файла»). Отмечу, что изменения могут носить самый разный характер – от простейших, типа смены модели камня и его тактовой частоты (подробнее об этом было рассказано в начале данного пункта) до навешивания на программатор вороха дополнительных функций (благо и памяти, и неиспользуемых портов в 328-й меге остается более чем достаточно). Тут давать какие-то рекомендации сложно, ведь всех требований заранее не предусмотреть, да и навряд ли человек, пошедший по второму пути, нуждается в моих советах. Скажу лишь, что среда «Arduino» позволяет генерить не только «обычные» HEX-файлы для МК, но и файлы, содержащие загрузчик – возможно, кому-то это будет полезно (оба файла создаются автоматически при нажатии на «Скетч => Экспорт бинарного файла»). Ну и еще не нужно забывать, что фузы, прошиваемые в камень создаваемого программатора, должны учитывать все особенности вашего проекта: тактовая частота и напряжение питания кирпича, наличие и размер бутлоадера, необходимость защиты от чтения/записи и т.д. И последнее напоминание – чтобы в среде «Adruino» у вас всё нормально собралось, необходимо в стандартном ардуиновском файле «HardwareSerial.h» (расположен в папке «…\hardware\arduino\avr\cores\arduino») изменить значение константы SERIAL_RX_BUFFER_SIZE с 64 на 250.

Софт для программатора

Собранный программатор камней LGT8F328P – это, конечно, прикольно, но толку от него одного не особо много. Зачастую железо становится полезным только при наличии софта, обеспечивающего его общение с компом, и рассматриваемый программер – не исключение. Как было сказано выше, программатор, созданный на базе скетча «LGTISP», с точки зрения компьютера работает по протоколу STK500, поэтому он, по идее, должен видеться любой программой, поддерживающей работу с данным протоколом. Однако, при попытке достучаться до него из, например, AVR Studio 4.19.730, среда шлет вас нахер, мол, больно уж я стара́ для вашей версии программатора:



и общаться с китайским камнем не дает (правда, сам программатор при этом она видит, тут всё ок). Возможно, какие-то другие программы, понимающие STK500, тоже будут глючить при работе с рассматриваемым программером, не знаю. Однако, я точно знаю, какой софт прекрасно с ним уживается – это (вот уж «неожиданность») утилита AVR Dude, широко известная в узких кругах как «дудка». Данная программа снискала себе огромную популярность у AVR-разработчиков из-за того, что поддерживает кучу кирпичей и программаторов, а если она чего и не знает, то обычно это можно довольно легко в нее добавить. Однако, дудка является консольной, т.е. работает из-под командной строки, а я это дело не очень люблю (имеется ввиду процесс разработки поделки, а не работа с уже́ доведенной до ума партией устройств). Поэтому для прошивки камней через AVR Dude я использую графическую оболочку «SinaProg», которая не только позволяет пользователю работать с привычным «виндосовским» интерфейсом, но и умеет самостоятельно рассчитывать состояние фузов (правда, для LGT8F328P это умение не актуально, см. ниже). Вообще говоря, краткое описание данной оболочки, а также пример работы в ней я давал в заметке про FT232RL и здесь повторяться смысла не вижу. Скажу лишь, что если вы таки будете работать с дудкой из командной строки, то программатор на базе скетча «LGTISP» она видит как «stk500v1», либо как «avrisp» (ключ «-c»; работают оба варианта), камень для работы с LGT8F328P (ключ «-p») надо задавать как «m328p», при этом значение, указываемое в поле «bitclock» (ключ «-B»), на скорость программирования ожидаемо не влияет (в отличие от битбангового программера на FT232RL):



Таким образом, для того, чтобы собранный программатор превратился из бесполезной железки в полезный дывайс, рекомендую следующую последовательность действий:

•  подключаем программер к камню LGT8F328P в соответствии со следующей схемой:



Обратите внимание на то, что если у китайского кирпича есть собственное питание (а по уму оно так и должно бы быть), подключать шину «+5.0V» программатора к камню не требуется. Кроме того, при отсутствии схемы согласования уровня сигналов (см. выше) питание LGT8F328P должно составлять +5,0В

; •  скачиваем дудку вместе с графической оболочкой «SinaProg»;

•  распаковываем скачанный архив на диск (установки синапрога не требует). Отмечу, что, по некоторым сведениям, распаковывать «SinaProg» лучше прямо в корень системного диска, но у меня она годами живет по адресу «D:\Distrib\Work\SinaProg 1.4.5.10» и отлично работает;

•  для того, чтобы синапрога нормально работала на 64-разрядных версиях Windows, необходимо еще скачать и установить фреймворк «LabView RunTime Library» от National Instruments. Причем, после установки его можно сразу снести – «SinaProg» продолжит нормально работать (видимо, требуемые библиотеки при этом не удаляются);

•  скачиваем архив с тестовыми прошивками для LGT8F2328P. Прошивки представляют собой обычные светодиодные мигалки, выводящие на порт PB5 (D13 у ардуины) одну, три или пять вспышек с паузой примерно в одну секунду (количество вспышек указано в названии файла). Отмечу, что на плате BTE17-14 на этот порт повешен штатный светодиод, если же вы используете другую плату, озаботьтесь подключением лампочки самостоятельно;

•  распаковываем тестовые прошивки (и вот тут длинных путей точно лучше не делать);

•  вставляем программатор в USB-порт, смотрим, на каком COM-порту он появился. Если номер порта больше 9, его нужно поменять, ибо по умолчанию «SinaProg» не работает с портами, выбивающимися из диапазона COM1…COM9 (см. файл «Port.txt»);

•  открываем «SinaProg», выбираем камень «ATmega328P» (секция «Device»), далее выбираем программатор «AVRISP» или «STK500 v1», указываем номер COM-порта, на котором нашелся программер, а также «скорость программирования» (левое, среднее и правое окно секции «Programmer» соответственно):



Напомню, что «скорость программирования» может быть любой (она ни на что не влияет), но в правом окне секции «Programmer», должно всё-таки быть какое-то число, а не значение «х-», как это будет после запуска синапроги. Ну а далее жмем «кнопку-галочку» для открытия окна, отображающего процесс работы дудки, после чего пробуем найти китайский камень при помощи нажатия на кнопку «Search». Если всё было сделано верно, то кирпич найдется, а справа выведется следующее:



Причем, заметьте, там даже сигнатура правильная определяется, хотя, у LGT8F328P, судя по всему, нет даже такого понятия, как «сигнатура» (во всяком случае, меня поиск в официальном даташыте по фразам «1e» и «0f» ни к чему не привел);

•  ну и прошиваем, наконец, камень одной из тестовых прошивок, скачанных ранее – нажимаем «Открыть» в секции «Hex file», выбираем соответствующий бинарник, а дальше жмем «Program» в секции «Flash» (не в «EEPROM»!!!), после чего наслаждаемся могранием штатной лампочки платы BTE17-14, а также сообщениями об успешной прошивке кирпича:



Собственно говоря, на этом первый этап знакомства с микроконтроллерами LGT8F328P можно считать завершенным – мы получили аппаратную и программную базу для заливки в кирпич бинарного файла. Лично для меня это означает, что можно смело начинать изучение данных МК – по крайней мере, мы сможем прошить в чип результаты наших исследований и посмотреть, работают они или нет. Так что теперь имеет смысл разобраться с тем, как писа́ть программы для китайского камня, чему будет посвящен следующий пункт данной вводной заметки.

Софт для написания программы

Теперь, когда мы можем заливать файлы прошивки в камни LGT8F328P, самое время разобраться с тем, как эти файлы формировать. Иными словами, неплохо было бы научиться писать программы для именно китайских кирпичей, причем, крайне желательно, чтобы софт при этом остался старым и привычным. И тут нас ожидает приятный сюрприз – во-первых, на уровне ассемблерных команд обычные меги и китайские МК полностью совместимы. Я специально потратил полдня, сравнивая мнемоники инструкций и их описание для LGT8F328P и для ATmega328P – совпадение практически 100%. Не полные 100% получились из-за того, что а) LGT не поддерживает команду записи данных во FLASH («SPM») и б) китайский документ – это всё-таки китайский документ. С записью данных ожидать другого было бы странно, если учесть особенности организации памяти у LGT8F328P (про это будет написана отдельная заметка). Ну а про качество китайских даташытов ходят легенды, и в нашем случае всё еще более-менее сносно – просто забыли про инструкции «SEH» и «CLH», продублировали описание для «LD», «LDD», «LDS», «NOP» и «SLEEP», ну и в нескольких местах указали не те флаги регистра статуса SREG, которые реально должны изменяться.

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



дабы убедиться в этом. Из представленной таблицы видно, что внутренний мир у китайца существенно богаче AVR-овского, но при этом для всех регистров Меги у камня LGT8F328P существует брат-близнец, расположенный по такому же адресу и имеющий такое же название. Да, есть некоторые расхождения в именах (все они на рисунке выше выделены красным), но единственное существенное несовпадение – это регистр, расположенный по адресу 0x57. В AVR-ках данная ячейка памяти управляет процессом записи данных во FLASH и носит название «SPMCSR». В камнях LGT вышеупомянутый процесс реализован при помощи специализированного контроллера E2PCTL, поэтому вроде как логично, что по адресу 0x57 у него располагается один из регистров данных этого контроллера (E2PD2), а не бесполезный для LGT8F328P регистр SPMCSR. Так что с учетом того, что в обычной AVR-ной программе (т.е. не в загрузчике) регистр SMPCSR вам вряд ли встретится, можно считать рассматриваемое несовпадение имен абсолютно некритичным. Что же до остальных «красных» ячеек, то там разница в названиях чисто декоративная и обусловлена наличием у китайских кирпичей более жирной периферии:

•  EEDR / E2PD0 (адрес 0x40) – отличие, опять же, из-за наличия на борту китайца контроллера E2PCTL. Обратите внимание – при описании работы данного контроллера в режиме совместимости с AVR даташыт на LGT8F328P вовсю оперирует именно «старым» названием EEDR, а «новое» используется только в 32-битном режиме обмена. Таким образом, функционал рассматриваемых регистров в AVR и LGT идентичен, ну а в том, что одному адресу в ОЗУ МК соответствует два разных имени, нет ничего необычного;

•  ACSR / C0SR (адрес 0x50) – отличие обусловлено тем, что в китайском чипе присутствует два компаратора, а не один (регистр управления/состояния для второго компаратора (C1SR) лежит по адресу 0x2F). Функционал регистров в AVR и LGT идентичен;

•  PRR / PRR0 (адрес 0x64) – отличие обусловлено тем, что китаец, помимо всего прочего, позволяет отключать тактирование собачьего таймера, таймера Т3, контроллера E2PCTL, а также обесточивать схему питания прерываний типа INT и PCINT. Соответственно, всё это в один регистр PRR уместить было невозможно, поэтому потребовался второй регистр PRR1, ну а первый переименовали в PRR0. При этом, естественно, функционал регистров PRR и PRR0 абсолютно идентичен;

•  OSCCAL / RCMCAL (адрес 0x66) – отличие обусловлено тем, что в китайском чипе присутствует не один, а два RC-генератора – один на 32МГц, другой на 32кГц. Соответственно, оба они могут быть откалиброваны, но для этого требуется не один регистр, а два (калибровка для килогерцового осциллятора пишется в регистр RCKCAL, расположенный по адресу 0x67). Таким образом, функционал рассматриваемых регистров в AVR и LGT полностью идентичен, а «RCMCAL» расшифровывается как «RC Megahertz CALibration byte», в отличие от «OSCillator CALibration»;

•  ну и, наконец, пачка регистров, расположенных по адресам 0xC0…0xC2, 0xC4…0xC6 и отвечающих за работу с UART. Как видно из таблицы, приведенной выше, названия данных регистров полностью идентичны, только в китайском варианте у них почему-то отсутствует индекс «0». Зачем так делать, раз уж вы создаете кирпич, подчеркнуто совместимый с ATmega328P, мне неведомо, но функционал регистров в AVR и LGT абсолютно одинаков, поэтому и здесь проблем тоже не будет;

•  отмечу, что поскольку функциональность «красных» регистров в AVR и в LGT одинакова, при разработке программ вполне можно пользоваться «старыми» именами взамен «новых». Однако, на мой взгляд, «новые» названия при использовании LGT8F328P будут более логичными (за исключением, конечно, UART’а – тут отсутствие индекса «0» никакой смысловой нагрузки не несет). Ну а наилучшим решением, наверное, будет возможность использования как «старых», так и «новых» наименований регистров, без необходимости перекомпиляции проекта под конкретную модель камня.

Из приведенных выше фактов ясно, что для компилятора микроконтроллер ATmega328P (да и его «младшие» модели тоже) абсолютно ничем не отличается от камня LGT8F328P – те же самые инструкции производят те же самые операции над ячейками памяти с теми же самыми адресами (я, конечно, не проверял идентичность функционала всех битов во всех регистрах китайского камня, но выборочная проверка дала строго положительный результат). Поэтому очевидно, что код, написанный, собранный и откомпилированный под ATmega328P, можно вообще безо всяких переделок залить в LGT8F328P. Таким образом, ваш привычный софт для работы с AVR подойдет и к китайскому камню, так что переезжать на другую программную платформу вряд ли придется. Другое дело, что если в коде вообще ничего не трогать, то китаец сможет работать лишь от внутреннего генератора и только на дефолтной частоте 4МГц (если, конечно, в программе не меняется значение регистра CLKPR). Связано это с тем, что камни LGT8F328P, в отличие от AVR, не знают такого понятия, как «фузы», и тактовая частота кирпича (как и ее источник) устанавливается прямо в теле программы при помощи регистров ввода-вывода (см. ниже). Поэтому если вам захочется эту частоту поменять (или, например, тактировать камень от внешнего кварца), то придется общаться с данными регистрами, а для этого надо будет сообщить компилятору их адрес и (для наглядности) описать соответствующие биты. Конечно, в случае тактовой частоты таких регистров будет всего два, причем, один из них уже́ есть в ATmega328P – это CLKPR. Ну а уж для одного-то регистра прописать все нужные константы-определения можно тупо вручную прямо в теле основной программы. И точно так же можно поступать каждый раз при использовании «эксклюзивных» возможностей LGT8F328P: как только мы решили использовать какой-либо «нестандартный» регистр китайца (например, откалибровать опору АЦП), надо просто его объявить где-нибудь выше и в дальнейшем со спокойной душой пользоваться объявленным именем. Однако, гораздо удобнее воспользоваться готовыми файлами-определениями, где всем регистрам камня LGT8F328P уже́ присвоен необходимый адрес, а их битам – требуемые названия. И теперь осталось только понять, где взять эти готовые файлы.

Конечно, проще всего описывать свой личный опыт, но, думаю, мой случай вряд ли кому будет интересен. Дело в том ,что я пишу программы для камней исключительно на ассемблере, используя среду «AVR Studio Version 4.19 Build 730», а весь прогрессивный мир давным-давно пересел на языки высокого уровня. Тем не менее, лично я считаю, что и ассемблер имеет право на жизнь, поэтому рассказать о «моем пути к LGT8F328P» будет нелишним, тем более что в случае ассемблера переход на китайский камень получается максимально прозрачным. Собственно, данный процесс сводится к двум шагам:

•  скачиваем файл с описанием регистров ввода-вывода LGT8F328P и их битов для древней AVR-Студии. Оригинал данного файла был создан Юрием Ревичем, взять его можно здесь. Отмечу, что рассматриваемый источник не является безупречным – даже такой новичок в плане LGT8F328P, как я, уже нашел в нем пару косяков (не были определены биты «EEPM3» и «EEPM2» в регистре EECR, не было внесено определение регистра E2PD0 и т.д.). Однако, с точки зрения «фундамента» автором была проведена большая работа, ну а небольшие огрехи можно исправить и самостоятельно;

•  распаковываем скачанный файл в папку «C:\Program Files (x86)\Atmel\AVR Tools\AvrAssembler2\Appnotes» (ну, или где там у вас находятся все файлы типа «*.inc» для Студии);

•  в программе, предназначенной для кирпича ATmegaX8, меняем подключаемый камень. Делается это довольно просто – в любом ассемблерном листинге под AVR есть следующая строка (на примере 328-го кирпича):

.include "m328pdef.inc".

И если заменить ее на строку

.include "lg328Pdef.inc",

нам автоматически станут доступны все плюшки китайского камня (естественно, с поправкой на возможные косяки начального файла Юрия Ревича);

•  собственно, на этом переделка любимого «ассемблерного» софта завершена – теперь у нас появилась возможность работы с камнями LGT. Нам стали доступны все регистры ввода-вывода кирпича LGT8F328P, включая «эксклюзивные». Обращаю ваше внимание на то, что скачанный файл с описанием регистров ввода-вывода китайца не дополняет стандартный, а заменяет его, т.е. он должен вставляться именно взамен "m328pdef.inc", а не в дополнение к нему. Возможно, более правильным было бы просто добавить к стандартному файлу для ATmega328P файл с описанием дополнительных возможностей китайца, но у меня исторически сложилось именно так, как написано выше.

Если же вы пишете программы для камней AVR на Си, то могу дать лишь приблизительные рекомендации по переходу на LGT8F328P, поскольку с высокоуровневыми языками программирования я не особо знаком. В моем понимании, такой софт при выборе, например, камня ATmega328P по умолчанию подключит вам к проекту файл с определением регистров/битов для 328-й меги, и для обеспечения возможности работы с LGT8F328P нужно будет только добавить к нему «эксклюзивные» регистры китайца. Файл с описанием оных несложно нашарить в Интернете, однако, я рекомендую взять его из фирменного SDK, распространяемого изготовителем рассматриваемых камней (см. ниже). Если зайти в «…\LGTSDK_Builder_1.5beta29\SDK\LGT8F328P\inc», то можно найти аж шесть файлов-определений, относящихся к LGT8F328P, однако, нас в первую очередь интересует файл «lgt8f328p_spec.h» (если вы используете компилятор «AVR-GCC») или «lgt8f328p_ioavr.h» (если вы работаете с IAR-ом). Именно в них содержатся описание недостающих регистров и их битов, и именно их надо прикрутить к проекту после подключения стандартного файла 328-й Меги. В остальных файлах, насколько я могу судить, расположены куски кода, относящиеся к векторам прерываний, делителю тактовой частоты, выбору типа этой частоты и прочим вспомогательным вещам. Вообще говоря, вещи эти довольно полезны, однако, в простейших проектах, наверное, можно обойтись и без них.

Ну и, конечно, нельзя еще раз не упомянуть о том, что производитель камней LGT8F328P предлагает для работы с ними свой собственный SDK под названием «LGTSDK Builder», скачать который можно отсюда. На момент написания данной заметки актуальной была версия «v1.5beta29»; я ее сохранил к себе на всякий случай, но, возможно, со временем там что-то поменяется. Установки программный пакет «LGTSDK Builder» не требует, и для работы с ним нужно просто распаковать скачанный архив в любое удобное место на диске. Отмечу, что лично мне подобный софт не сильно интересен, поскольку ассемблер – это наше всё, однако, нормальным людям данный SDK вполне может облегчить знакомство с китайскими кирпичами (по крайней мере, на первых порах). У меня от «LGTSDK Builder» остались несколько противоречивые ощущения. С одной стороны, не может не радовать легкость первоначальной настройки камня – благодаря окну «Pinout Configuration Viewer», вызываемому по нажатию на кнопку «Pin View», с этой задачей справится даже полный дебил:



Достаточно в этом окне просто щелкнуть на нужную функцию камня, как софт сразу же переносит нас в нужный раздел, где можно настроить соответствующий узел по своему усмотрению. Например, если нажать на прямоугольник с надписью «ADC0», в окне «Device View» отобразятся настройки модуля АЦП, где можно активизировать данный модуль, а также установить его требуемые характеристики (опора, тактовая частота, выравнивание результата и т.д.):



Но с другой стороны, если после завершения всех настроек щелкнуть на кнопку «Build», то начинают сыпаться сообщения «Invalid Argument» и «Clean Error». Наверное, в этом можно разобраться и как-то всё исправить, но я не думаю, что данный геморрой поспособствует росту интереса пользователя к камню LGT8F328P. С третьей стороны, нужные файлы с расширениями «*.c» и «*.h» «LGTSDK Builder» всё-таки генерит, так что, возможно, и есть смысл поковыряться в нем поподробнее. Ну и в любом случае – в состав пакета входят не только файлы, описывающие «эксклюзивные» регистры LGT8F328P и их биты (см. выше), но и исходники для работы с различными узлами китайского камня, учитывающие его специфику. Расположены они в папке «…\LGTSDK_Builder_1.5beta29\SDK\LGT8F328P\inc», и для тех, кто пишет на Си, вполне могут быть интересны хотя бы в качестве примеров.

Нюансы работы с LGT8F328P

В завершение данной вводной заметки рассмотрим несколько ньюансов работы с микроконтроллерами LGT8F328P (естественно, не в «общем» смысле, а относительно кирпичей AVR). Сразу отмечу, что я в этом вопросе пока являюсь новичком, и особенностей у LGT8F будет явно побольше, чем упомянуто ниже. Однако, ньюансы, перечисленные в данном пункте, важны именно с точки зрения начинающего, ибо они касаются фундаментальных отличий камней, а без понимания этих отличий китайца даже на нужной тактовой частоте не запустишь. При этом следует понимать, что мой опыт работы с LGT8F328P ограничен лишь ассемблером, и в высокоуровневых языках различия камней вообще могут учитываться автоматически (см., например, фирменный SDK «LGTSDK Builder», упоминавшийся выше). Однако, даже в этом случае понимать разницу между китайцем и AVR-ками на аппаратном уровне будет нелишним.

▪ Отсутствие фузов

На мой дилетантский взгляд, прямо вот кардинальных отличий между пациентами три. Первое – отсутствие в китайском чипе такого понятия, как «FUSE». Все параметры и режимы работы камня, которые в AVR-ках устанавливаются при помощи фузов (тактовая частота кирпича, ее источник, уровень срабатывания BOD и т.д.), в LGT8F задаются прямо в теле программы на общих основаниях, т.е. при помощи регистров ввода-вывода. И это, на самом деле, просто охеренное решение, которое, с одной стороны, позволяет не выполнять «лишних» действий при прошивке МК, а с другой – исключает возможность превращения камня в настоящий кирпич путем записи в него неправильных фузов. Думаю, вы прекрасно знаете, как можно наглухо залочить чип AVR при помощи тех же битов «CKSEL[3:0]», особенно на первых порах, когда ты еще не в курсе, что «For all fuses “1” means unprogrammed while “0” means programmed.». В кирпичах же LGT8F328P этого нельзя сделать в принципе – камень всегда будет стартовать на «внутренней» частоте 4МГц, и только потом пользователь сможет установить разные значения ее предделителя и/или выбрать другой источник тактирования.

Система формирования тактовой частоты ядра в китайских камнях показана на рисунке ниже:



а управление данной системой осуществляется при помощи двух регистров PMCR и CLKPR:





Первый регистр включает/выключает разные источники тактирования, а также определяет, от какого из этих источников будут браться тактовые импульсы для ядра камня и для сторожевого таймера. Второй регистр позволяет при необходимости поделить эту частоту на 2/4/8/16/32/64/128/256, а также вывести результат деления в порт PB0 и/или PE5. Соответственно, чтобы кирпич заработал, например, на 32 мегагерцах от встроенного RC-генератора, регистр PMCR трогать не надо (там работа от «быстрых» внутренних часов прописана по умолчанию), а в CLKPR нужно записать нуль (чтоб делитель стал равен 1). А для того, чтобы тактироваться от внешнего кварца без предделителя, нужно установить биты «CLKSS» и «OSCMEN» в регистре PMCR, а CLKPR, опять же, обнулить. При этом следует помнить, что изменение содержимого PMCR и CLKPR производится несколько хитрее, чем обычно – сначала нужно установить бит №7 соответствующего регистра, и только потом можно писать в него требуемое значение. Причем, нужно обязательно успеть это сделать за 4 машинных такта – в противном случае старший бит автоматически сбросится, и дальнейшие попытки изменения содержимого регистра ни к чему не приведут. Именно поэтому в файлах «lgt8f328p_gcc.h» и «lgt8f328p_iar.h», входящих в состав программного пакета «LGTSDK Builder» (см. выше), куски кода, относящиеся к установке параметров системы тактирования, оформлены в виде ассемблерных вставок. Такой подход позволяет с гарантией успеть произвести запись требуемого значения в регистры PMCR и CLKPR до того, как бит №7 соответствующего регистра будет автоматически сброшен.

Наверное, вы уже заметили, что регистры PMCR и CLKPR в LGT8F328P выполняют те же функции, что и фузы «CKDIV8» и «CKSEL[3:0]» в ATmega328P. Кроме того, в регистре CLKPR есть бит «CLKOE0», функционал которого совпадает с оным у фуза «CKOUT» (вывод тактовой частоты ядра в порт PB0). Что же до прочих фузов 328-й меги, то ближайшие аналоги у китайца будут такими:

•  «SUT[1:0]» – биты, определяющие задержку начала выполнения программы после того, как все источники сброса станут неактивны. Судя по всему, в LGT8328P аналогов у данных фузов нет. Во всяком случае, на всех картинках, где изображен процесс сброса, приводятся просто конкретные цифры, а не настраиваемое значение «tOUT»:



На рисунке выше показаны примеры работы схемы «POR» (сброс при включении питания) для LGT8328P (слева) и для обычной AVR-ки (справа). Примерно такие же картинки приводятся в китайской документации для внешнего сброса, для детектора пониженного напряжения (Brown-Out Detector) и для сторожевого таймера, только цифры на них будут другими. Отсюда я делаю вывод, что заморачиваться по поводу настраиваемой задержки при сбросе китайцы не стали, хотя, при дальнейшем изучении LGT8F328P, возможно, окажется, что это не так;

•  «BODLEVEL[2:0]» – биты, определяющие уровень срабатывания детектора пониженного напряжения (Brown-Out Detector). В китайском камне за настройку данного узла отвечает регистр VDTCR, в частности, биты «VDTS2[2:0]». Отмечу, что у LGT8F328P имеется аж 8 значений пороговых напряжений, в отличие от AVR-ок с их типовой «троицей» +1,8В / +2,7В / +4,3В;

•  «RSTDISBL» – фуз, делающий вход сброса МК обычным портом ввода-вывода PC6. В китайском камне аналогом данного фуза является бит «C6EN» (либо «RSTIOEN») в регистре PMX2. Обратите внимание на то, что в случае AVR активация бита «RSTDISBL» делает невозможной дальнейшую прошивку оного по интерфейсу ISP (исправить это можно только параллельным программатором). Для LGT8F328P же установка бита «C6EN» абсолютно безопасна, так как камень шьется по шине SWD, для которой линия сброса совершенно необязательна (см. выше). Там, правда, есть кое-какие особенности использования порта PC6, но в целом убить китайский МК с его помощью не удастся;

•  «WDTON» – фуз, принудительно переводящий сторожевой таймер камня ATmega328P в режим сброса независимо от состояния битов «WDE» и «WDIE» регистра WDTCSR. Честно говоря, смысл данного бита в подсемействе кирпичей MegaX8 я не сильно понимаю, в отличие от «старых» моделей Мег (типа ATmega8 или ATmega16), где активизация «WDTON» делала невозможным программное отключение собаки в принципе. В китайском камне аналога данного фуза нет, однако, перевести сторожа в режим сброса можно при помощи битов «WDE» и «WDIE» регистра WDTCSR (как, собственно, и Мегу328);

•  «SPIEN» – фуз, при деактивации которого камень AVR превращается в настоящий кирпич для большинства пользователей, поскольку пропадает возможность дальнейшей его прошивки по интерфейсу ISP. Справедливости ради следует сказать, что во всех современных моделях Мег изменить этот фуз можно только при помощи параллельного программатора, так что случайно залочить чип таким путем у вас не получится. В китайском камне есть бит «SWDD» в регистре MCUSR, функционал которого близок к фузу «SPIEN» – при установке данного бита пропадает возможность прошивки и отладки МК по шине SWD. Однако, включить эту шину обратно довольно просто – для этого надо выключить питание, затем вновь включить его, прижав линию сброса («RSTN») к земле, и в этом состоянии уже́ можно будет залить в камень требуемую прошивку;

•  «BOOTRST» – фуз, позволяющий перенести точку старта программы (т.е. адрес, на который камень переходит после сброса) в область загрузчика. В этом вопросе я пока глубоко не разбирался, но, судя по всему, у китайского камня аналога данного фуза нет, т.к. вектор сброса у него всегда располагается по адресу 0x0000. При этом следует отметить, что остальную таблицу векторов LGT8F328P можно таскать по памяти гораздо свободнее, чем в случае AVR (см. бит «IVSEL» регистра MCUCR и регистр IVBASE целиком);

•  ну и фузы «DWEN», «EESAVE» и «BOOTSZ[1:0]» в камнях LGT8F328P тоже не имеют аналогов ввиду отсутствия у китайцев отладчика «debugWIRE», «настоящей» памяти EEPROM и такого понятия, как «область загрузчика».

На этом рассказ про отсутствие фузов в LGT8F328P завершен. На мой взгляд, от этого отсутствия китайские камни абсолютно ничего не потеряли, а в ряде случаев даже приобрели. Единственный момент, где с этим можно хоть как-то поспорить, относится к фузам задержки включения SUT[1:0], однако, лично я никогда их не настраивал – просто ставил задержку на максимум и всё. Возможно, в будущем придется следить, чтобы скорость нарастания напряжения питания МК была пошустрее, но здесь никаких проблем я не вижу. Конечно, отсутствие фузов повлечет дополнительные затраты памяти на начальную конфигурацию кирпича, но с учетом того, сколько флэша есть на борту LGT8F328P, сокрушаться по данному поводу просто смешно. Зато все настройки камня будут находиться в коде программы, а не раскиданы одно здесь, другое там (сколько раз бывали ситуации, когда файл прошивки в наличии, а какие фузы шить – хер его знает). Ну и невозможность случайно убить камень неправильной установкой битов конфигурации тоже, на мой взгляд, весьма полезная фича.

▪ Отсутствие EEPROM

Перейдем к следующему существенному отличию китайцев от AVR – отсутствию EEPROM. Вообще говоря, организация памяти у LGT8F328P заслуживает отдельной заметки, но для начала работы будет достаточно и базовых сведений. Основной факт здесь – на борту китайского камня не существует «настоящей» памяти EEPROM, в качестве оной используется кусок общей FLASH-памяти кирпича. Причем, прелесть именно модели LGT8F328P состоит в том, что пользователь сам может выбрать размер данного куска, а может и вовсе оставить всю память МК под программу, если сохранять данные в EEPROM не нужно. Доступные комбинации размеров областей «FLASH» и «E2PROM» приведены на рисунке ниже:



Обратите внимание на то, что каждый килобайт памяти данных отожрет целых два килобайта памяти программ (пока нам не особо важно, почему так получается – просто запомним сей факт). Физически область «E2PROM» будет располагаться в самом конце флэша:

,


а адрес ее начала может быть вычислен как суммарный объем доступной FLASH-памяти минус удвоенный объявленный размер еепрома. Соответственно, для камней LGT8F328P с их 32 килобайтами флэша адрес «E2P_BEGIN» на рисунке выше может принимать следующие значения:

•  0x7800 (если размер области «EEPROM» равен 1КБ);

•  0x7000 (если размер области «EEPROM» равен 2КБ);

•  0x6000 (если размер области «EEPROM» равен 4КБ);

•  0x4000 (если размер области «EEPROM» равен 8КБ).

И вроде бы на первый взгляд здесь всё просто, однако, без нормального гайда при переезде с AVR на LGT8F могут возникнуть проблемы – какие регистры/функции использовать, где разместить данные и т.д. В китайской документации, как обычно, черт ногу сломит, да к тому же некоторых сведений в ней тупо нет (например, попробуйте найти адреса́, по которым будет располагаться выделенный вам кусок памяти EEPROM – лично мне пришлось выяснять это экспериментально). Поэтому ниже я коротко расскажу, как можно переделать код, написанный для обычной Меги и относящийся к данным пользователя, под камень LGT8F328P.

Отмечу, что китайцы постарались максимально приблизить свой кирпич к AVR-ке, несмотря на то, что работа с EEPROM и FLASH на физическом уровне принципиально отличается (в первом случае мы можем записывать данные отдельно в каждую ячейку памяти, во втором – только постранично). Для этих целей они изобрели целый специализированный контроллер по имени E2PCTL*, который позволяет общаться с «EEPROM»-ом LGT8F328P точно так же, как с EEPROM-ом обычной AVR-ки**. Благодаря этому, при написании программ под LGT8F пользователь может не менять функции чтения и записи, использующиеся для работы с AVR***. Однако, для того, чтобы всё нормально функционировало, перед работой с контроллером E2PCTL надо правильно его настроить, для чего используются регистры ECCR и EECR:

* – хер его знает, что это означает, ибо ни одна аббревиатура в даташыте не расшифровывается;

** – только нужно учитывать, что при таком подходе чудовищно сокращается ресурс памяти, т.к. для записи одного байта мы каждый раз стираем целую страницу данных. В итоге заявленный ресурс будет относиться не к каждой ячейке «EEPROM», а ко всей памяти пользователя в целом. Например, если вы перепишете 100 раз всего один байт, то те же сто раз будет перезаписана вообще вся область «EEPROM» (на самом деле, всего 50 раз, но это уже тонкости);

*** – здесь есть один важный момент: после установки бита «EERE» в регистре EECR при чтении байта необходимо добавить две пустых операции «NOP». Ну или можно не корежить вашу штатную функцию чтения, но тогда после ее вызова нужно дополнительно приравнять требуемую переменную к регистру EEDR, например (в случае использования языка Си):

value = eeprom_read_byte(0x7800);
value = EEDR;

Исходники функций для работы китайских камней с EEPROM есть в папке «…\LGTSDK_Builder_1.5beta29\SDK\LGT8F328P\src» программного пакета «LGTSDK Builder»; файл «DrvEEPROM.c». Категорически рекомендую сравнить функции «DrvEEPROM_ProgByte» и «DrvEEPROM_ReadByte», находящиеся в этом файле, с подпрограммами записи/чтения байта в/из EEPROM, которые использует ваш софт.





При помощи регистра ECCR мы разрешаем использовать кусок общей памяти камня в качестве «EEPROM», а также задаем размер этого куска (см. биты «EEN» и «ECS[1:0]» соответственно). В регистре же EECR путем сброса флагов «EEPM3:0» мы должны установить 8-битный режим работы контроллера E2PCTL (только в этом режиме будет достигнута совместимость LGT8F с AVR). Обратите внимание на то, что перед внесением каких-либо изменений в регистр ECCR необходимо установить его старший бит «EWEN», и лишь затем можно будет записывать требуемое значение. Причем, сделать это нужно в течение 6 машинных тактов, иначе бит «EWEN» автоматически сбросится, после чего попытки изменения содержимого ECCR ни к чему не приведут. Кроме того, для корректной работы контроллера E2PCTL в 8-битном режиме требуется, чтобы биты «CP1» и «CP0» были установлены (вообще говоря, это их состояние по умолчанию, но я решил-таки заострить внимание на данном моменте, дабы вы их случайно не сбросили).

Однако, основной геморрой при переходе с AVR на LGT8F заключается не в этом. Напомню, что в AVR-ках память EEPROM физически отделена от памяти программ, вследствие чего она имеет собственное адресное пространство и при сборке проекта ее код генерится в отдельный файл с расширением «.eep». В китайских же камнях данные пользователя хранятся прямо во FLASH, поэтому они должны прошиваться в камень вместе с кодом программы, т.е. их нужно располагать прямо в HEX-файле. И в случае с ассемблером AVR-студии сделать это несложно – надо просто перебросить объявление/инициализацию ячеек EEPROM из сегмента «.eseg» в сегмент «.cseg» и расположить всё это по нужному адресу:



(для языка «Си» рекомендации будут даны ниже). На приведенной картинке кусок кода для EEPROM тупо скопирован в конец программы, а директива «.ORG» указывает на адрес, с которого будет начинаться этот кусок во FLASH-памяти. Значение «7800» в данной директиве говорит нам о том, что размер области «EEPROM» в проекте был выбран равным 1КБ (выше приводились адреса́ начала еепрома для всех возможных его размеров в камне LGT8F328P). Деление же этого адреса́ на два необходимо из-за того, что память программ в кирпичах AVR и LGT8F имеет 16-разрядную, а не побайтовую организацию.

Следует отметить, что последний факт обуславливает еще несколько важных моментов при переезде на китайца. Во-первых, при доступе к данным, расположенным в области «EEPROM», должен указываться их удвоенный адрес (точно так же, как при использовании инструкции «LPM»). Например, для переменной «ee_Current_H» из примера выше сохранение ее адреса в регистровой паре EEARH:EEARL будет выглядеть так:


ldi R16, high(ee_Current_H*2)
out EEARH, R16
ldi R16, low (ee_Current_H*2)
out EEARL, R16


то есть, переписывать придется все строки, содержащие имена соответствующих ячеек памяти. Правда, этого геморроя можно избежать, если переименовать переменную, например, в «ee_Current_H1», после чего вручную прописать

.equ ee_Current_H = ee_Current_H1*2

но здесь тоже предстоит писанина, и не факт, что ее будет меньше. Во-вторых, если для объявления двухбайтовых переменных вы используете директиву «.DW» (что, в общем-то, более правильно), можете радоваться – в сегменте «.cseg», точно так же, как и в сегменте «.eseg», первым в памяти будет расположен младший байт, так что здесь ничего менять не потребуется. Но, в-третьих, для всех однобайтовых переменных придется выбирать – либо мы экономим память и в ряде случаев теряем возможность использования прямого имени переменной, либо оставляем всё, как есть, что приведет к избыточному расходованию флэша. Чтобы было понятней, поясню. В примере, приведенном на рисунке выше, все переменные являются однобайтовыми, поскольку для их объявления используется директива «.DB»*. Поэтому при компиляции проекта для каждой строки области «EEPROM» будет выведено предупреждение – мол, вы пытаетесь забить память программ байтами, а она организована пословно, поэтому неиспользованный байт в каждом слове мы заполним нулём. В итоге под 6 переменных будет отведено целых 12 байт памяти, что увеличит расход флэша вдвое, однако, при этом мы сможем напрямую использовать имя каждой переменной, т.к. все метки сохранились. В качестве альтернативы можно рассмотреть вариант, при котором все ячейки памяти будут заполнены «полезной» информацией – для этого надо просто записать по два/четыре/шесть… значений переменных в одной строке, например, вот так:



При таком подходе все предупреждения компилятора исчезнут, а переменные займут в памяти положенные 6 байт, однако, к половине из них нельзя будет обратиться напрямую по имени, т.к. соответствующие метки пропали. Какой вариант инициализации памяти выбрать в итоге – решать вам, всё зависит от вашего стиля написания кода программы. Например, мне намного больше нравится первый подход, поскольку я активно использую имена переменных, и количество исправлений из-за отсутствия половины имен у меня будет значительным. Поэтому мне проще распрощаться с небольшим «лишним» куском памяти – с учетом того, что под данные пользователя в камне LGT8F328P можно отвести аж 8КБ, а суммарный объем его флэша составляет 32КБ, такая расточительность не особо напрягает. Необходимо только помнить, что при этом каждая переменная в области «EEPROM» будет занимать не один байт (как это задумывалось изначально), а два – это нужно учитывать, например, при работе с массивами и целыми блоками данных в еепроме. Однако, если вы привыкли работать с двухбайтными переменными как именно с двухбайтными, то, возможно, есть смысл рассмотреть второй подход к инициализации области «EEPROM».

* – я привык отдельно объявлять старший и младший байты двухбайтной переменной, хотя, повторюсь, правильней было бы столбить под нее сразу целое слово при помощи директивы «.DW».

Это что касается ассемблера. Совсем другие расклады будут, если вы пишете на Си. Там, со слов коллеги, данные, располагаемые в памяти EEPROM, объявляются и инициализируются как обычные переменные, только с атрибутом «EEMEM» (на примере связки «avr-gcc» + «Eclipse»):

#include <avr/eeprom.h>
uint8_t EEMEM value1 = 0x00;
uint8_t EEMEM value2 = 248;
uint16_t EEMEM value3 = 4096;

При этом программисту вообще похер, по каким адресам в памяти будут лежать эти данные – там при сборке проекта комп сам со всем разберется. Поэтому когда встал вопрос о переносе данных пользователя из EEPROM во FLASH, коллега только почесал репу – мол, я не знаю, как это сделать. Пришлось затеять расследование, в результате которого оказалось, что под переменные еепрома создается отдельная секция «.eeprom» (см. файл «avr/eeprom.h»):

#define EEMEM __attribute__((section(".eeprom")))

и если при объявлении данных перед их именем указано «EEMEM», то для компилятора и линковщика они будут ассоциироваться именно с энергонезависимой памятью, а не с ОЗУ. Проблема заключается в том, что при сборке проекта все эти переменные отправятся в отдельный файл с расширением «.eep», и как перенаправить их во FLASH, т.е. в HEX-файл, мы так и не нашли. Однако, было установлено, что если определить свою собственную секцию данных, то вот ее можно расположить в любом месте памяти программ, главное – не забыть сообщить об этом линкеру. И в итоге рабочий алгоритм по созданию инициализированной области «EEPROM» в камне LGT8F328P будет выглядеть следующим образом (опять же, на примере связки «avr-gcc» + «Eclipse»):

•  определяем в программе собственную секцию для хранения данных пользователя (по аналогии с EEMEM/eeprom):

#define E2PROM __attribute__((section(".e2prom")))

•  объявляем/инициализируем требуемые переменные, которые должны быть ассоциированы с созданной секцией:

uint8_t E2PROM value1 = 0x00;
uint8_t E2PROM value2 = 248;
uint16_t E2PROM value3 = 4096;

•  говорим линкеру, с какого адреса во FLASH-памяти должна начинаться созданная секция. Делается это при помощи опции (ключа) «-Wl»; для области «EEPROM» размером 1КБ заветная фраза будет звучать так:

-Wl,--section-start=.e2prom=0x7800

(для размеров 2КБ/4КБ/8КБ значение «0x7800» нужно заменить на 0x7000/0x6000/0x4000 соответственно). Однако, тут нужно знать, куда именно вставить эту заветную фразу. В «Эклипсе» она прописывается в общих свойствах линкера (на рисунке ниже фраза выделена синим)



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

В результате выполнения вышеперечисленных действий, после компиляции и линковки в код вашей программы добавятся значения всех переменных, у которых есть атрибут «E2PROM». Располагаться они будут, начиная с адреса 0x7800 (или какой вы там указали) – в этом можно убедиться, просмотрев файл с расширением «.lss». То есть для того, чтобы переделать существующий AVR-овский проект под китайский кирпич, вам просто понадобится определить секцию «.e2prom», заменить все «EEMEM» на «E2PROM» и сказать линковщику, откуда должна начинаться новоиспеченная секция. Никаких удвоений адресов и еботни с укладыванием в память однобайтовых переменных (как в случае с ассемблером) не потребуется – комп сам со всем разберется. И это, наверное, первый раз в жизни, когда я действительно ощутил пользу от языка «Си».

▪ Таблица векторов прерываний

Третье серьезное отличие камней LGT8F328P – размер таблицы векторов прерываний:

1 – при запрограммированном фузе «BOOTRST» вектор сброса будет располагаться в начале загрузчика.

Как следует из данного рисунка, у китайских кирпичей таблица больше, чем у AVR-ок, и включает в себя прерывание от дополнительного компаратора AC1, от таймера/счетчика T3, а также от «недостающих» портов ввода-вывода (отметим, что вектор PCI4 относится к портам PF0…PF7, которые в корпусе TQFP-32 отсутствуют). При этом для меня несколько странен тот факт, что прерывание ANA_COMP1 введено взамен прерывания SPM READY, а следующая строка в таблице тупо не используется. Вроде бы, логичнее было перекинуть компаратор в вектор #27, а неиспользуемым сделать вектор #26, но разработчикам, конечно, виднее (к тому же, прерывание по готовности памяти программ обычно юзается только в области загрузчика, а у китайцев такой области просто нет). Однако, если в «обычной» программе для AVR вы для каких-то целей используете прерывание SPM READY, об этой особенности таблицы векторов LGT8F328P следует помнить (как и об отсутствии в ней данного прерывания как такового).

В целом можно сказать, что если вам необходимо переделать под китайца программу, написанную для AVR, увеличенный размер таблицы векторов можно не учитывать. Связано это с тем, что в камнях линейки ATmegaX8 дополнительный компаратор, таймер T3* и порты PEx*/PFx отсутствуют, поэтому прерывания от данных узлов заведомо будут отключены, и перехода на соответствующий вектор не произойдет. Единственный серьезный геморрой, повторюсь, здесь может возникнуть только в том случае, если вы за каким-то бесом используете прерывание SPM READY (хотя для чего оно нужно в «обычной» программе – ума не приложу). А вот если вы хотите не просто портировать код, а расширить его функционал, тогда да – требуемые вектора́ в таблицу для LGT8F328P придется дописывать вручную.

* – если, конечно, речь не идет о камне ATmega328PB, но он вообще стоит особняком, и в качестве «обычной» Меги его рассматривать не сто́ит.

▪ Прочие отличия

Остальные попавшиеся мне отличия китайских камней от AVR-ок не столь фундаментальны, но не менее интересны. Конечно, мое знакомство с LGT8F328P только-только начинается, но и того, на что я успел наткнуться, уже́ с лихвой хватит для принятия решения о переезде на китайца (даже если не обращать внимания на то, что он тупо дешевле):

•  максимальная частота работы у LGT8F328P составляет 32МГц при любом напряжении питания, в то время, как у AVR – всего 20МГц и только при Vcc = +5,0В. Да, я знаю, что про это уже́ говорилось выше (собственно, с этого и начиналась заметка), но упомяну данные фичи еще раз, ибо прирост скорости работы камня на 60% – неплохой, мягко говоря, аргумент в пользу китайца;

•  добрая половина команд в LGT8F328P выполняется быстрее, чем в AVR, что дополнительно увеличивает быстродействие китайца. Причем, с учетом того, что в большинстве случаев разница в выполнении инструкций описывается фразами «один машинный такт вместо двух» и «два машинных такта вместо трех», реальное увеличение производительности для конкретной команды составит 50% и 33% соответственно;

•  особенно хотел бы отметить скорость выполнения команд «SBI», «CBI» и «RJMP». Если про время исполнения большинства инструкций в LGT8F328P я говорю, опираясь (пока) исключительно на документацию, то эти три были опробованы вживую. И могу подтвердить – все три команды выполняются всего за один машинный такт, в отличие от AVR, где каждая из них длится два цикла. Данная фича может быть крайне полезна во многих случаях (например, в приложениях, основная задача которых заключается в формировании шустрого ногодрыга);

•  по умолчанию LGT8F328P стартует на частоте 4МГц, а AVR – на 1МГц. Это не про «хуже/лучше», а просто лишний раз напоминаю.

И в завершение данного пункта хотел бы обратить ваше внимание на следующее. В большинстве случаев увеличенная скорость выполнения инструкций китайцем – это, конечно, благо. Однако, есть ряд задач, где критична длительность определенных временных интервалов, и если команды выполняются быстрее, программа будет работать некорректно. Например, у меня есть AVR-овский проект, формирующий на выходе импульсы с точной задержкой относительно входных, так там всё завязано на количество машинных тактов, которое требуется для выполнения инструкций SBI/CBI/LD/RJMP и т.д. И если данный проект портировать на LGT8F328P, код программы придется переделывать, иначе задержки будут неверными. Конечно, дописать несколько NOP-ов – это не проблема; гораздо сложнее было бы решить обратную задачу (сформировать такие же задержки на более укуренном кирпиче). Однако, сама по себе эта особенность при переезде на китайца может всплыть, поэтому упомянуть о ней следовало.

Ну а у меня на этом всё. Желаю удачи при переезде на китайские камни!

Обсудить эту заметку можно в Телеге или в ВК



◄▪▪▪ Предыдущая заметка К перечню заметок Следующая заметка ▪▪▪►

Место для разного (сдается)

 




Создание, "дизайн", содержание "сайта": podkassetnik
Для писем и газет: Почта России электрическая

Место для © (копирайта, понятно, нет, но ссылайтесь хотя бы на первоисточник)

Since 2013 и до наших дней