STM32F103C8T6 — делаем осциллограф. Часть 3

Третья часть (первая и вторая) про то как я делаю осциллограф из отладочной платы ценой менее $3. Демонстрационное видео работы:

А описание некоторых ключевых особенностей под катом.

Аналоговая чать


Почти всё как было описано во второй части, кроме источника двухполярного питания. ОУ потребляют значительный ток (порядка 10 мА) и как не пытался схемами умножителей напряжения на диодах и конденсаторах получить приемлемых результатов — не удалось. Поэтому для положительного напряжения поставил вот такой модуль на основе МТ3608:
a574ce3651e04e83bccd2b49c60f38a2.jpg
настроенный на 10 В выходного напряжения. А отрицательное напряжение получаю путём инвертирования положительного с помощью LT1054.

Про размер кода


В первой части я писал, что памяти потребляется очень много. Теперь я дошёл до того, что программа не влазит в память и изучил этот вопрос подробней.

CooCox CoIDE выводит информацию о размер программы в таком виде:

      text      data     bss     dec     hex filename
     60224         2500   10540   73264   11e30 projectName.elf


где

  • text — размер сегмента с кодом, векторами прерываний и константами только на чтение;
  • data — размер сегмента с инициализированными не нулём переменными;
  • bss — размер сегмента с неинициализированными и инициализированными нулём переменными.

Вся программа занимает:

  • флеш — text + data + 10…50 байт
  • ОЗУ — data + bss + 10…50 байт

Теперь посмотрим на что тратится память. Делаем новый проект и компилируем:

      text      data     bss     dec     hex filename
       364         1080      32    1476     5c4 test-size.elf


Чтобы использовать макросы типа GPIO_BSRR_BS9 надо подключить файл stm32f10x.h.
Чтобы подключить файл stm32f10x.h надо в репозитоях добавить компонент STM32F10x_MD_STDLIB, который подтягивает за собой cmsis_core. В итоге для программы, записывающей одно значение в регистр получаем:

      text      data     bss     dec     hex filename
      1316         1104      32    2452     994 test-size.elf


Далее меня интересуют функции типа sprintf и sscanf. Чтобы их использовать надо определить некоторые функции типа _sbrk и возможно некоторых других. Я взял готовый файл (есть в архиве с проектом). Добавляем 1 вызов sscanf и получаем:

Попробуйте угадать сколько, прежде чем смотреть!
      text          data     bss     dec     hex filename
     39312         2264      96   41672    a2c8 test-size.elf


41 кБ флеша! Больше половины, того, что есть в контроллере!
В рабочей же прошивке при использовании printf добавление sscanf увеличивает потребление флеша на 13.2 кБ. В итоге от sscanf отказался, а команды от ПК стал парсить менее ресурсоёмким методом.
Отказ же от printf позволяет сэкономить ещё 8.3 кБ.

Режимы работы


Реализовал 3 режима по принципу действия: непрерывный, пакетный и логический и 3 по количеству каналов: 1, 2 и 4-х канальный.
МК имеет 9 аналоговых входов, но я не представляю когда мне может понадобиться больше 4-х каналов.

Непрерывный


Тут всё просто: в главном цикле МК считываем данные АЦП и передаём их на ПК, где можем строить непрерывный график. Недостаток — ограничение скорости со стороны канала МК → ПК. Чтобы его обойти реализовал ещё 2 режима.

Пакетный


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

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

В отличие от проекта baghear у меня триггер программный. Преимущества такого решения:

  • Меньше деталей, а значит меньше цена и проще монтаж;
  • Возможность в будущем реализовать более сложные триггеры, а не просто «сигнал в A канале стал больше Х».

В одноканальном режиме оба АЦП по очереди преобразуют значение одного канала.
В двухканальном — каждый АЦП преобразует свой канал запускаясь одновременно с другим.
В 4-х канальном — у каждого АЦП есть 2 канала, которые он преобразует. Старт обоих АЦП одновременный.
Очевидно, что скорость частота преобразования канала обратнопропорциональна количеству каналов.

Логический анализатор


Самый быстрый режим. Примерно 20 MSPS на каждом канале. Самый быстрый код для этого режима выглядит так:

u32 i = 0;
dataBuffer.u8[i] = GPIOA->IDR;
dataBuffer.u8[++i] = GPIOA->IDR;
dataBuffer.u8[++i] = GPIOA->IDR;
dataBuffer.u8[++i] = GPIOA->IDR;
dataBuffer.u8[++i] = GPIOA->IDR;
dataBuffer.u8[++i] = GPIOA->IDR;


и так далее на весь буфер.
Значение переменной i в этом случае вычисляются на этапе компиляции и в итоге из dataBuffer.u8[++i] = GPIOA→IDR; получается всего 2 операции — загрузить данные в регистр из порта и сохранить данные в память по заранее посчитанному адресу. Никакими циклами такой производительности достичь не получилось.

Программа для ПК


Главные, на мой взгляд, измение — переход на OpenGL. С ним графики рисовать стало проще (для меня это оказалось неожиданно, но там всё действительно просто и кратко!), рисуются они быстрее и получаются гораздо красивей, чем были раньше.

Итог


Проект не завершён, есть глюки, допиливать ещё много чего, но каких-то прорывов уже не предвидится. Для более быстрых систем нужно другое железо, например, отдельный АЦП + ПЛИС + память —, а это уже будет гораздо дороже и сложнее монтировать.

Почитав комментарии к статье «История одного осциллографа на stm32» сразу отвечу на некоторые вопросы:

  • Дисплей прикручивать не собираюсь т.к.:
    • Он стоит денег, а комп есть.
    • По качеству будет хуже, чем на большом экране ПК.
    • Создавать и изменять пользовательский интерфейс на C# проще, чем паять и перепаивать.

  • Я не планирую его доводить коммерческого продукта и продавать.
  • Делал для 2-х целей: освоить МК и сделать себе цифровой осциллограф.

Архив с проектом
Если у кого появятся вопросы, а тут не зарегистрированы, пишите в почту: adefikux на gmail точка com.

© Geektimes