В предыдущей моей статье про программатор spi flash и ic eeprom из arduino после её публикации самый первый вопрос-комментарий был «программирует ли он PIC-и?». Вопрос, конечно, от невнимательного читателя, но он лишний раз подтвердил, что, хоть микроконтроллеры PIC довольно старые, но до сих пор ещё пользуются большим интересом у радиолюбителей, так как за долгое время было разработано огромное количество устройств на их базе. Но вот прошивать эти микроконтроллеры — то ещё приключение. Как известно для PIC-ов есть множество различных программаторов: простейшие, типа несколько проводков для LPT или COM портов (типа JDM и других), USB программаторы (PICKIT1,2,3 и их самодельные клоны). У обоих видов есть свои плюсы и минусы: первые — очень просты в сборке, но требуют наличия соответствующих портов, которые не всегда есть в современных ПК, а тем более в ноутбуках. Вторые — очень удобно использовать, так как они подключаются по USB, их можно подключать к любому современному и не очень ПК/ноутбуку, но! у них тоже есть весомый недостаток — они не очень дешёвые (даже на известном китайском маркетплейсе PICKIT2 стоит порядка 15-20$, а микроконтроллер PIC18F2550 для сборки самодельного PICKIT2 порядка 10$ и то не факт, что не нарвётесь на подделку/перемаркировку/горелку). Не каждый начинающий и даже более опытный радиолюбитель захочет покупать такой программатор, если ему он может пригодиться раз-два в год.
Так вот, после того самого комментария у меня возник спортивный интерес, смогу ли я сделать свой программатор для PIC-ов? Тем более, ранее я уже пытался «бороться» с этими микроконтроллерами. И скажу честно, у меня в первый раз не получилось ничего, от слова «совсем». Даже ID PIC16F73 не смог прочитать. Но уже тогда я понял, что эта задача — крайне сложная. Даже не из-за моего неудачного опыта. Просто я почитал несколько «Programming datasheet» на разные линейки PIC-ов и был просто в ужасе: оказалось что у этих «камней» куча подсемейств, состоящих от 1-2 представителей до нескольких десятков. И таких подсемейств — просто огромное количество. А самое интересное, что все они программируются по разному: у некоторых всего 6-8 команд, а в некоторые вообще нужно пихать ассемблерные коды через программатор! Единственное, что их объединяет — это название пинов для подключения программатора (PGC, PGD, PGM и MCLR). На этом сходства заканчиваются. Даже, например, PIC16F84 и PIC16F84A или PIC16F628 и PIC16F628A в плане программирования — это совсем разные «камни»! Но и это ещё не всё!!! Вторая, не менее важная проблема — это HEX-файлы для PIC-ов. У них тоже нет никакого стандарта! В одном единственном файле располагается CODE сегмент (т.е. основная программа), DATA сегмент (т.е. EEPROM) и слова конфигурации. А самое смешное, что у каждого подсемейства все эти сегменты располагаются по разным адресам смещения и количество конфигурационных слов — тоже у всех разное. Наверное, после этого до меня дошло, почему PICKIT такой дорогой? Это ж у программиста может съехать крыша в попытках хоть как-то стандартизовать алгоритмы программирования такого «приятного разнообразия». Представляю, как инженеры Microchip в своё время вели разработку: «А давайте, следующая линейка будет программироваться не командами, а ассемблерными кодами ядра и желательно развёрнутыми задом-наперёд… А что? Нечего программистам расслабляться».
Но вот, прошло некоторое время, у меня появились свои наработки в плане программного обеспечения и схемотехники, и я решил попробовать снова побороть PIC-и. Накидал в голове приблизительный алгоритм, как можно более-менее унифицировать софт. А также продумал «железную» часть программатора. С описания которой я, пожалуй, и начну.
За основу программатора я решил взять пятивольтовый микроконтроллер, так как логические уровни большинства PIC-ов именно 0 — 5 вольт (не считая PIC24, dsPIC, но о них я даже и думать не хотел). Я мог взять что-то очень дешёвое, типа STC8, но решил, что они не очень популярны и сделал свой выбор в пользу AVR. Тем более, что они используются в известнейших платах arduino, которые найдутся в загашниках почти любого радиолюбителя, да и стоят не очень дорого (относительно). Но одной лишь платы arduino или микроконтроллер AVR для программирования PIC-ов будет недостаточно. Наверное, многие знают, что микроконтроллеры PIC (большинство PIC16, PIC18) используют высоковольтное программирование, т.е. для входа в режим программирования необходимо подавать на определённый пин (MCLR) повышенное напряжение 12-13 вольт. Нужен дополнительный источник повышенного напряжения и схема управления подачей этого самого напряжения. Так вот, в первом варианте схемы я решил использовать повышающий преобразователь напряжения на микросхеме MC34063A, чтобы питать программатор исключительно от USB. И он заработал!!! Но радость «победы» длилась недолго. Программатор читал прошивку, читал ID, конфигурационные биты, но упорно отказывался стирать/записывать. В Programming datasheet я увидел, что операции записи/стирания требуют напряжения питания VDD 4,5-5,5 вольт. Я решил померять напряжение VDD во время процесса чтения и получил огромную просадку до 3,8 вольта. Естественно, при такой просадке о записи/стирании и речи идти не могло. Поэтому, я решил отказаться от идеи с преобразователем напряжения в пользу дополнительного источника питания 12 вольт. И это дало результаты — с внешним источником 12 вольт процессы стирания и записи пошли! В итоге уже второй вариант схемы — стал окончательным. Схема получилась очень простая. Я не стал использовать буферных элементов на дискретной логике, а сделал ключи управления напряжениями VPP и VDD (12в и 5в) на транзисторных ключах (2 каскада каждый). Также в схеме используется линейный стабилизатор на 5 вольт (7805). Стабилизацию по 12 вольтам я решил не ставить, так как боялся, что некоторые PIC-и могут потребовать более высокое напряжение (12,5-13,5 вольт) и с установленным стабилизатором типа 7812 поменять напряжение VPP уже не получится. В итоге, получилось не очень сложная схема. Под силу даже новичку с советским паяльником толщиной с палец:
Выглядит как-то так (все детали в SMD исполнении, расположены на нижней стороне платы):
На фото — первый вариант платы, я его показываю исключительно для наглядности, как можно скомпоновать плату-шилд для arduino nano.
Окончательный вариант своего программатора я сделал в виде отдельной платки с ATmega328P. Схема её следующая:
Я не рисовал ключи управления напряжениями на этой схеме, иначе бы она заняла пол экрана. Здесь изображена схема подключения атмеги к USB-UART конвертеру и ключам управления напряжениями. Моя окончательная плата программатора выглядит следующим образом (сразу извиняюсь за остатки флюса и ваты, которой я пытался отмыть флюс):
Платка получилась достаточно компактная, всего 44х47 мм, это всё благодаря миниатюрной микросхеме USB-UART преобразователя CH340E, которая к тому же не требует подключения кварцевого резонатора и атмеге в корпусе TQFP32, которая тоже занимает места не очень много. Линейный стабилизатор и вся мелочёвка также в SMD исполнении. Кстати, транзисторы я использовал Q1,Q2 — MMBT3904 (маркировка корпуса 1AM), Q3,Q4 — BC856 (маркировка корпуса 3B). Рисунок своей печатной платы я выложу в конце статьи. Хотя, плату каждый может сделать под имеющиеся компоненты. Кстати, при наличии у USB-UART преобразователя сигнала DTR, его необходимо повесить на пин RESET атмеги через конденсатор 0,1uF, чтобы работал автоматический сброс при программировании атмеги через UART bootloader. Как раз у CH340E этого сигнала нет и приходится сбрасывать атмегу вручную кнопкой.
Теперь поговорим о софте. Программу для ПК я традиционно написал в среде MS Visual Studio на C#. Внешний вид первой версии программы такой:
Интерфейс программы, думаю, не окончательный, потому что я не в силах предугадать, какие ещё параметры могут быть у каких либо семейств PIC и что надо добавить/убавить. Будет видно потом, в ходе добавления новых МК.
Как видите, есть два больших текстовых поля с данными CODE (верхнее) и DATA (нижнее) сегментов прошивки. Семь текстовых полей для слов конфигурации (брал за максимум PIC18F4550). Поле выбора модели PIC. Куча кнопок с интуитивно понятными по названиям функциями. В нижнем правом углу — текстовое поле для вывода информации. Три поля «Oscal» делал про запас, они пока никак не задействованы, но знаю что в 8-ногих пиках есть байты калибровки RC-генератора, которые можно случайно затереть, поэтому — могут пригодиться.
Работать с программой очень просто: подключаем программатор к USB. Должен появиться COM порт. В верхнем правом углу программы выбираем COM, на котором висит наш программатор (если программатор был подключен после запуска программы, то надо нажать кнопку «Scan» для получения списка доступных портов) и нажимаем кнопку «Open», если всё правильно, она должна стать зелёной и появится соответствующее сообщение в консоли внизу справа. Чтобы проверить, отзывается ли программатор, нажимаем кнопку «Check», в консоли должно появиться сообщение «Программатор подключен». Дальше надо выбрать модель PIC в окошке «Device». И только после этого можно читать, писать, стирать. Есть некоторые особенности работы с конфигурационными словами: при загрузке HEX-a они автоматически вставляются в соответствующие окошки (Config1 — Config7), при нажатии кнопки «Write» они будут записаны автоматом после CODE и DATA сегментов, либо при нажатии кнопки «WR Cfg» они запишутся отдельно. Но значение Config-ов пишутся не напрямую из HEX-а, а именно из этих окошек, то есть если после открытия HEX-файла переписать свои корректные значения в эти окошки, то будут записаны именно они.
Так как программа очень сырая, многие исключения в ней не обрабатываются, то есть, если сделать что-то неверно, программа вылетит с ошибкой. Например, если выбрать модель PIC и открыть HEX для совершенно другой модели. Или вписать некорректные значения в окошки Config (кириллицу или не 16-ричные знаки не «0-9,A-F»).
Что касается скорости работы программатора, то она — вполне достойная. Конечно, скорость чтения/записи зависит от модели PIC. Но если взять PIC18F4550 как наиболее «жирный» из уже поддерживаемых, то скорость чтения около 2 Килобайт в секунду, запись, естественно, чуть медленнее. При 32 Килобайтах CODE сегмента время чтениязаписи не напрягает.
Программу я старался написать таким образом, чтобы она была универсальна для любого семейства PIC, и не требовала никаких изменений при добавлении поддержки новых моделей. Для добавления новых устройств используется файл настроек («pic.xml»), который находится в папке с программой. В нём содержатся однотипные структуры с необходимыми параметрами микроконтроллера, которые программа использует в первую очередь для разбора HEX-файла. Таким образом работа по добавлению новых моделей PIC ведётся в файле настроек и в большей степени в прошивке атмеги. Вид файла настройки следующий:
<name model="PIC16F628A"> <id>0x1060</id> <type>2</type> <f_size>4096</f_size> <f_psize>1</f_psize> <ee_size>128</ee_size> <f_start>0</f_start> <ee_start>002100</ee_start> <ee_type>0</ee_type> <conf1>002007</conf1> <conf2>none</conf2> <conf3>none</conf3> <conf4>none</conf4> <conf5>none</conf5> <conf6>none</conf6> <conf7>none</conf7> <oscal1>none</oscal1> <oscal2>none</oscal2> <oscal3>none</oscal3> <delay>4</delay> <word>3FFF</word> </name>
Как видите, параметров много, хотя если сравнить с подобным файлом настроек программатора K150, то и не особо.
На данный момент в программе не реализованы функция сохранения считанного в HEX-файл, работа с oscal байтами калибровки. По поводу сохранения HEX-а я уже приблизительно себе представляю как это сделать. Oscal пока даже не в ближайшей перспективе.
Что касается прошивки атмеги. Никаких скетчей для arduino вы не увидите, вопреки названию статьи. Код написан на Си. Для написания кода я использовал среду Eclipse с плагином для AVR (использует AVR-GCC, точно такой же как и Atmel studio). Поэтому все *.h и *.c файлы проекта могут быть спокойно перенесены в Atmel studio без изменений. Также можно с минимальными изменениями перенести код на другие модели AVR с бОльшим объёмом флеш памяти (например, ATMEGA64, ATMEGA128 и др.). Из специфических функций там только USART (настроен на скорость 57600, передача и приём плюс прерывание по приёму байта). И то модуль USART у большинства атмег почти одинаковый, достаточно подправить названия регистров (например UDR на UDR0, UCSR0A на UCSRA и т.д.). Все остальные функции — реализованы с помощью «ногодрыга». Настройка пинов PGD,PGC,PGM,VPP и VDD реализована с помощью макросов:
#define PGD_PIN 1 //D9 #define PGD_PORT PORTB #define PGD_DDR DDRB #define PGD_IDR PINB #define PGC_PIN 2 //D10 #define PGC_PORT PORTB #define PGC_DDR DDRB #define PGM_PIN 0 //A0 #define PGM_PORT PORTC #define PGM_DDR DDRC #define VDD_PIN 7 //D7 #define VDD_PORT PORTD #define VDD_DDR DDRD #define VPP_PIN 6 //D6 #define VPP_PORT PORTD #define VPP_DDR DDRD #define PGD_HIGH PGD_PORT |= (1 << PGD_PIN); #define PGC_HIGH PGC_PORT |= (1 << PGC_PIN); #define PGM_HIGH PGM_PORT |= (1 << PGM_PIN); #define VDD_HIGH VDD_PORT |= (1 << VDD_PIN); #define VPP_HIGH VPP_PORT |= (1 << VPP_PIN); #define PGD_LOW PGD_PORT &= ~(1 << PGD_PIN); #define PGC_LOW PGC_PORT &= ~(1 << PGC_PIN); #define PGM_LOW PGM_PORT &= ~(1 << PGM_PIN); #define VDD_LOW VDD_PORT &= ~(1 << VDD_PIN); #define VPP_LOW VPP_PORT &= ~(1 << VPP_PIN);
Поэтому переназначить ножки микроконтроллера на любые другие не представляет особого труда. Достаточно переписать номер пина и букву порта.
Прошить сам программатор HEX-файлом можно двумя способами: любым программатором для AVR, если это чистая атмега или с помощью программ для загрузки HEX-а в arduino, таких как XLoader, ArduinoUploader и других (можно найти, например, тут. Это не реклама стороннего сайта!).
На данный момент прошивка занимает 5 Килобайт с небольшим из 30 доступных в ATMEGA328Р (2 Кб занимает bootloader arduino). Так что места ещё навалом, можно добавить поддержку многих семейств.
Теперь — самое главное. Поддержку каких камней удалось добавить? Скажу сразу, пока — не густо. Так как пока что от начала разработки до момента написания статьи прошло около недели, и то я вёл разработку в свободное время, на данный момент добавлена поддержка и проверена работа (с реальным устройством на данном МК) PIC16F628A (в его подсемействе есть ещё два камня 627A и 648A). PIC16F73 (проверена работа методом сравнения прочитанного и записанного с референсным JDM программатором), у этого «камня» есть ещё три одногрупника (F74, F76, F77). Также добавлена поддержка и проверена работа с PIC18F4550, добавлен PIC18F2550 (так как это в плане программирования одно и то же). Единственное, не смог победить чтение ID у этого семейства, возможно, мой экземпляр работает некорректно. У последних двух «камней» в одногрупниках — несколько десятков МК, поддержку которых легко сделать всего лишь правкой конфигурационного файла без изменения прошивки атмеги. Должны работать, но мне пока не на чем проверить.
В ближайших планах добавить поддержку таких старичков как PIC16F84A и его одногрупников (так как он у меня есть для тестов), PIC16F87X. Дальнейшая поддержка будет зависеть от интереса к устройству и, возможно, помощи в тестировании программатора. Так как мне одному не под силу и не по карману сделать это самому (покупать разнообразные PIC-и только для тестов).
На этом пока всё. Спасибо за внимание. Адекватная критика в комментариях приветствуется.
Список радиоэлементов
Обозначение | Тип | Номинал | Количество | Примечание | Магазин | Мой блокнот | |
---|---|---|---|---|---|---|---|
Схема №1 (на arduino nano) | |||||||
U1 | Плата Arduino | Arduino Nano 3.0 | 1 | ||||
Q1, Q2 | Биполярный транзистор | КТ3102 | 2 | ||||
Q3, Q4 | Биполярный транзистор | КТ502Е | 2 | ||||
U2 | Линейный регулятор | LM78M05 | 1 | ||||
D1 | Диод Шоттки | 1N5819 | 1 | ||||
R1-R4 | Резистор | 1 кОм | 4 | ||||
R5, R6 | Резистор | 4.7 кОм | 2 | ||||
R7, R8 | Резистор | 10 кОм | 2 | ||||
C1, C2 | Конденсатор | 0.1 мкФ | 2 | ||||
Схема №2 (на atmega328p) | |||||||
U1 | Микросхема | CH340E | 1 | ||||
U2 | МК AVR 8-бит | ATmega328P | 1 | ||||
D1 | Диод Шоттки | 1N5819 | 1 | ||||
Led1 | Светодиод | любой | 1 | ||||
C1 | Конденсатор | 0.1 мкФ | 1 | ||||
C2, C3 | Конденсатор | 16 пФ | 2 | ||||
R1 | Резистор | 560 Ом | 1 | ||||
R2 | Резистор | 10 кОм | 1 | ||||
XT1 | Кварцевый резонатор | 16MHz | 1 | ||||
KEY1 | Тактовая кнопка | любая | 1 | ||||