STM32 — Моделирование аналоговых сигналов и АЧХ (математическим методом)

в 10:37, , рубрики: Без рубрики

Приветствую всех любителей электроники!

Данная статья ориентирована на людей со средним ("молодым"🙃) уровнем знаний в электронике и выше.

Используемые сокращения в статье

АЧХ — Амплитудно-частотная характеристика

МК — Микроконтроллер

БПФ — Быстрое преобразование Фурье

ДПФ — Дискретное преобразование Фурье

Бывают ситуации, когда нам известна АЧХ сигнала, но форму сигнала мы не можем представить в виду сложности основого сигнала. Ещё чаще возникает вопрос об построении АЧХ сигнала, но вот прибора под рукой нет, особенно если работаешь в гаражном кооперативе дома.

В данной статье я хочу поделиться скромным девайсом, моделирующее аналоговый сигнал сложной формы математическим методом и составляеющее АЧХ по данному сигналу.

"Математическим методом!?"

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

Демонтрация устройства на базе STM32F103ZET6 и TFT-дисплея на базе ILI9341

Демонтрация устройства на базе STM32F103ZET6 и TFT-дисплея на базе ILI9341
/*кхе-кхе*/

Да, цифры перед фамилией для университета, я студент, параллельно работающий техником (программирование STM32)


Краткие сведения

Для разработки устройства нам потребуется:

  • Любая отладочная плата с МК семейства STM32F1xx. В моём случае это </p>" data-image="https://habrastorage.org/getpro/habr/upload_files/7b3/aaf/31d/7b3aaf31dac253864c3412455155e636.jpg" data-abbr="STM32F103ZET6" data-image-width="690" data-image-height="544">STM32F103ZET6

  • Любой доступный программатор для STM32, у меня это самодельный </p>" data-image="https://habrastorage.org/getpro/habr/upload_files/a3a/d87/8a1/a3ad878a1a8d544de4db38398145780e.jpeg" data-abbr="st-link V2.1" data-image-width="729" data-image-height="369">st-link V2.1. Если будут желающие, выкачу отдельную статью про него и исходными файлами, вещь!

  • Китайский модуль В проекте использовал 3.8 дюйма, но это уже лирика</p>" data-image="https://habrastorage.org/getpro/habr/upload_files/e42/93f/034/e4293f034f9450aeaa381f6b4173ad67.jpeg" data-abbr="TFT-экрана" data-image-width="1000" data-image-height="1000">TFT-экрана;

  • 3 Брал именно с такой длиной ручки, т.к. на такую идеально садится колпак, полностью закрывает шайбу с гайкой. Может и с большей длиной ручки нормально, честно говоря не проверял.</p>" data-image="https://habrastorage.org/getpro/habr/upload_files/18e/c74/bff/18ec74bffec1e0da0eaa1f264dd543b8.jpg" data-abbr="потенциометра " data-image-width="800" data-image-height="800">потенциометра на 10кОм стандартные китайские;

  • соединительные провода, обычные провода (придётся паять), корпус пластиковый, крепёжные винты м3 для экрана.

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

Разработка кода будет на HAL+CMSIS через STM32CubeMX в Keil uVision5.


Создание проекта в STM32CubeMX

Выбираем контроллер STM32F103ZET6. Выбираем программирование МК по последовательной шине (SWD – Serial Wire Debug). Подключаем внешний кварц на 8МГц. Подключаем SPI (serial peripheral interface), характеристики SPI:

Характеристики SPI1

Характеристики SPI1
Подробнее об этом этапе

Тут можно выбрать любой МК семейства F1 как было сказано ранее, у них всё одно и тоже, в том числе и распиновка SPI и АЦП. Способ программирования у меня стоит по SWD. Выбирайте как вам удобно, с этим понятно.

Также включаем АЦП. Задействуем 3 канала АЦП, под одну составляющую гармонического сигнала отводим 1 канал АЦП. Забирать данные с АЦП будем при помощи DMA в режиме прерывания. АЦП и DMA выставим в одиночном режиме, будем вручную выключать АЦП и включать, когда это будет нужно. Не забудьте включить прерывание по DMA!

Характеристики АЦП

Характеристики АЦП

