STM32F103C8T6 — первые шаги. Начинаем делать осциллограф
О радостях и трудностях первого знакомства с STM32 после AVR. Как я реализовывал простейшую задачу — передачу данных на ПК.
Имея некий опыт работы с AVR, хочется сравнить приехавшие контроллеры (которые по отдельности стоят 1.7$/шт) с близкими к ним по цене ATMEGA328 (1.4 $/шт).
ATMEGA328 | STM32F103C8T6 | Выигрыш, раз | |
---|---|---|---|
Flash, кБ | 32 | 64 | 2 |
ОЗУ, кБ | 2 | 20 | 10 |
Максимальная частота, МГц | 20 | 72 | 3.6 |
Скорость АЦП, kSPS | 15 | 2*1000 (можно разогнать) | 133 |
На фоне роста показателей производительности в 10-100 раз, Flash увеличилась всего в 2 раза. Причём, эти 64 кБ расходуются чуть ли не быстрей, чем 32 на AVR. Логично применять такие контроллеры там, где нужна высокая производительность, но нет кодоёмких алгоритмов… например, осциллограф.
Чем программировать
Сред программирования STM32 великое множество — IAR, Keil, Coocox… поначалу кажется, что это хорошо и точно найдёшь что-то подходящее. Потом приходит понимание как такой зоопарк образовался. Просто кто-то сделал не очень хорошую IDE. Остальные на это посмотрели и решили, что они могут сделать лучше. И сделали. В чём-то получилось лучше, в чём-то хуже. Почитав обзоры и попробовав IAR, остановился на Coocox.
Есть ещё одна программа — STM32CubeMX. Дело в том, что периферии в STM32 гораздо больше, чем в AVR. Инициализировать её гораздо сложнее. STM32CubeMX позволяет выбрать контроллер, потыкать мышкой и сгенерировать код инициализации. Даже если мы не хотим использовать этот сгенерированный код, в STM32CubeMX удобно посмотреть распиновку и схему тактирования, подобрать делители, множители и вручную их прописать в своём коде! Очень рекомендую всем начинающим!
STMStudio — программа позволяющая в реальном времени наблюдать значения переменных в МК.
В качестве программатора решил использовать дешёвый ST-Link V2 за 2.6$.
Подключается всё очень просто. Берём распиновку JTAG,
смотрим рисунок на ST-Link,
и соединяем одноимённые выводы. Запускаем CoIDE, пишем
#include "stm32f10x.h"
int main(void)
{
RCC->APB2ENR |= RCC_APB2Periph_GPIOC; // включаем тактирование порта
GPIOC->CRH |= (0x3 << 20); // ставим частоту 50 МГц
GPIOC->CRH &= (~(0xC << 20)); // переводим ногу в режим выхода тяни-толкай
volatile long i = 0;
while(1)
{
GPIOC->BSRR = GPIO_BSRR_BR13;
for(i = 0; i < 1000*1000*5; i++){;};
GPIOC->BSRR = GPIO_BSRR_BS13;
for(i = 0; i < 1000*1000*5; i++){;};
}
}
Не сильно сложней, чем в AVR, однако, занимает программа 2264 байта во Flash… Это при том, что на AVR весь код металлоискателя занимал меньше.
Ради интереса удалил весь код и скомпилировал пустую программу — 2176 байт.
Отключил STDLIB — 1476 байт.
компилируем, прошиваем… и всё сразу заработало! Безо всяких танцев с бубном! Даже внутрисхемный отладчик заработал! Запускаем STMStudio — и она работает. Строит графики переменных во время работы МК! На плате есть перемычки, но ничего переключать, чтобы запрограммировать/запустить МК не надо! Прям как с Arduino! Ну не может же быть всё так хорошо… да не может.
Начинаем делать осциллограф
В моих мечтах осциллограф должен был работать следующим образом:
Оба АЦП одновременно обрабатывают сигнал со скоростью 1-2 MSPS. Далее 2 варианта:
- Всё это в реальном времени передаётся на ПК по USB и там принимается решение о том, что с этим делать (запомнить, построить график, как-то обработать, ...);
- После каждого преобразования происходит прерывание. В обработчике прерываний мы принимаем решение: ждать ещё или начать запоминать данные (например, хотим чтобы сигнал на экране начинался с некого уровня, как в аналоговом осциллографе, или чуть раньше этого уровня). В этом же обработчике складируем данные в буфер и по его заполнению отправляем на ПК.
Оба эти варианта реализовать не удалось.
Первый потому, что я не смог запустить USB. Вернее смог только сгенерировав проект в STM32CubeMX. Но после экспорта его в CoIDE потребовалось перемычками менять загрузчик для программирования/работы, что не удобно. Поэтому от этого варианта отказался. Ну и вдобавок скорость USB всего 12 МБит/с. Данные на высокой скорости в реальном времени всё равно не влезут. Чтобы хоть как-то передавать данные на комп, подключил преобразователь USB UART
купленный в своё время для программирования Arduino Pro Mini.
Второй вариант накрылся т.к. обработчик прерывания работает дольше, чем АЦП. Скорость ограничилась всего 340-500 kSPS, что в разы меньше ожидаемой.
Единственным рабочим высокоскоростным вариантом оказался такой: АЦП непрерывно работают, когда нам нужен замер, включаем DMA, ждём наполнения буфера, отключаем DMA и потихоньку передаём данные на ПК через USART. Этот вариант превзошёл все ожидания. МК можно разогнать так, что получается 9 MSPS с двух АЦП! Т.е. в 4.5 раза больше, чем по документации! При этом достаточно комфортно наблюдать сигнал частотой до 1 МГц. По сравнению с тем, что удалось достичь раньше на Arduino (10 kSPS) результат очень хороший — скорость увеличил в 900 раз!
Однако, с разгоном не всё так радостно. В дальнейшем, чтобы мог работать USB, частоту придётся снизить в 16/9 = 1.8 раз и тогда получится всего 5 MSPS.
Пока пытался разобраться с USB и прочей периферией осознал существенный недостаток этих контроллеров — очень мало информации в интернете. Если на AVR есть куча всего, то тут найти пример одновременной работы двух АЦП в режиме Fast interleaved оказалось не так просто.
В качестве генератора сигналов для теста осциллографа был выбран… Arduino UNO! Не потому что он хороший или ещё что… просто это очень быстро.
void setup() {
pinMode(2, OUTPUT);
long d = 10;
for(;;){
PORTD = 255;
delayMicroseconds(d);
PORTD = 0;
delayMicroseconds(d);
}
}
void loop() {
}
Подключить USB + 1 проводок (чтобы 3.3 вольтный STM32 не умер от 5 вольтного сигнала, сигнал подан через резистор в 2 кОм) и готово!
Получилось следующее (под каждым изображением фотография этого же сигнала на экране аналогового осциллографа):
Период сигнала 0.9 мкс. 1 замер = 10 пикселей. На осциллографе 1 деление = 0.5мкс.
Период сигнала 10 мкс. 1 замер = 5 пикселей. На осциллографе 1 деление = 2мкс. Верхушки обрублены из-за превышения сигналом опорного напряжения АЦП.
Что дальше
В планах:
- Победить USB, чтобы отказаться от преобразователя USB <-> USART;
- Доделать аналоговую часть, чтобы диапазон входных напряжений был не 0 — 3.3 В, а более приличным;
- Сделать многоканальный режим;
- Реализовать управление с ПК;
- Сделать законченное устройство в корпусе.
В заключение обращаю внимание на два вскрывшихся недостатка STM32 по сравнению с AVR:
- Повышенный расход Flash памяти;
- Сложная инициализация периферии, которая усугубляется нехваткой материалов.
На случай, если кому понадобится: проект CoIDE и рисовалка графика на C#. Код везде сырой. Не знаю как, но на такую простую задачу, ушло 31 кБ Flash.