Начинаю выкладывать курс по изучению контроллеров RISC-V на примере GD32VF103 и чуть более мощного CH32V303. Основной упор будет скорее на теорию и технологии, чем на «быстрый старт» и «электронику для домохозяек». То есть ассемблер, регистры и самодельные печатные платы.
1. Введение
1.1. Специфика микроконтроллеров
Встраиваемая система (англ. embedded system) — специализированная микропроцессорная система управления, контроля и мониторинга, концепция разработки которой заключается в том, что такая система будет работать, будучи встроенной непосредственно в устройство, которым она управляет (определение из Википедии). Как следует из определения, такая система обычно не обладает «стандартными» средствами ввода-вывода — монитором, клавиатурой, мышкой. Вместо этого она взаимодействует с окружающей средой при помощи множества разнообразных датчиков, кнопочек, исполнительных механизмов и индикаторов. Например, встроенная в стиральную машину система может уметь управлять асинхронным мотором (несколько линий ШИМ, датчики Холла, измерители тока), подогревом воды (управление реле нагревателя, датчик температуры), оценивать массу заложенного белья (датчик нагрузки или ускорения), управлять водяным клапаном, позволять пользователю выбирать режим работы («крутилка» энкодер, кнопки, знаковый дисплей). При этом важным является не умение проводить сложные расчеты, не умение запускать множество интерактивных программ одновременно, а именно взаимодействие с периферией и быстрая реакция на внешние события. Если в процессе работы двигатель по какой-то причине замедлился, надо срочно подстраивать управляющие сигналы, а то и останавливать его, если что-то пошло совсем не так. В ряде случаев роль играют такие параметры, как энерноэффективность и занимаемый объем. Поскольку подобные системы обычно предоставляются пользователю в виде законченного монолитного устройства, то и схемотехнику, и софт также обычно делают монолитными. Вместо кучи драйверов под все существующее оборудование, жестко прописывают только то, которое реально установлено на плате. Вместо возможности запуска сторонних программ со всеми защитами и разделением прав доступа, сразу закладывают весь возможный функционал без возможности расширения. В конце концов, кому придет в голову вскрывать корпус той же стиральной машины чтобы подпаять туда дисплей и любоваться графикой?! Хотя умельцы, конечно, находятся… Отсюда вытекает желание производителей уместить на одном чипе не только вычислительное ядро (процессор), но и память, и периферию.
Рассматривать будем микроконтроллер GD32VF103 (не путать GD32VF103 с GD32F103 — первый на ядре RISC-V, второй на ARM). Позже перейдем на CH32V303CBT6, а пока относительно его особенностей ограничусь примечаниями. В целом пецифика программирования контроллеров, в отличие от программирования «большого» компьютера:
-
Отсутсвие стандартной операционной системы, библиотеками которой можно пользоваться. Если операционная система и используется, она неотделима от прошивки.
-
Отсутствие стандартных средств ввода-вывода (монитор, клавиатура)
-
Наличие большого количества встроенной периферии (UART, SPI, I2C, USB, …) для взаимодействия со всем спектром устройств нестандартных.
-
Малая вычислительная мощность (в нашем случае GD32VF103 108 МГц тактовой частоты, 32 кБ оперативной памяти, 128 кБ флеш-памяти программ, нет модуля FPU. В случае CH32V303, частота чуть выше, до 144 МГц, и FPU в наличии).
-
Слабая поддержка многозадачности: нет модуля виртуальной памяти (MMU), виртуализации. Преимущественно монолитная организация программ (хотя выполнение кода из оперативной памяти все же поддерживается).
-
Хорошая поддержка реакции на внешние события (контроллер прерываний)
1.2. Железо
Такие контроллеры вполне доступны в свободной продаже как в виде отдельных микросхем, так и установленными в отладочные платы вроде Longan Nano. Стоит отметить, что корпус не слишком дружелюбен к радиолюбителям (планарный с шагом выводов 0.5 мм), хотя и поддается мастерам ЛУТа. Бывают и более противные корпуса вроде 200-ногих BGA, про которые здесь упоминать не стоит.
Преимуществом данного контроллера перед многими другими является то, что для его программирования можно обойтись вообще без программатора, поскольку с завода в нем встроен код, позволяющий обновлять прошивку через USB или UART. Чтобы перейти в этот режим надо на ножку BOOT0 подать лог.1, а на BOOT1 — лог.0, после чего перезагрузить контроллер. После прошивки надо вернуть BOOT0 в лог.0 и снова перезагрузить контроллер чтобы проверить работу прошитого кода.
Для прошивки через USB:
$ dfu-util -a 0 -s 0x08000000 -D firmware.bin
Здесь firmware.bin — файл прошивки
Для прошивки через UART придется сначала соединить TX0 контроллера с RX переходника USB-UART (или COM-UART, или USB-TTL), вывод RX0 контроллера с TX переходника и, разумеется, земли. Подключать контроллер при этом к USB нельзя — иначе будет пытаться прошиться именно через него. Лучше подать питание с переходника.
$ stm32flash /dev/ttyUSB0 -w firmware.bin
Здесь /dev/ttyUSB0 — имя переходника в системе
Прошивка через JTAG также возможна. И даже не потребует дергать Boot0, reset. Можно использовать покупные JTAG отладчики вроде JLINK или ST-LINK. Я проверял на самодельном на FT4232H. На момент написания первой версии этой статьи (2022 год) обычный openocd из репозитория работать с gd32 не умел. Сейчас, насколько я знаю, это исправили. Но не исправили поддержку CH32 (по крайней мере на 2024 год). В целом, мне работа через JTAG не понравилась.
Продвинутая прошивка через UART. Давным-давно, осваивая USB в stm32, я собрал переходник USB -> UART + UART + GPIO + MSD. Дальнейшим его развитием стал целый стенд для удаленной отладки, названный Каракатицей. За счет возможности управления несколькими ножками ввода-вывода, можно программно дергать boot0 и reset, что делает прошивку весьма удобной.
Грабли с CH32: его бутлоадер довольно нестандартный, и вменяемых утилит для работы с ним мне найти не удалось. Поэтому пришлось частично дизассемблировать этот самый бутлоадер и написать собственную утилиту. В отличие от других сторонних утилит, эта умеет работать и через USB, и через UART, плюс, при работе через UART, управлять ножками RTS, DTR, которые можно подключить к reset, boot0 и, как и было сказано раньше, не дергать соответствующие линии вручную.
1.3. Софт
Никакой экзотики, все программы ставятся из стандартного репозитория (кроме openocd, если найдете программатор. Вот его надо собирать из исходников, предоставленных GigaDevice, кстати, в них есть ошибки, на которые компилятор ругается…):
gcc-riscv64-unknown-elf, make -- компиляция и сборка. dfu-util, stm32flash, openocd -- прошивка screen -- отладка через UART kicad или другая программа трассировки плат -- если возникнет желание делать отладочную плату, модули или финальное изделие самостоятельно
Еще, разумеется, текстовый редактор (или IDE), эмулятор терминала, калькулятор и прочие служебные программы по вкусу.
Компиляция, анализ и прошивка существующего ассемблерного файла:
$ riscv64-unknown-elf-gcc -march=rv32imac_zicsr -mabi=ilp32 -mcmodel=medany -nostdlib main.S -o firmware.elf -march - список расширений, поддерживаемых ядром: i - base interger instructions, ver.2.0 (в отличие от e, где версия 1.9) m - multiplication and division (integer) a - atomic instructions c - compressed instructions, возможность кодирования инструкций не только по 32 бита, но и по 16. Но физику не обманешь, и закодировать так можно далеко не все. f(не у всех) - floating point instructions, поддержка аппаратной работы с числами с плавающей точкой одинарной точности (32 бита). zicsr - поддержка управляющих регистров CSR -mabi - размеры типов данных для взаимодействия ОС с программами -mcmodel - рекомендация для компилятора Си как размещать код. Для medlow только в младших 2 ГБ памяти, medany - в любых 2 ГБ. Не уверен, что разница есть, но вдруг захотим исполнять код из ОЗУ... -nostdlib - не подключать стандартную библиотеку. Все равно без настроек она не заработает
При использовании компилятора из репозитория ALT Linux обнаружилось еще несколько флагов, обусловленных тем, что там нет отдельного компилятора для компьютера и отдельного для контроллера. Приходится указывать, что у нас нет операционной системы, нет динамического размещения кода и т.д. В таком случае к флагам компилятор добавится что-то вроде -static -fno-plt -fno-pic -fno-asynchronous-unwind-tables -fno-unwind-tables -Wl,--build-id=none
Конвертация из обычного elf в чистый бинарный файл без отладочной и прочей информации
$ riscv64-unknown-elf-objcopy -O binary firmware.elf firmware.bin
Дизассемблирование обычного elf файла. Позволяет проверить во что разворачиваются макросы, псевдооперации, адреса и конструкции языков высокого уровня
$ riscv64-unknown-elf-objdump -D -S firmware.elf > firmware.lss
Дизассемблирование hex- или bin-файла. Например, считанной из контроллера прошивки. Обратите внимание, что в отличие от elf, не сохраняется вообще никакой информации об исходнике. Только коды инструкций, который дизассемблер с трудом отличает от данных
$ riscv64-unknown-elf-objdump -b binary -m riscv:rv32 -D -z --start-address=0x0 --stop-address=0x1802 firmware.bin > disasm.lss
Проверка занимаемой памяти, чтобы оценить сколько процентов камня использовано
$ riscv64-unknown-elf-size res/firmware.elf
Команды для прошивки были приведены ранее. Удобнее будет оформить эти команды в makefile.
При работе с UART, особенно если переходников несколько, бывает удобно дать им осмысленные имена, чтобы не по /dev/ttyUSB0, /dev/ttyACM50 обращаться, а хотя бы /dev/tty_ft232_0 — сразу понятно какой именно переходник используется. Для этого можно воспользоваться демоном udev. В каталог /etc/udev/rules.d добавляется файл с именем, начинающимся на 99-, 98- или еще что-то ближе к сотне (у меня это 98-usbserial.rules). В файле прописываются идентификаторы интересующего нас устройства и то, как мы хотим его назвать в системе:
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", PROGRAM="/bin/bash -c \"ls /dev | grep tty_ft232r_ | wc -l \"", SYMLINK+="tty_ft232r_%c"
Эта строчка описывает, что устройство должно иметь определенные idVendor и idProduct (их можно посмотреть в lsusb), и должно получить название /dev/tty_ft232r_0. Ноль на конце получается при выполнении кода, указанного в PROGRAM. Он считает сколько этих /dev/ttyft232r уже в наличии, и какой номер выдать следующему. Аналогичные строчки можно прописать в том же файле для переходников других производителей.
Заодно допишу правило, подставляющее имя интерфейса (части составного устройства USB) для своих поделок (спасибо intelfx с ЛОР):
SUBSYSTEM=="tty", ATTRS{manufacturer}=="COKPOWEHEU" ENV{CONNECTED_vusb}="yes" ENV{CONNECTED_vusb}=="yes", SUBSYSTEM=="tty", ATTRS{interface}=="?*", PROGRAM="/bin/bash -c \"ls /dev | grep tty_$attr{interface}_ | wc -l \"", SYMLINK+="tty_$attr{interface}_%c"
Таким образом, поиграться с таким контроллером может каждый. А если есть навыки работы с паяльником, то и не только поиграться, но и сделать на его основе что-нибудь полезное.
Кстати
Не только китайцы выпускают контроллеры на ядре RISC-V. Неожиданно обнаружились, например, К1986ВК025 от Миландра и МIК32 АМУР от Микрона.
2. Первая программа
2.1. Работа с документацией
В первую очередь, как и в случае любой электроники, надо найти электрическую схему или хотя бы распиновку. Как иначе узнать на каких выводах что висит и на какие выводы что подавать? В случае самодельной платы они и так есть (схему и распиновку отладочной платы, на которой я буду все демонстрировать, можно увидеть на рисунках ниже). В случае готовых — искать на сайте производителя.
Отдельно стоит предостеречь, что максимальное допустимое напряжение питания для GD32VF103 составляет 3.6 В, напряжение на многих ножках также ограничено этой величиной. Есть, правда, и специальные ножки, способные выдерживать подачу на них 5 В, но с ними надо быть еще аккуратнее, поскольку такой широкий диапазон напряжений был получен ценой отказа от защитных диодов. На обычный 3.6-вольтовый вывод все же можно подать, скажем, 12 В через ограничительный резистор — защитный диод будет изо всех сил сбрасывать избыток напряжения на линию питания, и пока не превышена его нагрузочная способность (она небольшая, вряд ли больше пары миллиампер), не позволит ему выйти за безопасный предел. Но на 5-вольтовых линиях такой защиты нет, и если подать туда слишком большое напряжение — уже безразлично, с резистором или без — можно легко пробить транзисторы. На практике, конечно, на подобных схемах типичные напряжения 2 — 5 В, а силовые цепи тщательно отделены от сигнальных, но осторожность соблюдать надо.
UPD: В документации на GD32VF103 мне этого найти не удалось, но для STM32F103 (откуда, похоже, срисована архитектура) говорится, что у 5-вольтовых линий есть свое питание и какая-никакая защита. При этом величина втекающего тока ограничена 0 мА, то есть даже там подавать высокое напряжение не стоит. Если уж на ножке может оказаться что-то выше 5 В, поставьте внешний токоограничивающий резистор и либо диод к питанию, либо стабилитрон.
Следующим важным документом является Datasheet и User Manual (иногда также называется Reference manual) на контроллер. Скачать их можно (и нужно!) с официального сайта производителя. В Datasheet приведены электрические и механические характеристики — допустимые напряжения и токи, потребление, тайминги. Что важно, в нем приведена распиновка, то есть то, какая функциональность соответствует каждой ножке корпуса. Например, на странице 31 можно увидеть, что 12-я ножка по умолчанию работает как PA2, но может быть перенастроена на USART1_TX, TIMER4_CH2, ADC01_IN2 или TIMER1_CH2. Лично мне пользоваться таблицей из даташита оказалось неудобно и я свел эти данные в электронную таблицу.
User Manual / Reference Manual — описание встроенной периферии контроллера. В нем описан принцип ее работы, возможности, а главное — регистры и биты в них. Вот этот документ должен быть под рукой всегда. Суровая реальность: местами документация, увы, китайская, так что нелишним будет посматривать и в рефманы от stm32. Большая часть периферии у них одинаковая, а принципы работы у stm32 расписаны несколько лучше.
2.2. Взаимодействие с периферией
Микроконтроллер, как и микропроцессор — в первую очередь вычислительное устройство, но исходные данные ему нужно откуда-то взять, а результат куда-то отправить. Такое взаимодействие с внешним миром осуществляется при помощи периферийных устройств, то есть аппаратных модулей, не входящих в ядро процессора и предназначенных переводить его сообщения на язык, понятный окружающим. Например, в нашем gd32vf103 есть такая периферия, как порты ввода-вывода (каждый бит переданного туда числа отображается как уровень напряжения на ножке), ШИМ-таймеры (число представляется как соотношение длительности импульса к длительности паузы), UART, USB (кодирование последовательностями импульсов) и т.д.
В некоторых контроллерах для взаимодействия с периферией используются специальные команды, обычно in, out
. В них указывается номер порта и значение, которое туда надо послать. Номер порта у каждого периферийного устройства свой, намертво прибит при изготовлении контроллера и описан в документации.
А как ввод-вывод можно сделать по-другому? Давайте предположим, что мы можем волшебным образом заглянуть в мозги контроллера и считать информацию непосредственно оттуда. Впрочем, магия для этого не нужна, ведь прекрасно известно где находится оперативная память и как с ней работать. Вот только городить целую схему «подсматривания» за всей памятью было бы довольно сложно и неудобно. Давайте лучше выделим диапазон адресов, в которые контроллер сам будет писать то, что хочет сообщить. А еще лучше, чтобы через эти адреса он сообщал не «подсматривателю», а сразу нужной периферии. Таким образом мы вернулись к идее портов, но без необходимости вводить лишние сущности вроде команд in, out
. Разумеется, эти адреса также жестко заданы и описаны в документации. Например, в gd32vf103 по адресу 0x4001 2C00
находятся настройки одного из таймеров, а по адресу 0x4001 0C00
— настройки PortB. Этот подход, отображение периферии на память называется memory-mapped input-output, MMIO и применяется как в нашем контроллере, так и во многих других контроллерах и процессорах.
Забавный факт: есть такое старое семейство контроллеров, как AVR. Изначально у них доступ к периферии осуществлялся именно через in, out
. Однако из-за особенностей кодирования инструкций, под номер порта отводилось всего 6 битов, что ограничило общее количество портов 64. И когда разработчики стали делать более мощные контроллеры, с большим набором периферии, они в это ограничение воткнулись. Решением было вынести «лишнюю» периферию в область MMIO. То есть с регистрами от 0 до 63 можно работать как через in, out
, так и через ld, st
(это тамошние команды для чтения и запись в ОЗУ), а со всеми остальными — только через ld, st
.
2.3. Первая программа
Традиционно первой программой для контроллера является blink — мигание светодиодом. Действительно, у нас ведь нет монитора, на который можно было бы вывести Hello World. Мигать светодиодом будем самым простым способом — подавая на соответствующую ножку поочередно 0 и 3.3 В (лог.0 и лог.1 соответственно). Если посмотреть на схему, светодиоды соединены с ножками PB5, PB6, PB7. Но прежде надо ножку, да и весь контроллер, настроить.
В самом начале упоминалось, что одной из специфик микроконтроллеров и встраиваемых систем вообще может являться минимально возможное энергопотребление. Оно достигается как специальными режимами работы (или не-работы, то есть сна, sleep-mode), так и отключением неиспользуемой периферии.
Чтобы включить периферию и сделать ее используемой, надо разрешить ее тактирование. Делается это в регистрах RCU_AHBEN, RCU_APB1EN, RCU_APB2EN в зависимости к какой из внутренних линий данная периферия подключена. PORTB, на котором висят светодиоды, висит на APB2 и управляется 3-м битом соответствующего регистра. Чтобы разрешить тактирование, надо этот бит выставить в 1, не трогая при этом остальные биты. Мало ли, вдруг порт — не первое, что настраивается в программе. Можно, конечно, заранее просчитать все, что будет использовано, но при отладке очень легко что-то упустить. Нет уж, каждая периферия будет настраиваться сама по себе, не трогая соседей. Чтобы поменять один бит используются так называемые битовые маски, о которых будет подробно рассказано позже.
.equ RCU_APB2ENR_PBEN, (1<<3) .equ RCU_APB2ENR, 0x40021018 ... li t0, RCU_APB2ENR lw t1, 0(t0) ori t1, t1, RCU_APB2ENR_PBEN sw t1, 0(t0)
Далее надо настроить сам порт. Поскольку производитель заранее не знает, как именно будет использоваться контроллер, порты ввода-вывода сделаны универсальными: они могут работать на вход (читать что на них подала внешняя схема), на выход (выдавать на внешнюю схему сигналы) или управляться какой-либо периферией. Так, упомянутый ранее PA2 может быть не только обычным портом ввода-вывода, но и линией передачи модуля USART, служить входом для АЦП или соединяться с таймерами 1 или 4. За режим работы отвечают биты CTL и MD регистров GPIOB_CTL0, GPIOB_CTL1, причем разделены ножки между регистрами пополам: PB0 — PB7 относятся к GPIOB_CTL0, а PB8-PB15 к GPIOB_CTL1. Это несколько усложняет программирование, но к счастью менять режим вывода требуется нечасто. Если посмотреть в Reference Manual, можно найти, что режиму push-pull, 50 МГц (обычный выход на максимальной частоте) соответствует CTL=00, MD=11, то есть четверка битов 0b0011, которую и надо записать в регистр GPIOB_CTL0. Опять же, не трогая остальных битов. Адреса регистров в документации указываются как базовый адрес плюс смещение. В случае порта B базовый адрес равен 0x40010C00. От него отсчитываются все регистры, управляющие портом.
.equ PORTB_BASE, 0x40010C00 .equ PORTB_CTL0, (PORTB_BASE + 0x00) .equ LED, 5 ... li t0, PORTB_CTL0 lw t1, 0(t0) li t2, ~(0b1111 << (4*LED)) and t1, t1, t2 li t2, 0b0011 << (4*LED) or t1, t1, t2 sw t1, 0(t0)
Здесь битовая магия еще страннее, чем в RCU_APB2EN, она сначала обнуляет 4 бита, отвечающих за PB5, а потом выставляет их в 0b0011.
Стоит немного пояснить, чем отличаются скорости работы порта, если все переключения задаются из кода, где их могут дергать хоть 54 миллиона раз в секунду. На самом деле это всего лишь скорость нарастания и спада фронтов сигнала. Микроконтроллер ведь не сферический в вакууме существует, у него есть конечное сопротивление выходных цепей, конечная емкость. И чтобы эту емкость перезарядить, нужен конечный ток. И чем этот ток больше, тем быстрее она перезарядится. То есть если выставить скорость минимальной (минимальный ток) и начать быстро-быстро дрыгать ногой, вместо красивого прямоугольного меандра получится корявая синусоида. С другой стороны, большой ток это большое потребление и большие помехи на внутренних цепях. У нас пока нужды экономить питание нет, а аналоговые узлы не используются, поэтому выбираем максимальную частоту и не паримся.
Теперь порт настроен, и им можно наконец мигать. Для этого в нужный бит регистра PORTB_OCTL надо записывать последовательно 0 и 1 — это же значение появится на ножке. Чтобы просто инвертировать один бит переменной, существует очень удобная логическая операция — исключающее или, она же XOR:
.equ PORTB_OCTL, (PORTB_BASE + 0x0C) ... li t0, PORTB_OCTL lw t1, 0(t0) xori t1, t1, (1<<LED) sw t1, 0(t0)
Теперь светодиод начал мигать, правда со слишком большой скоростью, ведь по умолчанию контроллер тактируется от частоты 8 МГц. Чтобы замедлить мигание, достаточно вставить «тупую задержку»:
li t0, 500000 sleep: addi t0, t0, -1 bnez t0, sleep
Термин «тупая задержка» обозначает, во-первых, что во время нее контроллер не занимается ничем полезным, а во-вторых, что задержка завязана на подсчете тактов и не является точной. Такие задержки могут использоваться в простом коде, когда контроллеру все равно больше нечем заняться, или если задержка слишком мала чтобы за время нее успеть выполнить что-то другое.
Вот теперь контроллер начал мигать светодиодом.
Сравнение имен регистров в gd32, ch32 и, например, stm32:
GD32VF103 | CH32V303 | STM32F103 | Адрес |
---|---|---|---|
RCU_APB2ENR | RCC->APB2PCENR | RCC->APB2ENR | 0x40021018 |
GPIO_CTL0(GPIOB) | GPIOB->CFGLR | GPIOB->CRL | 0x40010C00 |
GPIO_CTL1(GPIOB) | GPIOB->CFGHR | GPIOB->CRH | 0x40010C04 |
GPIO_OCTL(GPIOB) | GPIOB->OUTDR | GPIOB->ODR | 0x4001080C |
GPIO_ISTAT(GPIOB) | GPIOB->INDR | GPIOB->IDR | 0x40010808 |
2.4. Обработка кнопки
Раз уж про вывод рассказано, имеет смысл рассказать и про ввод, на примере обработки кнопки, котор6ая висит на PB0 Если за вывод ножки отвечал регистр OCTL, то за ввод — ISTAT. Точно так же каждый бит соответствует одной ножке. И естественно не забываем настроить CTL0 на вход — этому соответствует четверка битов 0b0100:
.equ BTN, 0 ... li t0, PORTB_CTL0 lw t1, 0(t0) li t2, ~(0b1111 << (4*BTN)) and t1, t1, t2 li t2, 0b0100 << (4*BTN) or t1, t1, t2 sw t1, 0(t0) ... li t0, PORTB_ISTAT lw t1, 0(t0) andi t1, t1, (1<<BTN) bnez t1, SKIP_LED
Исходный код примера, кое-какая документация, makefile и прочее доступно на github
2.5. Разработка собственной платы
Несмотря на обилие готовых отладочных и макетных плат, я все же считаю важным уметь их разрабатывать и изготавливать самостоятельно. Это потом, когда схема и код будут отлажены, можно заказать изготовление сразу партии, сразу с запаянными деталями, но вот отладку проще проводить на самодельной. Как минимум, не жалко будет проводящие дорожки резать, если когда обнаружится ошибка. Да и просто набить руку на стандартных схемотехнических узлах. И на это оптимистичной ноте самое время сообщить, что рассказывать о собственно изготовлении платы я сейчас не буду. Технологий много, от навесного монтажа или процарапывания фольги на стеклотекстолите ножом, до лазерно-утюжной технологии, фоторезиста или фрезеровки. Спойлер: почти через два года после написания первого варианта этого текста, я записал для Ютуба ролик по изготовлению платы для CH32V203 максиально подручными средствами.
Здесь же стоит рассказать о компонентах отладочной платы, которые на ней нужны. Или не нужны.
-
Система питания. И питание, и землю к контроллеру надо подводить на все соответствующие ноги. Разумеется, на плате должен быть какой-то разъем, чтобы это питание подавать. Причем с защитой от неправильного напряжения и от переполюсовки. В моем случае основным разъемом питания является mini-USB, защита от переполюсовки в нем обеспечена самой формой разъема. Если же вы захотите использовать клеммную колодку или гребенку, лучше поставить диод. Для снижения напряжения от USB’шных 5 В до пригодных для контроллера +3.3 — +3.6 В используется стабилизатор вроде 78l33. Важно: не экономьте на конденсаторах! Если планируете изготовление энергоэффективных приборов, стоит поставить перемычку в разрыв питания после стабилизатора перед контроллером, чтобы можно было вместо нее поставить амперметр (для особых оптимистов — микроамперметр) и работать с режимами сна.
-
Разъемы программирования и отладки, естественно. Наш контроллер может прошиваться как через JTAG, так и через UART. На отладочной плате стоит поставить и то, и другое. И, разумеется, не забываем про Reset, Boot0, Boot1.
-
Разъемы для внешней периферии. Обычно это гребенки, которые просто соединены с GPIO ножками контроллера. Обычно большего не требуется. Но стоит предусмотреть возможность воткнуть отладочную плату в беспаечную макетку или плату расширения. Не обязательно выводить вообще все ножки, главное чтобы присутствовало хотя бы по одному экземпляру важной периферии — таймеров, UART, SPI, I2C, АЦП и т.д. В этом смысле удачно получилось у Longan Nano: все ножки выведены на штырьки и торчат вниз от платы. Я же ограничился половиной порта А, при этом плата вставляется вертикально.
-
Источники тактового сигнала. Стоит поставить или хотя бы предусмотреть посадочные площадки для обычного и часового кварцев. А вот если захотите сделать кварц съемным (соединять через разъем) — так делать не стоит. Система тактирования у нас гибкая, и менять кварц не придется. Даже если захотите проверить как поведет себя контроллер при его сбое — проще закоротить ножки.
-
Элементарный отладочный ввод-вывод. Хотя бы 2-3 светодиода и 1-2 кнопки. Об этом почему-то забывают многие производители, а для отладки очень удобно.
-
Дисплей: скорее все же не нужен. Это достаточно сложная периферия, которая занимает много места, а отладка все равно в основном производится по UART. Впрочем, в формате Longan Nano выглядит достаточно удобно.
-
Прочая периферия: акселерометры, потенциометры и тому подобное: однозначно не нужно. Чтобы все это освоить, достаточно подключить внешнюю платку к разъемам GPIO на время освоения. Все остальное время они будут только мешать. Хотя бы тем, что увеличивается размер платы и ее сложнее носить. Туда же резервная батарейка для часов реального времени.
Дополнительная информация
Оригинал этого же материала на github pages
Моя старая статья на Хабре часть 2
ссылка на оригинал статьи https://habr.com/ru/articles/861310/
Добавить комментарий