Кроме этого, активируем ещё 3 вывода МК, настроем их как выходы и присвоим названия. Данные выводы будут использованы для управления дисплеем. Выбор драйвера (chip select), сброс (reset), и разрешения записи данных (data chip).

Помимо этого нужно конечно же подать питание, и включить подсветку. Подсветку можно сделать постоянную, а можно организовать регулируемую, через транзистор с помощью таймера в режиме ШИМ. Питание и подсветку я подавал от самой платы...

STM32 — Моделирование аналоговых сигналов и АЧХ (математическим методом) - 4

Теперь перейдём в дерево тактирования (clock tree) и разгоним МК до максимально допустимой частоты в 72МГц.

Тактирование микронтроллера

Тактирование микронтроллера

Стоит заметить, что в таком положении частоту АЦП можно выставить только на 12МГц, при 14МГц возможных. Чтобы выставить максимум необходимо понизить частоту системной шины. Однако в данном проекте АЦП не выполняет сверхважную задачу, поэтому оставляем 12МГц.


Разработка программы для микроконтроллера

Прежде всего необходима библиотека для дисплея на базе ILI9341. В интернете есть множество вариантов готовых библиотек для данного дисплея, однако в большинстве своём они ограничены, тяжелы в понимании за счёт отсутствия эстетики написания кода, могут вообще не работать или написаны под какое-то конкретное семейство STM32. Поэтому было принято решение написать универсальную для любого семейства STM32 библиотеку уже с полным набором удобных функций на базе библиотек из интернета, однако данная библиотека подходит для любого драйвера, так как она написана на CMSIS. Таким образом её можно подключать в любой проект. Саму инициализацию SPI будет делать CubeMX (так быстрее, при желании скину код инициализации SPI на CMSIS с возможностью разгона частоты до 44МГц). Инициализацию библиотека делает сама. Она имеет понятные названия функций, их легко использовать.

Основные функции:

1) void taskSolution (uint16_t k); Данная функция решает основное уравнение, описывающее гармонический сигнал, подготавливая значение выборки для удобного вывода на дисплей. Для данного проекта выбрано следующее уравнение:

f(x)=(1.0∙cos⁡((2π/N)∙i_1∙k+π/2)++(1.0∙cos⁡((2π/N)∙i_2∙k+π/2)++(1.0∙cos⁡((2π/N)∙i_3∙k-π)

где in – это частота гармоники, величина которой зависит от изменения на входе канала АЦП;

      k – номер выборки;

      N – количество выборок (N = 256);

void taskSolution(uint16_t k){
	firstHarm = 1.0 * cos(((2*PI)/X_LINE_LENGTH)*firstHarmFreq*k + PI/2); //значение 1-ой гармоники
  	secHarm = 1.0 * cos(((2*PI)/X_LINE_LENGTH)*secHarmFreq*k + PI/2); // 2-ой
	thirdHarm = 1.0 * cos(((2*PI)/X_LINE_LENGTH)*thirdHarmFreq*k - PI); // 3-ей
	Fk = firstHarm + secHarm + thirdHarm; // Сложеник гармоник и получие выборки
//Главное уравнение: 
//Fk = (1.0 * cos((2*PI/N)*0*k + PI/2) + 1.0 * cos((2*PI/N)*2*k + PI/2) + 1.0 * cos((2*PI/N)*3*k - PI));
	Fk = ((Fk*MULT_SIGNAL_VAL)+START_POINT_Y); // конверсия значения для отображения на экран
}

Само собой функция стоит в цикле до некого опеределённого значения, в данном случае 256 выборок (X_LINE_LENGTH). По мимо этого в конце функции я зачем-то делаю конверсию. Тут всё просто. Я использую функцию косинуса и значит у меня значение колеблется от 0 до 1, это понятно. Значит мне его нужно просто умножить на некую константу, чтобы удобно было вывести уже большое значение (номер пикселя) на экран + это помогает сразу получить из "плавующей" переменной довольно точную целую.

2) uint32_t MAP (uint32_t au32_IN, uint32_t au32_INmin, uint32_t au32_INmax, uint32_t au32_OUTmin, uint32_t au32_OUTmax); Данная функция конвертирует диапазон изменения значения АЦП (0-4095) в диапазон частот сигнала (0-15);

3) void displaySignal (uint16_t k); Данная функция выводит Полученное значение выборки на дисплей;

4) void clearNextPosition (uint16_t k, uint16_t COLOR). Перед выводом на дисплей используем функцию очистки следующего значения выборки. Так как экран обновляется в данном случае слева направо, и чтобы не очищать сразу весь участок сигнала (устранение мерцания), а добиться плавности отображения изображения очищаем только следующий столбец пикселей экрана;

