В статье продолжу рассказ о "написании" программного обеспечения для "приставки-осциллографа" с видеовыходом на основе платы "BlackPill" (микроконтроллер stm32f401ccu6). Статья не является "учебным пособием", скорее рассказ о первом опыте работы с микроконтроллером от stm.
В предыдущей части рассматривается программное обеспечение (ПО) для формирования видеовыхода, предполагается, что уважаемый читатель ознакомился с материалом и тестировал ПО (osc_tv_out1.zip).
В качестве инструмента разработчика используется stm32CubeIDE версии 1.9.0.
Для преобразования аналогового сигнала используется встроенное АЦП микроконтроллера. Тактовая частота АЦП выбрана максимально возможной - 36 мГц, в связи с этим пришлось понизить тактовую частоту микроконтроллера с 84 мГц до 72 мГц. Если Вы знаете как в серии f4 иным способом решить данную задачу - пишите в комментариях. Несмотря на "бюджетность" проекта достаточно "комфортно" наблюдать периодические сигналы частотой до 250 кГц, меандр различим на частоте 500 кГц. В качестве источника сигналов использовался генератор, встроенный в осциллограф Hantek DSO2D15 (мини обзор), с выходным сопротивлением 50 Ом, что позволило провести ряд опытов по пробоотбору сигнала. Достигнутые параметры практически в 20 раз превосходят параметры моего осциллографического пробника на avr mega 2560 (описание), применяемого мною преимущественно в дачный сезон.
Продолжим проект из предыдущей части, откроем Pinout&Configuration, настроим АЦП и GPIO для светодиода и пользовательской кнопки (расположены на плате черной пилюли). Кнопка ("wakeup", и при определенных прошивках (микропитон, например) для "просыпания" и служит) будет использоваться для переключения диапазона "время/деление". Светодиод только для "сигнализации" реакции на нажатие кнопки.
На снимках экрана ниже представлен процесс настройки АЦП.
Прескалер ставим на 2 (72 мГц/2 = 36 мГц), используем 4 канал, вывод PA4, устанавливаем разрядность 8 бит (можно поэкспериментировать с другими разрядами, но 8 бит по моему мнению оптимально), включаем режим непрерывной работы АЦП (в рамках запроса DMA), устанавливаем, что нам нужен флаг после проведения всех преобразований, и, ставим время в 3 такта для зарядки конденсатора АЦП. Далее настраиваем DMA канал для АЦП. Вот тут есть варианты: можно использовать буфер fifo для DMA (то есть АЦП будет кидать данные в буфер (до 16 байт), а DMA будет договариваться на "пакетную" передачу из буфера в память, вроде так правильно.... и нагрузка на шину более "ритмичная"), а можно и не использовать - в своих опытах для данной задачи не заметил существенной разницы. И включаем прерывание - будем ловить окончание преобразований в функции HAL_ADC_ConvCpltCallback.
На снимках ниже приведены настройки GPIO (кнопка и светодиод).
Светодиод подключен к PC13, устанавливаем режим Output. Кнопка подключена к PA0 по схеме приведенной на Рисунке 1 (полная схема здесь).
Кнопка "работает" на землю (в предыдущих версиях плат было на питание), включаем встроенную подтяжку к напряжению питания (pullup). Режим работы ставим EXTI (внешнее прерывание), переходим в закладку NVIC, включаем EXTI line0. "Ловить" прерывание после нажатия кнопки будем функцией HAL_GPIO_EXTI_Callback, запоминаем, что прерывание будет на line0 (следовательно, для того, что бы выключить прерывание от кнопки надо выполнить HAL_NVIC_DisableIRQ(EXTI0_IRQn), нолик я выделил не зря (line0)).
Необходимо сделать еще одну настройку - подправить приоритеты прерываний. Тут немного философии: у нас две задачи, формирование видео, и считывание с АЦП. И обе задачи критичны к времени. И вероятность "пересечения" прерываний достаточно высока (выражается в сдвигах строк на экране). В этом случае вступает в работу арбитр прерываний по табличке NVIC (если нет разбивки на группы, стр. 197 RM0368). А из таблички видим - сначала будет выполнено DMA1, потом ADC, TIM2, TIM3, SPI1, DMA2. А хотелось бы TIM2, TIM3 "выпихнуть" вперед. Здесь можно поэкспериментировать, "двигая очередь" прерываний, или разбивая на группы (в приложенных исходниках будет несколько другая "табличка" приоритетов, так как в процессе написания и отладки проверялись некоторые простые вещи, понятные профи по микроконтроллерам, и совсем непонятными автору). Я просто понизил приоритет DMA1, "сдвиг" строк прекратился (здесь очень кратко описан принцип работы NVIC). Если я что-то написал неправильно, поправьте меня. Настройки приведены на снимке экрана ниже.
И еще одно замечание. Для вывода переменных с плавающей запятой (в нашем случае float), необходимо их преобразовать в строчный тип. Это можно сделать разными способами, но самый простой - использовать sprintf (достаточно затратный на ресурсы), а для того, чтобы это все "взлетело" необходимо добавить ключик компилятору. Из руководства по программированию:
Problem description: The float formatting support is not enabled, check your MCU Settings from "Project Properties > C/C++ Build > Settings > Tool Settings", or add manually "-u _printf_float" in linker flags.
Как это сделать в stm32CubeIDE на снимке экрана ниже.
Так как формат статьи на Дзене к сожалению не приспособлен для размещения кода, постарался достаточно подробно комментировать код в "исходниках". Отмечу лишь некоторые моменты. Реакция на нажатие кнопки реализовано с помощью прерывания. Для инициализации АЦП c пользовательскими настройками использован клон MX_ADC1_Init(), сделано для того, чтобы беспрепятственно использовать Cube MX без затирания правок (например, при экспериментах с приоритетами прерываний), потом можно убрать. Практически код основан на использовании библиотек HAL (включая функции callback для АЦП и GPIO_EXTI), непосредственное использование регистров только в "сервисной" части для счетчика DWT и необязательно. АЦП запускаем в непрерывном режиме, данные снимаем непосредственно в память (DMA).
В двух словах алгоритм: основной цикл -> ловим флаг прерывания нажатия кнопки -> присваиваем пользовательские настройки АЦП -> инициализируем АЦП -> ловим последнюю видимую строку кадра (у меня 276) -> запускаем АЦП, ждем окончания работы АЦП (снимаем 1440 отсчетов, для максимального использования "спокойного" времени гашения кадрового импульса (1.6 мс) используем последние строки кадра) -> ищем максимальное значение и минимальное, вычисляем среднее, называем уровнем синхронизации -> вычисляем "сдвиг" в массиве по пересечению с уровнем синхронизации -> производим "выборку" для отображения на экране -> рисуем.
Организация времени развертки: 10 мкс - максимально информативная, частота 36 мГц, 3 цикла АЦП пробоотбора, 33 точки на деление, "примерно вписывается" один период сигнала 100 кГц. Развертки 5 мкс, 2.5 мкс, 1.25 мкс - уменьшение числа отображаемых точек на деление экрана. 20 мкс и 40 мкс - соответственно меняется частота АЦП (18 мГц и 9 мГц). Более медленные развертки - увеличение времени пробоотбора (зарядки конденсаторов) АЦП. В связи с этим, конечно, измерения уровней сигнала на диапазонах до 40 мкс и после отличаются (незначительно).
На снимках приведенных ниже показаны результаты работы программы.
Сигнал до 400 кГц хорошо различим, комфортно работать до 300 кГц, и, надо сказать про опасения использования библиотек HAL - может быть и не достигнуты все возможности АЦП микроконтроллера, как при использовании библиотек SPL или LL, но вполне работоспособно. Единственный минус - "временной налог" за удобство - например функция HAL для работы с АЦП и DMA "кушает" 3670 тактов при запуске.
А теперь о грустном - программа "сырая". Есть вопросы к синхронизации (или делать внешний компаратор, или кто-то мне подскажет по железной части микроконтроллера про компаратор), программная синхронизация все таки за уши притянута. Второй минус в временном конфликте прерываний таймера и АЦП. На низких частотах 12 тактов перехода от одного прерывания к другому не заметны, а на частотах выше 150 кГц уже заметны. Или выкручиваться без прерываний. Короче вопросов пока больше, чем ответов. И да, частотомер просто условный, с слишком большим шагом. Поэтому для не желающих "ковырять" программную часть могу посоветовать посмотреть статью автора В. Попова "Осциллографическая приставка к VGA-монитору" в журнале "Радио" 2021 года номер 7 и немного доработать под видеовыход и f401ccu6 камень (код написан с использованием библиотек SPL).
И немного экспериментов с "паразитным" в одном случае (aliasing), и очень "полезным" в другом случае (sampling scopes). В первом случае стробоскопический эффект может ввести в заблуждение, во втором случае позволяет наблюдать периодические сигналы частотой значительно выше "прямого счета" АЦП.
В нашем случае частота пробоотбора АЦП (8 бит) 36 мГц/14 циклов = 3 272 727 Гц. При 12 бит паспортное значение 2.4 мГц. Проверим? Нам нужно две точки на период. Ставим меандр частотой 1.636 мГц. Выберем развертку 1.25 мкс, зная, что разрешение 4 точки на деление. По идее получим пилу. Что и видим на снимке.
Теперь про время зарядки конденсаторов АЦП (время пробоотбора). Минимальное время для данного микроконтроллера 3 цикла / 36 мГц = 83 нсек. Что можно увидеть с таким огромным временем пробоотбора (с точки зрения стробоскопических осциллографов)? Например частоту 3.336 мГц. Снимки экрана сделаны с осциллографа Hantek DSO2D15 в режиме фильтра 20 мГц.
А можно и 6 мГц:
Ну и почти 10 мГц:
Очень похоже на "правду". Теперь фронты, где отчетливо видно все "артефакты" большого времени пробоотбора и дрожания сигнала.
Есть стойкое ощущение, что реальное время пробоотбора менее 83 нсек (причем не только у меня, китайские радиолюбители на своих форумах говорят о слишком пологом спадающем фронте ключа устройства выборки хранения, и о том, что "окно" может быть около 25 нсек, что очень похоже на правду). К сожалению, в этой области АЦП не исследовал, и это только предположения. Низкое сопротивление источника сигнала дает достаточное время для зарядки конденсаторов (8 пф, 50 Ом, 0.4 нсек) УВХ АЦП.
При не сильно красивых картинках сигнала этот эффект можно использовать - надо знать частоту исследуемого сигнала и не сложно посчитать частоту пробоотбора, или классическим способом ставить "железный" компаратор, делать пилообразный сигнал (рампу) и по внешнему прерыванию запускать АЦП. Так или иначе задача сильно усложняется и имеет чисто "академический" радиолюбительский интерес.
И да, чуть не забыл, проект для stm32CubeIDE (v.1.9.0) можно забрать здесь (архив 7zip).
Спасибо за уделенное Вами время на мой скромный труд, пишите комментарии - это помогает не забросить канал и чувствовать необходимость продолжать выпускать статьи.