мая 132020
 

https://www.drive2.ru/b/532287598853358311/

Предисловие

В конце 90-х, фирма Atmel ворвалась на рынок микроконтроллеров со своей 8-битной архитектурой AVR с достаточно простой периферией, заманчивыми электрическими параметрами и выполнявшей большинство инструкций за такт. Чтобы переманить разработчиков с ставших уже популярными PIC и уже набившей оскомину 51 архитектуры (микроконтроллеры на которой выпускались и самой Atmel), Atmel немало вложилось, чтобы поддерживать сообщество разработчиков, делать документацию понятной и доступной и, самое главное, обеспечить разработчиков бесплатной доступной полноценной средой разработки. Так появились компилятор gcc для avr со всеми тулчейнами и причиндалами и AVR Studio, позже ставшая Atmel Studio – лицензированная Visual Studio от Microsoft и переделанная под нужды архитектуры. В итоге, любой любознательный разработчик с доступом в интернет мог за полчаса организовать себе среду разработки и с головой окунуться в документацию и сообщество AVR.

В середине нулевых, со своими достаточно мощными 32-битными микроконтроллерами с ядром популярной архитектуры ARM, на рынок ворвалась фирма STMicroelectronics. И хотя ARM ядра осваивали и другие производители, включая и Atmel, решения от STM отличались достаточно продуманной богатой периферией вкупе с вычислительной мощью 32-битного ядра ARM, и при этом сравнительно небольшой стоимостью.

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

Нужно ли говорить, что любопытные разработчики, решившие освоить хоть что-нибудь, потыкавшись, всё-таки выбирали для начала AVR.

Таким любопытным разработчиком и был я где-то 5 лет назад, погрузившись в AVR.
Столкнувшись с необходимостью больших вычислительных способностей, я несколько раз пытался влиться в струю STM32, даже обзавёлся платой Discovery, но глючность бесплатных сред разработки и жуткое неудобство освоения архитектуры останавливали меня от этой затеи.

d4AAAgEIxuA-960.jpg
Полный размер
STM32VLDiscovery: плата с STM32F100RBT6B + ST-Link v1 прямо на борту

Так продолжалось до недавних пор.

Анатоллик и КубеИде

Видимо, особенно на фоне того что после покупки Atmel Microchip’ом, PIC и AVR теперь один и тот же конкурент, менеджеры из STM таки решили что негоже разбрасываться неокрепшими умами потенциальных разработчиков. Мало того, что вкладывается немало сил в причёсывание и структурирование документации (впрочем, «казачки-разбойнички» никуда не делись), так ещё в 2018 году STMicroelectronics разродилась покупкой одной из наиболее удачных платных сред разработки – Atollic TRUEStudio. А недавно на сайте появилась информация, что теперь официальной средой для разработки стала STM32CubeIDE, а Атоллик больше не поддерживается.

Обе среды разработки основаны на Eclipse, обе снабжены тулчейном и всеми необходимыми библиотеками, связанными с ARM и МК STM32, поддерживают дебаг через ST-Link, и позволяют создать шаблон проекта буквально в пару кликов мышкой, выбрав нужный МК из списка. Причём STM32CubeIDE позволяет в графическом интерфейсе задать начальные настройки портов и периферии, на основе которых сгенерируется код.

Любопытно, что когда я подключил свою плату STM32VLDiscovery, мне написали что мол, программатор ST-Link v1, который установлен на плате, не поддерживается. Нужна версия 2, не меньше. Ну а то ж! С её выпуска прошло аж 8 лет, кому нужно поддерживать такую древность.

Поэтому пришлось пользоваться отдельным STM32 ST-Link Utility

Та ещё ардуйня

Многие (да и я не в последнюю очередь) критикуют среду Arduino за то, что она приучает разработчиков вместо работы с регистрами процессора вызывать какие-то абстрагированные функции, которые медленно и печально за несколько десятков тактов приходят к тому же результату, которого можно было бы достичь за один такт. В результате получается неэффективное приложение, а разработчики лепят код, не желая разбираться в устройстве микроконтроллера.