5) void visualSignalIncrement (uint16_t k, uint16_t COLOR); Чтобы визуально сигнал был хорошо различим, зажжём сверху и снизу основного значения функции ещё один пиксель, таким образом сигнал будет жирнее;

6) Функции:

void visualFFT_Increment (uint16_t k, uint16_t harmonic, uint16_t COLOR);

void displayFFT (uint16_t k); void clearLastFFT_VAL (uint16_t k);

void displayFFT_VAL (uint16_t k);

аналогичны функциям вывода сигнала на экран. Принцип работы такой же, отличие заключается только в определении амплитуды и частоты каждой гармоники, и расчёт АЧХ;

Нюанс построения АЧХ в проекте

Я не использовал сложные формулы для вычисления АЧХ в данном проекте. Т.к. мне известна формула гармонического сигнала, его параметры, я просто брал значение его аплитуды и частоты и рисовал соответсвующую гармонику на АЧХ. В дальнейшем конечно, устройство можно доработать, тем более, что это может лишь показаться сложным на первый взгляд. Если хочется понять принцип работы алгоритма БПФ и ДПФ, на harb есть понятная и позновательная статья на данную тему, там использован другой язык и нет микроконтроллера, но это не важно, поскольку очень понятно описывается сам алгоритм, чтобы вы могли применить его у себя в проекте.

7) Для отслеживания параметров сигнала и АЧХ необходимо нарисовать координатные оси с соответствующими значениями. Данную операцию выполняют функции drawGridSignal(BLACK); drawGridFFT(BLACK); Из названия функции можно увидеть, что первая функция отвечает за отрисовку координатной оси для гармонического сигнала, а вторая за АЧХ. Количество выборок: 256 (X_LINE_LENGTH). Максимальная частота гармоники: 15 Гц (LIMIT_FREQ);


Процесс сборки устройства

Устройство мне нужно было необходимо сделать довольно быстро для своих нужд, но так, чтобы ничего не развалилось и нормально работало. Поэтому плату я не делал, это было бы долго и дорого, даже если не учитывать около недели на разработку платы, китайцы бы везли платы больше месяца + они стоили бы около 25-35$. А так плат с STM у меня в избытке, как и программаторов с иными коплектующими, поэтому, условно, собрал из того, что было, не задумываясь над ценой конструкции. Разве что покупал пластиковый корпус, так как у меня пока нет своего 3D-принтера, увы... Вырезать его конечно то ещё удовольствие и не очень красиво выходит по итогу, но тут уж как говорится:

"Лучше сделать и пожалеть, чем не сделать и потом жалеть" 😅

Внутреннее строение устройства

Внутреннее строение устройства

Все исходники я выложил на GitHub. Там вы можете скачать проект в CubeMX и сам проект в Keil uVision5. Проект должен завестись сразу же, файл библиотеки уже подключён в проект, ничего стороннего скачивать не нужно.

По вопросам моего самодельного программатора и прочим пишите в комментариях. Кроме этого недавно приехали </p>" data-image="https://habrastorage.org/getpro/habr/upload_files/792/9e4/002/7929e4002a8a987183561c436464a541.jpeg" data-abbr="сделанные мной платы на недавно появившуюся STM32G4" data-image-width="720" data-image-height="1280">сделанные мной платы на недавно появившуюся STM32G4 (48 пин делал). Очень удобная, с дополнительным функциональным, по сравнению с китайскими платами, да и китайцы сами ещё не успели выкатить дешёвую и удобную версию для данного семейства, если есть желание узнать про это побольше, пишите, с огромной радостью выкачу обзор.

Спасибо за внимание!

Автор: Павел

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js