Вот если использовать C++ код, насколько больше прошивка в отличии от чистого Си
Теоретически, на С++ можно получить более гибкий и оптимизированный код, чем на Си. Но для этого надо гораздо больше знаний и внимательности. Лично я С++ в достаточной мере не знаю.
И есть ли смысл изучать другие системы сборки кроме makefile? Просто хочется один раз настроить и забыть.
Многие вообще пользуются "проектами" тех или иных IDE и не лезут смотреть что там под капотом. Система сборки - наименьшая из проблем: для тех или иных задач ее приходится иногда подстраивать, но это почти всегда элементарные действия. То же встраивание бинарника wav-файла, которое я описывал, пожалуй, одно из самых сложных - и то меньше десятка строчек. А стандартная задача - добавить в "проект" еще один *.c файл - решается и вовсе элементарно.
В принципе на линуксе это можно сделать, а что с виндой?
Во-первых, никто не отменял фирменные IDE. NucleiStudio от GigaDevice, MounRiverStudio от WCH, не говоря уж об обычных Eclipse, Keil и т.п.. Пожалуй, это самый простой способ. Во-вторых, можно развернуть всю систему сборки самостоятельно. Найти инсталляторы соответствующих gcc, библиотеки и т.п. В-третьих, никто, кажется, не отменял WSL. Но с моей стороны это больше теория, поскольку под виндой контроллеры не программировал лет пятнадцать, так что было бы интересно послушать настоящих, опытных виндузятников.
С пустыми скобками тоже допустимо. Насколько я знаю, с C23 пустые скобки сделали синонимом void, а в более старых стандартах - произвольные аргументы. Ни то, ни другое в данном случае не ошибка.
Посмотрите информацию про барьеры памяти/инструкций, про конвейер в современных микроконтроллерах
Atomic это лишь расширение, его может и не быть, поэтому пока не рассматриваем.
А по остальному - немножко теории я об этом знаю, и со сложностями дизассемблирования оптимизированного кода знаком (в частности, когда ковырял загрузчик v307-го, много забавных моментов попалось). И про аппаратное переупорядочивание инструкций, конвейер, проброс значений и инструкцию fence слышал. Но теория это теория. Не будешь же fence после каждого взаимодействия с периферией ставить.
Если обеспечить синхронизацию, то переменную, через которую передаются данные, не нужно отмечать как volatile.
Хотелось бы пример кода.
Сразу и не придумаешь задачу, где бы это было наглядно... Ну допустим, порты ввода-вывода. Стандартный способ работы с ними (и хедер с этим согласен) - объявить volatile. И стандартная проблема, когда нужно разом поменять несколько битов. Если делать это в две операции
GPIOA->OUTDR &=~mask;
GPIOA->OUTDR |= newval;
то между ними будет сбрасывание значения в регистр и повторное чтение. Чтобы этого не было, надо либо записывать в одну строчку GPIOA->OUTDR = (GPIOA->OUTDR &~mask) | newval;, либо руками кешировать во временной переменной. И то и другое не слишком красиво.
volatile не для синхронизации, а для обмена данными между потоками. И про отсутствие оптимизаций вроде упомянул, и про то, что volatile не гарантирует атомарности... Возможно, и правда, не стоило добавлять это в ассемблерную части статьи. Ну ладно, в следующей как раз хотел уже переходить к Си и, наверное, объединить с таймерами. Как раз будет хороший пример софтового обеспечения атомарного чтения 64-битного таймера.
Тогда это, мне кажется, не перемещение с сохранением, а изменение раутинга к регистрам.
Не знаю. В документации подробностей вроде нет. Но называют они это аппаратным стеком.
Это слегка проблемно с точки зрения секьюрити - легко упустить какой-то регистр и сделать утечку внутренних данных.
Утечку куда? Если программисту надо влезть из одного куска своего кода в другой, зачем ему мешать.
Но в полный рост это выстреливает не для аппаратных прерываний, а для сисколлов, где поэтому сохраняют все регистры, кроме возвращаемых данных
Что прерывания, что сисколы это не функции, это именно исключительные ситуации, которые должны быть как можно более прозрачными. К прерываниям это вообще жесткое требование. Сисколы иногда, по соглашению, могут пользоваться a0,a1,a7, но не более.
А говорить о регистрах в контексте безопасности вообще странно: их же используют все подряд. Да и количество, всего 31 штука, можно и руками отследить. Нет, если говорить о безопасности, то в первую очередь это память и CSR-регистры.
Вероятно, в мелких embedded это можно игнорировать, "тут все свои".
Это вообще единая программа. Соглашения по безопасности это скорее "как не отстрелить себе ногу", а не "как не дать левой руке доступ к правому уху".
В ch32 аппаратно сохраняются ra, t0-t6, a0-a7, всего 16 штук, ровно половина. Все остальные вызываемая функция и так обязана сохранить-восстановить сама. Причем, если верить документации, сохраняются они за один такт все, а не по по одному такту на регистр, как в ARM. И, как я понимаю, именно поэтому аппаратных стеков ограниченное количество - это ведь не обычный стек, а набор специальных регистров PFIC. Понятно, что когда весь код прерывания находится в одной единице трансляции, компилятор может распорядиться регистрами более эффективно и не сохранять те, которые не используются. И в обычном RISC-V на это и расчет: если хочешь супер-быстрое прерывание, можно не тратить такты на все 16. Но если вызывается сторонняя функция, то уже никуда не деться, прерывание ведь не знает какие регистры используются там. Впрочем, обычно прерывания заметно длиннее и менее требовательны к времени выполнения, так что это wch-ное извращение можно оставить на совсем уж крайний случай. В gd32 вон подобного делать не стали.
Они так и делают. Слава китайским богам, что они сообразили привести свое расширение в соответствие со стандартом. Только благодаря этому вариант с naked работает не только в патченном gcc, но и в обычном. Но есть ведь FPU-регистры, которые и на аппаратном стеке сохранять невыгодно, и на обычном сложно. Ну и всякие пограничные случаи я не искал, там тоже могут быть проблемы. Впрочем, если кому-то нужна действительно настолько быстрая реакция на прерывания, что даже десятка тактов на сохранение регистров жалко, он, наверное, перепроверит и машинный код.
В учебных целях. Пока идет речь о внутреннем устройстве RISC-V и регистрах контроллера, ассемблер дает лучшее представление. Но скоро буду рассказывать и о Си. Зависит от того, уйдет ли на рассказ о прерываниях целая статья или хватит половины.
Точно не проверял. Возможно, зависит от настроек количества стоповых битов. Хотя, кажется, в документации проскакивало, что количество стоповых битов работает только на передачу. Упоминаний о точном времени возникновения прерывания не видел. Так что только тестировать и надеяться, что во всех контроллерах будет одинаково.
Либо вы невнимательно читали, либо я коряво написал. Попробую еще раз пересказать пункт 1.2. Есть три способа прошить контроллер:
Через программатор по JTAG/SWD. Придется купить или сделать программатор и, возможно, пересобрать openocd из исходников (либо взять уже собранный бинарник от производителя). Зато можно будет поставить выполнение на паузу, посмотреть регистры, память и т.п. Лично мне этот вариант не понравился.
Бутлоадером через USB. Платка втыкается прямо в USB компьютера. Не требует дополнительных плат или устройств, но нужно при каждой прошивке дергать ножки boot0, reset (или выдергивать платку). Не слишком удобно.
Бутлоадером через UART. Платка соединяется с переходником COM-UART (например, на max232) или USB-UART (pl2102, ch340, ft232, ...) и прошивается через него. Переходник купить все-таки придется, зато через тот же UART возможна передача отладочной информации. Довольно удобно, если бы не все та же возня с boot0, reset. 3+. Развитие предыдущего способа. boot0, reset цепляются либо к DTR, RTS переходника, либо к специальным ногам самодельной платы. И stm32flash, и моя wch-isp эти линии дергать умеют. Но для себя я сделал "Каракатицу", которая эмулирует не один UART, а два, на одних и тех же ножках. Через один прошиваем, через второй отлаживаем.
Еще можно написать собственный бутлоадер и прошивать им хоть через захват таймера - но сначала этот бутлоадер все равно придется прошить одним из предыдущих способов.
Если это все же пробел в моих навыках лектора, а не ваших - читателя - буду благодарен если подскажете как сформулировать лучше.
У GD32VF103 обычно есть несколько выводов, поддерживающих USART, например
Именно на счет GD32 не уверен, но CH32 прошиваются только через UART1 (кроме ch32v203g8, который прошивается только через UART2 - не знаю, что курили китайцы, когда это придумывали). В смысле через UART3 их не прошьешь.
без нужды в программаторах для счета или загрузки
Кстати о "счете" (чтении): загрузчик ch32 этого не поддерживает. Можно прошить, можно стереть и можно проверить. Все. Не считая нескольких ch-специфичных функций, которые сейчас интереса не представляют.
Странно что в статье не упоминается применение классического CH340 драйвера, который на раз - два коммуницирует UART или RS232 стандартным терминалом
CH340 ничем не лучше любого другого переходника - да хоть той же FT232. А вообще, как раз следующая статья будет про UART. Материала у меня записано довольно много, в одну статью так и так не влезет. Ну и еще за несколько тем я пока даже не брался.
Это если бы я рассказывал о силовой электронике. И начинать бы пришлось с законов Ома и Кирхгофа. Но я рассказываю об архитектуре ЭВМ. Здесь светодиоды - всего лишь индикация. Описать вообще все области, где применяются контроллеры, я не смогу физически.
Набор расширений - IMA(F)C. Раз расширение "A" поддерживается, значит, должны быть. Но, еще раз говорю, сам я его не тыкал. Хм, а остальные тыкал. Может, и исправлю когда-нибудь этот пробел. Но не в приоритете, в этих камнях куча куда более интересных вещей. Да хоть прерывания или уровни привилегий.
У stm тоже не все так гладко. Недавно натыкался то ли, то ли на вопрос на форуме на статью (сейчас с ходу не нашел), что если подавать 5 В при выключенном питании, оно просачивается куда не надо, а это опасно. То есть защита там включается только при подаче питания на сам контроллер. И у GD / WCH такая защита тоже наверняка есть. Но она не описана в документации и, в частности, неизвестно, какой ток она держит.
Там же в статье два примера. Юсб устройства со сменными разъёмами
А, на картинках. Не думаю, что сменные разъемы для макетки нужны. Я бы ограничился mini-usb или, если хочется еще надежнее, type-B (квадратный который). Уж подходящий проводок найти проще, чем хитрый самодельный модуль.
А самая суть соединения встык это уменьшение торчащих вверх проводов, увеличение компактности и увеличение возможных комбинаций (в контексте клемников).
Для этого нужна сильная стандартизация. Чтобы разъем на одном модуле соответствовал разъему на другом. В таком случае главный вопрос - какой протокол будет использоваться? Впрочем, с UART может получиться, если использовать распиновку Rx-GND-Tx. Или шины вроде I2C или SPI в "сквозном" варианте, то есть на одной стороне платы "вход", на другой - "выход".
Напрашивается сделать основание проводящим и соединить с землей. Как минимум, меньше проводков к каждому модулю тянуть. Естественно, это означает и одно из посадочных отверстий на модуле также соединить с землей. А если понадобится земли разделить - есть изолирующие стойки, да и просто выкрутить можно, на трех будет держаться не хуже. Еще напрашивается разместить на основании клеммники для питания и связи с внешним миром. Тем более что они бывают разъемными. Это надежнее, чем цепляться к каждому модулю непосредственно. И небольшой набор жестко зафиксированных кнопок и светодиодов, которые можно соединить с любым модулем. Небольшая проблема с покупными модулями. В них посадочные отверстия могут быть расположены как левая пятка пожелает. Но это решается переходниками. Опасение вызывает большое количество перемычек. Каждый механический контакт это опасность поиска какой же из 100500 проводов в клубке решил оторваться. То есть ровно та же проблема, что с беспаечной макеткой. И точно так же непонятно как ее решать. Проблему "размещения встык" я не понял. Ну будет между платками зазор, ну и что? Это ведь и так макет.
Не проверяли ли на контроллерах с кешированием всего кода - v203, v303, ...?
Не проверяли ли DMA?
Ну так зачем там "должны быть void"? Что без этого будет работать неправильно?
Теоретически, на С++ можно получить более гибкий и оптимизированный код, чем на Си. Но для этого надо гораздо больше знаний и внимательности. Лично я С++ в достаточной мере не знаю.
Многие вообще пользуются "проектами" тех или иных IDE и не лезут смотреть что там под капотом. Система сборки - наименьшая из проблем: для тех или иных задач ее приходится иногда подстраивать, но это почти всегда элементарные действия. То же встраивание бинарника wav-файла, которое я описывал, пожалуй, одно из самых сложных - и то меньше десятка строчек. А стандартная задача - добавить в "проект" еще один *.c файл - решается и вовсе элементарно.
Во-первых, никто не отменял фирменные IDE. NucleiStudio от GigaDevice, MounRiverStudio от WCH, не говоря уж об обычных Eclipse, Keil и т.п.. Пожалуй, это самый простой способ.
Во-вторых, можно развернуть всю систему сборки самостоятельно. Найти инсталляторы соответствующих gcc, библиотеки и т.п.
В-третьих, никто, кажется, не отменял WSL.
Но с моей стороны это больше теория, поскольку под виндой контроллеры не программировал лет пятнадцать, так что было бы интересно послушать настоящих, опытных виндузятников.
С пустыми скобками тоже допустимо. Насколько я знаю, с C23 пустые скобки сделали синонимом void, а в более старых стандартах - произвольные аргументы. Ни то, ни другое в данном случае не ошибка.
Atomic это лишь расширение, его может и не быть, поэтому пока не рассматриваем.
А по остальному - немножко теории я об этом знаю, и со сложностями дизассемблирования оптимизированного кода знаком (в частности, когда ковырял загрузчик v307-го, много забавных моментов попалось). И про аппаратное переупорядочивание инструкций, конвейер, проброс значений и инструкцию fence слышал. Но теория это теория. Не будешь же fence после каждого взаимодействия с периферией ставить.
Подскажите что почитать, чтобы не голую теорию.
Хотелось бы пример кода.
Сразу и не придумаешь задачу, где бы это было наглядно... Ну допустим, порты ввода-вывода. Стандартный способ работы с ними (и хедер с этим согласен) - объявить volatile. И стандартная проблема, когда нужно разом поменять несколько битов. Если делать это в две операции
то между ними будет сбрасывание значения в регистр и повторное чтение. Чтобы этого не было, надо либо записывать в одну строчку
GPIOA->OUTDR = (GPIOA->OUTDR &~mask) | newval;
, либо руками кешировать во временной переменной. И то и другое не слишком красиво.Как я понимаю, вы знаете способ лучше.
volatile не для синхронизации, а для обмена данными между потоками. И про отсутствие оптимизаций вроде упомянул, и про то, что volatile не гарантирует атомарности... Возможно, и правда, не стоило добавлять это в ассемблерную части статьи. Ну ладно, в следующей как раз хотел уже переходить к Си и, наверное, объединить с таймерами. Как раз будет хороший пример софтового обеспечения атомарного чтения 64-битного таймера.
Не знаю. В документации подробностей вроде нет. Но называют они это аппаратным стеком.
Утечку куда? Если программисту надо влезть из одного куска своего кода в другой, зачем ему мешать.
Что прерывания, что сисколы это не функции, это именно исключительные ситуации, которые должны быть как можно более прозрачными. К прерываниям это вообще жесткое требование. Сисколы иногда, по соглашению, могут пользоваться a0,a1,a7, но не более.
А говорить о регистрах в контексте безопасности вообще странно: их же используют все подряд. Да и количество, всего 31 штука, можно и руками отследить. Нет, если говорить о безопасности, то в первую очередь это память и CSR-регистры.
Это вообще единая программа. Соглашения по безопасности это скорее "как не отстрелить себе ногу", а не "как не дать левой руке доступ к правому уху".
В ch32 аппаратно сохраняются ra, t0-t6, a0-a7, всего 16 штук, ровно половина. Все остальные вызываемая функция и так обязана сохранить-восстановить сама. Причем, если верить документации, сохраняются они за один такт все, а не по по одному такту на регистр, как в ARM. И, как я понимаю, именно поэтому аппаратных стеков ограниченное количество - это ведь не обычный стек, а набор специальных регистров PFIC.
Понятно, что когда весь код прерывания находится в одной единице трансляции, компилятор может распорядиться регистрами более эффективно и не сохранять те, которые не используются. И в обычном RISC-V на это и расчет: если хочешь супер-быстрое прерывание, можно не тратить такты на все 16. Но если вызывается сторонняя функция, то уже никуда не деться, прерывание ведь не знает какие регистры используются там.
Впрочем, обычно прерывания заметно длиннее и менее требовательны к времени выполнения, так что это wch-ное извращение можно оставить на совсем уж крайний случай. В gd32 вон подобного делать не стали.
Они так и делают. Слава китайским богам, что они сообразили привести свое расширение в соответствие со стандартом. Только благодаря этому вариант с naked работает не только в патченном gcc, но и в обычном. Но есть ведь FPU-регистры, которые и на аппаратном стеке сохранять невыгодно, и на обычном сложно. Ну и всякие пограничные случаи я не искал, там тоже могут быть проблемы.
Впрочем, если кому-то нужна действительно настолько быстрая реакция на прерывания, что даже десятка тактов на сохранение регистров жалко, он, наверное, перепроверит и машинный код.
В учебных целях. Пока идет речь о внутреннем устройстве RISC-V и регистрах контроллера, ассемблер дает лучшее представление. Но скоро буду рассказывать и о Си. Зависит от того, уйдет ли на рассказ о прерываниях целая статья или хватит половины.
Точно не проверял. Возможно, зависит от настроек количества стоповых битов. Хотя, кажется, в документации проскакивало, что количество стоповых битов работает только на передачу. Упоминаний о точном времени возникновения прерывания не видел. Так что только тестировать и надеяться, что во всех контроллерах будет одинаково.
Не думаю, что подобные излишние уточнения здесь уместны.
Либо вы невнимательно читали, либо я коряво написал. Попробую еще раз пересказать пункт 1.2. Есть три способа прошить контроллер:
Через программатор по JTAG/SWD. Придется купить или сделать программатор и, возможно, пересобрать openocd из исходников (либо взять уже собранный бинарник от производителя). Зато можно будет поставить выполнение на паузу, посмотреть регистры, память и т.п. Лично мне этот вариант не понравился.
Бутлоадером через USB. Платка втыкается прямо в USB компьютера. Не требует дополнительных плат или устройств, но нужно при каждой прошивке дергать ножки boot0, reset (или выдергивать платку). Не слишком удобно.
Бутлоадером через UART. Платка соединяется с переходником COM-UART (например, на max232) или USB-UART (pl2102, ch340, ft232, ...) и прошивается через него. Переходник купить все-таки придется, зато через тот же UART возможна передача отладочной информации. Довольно удобно, если бы не все та же возня с boot0, reset. 3+. Развитие предыдущего способа. boot0, reset цепляются либо к DTR, RTS переходника, либо к специальным ногам самодельной платы. И stm32flash, и моя wch-isp эти линии дергать умеют. Но для себя я сделал "Каракатицу", которая эмулирует не один UART, а два, на одних и тех же ножках. Через один прошиваем, через второй отлаживаем.
Еще можно написать собственный бутлоадер и прошивать им хоть через захват таймера - но сначала этот бутлоадер все равно придется прошить одним из предыдущих способов.
Если это все же пробел в моих навыках лектора, а не ваших - читателя - буду благодарен если подскажете как сформулировать лучше.
Именно на счет GD32 не уверен, но CH32 прошиваются только через UART1 (кроме ch32v203g8, который прошивается только через UART2 - не знаю, что курили китайцы, когда это придумывали). В смысле через UART3 их не прошьешь.
Кстати о "счете" (чтении): загрузчик ch32 этого не поддерживает. Можно прошить, можно стереть и можно проверить. Все. Не считая нескольких ch-специфичных функций, которые сейчас интереса не представляют.
CH340 ничем не лучше любого другого переходника - да хоть той же FT232. А вообще, как раз следующая статья будет про UART. Материала у меня записано довольно много, в одну статью так и так не влезет. Ну и еще за несколько тем я пока даже не брался.
Это если бы я рассказывал о силовой электронике. И начинать бы пришлось с законов Ома и Кирхгофа. Но я рассказываю об архитектуре ЭВМ. Здесь светодиоды - всего лишь индикация. Описать вообще все области, где применяются контроллеры, я не смогу физически.
Набор расширений - IMA(F)C. Раз расширение "A" поддерживается, значит, должны быть. Но, еще раз говорю, сам я его не тыкал. Хм, а остальные тыкал. Может, и исправлю когда-нибудь этот пробел. Но не в приоритете, в этих камнях куча куда более интересных вещей. Да хоть прерывания или уровни привилегий.
3.3 В паразитного питания хотя бы безопасно для основных цепей. А вот если пролезут паразитные 5 В, уже что-нибудь может сломаться.
У stm тоже не все так гладко. Недавно натыкался то ли, то ли на вопрос на форуме на статью (сейчас с ходу не нашел), что если подавать 5 В при выключенном питании, оно просачивается куда не надо, а это опасно. То есть защита там включается только при подаче питания на сам контроллер.
И у GD / WCH такая защита тоже наверняка есть. Но она не описана в документации и, в частности, неизвестно, какой ток она держит.
А, на картинках. Не думаю, что сменные разъемы для макетки нужны. Я бы ограничился mini-usb или, если хочется еще надежнее, type-B (квадратный который). Уж подходящий проводок найти проще, чем хитрый самодельный модуль.
Для этого нужна сильная стандартизация. Чтобы разъем на одном модуле соответствовал разъему на другом. В таком случае главный вопрос - какой протокол будет использоваться? Впрочем, с UART может получиться, если использовать распиновку Rx-GND-Tx. Или шины вроде I2C или SPI в "сквозном" варианте, то есть на одной стороне платы "вход", на другой - "выход".
Напрашивается сделать основание проводящим и соединить с землей. Как минимум, меньше проводков к каждому модулю тянуть. Естественно, это означает и одно из посадочных отверстий на модуле также соединить с землей. А если понадобится земли разделить - есть изолирующие стойки, да и просто выкрутить можно, на трех будет держаться не хуже.
Еще напрашивается разместить на основании клеммники для питания и связи с внешним миром. Тем более что они бывают разъемными. Это надежнее, чем цепляться к каждому модулю непосредственно. И небольшой набор жестко зафиксированных кнопок и светодиодов, которые можно соединить с любым модулем.
Небольшая проблема с покупными модулями. В них посадочные отверстия могут быть расположены как левая пятка пожелает. Но это решается переходниками.
Опасение вызывает большое количество перемычек. Каждый механический контакт это опасность поиска какой же из 100500 проводов в клубке решил оторваться. То есть ровно та же проблема, что с беспаечной макеткой. И точно так же непонятно как ее решать.
Проблему "размещения встык" я не понял. Ну будет между платками зазор, ну и что? Это ведь и так макет.