Но тут приходят они! Библиотеки аппаратной абстракции для STM32. В Atollic Truestudio продвигалась библиотека SPL, в то время как STM32CubeIDE пропихивает «обновлённый» вариант, которая так и называется – HAL.

Для примера, допустим, я хочу помигать светодиодом на свежеприобретённой у китайцев плате с STM32F103C8 на борту:

ZcAAAgEIxuA-960.jpg
Полный размер
Плата с STM32F103C8T6

Для этого создаём проект, выбираем микроконтроллер

1cAAAgEIxuA-960.jpg
Полный размер
STM32CubeIDE выбор МК

3iAAAgEIxuA-960.jpg
STM32CubeIDE создание проекта

Система генерирует нам проект с кучей файлов. Включаем сразу оптимизацию (потому что без нее совсем плохо и печально)

kiAAAgEIxuA-960.jpg
STM32CubeIDE включение оптимизации

И компилируем проект…

NOAAAgEIxuA-960.jpg

2,64, мать его, килобайта! Пустой проект! В котором ничего ещё нет! Ладно, архитектура такая, большая таблица векторов прерываний, плюс основные обработчики на то на сё, пусть 400, ну ладно, пусть 500 байт, но почти три килобайта! Хорошо что у меня МК с флешем 64кБ, а не 16… Это всё не считая того что этот самый HAL на системный таймер тиков без спроса вешает прерывание, которое срабатывает 1000 раз в секунду.

Хорошо, добавляем в конец main простой код для ритмичного мигания светодиодом. Делаем всё с использованием функций HAL. Что-то типа такого

// Включаем такирование порта ввода-вывода
__HAL_RCC_GPIOC_CLK_ENABLE();

// Инициализируем ножку PC13
GPIO_InitTypeDef init_gpio;
init_gpio.Pin = GPIO_PIN_13; // здесь можно через | объединить несколько и настроить их одной командой
init_gpio.Mode = GPIO_MODE_OUTPUT_PP; // Вывод, push-pull
init_gpio.Pull = GPIO_NOPULL; // Подтягивающие резисторы не нужны
init_gpio.Speed = GPIO_SPEED_FREQ_LOW; // Низкая скорость работы
HAL_GPIO_Init(GPIOC, &init_gpio);

uint8_t flsh = 0;
for (;;) {
if ((flsh & 5) != 0) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); // Высокий уровень — гасит светодиод
} else {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // Низкий уровень — зажигает светодиод
}
flsh++;
for (uint32_t i = 0 ; i < 200000 ; i++) { asm volatile(«nop»); }
}

К слову, инициализацию порта можно оставить на откуп генератору кода, покликав мышкой в нужных местах при создании проекта.

Итак, компилируем. Та-дам! Код уже занимает 3,22 кБ! Распух более чем на полкило!
Почему так происходит? Да потому, что при использовании каждой драной функции HAL, линкер тянет огромную процедурину в память. К примеру, так выглядит HAL_GPIO_Init, настраивающая порт:

AQAAAgMIxuA-960.jpg

И это только начало! Там ещё ниже продолжение на 2 экрана.
Можно только представить, сколько времени выполнения отнимает то, что можно сделать записью в пару регистров. Я уж не говорю про отожранную флэш-память.

Причём такой подход, где сначала заполняется структура в памяти, потом указатель на неё предаётся в процедуру инициализации – там повсеместно. Этот подход сюда перекочевал из SPL – там всё примерно то же самое. Казалось бы, ну и ладно! Создадим несколько статичных структур, отметим их как const, компилятор поместит их во флэш – сэкономит немного времени? А вот фиг! В описаниях параметров этих функций слово const не указано – причины этому нет, просто не продуманно, а значит, где бы не находилась структура изначально, её нужно будет сначала скопировать в оперативную память. Эта глупость как была в SPL, так и перекочевала в HAL.

Вот так, например, скомпилировалось условие внутри цикла:

HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // Низкий уровень — зажигает светодиод
8000b12: 4e0b ldr r6, [pc, #44] ; (8000b40 <main+0x68>)
8000b14: 4627 mov r7, r4
8000b16: f44f 5500 mov.w r5, #8192 ; 0x2000
if ((flsh & 5) != 0) {
8000b1a: f014 0f05 tst.w r4, #5
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); // Высокий уровень — гасит светодиод
8000b1e: bf14 ite ne
8000b20: 2201 movne r2, #1
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // Низкий уровень — зажигает светодиод
8000b22: 463a moveq r2, r7
8000b24: 4629 mov r1, r5
8000b26: 4630 mov r0, r6
8000b28: f7ff fca4 bl 8000474 <hal_GPIO_WritePin>

Компилятор старался и оптимизировал как мог, но всё равно, чтобы зажечь или погасить светодиод, сначала нужно загрузить в регистры параметры: ссылку на GPIOC, номер пина и действие, а затем вызвать HAL_GPIO_WritePin – процедурину, которая выглядит как-то так:

8000474: b912 cbnz r2, 800047c <hal_GPIO_WritePin+0x8>
8000476: 0409 lsls r1, r1, #16
8000478: 6101 str r1, [r0, #16]
800047a: 4770 bx lr
800047c: 6101 str r1, [r0, #16]
800047e: 4770 bx lr

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

Для сравнения, перепишем этот кусок кода на работу с регистрами:

// Включаем такирование порта ввода-вывода
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;

// Инициализируем ножку PC13
GPIOC->CRH = (GPIOC->CRH & ~(GPIO_CRH_MODE13 | GPIO_CRH_CNF13))
| (GPIO_CRH_MODE13_1);

uint8_t flsh = 0;
for (;;) {
if ((flsh & 5) != 0) {
GPIOC->BSRR = GPIO_BSRR_BS13; // Высокий уровень — гасит светодиод
} else {
GPIOC->BRR = GPIO_BRR_BR13; // Низкий уровень — зажигает светодиод
}
flsh++;
for (uint32_t i = 0 ; i < 200000 ; i++) { asm volatile(«nop»); }
}

Компилируем! Прошивка занимает 2,71кБ, то есть добавленный код увеличил её менее чем на 100 байт!

И теперь всё условие внутри цикла вместе с записью в порты выглядит вот так:

8000914: f44f 5100 mov.w r1, #8192 ; 0x2000
if ((flsh & 5) != 0) {
8000918: f012 0f05 tst.w r2, #5
GPIOC->BSRR = GPIO_PIN_13; // Высокий уровень — гасит светодиод
800091c: bf14 ite ne
800091e: 6101 strne r1, [r0, #16]
GPIOC->BRR = GPIO_PIN_13; // Низкий уровень — зажигает светодиод
8000920: 6141 streq r1, [r0, #20]

И всё! Условие – проверили, записали либо сюда, либо туда. Никаких миллионов регистров, никаких вызовов функций, всё просто и быстро.

Да, у библиотек аппаратной абстракции есть 2 неоспоримых преимущества:
1) Переносимость. В отличие от, например, AVR, где у всего семейства mega и tiny, например, порты конфигурируются одинаково (за исключением tiny4,5,9,10), то здесь в регистрах портов, скажем STM32F0 и STM32F1 – разница размером в пропасть. Если, например, мы пишем библиотеку для работы с дисплеем, то с использованием абстракции, её легко можно будет перенести с одного МК на другой.
2) Читаемость. Незнакомый с архитектурой человек глядя на строку
GPIOC->BRR = GPIO_BRR_BR13;
Не сразу поймёт что тут происходит. А вот что делает строка
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
очевидно человеку с небольшим опытом, кто худо-бедно знает английский язык.

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

GPIOC->BSRR = GPIO_BSRR_BS13 | GPIO_BSRR_BR10; // Зажгли пин 13 и потушили пин 10

Я для себя выбрал такой подход: сделал сам себе библиотечку, файлик, назвал его microhal.h, в нём встречаются, например, такие строки:

enum GPIOOutputMode { GPIOOutputMode_GP_PP = 0, GPIOOutputMode_GP_OD = 4, GPIOOutputMode_AF_PP = 8, GPIOOutputMode_AF_OD = 12 };

enum GPIOOutputSpeed { GPIOOutputSpeed_Low = 2, GPIOOutputSpeed_Medium = 1, GPIOOutputSpeed_High = 3 };

static inline void gpio_config_output(GPIO_TypeDef * gpio, uint8_t pin, enum GPIOOutputMode mode, enum GPIOOutputSpeed speed) {
if (pin < 8) {
gpio->CRL = (gpio->CRL & ~(0b1111 << (pin * 4))) | ((mode | speed) << (pin * 4));
} else {
gpio->CRH = (gpio->CRH & ~(0b1111 << ((pin — 8) * 4))) | ((mode | speed) << ((pin — 8) * 4));
}
}

static inline void gpio_set(GPIO_TypeDef * gpio, uint8_t pin) {
gpio->BSRR = (1 << pin);
}

static inline void gpio_clear(GPIO_TypeDef * gpio, uint8_t pin) {
gpio->BRR = (1 << pin);
}

Поскольку процедуры объявлены static inline, то вызов развернётся их кодом, и если на вход передаются константы, то все эти условия и формулы будут вычислены на этапе компиляции. В итоге код получается абсолютно идентичный тому, как если записывать напрямую в регистры, а вызовы этих функций выглядят так:

gpio_config_output(GPIOC, 13, GPIOOutputMode_GP_PP, GPIOOutputSpeed_Low);
gpio_clear(GPIOC, 13);
gpio_set(GPIOC, 13);

Читаемость кода? Такая же, как у библиотек абстракции. Переносимость? Если нужно переехать на новый МК, то достаточно переписать этот самый microhal.h, сохраняя сигнатуры функций и весь код снова заработает. Что если нужен экзотический режим работы? Боже ж мой! Это кусок собственного проекта, поэтому ничто не мешает дописать ещё пару функций в этот файлик.
Причём, разрабатывая под конкретную архитектуру, можно составить набор функций так, чтобы они давали максимально эффективный код.
Итак, кроме лени читать документации и разбираться в устройстве периферии микроконтроллера, какие остались причины использовать библиотеки абстракции?
Кто-то скажет что, дескать, HAL – она одна, каждый, кто с ней работал, поймёт и мой код. А то, что я там в своих функциях настрочил – это ещё разбираться надо. Поэтому если речь идёт про доработку чужого кода, то тут одни плюсы. На что я возражу – что и тут всё не так радужно. Помимо того, что кроме HAL есть ещё SPL, и чёрте что ещё, так даже HAL от версии к версии меняется до неузнаваемости, меняются названия функций, констант… Вон, в драйвере HAL есть раздел Legacy, как раз для переноса кода, написанного под старую версию.

Поэтому моё резюме таково: HAL – можно посмотреть, потыкаться, для чего-нибудь, что надо быстро слепить, лишь бы работало – сойдёт. Но для серьёзного изучения и для серьёзного применения МК – избавляться от этой пакости как можно скорее!

К слову, сейчас продвигается ещё одна версия аппаратной абстракции для STM32, называется LLL. Она и основана как раз на таких inline-функциях, в результате чего код компилируется достаточно компактно. Но, во-первых, это всё-таки усреднённая абстракция, не учитывающая особенности конкретной архитектуры. Пусть вместо одного действия будет делать не сто, а три, но всё же. Во-вторых, проблема остаётся та же самая: рано или поздно всё упирается в неспособность конкретной реализации обеспечить нужные режимы работы.

Документация

Те, кто работал с AVR, привыкли, что достаточно скачать один даташит и в нём будет всё: распиновка, список регистров, поддерживаемые инструкции, электрические характеристики, подробное описание периферийных блоков… Здесь же всё не так!

Ради примера: допустим, мы хотим запрограммировать STM32F103C8, типа того, что выше, на китайской плате. Нам понадобится:

Production Data с распиновкой, кратким описанием периферии, электрическими параметрами.
Reference Manual который опишет режимы работы периферии и соответствующие регистры. Но подождите! Для изюминки документ описывает сразу целый пучок микроконтроллеров, поэтому читая каждый раздел нужно внимательно следить – относится ли написанное к вашему экземпляру или нет. Вы ведь запомнили из Production Data какая периферия тут есть, а какой нет?
— Не забываем, что в этих микроконтроллерах ядро ARM Cortex относительно независимо от всей периферии. А значит, чтобы узнать как его программировать, где какие системные регистры и как управлять контроллером прерываний, нужен отдельный документ — Programming Manual.
— Разумеется, детальнее узнать о ядре Cortex-M3 поможет Technical Reference Manual непосредственно от ARM.
— Захотите, например, узнать, в какие регистры записывать, чтобы перезаписывать флэш? Тут нужен Flash Programming Manual

И так далее, в каждом документе ещё есть перечень related documents, который порекомендует к прочтению ещё пару недостающих кусков. Я же говорил, «казачки-разбойнички».

Чем STM32 лучше AVR?

— Разумеется, цена! Можно, как правило, получить STM32 куда большей вычислительной мощности, большего объёма памяти, с более богатой периферией и частотой работы, по сравнению с AVR за те же деньги.

— 32-бит! Вычисления, работа с регистрами, памятью и портами происходит в 32-битном режиме, что позволяет добиться большей вычислительной мощности при той же частоте.

— Мощное ядро. Очень продуманная архитектура позволяет динамично работать с данными. Продуманные режимы адресации, организация работы арифметики. Есть даже однотактовое умножение 32-битных чисел, а у МК на Cortex-M3 и старше – даже деление.

— Единое адресное пространство! Не нужно писать отдельные функции для работы с ОЗУ, отдельные – для флэша, доступ до всего организован единообразно. И, наоборот, чтобы поместить данные во флеш достаточно пометить их как const, компилятор сам сообразит.

— Частота! У AVR разве что в xmega встречаются частоты выше 20 МГц. А вот например, у того же STM32F103C8 – до 72 МГц.

— Развитая система тактирования. За счёт PLL, можно запустить на максимальной или почти максимальной частоте от внутреннего генератора, в отличие от AVR, где максимум от внутреннего – часто это 8 МГц.

— Калибровка генератора 1-2% с завода (в отличие от 5% на AVR), это значит, что запустив от внутреннего генератора микроконтроллер, можно смело настраивать USART – он будет работать в пределах допусков по скорости (я уж молчу про то что точность настройки частоты USART тут в 16 раз выше)

— Богатая и развитая периферия! Вспоминая, например, ATmega328, у которой два 8-битных и один 16-битный таймер, тут даже у самого простенького МК 4 таймера как минимум, причём таймеры 16-битные, те которые генерируют ШИМ, делают это в 4 канала. Более детальные настройки и более богатые режимы работы, в сравнении с AVR, у всей остальной периферии.

— DMA! Да, тут везде есть DMA, можно зарядить, например, передачу кучи байт в USART, без прерываний и вмешательства со стороны процессора.

— Мощный контроллер прерываний! Вообще, AVR хвалится тем, что вход в прерывание и выход занимают по 4 такта. Но кроме этого, обработчик прерываний должен сохранить флаги, регистры, в итоге вход и выход занимают минимум десятка полтора тактов. В STM вход в прерывание занимает 12 тактов, причём часть регистров уже оказываются сохранёнными. Мало того, можно настроить приоритеты прерываний, прерывания с большим приоритетом прервут работу обработчиков с меньшими. Есть хвостовое объединение прерываний: если, пока выполняется одно, происходит другое прерывание с тем же приоритетом, то по завершению первого не происходит восстановление регистров и новое сохранение, а сразу перескакивает на следующее. В итоге, можно очень гибко и эффективно работать с прерываниями.

— Толерантность к 5В! Многие входы МК могут работать с входными сигналами до 5В, несмотря на напряжение работы самого МК 2,0 – 3,6 В.

— 12-битный АЦП с частотой дискретизации до мегагерца.

— Встроенный загрузчик! В каждом STM32, в отдельной, не удаляемой области памяти, зашит загрузчик. Он запустится, если замкнуть нужные контакты и выполнить сброс, позволяя заливать прошивку через USART, а в некоторых случаях и через другую периферию.
Кстати вот вам ссылка на AN2606 о том, как запускать этот загрузчик в разных МК. А вот страница для загрузки прошивающего приложения.

— RTC! Почти у всех МК есть модуль реального времени, который работает от отдельной батареечки, сохраняет пару десятков байт данных и ведёт учёт времени, пока сам МК не работает.

— Продуманность структуры управляющих регистров! Они все более-менее консистентны, располагаются относительно друг друга одинаково, поэтому для работы с ними в исходном коде на C/C++ они объединены в удобные структуры.

И так далее, перечислять выгодные отличия можно бесконечно. Казалось бы, можно перейти на STM32 и забыть навсегда про AVR, но…

Чем STM32 хуже AVR?

Есть ряд существенных отличий, из-за которых, наверное, чуть ли не в половине своих проектов я бы отдал предпочтение AVR.

— Напряжение питания. Диапазон рабочих напряжений STM32 от 2,0 или 2,4 В до 3,6 В. 4,0 – указано как предельно допустимое. Против AVR, у которых рабочее от 1,8 до 5,5 В. Это значит, что, например, сделать схему с МК работающей от батареи куда сложнее. Напряжение на LiPo аккумуляторе, например, бывает до 4,2 вольт. А значит, для работы МК нужен преобразователь, который сам по себе будет разряжать батарею, или придумывать какие-то схематичные решения. Со многими датчиками, которые рассчитаны на 5 вольт, работать напрямую уже не получится – опять-таки, требуется схематическое усложнение.

— Ток. У AVR выходной ток на пине рабочий около 20мА, предельный – до 40мА. Здесь же 25мА уже предельный, а рабочий рекомендуют не более 8мА. На корпус в сумме здесь не более 150мА, (когда у AVR – 400мА). Значит часто управлять напрямую транзисторами и другим оборудованием может быть затруднительно.

— Ток в энергосберегающих режимах. Хотя STM и гарантирует ток порядка 2мкА, это всё равно значительно больше AVR-овских 0,1мкА.

— EEPROM. В STM32 он отсутствует напрочь! Для хранения настроечных параметров – либо использовать внешний EEPROM, либо встроенную память на батарейке (где она есть), либо перезаписывать страницы флэш-памяти.

— Корпуса. В линейке STM32 практически нет корпусов удобных для домашнего изготовления. Подавляющее большинство в корпусах у которых ноги с шагом 0,5мм. Я нашёл только 2 подходящих варианта: STM32F030F4P6 в корпусе tssop20 и ногами с шагом 0,65мм. И STM32F030K6T6 в корпусе lqfp32 с шагом ног 0,8мм (как у -AU корпусов AVR). Какие там DIP корпуса для макетных плат? Забудьте.

— Непредсказуемость времени выполнения. STM32 – это ядро ARM Cortex-M и куча независимой периферии, соединённые между собой многочисленными шинами данных. AVR – более менее монолитная архитектура, где периферия и ядро тесно интегрированы. В итоге на AVR всегда с точностью до такта можно сказать сколько времени займёт та или иная операция, когда на выходе МК появится сигнал, когда переключится периферия. Здесь же нельзя предсказать даже сколько тактов будет выполняться та или иная инструкция. Не опустошился ли буфер процессора? Не попали ли мы в цикл ожидания флэш памяти? Не занята ли оперативка сейчас контроллером DMA? И так далее. Запись в регистры портов отобразится изменением электрического уровня только через несколько тактов. То же самое с таймерами и ШИМ. То же самое с любой другой периферией. Как-то смеха ради на 20-мегагерцовом AVR я делал формирование VGA сигнала. Конечно, пиксели были в виде больших квадратов, но зато картинка была стабильная, поскольку время работы каждой инструкции строго определено архитектурой. На STM такой фокус уже не пройдёт.

Конечно, перечисленные выше пункты сложно назвать недостатками архитектуры. Скорее особенностями – платой за её большие возможности. Но и списывать со счетов их не стоит. STM32 не является «заменой» AVR, это просто другая архитектура, со своей сферой применения.

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

Понравилось? Поделитесь:

:

Sorry, the comment form is closed at this time.