[Из песочницы] История одного осциллографа на stm32

q.png

Чуть больше года назад, мне в голову пришла мысль о том, что хорошо бы было сделать осциллограф. Тогда мне хотелось, чтобы это было независимое устройство с собственным TFT дисплеем, да и вообще, идея разобраться с TFT дисплеями, мне казалась очень перспективной. Спустя некоторое время на али был заказан TFT размером 3.2 дюйма с драйвером SSD1289.

На тот момент у меня уже был опыт программирования микроконтроллеров AVR, поэтому решил запустить дисплей на моём любимом Atmega16. Дойдёт ли дело до создания осциллографа тогда ещё не знал, но то что буду в своих проектах использовать TFT знал точно, поэтому не стал искать сторонние библиотеки, а решил написать свою, которой пользуюсь и по сей день.
 
После того, как получилось инициализировать дисплей, стало понятно, что на Atmega16 сделать осциллограф не получится. Уж очень медленный он для дисплея такого размера. И что-то внутри подсказывало мне, что пора переходить на STM32, но оно же и останавливало меня. В общем, процесс обдумывания был недолгим и на али была заказана плата с STM32F103VET6 на борту. Но кроме причины описанной выше, была ещё одна причина перейти на STM32 — встроенный 12 битный АЦП на 1Msps, который можно использовать для оцифровки сигнала.
 
К моему удивлению, после нескольких недель работы с STM32 стало понятно, что в них нет ничего сложного и я перестал понимать, почему не перешёл на них раньше. Перенести код,  написанный для AVR, на STM32, не составило труда, но не давала покоя мысль о том, что мне необходимо, чтобы дисплей работал на максимальной скорости, а для этого надо было разобраться с FSMC. На самом деле и тут оказалось всё просто — это заняло у меня одни выходные. На этом подготовительные мероприятия с дисплеем были закончены и можно было переходить непосредственно к реализации осциллографа.
 
Первоочередной задачей было научиться выводить сигнал на дисплей, для этого накапливал необходимое количество выборок АЦП, затем выводил их на экран, заливал экран чёрным цветом и так по кругу. Кстати, уже тогда для сохранения данных в буфер использовал DMA.
 
Первый шаг был сделан и меня переполняла радость и гордость за проделанную работу. Далее, хотелось, чтобы синусоида не бежала, а стояла, для этого надо было научиться запускать преобразование АЦП по триггеру.
Сделать это можно с помощью обычного компаратора, как показано на схеме ниже.

1.png


Компаратор на ОУ.

На инвертирующий вход подаётся опорное напряжение, которое формируется с помощью ШИМ и RC цепочки. А на прямой вход подаётся сигнал, тот же что подаётся на вход АЦП.

Когда напряжение на прямом входе становится выше или ниже напряжения на инверсном входе изменяется полярность на выходе ОУ. Это изменение фиксирует вывод МК настроенный на внешнее прерывание. Изменяя активный фронт, для внешнего прерывания можно производить захват как по возрастающему, так и по спадающему фронту.

Далее, в прерывании включается DMA и работает до тех, пор пока буфер не заполнится. Да именно DMA, АЦП в моей реализации работает всегда. Резистор в обратной связи необходим для увеличения скорости нарастания, да и сам ОУ для этого дела, желательно выбрать побыстрее.
 
Спустя некоторое время мне на глаза попался обзор DSO 138 и из того же обзора я узнал, что его схема доступна в интернете и решил позаимствовать оттуда кусочек.

2.png


Фрагмент схемы DSO 138.

Что делает этот кусочек схемы?
Диапазон напряжений,  с которыми может работать АЦП определяют уровни опорных напряжений (+VREF и -VREF), они не должны выходить за диапазон питания микроконтроллера. Нижнюю границу диапазона ограничивает 0 вольт, верхнюю — 3.3 вольта. Отсюда становится понятно что измерять отрицательные напряжения АЦП не может, а это необходимо.

Для того чтобы АЦП чувствовало отрицательные напряжения необходимо, чтобы в отсутствие сигнала на его вход подавалась половина опорного напряжения в нашем случае 1.6 вольта. В таком случаем при измерении отрицательного напряжения, например, минус 0,3 вольта, напряжение на входе АЦП уменьшится на 0,3 вольта и станет равно 1,6 — 0,3 = 1,3 вольта. Такое напряжение АЦП с лёгкостью оцифрует. Этот пример приблизителен потому, что не учитывает коэффициент усиления схемы (в данном случае мы приняли его за 1), зато нагляден.
 
Также хотелось обратить внимание, что питание ОУ двухполярное, это надо для того чтобы ОУ мог работать с отрицательным напряжением. В случае если питание ОУ однополярное (на один вывод питания подаётся 0, на второй 5 вольт)и на вход ему подать отрицательное напряжение, ОУ просто его не почувствует и сделать с ним ничего не сможет, вот так вот.

Для реализации двухполярного питания использовал две зарядки от телефона, соединив плюс одной с минусом другой, и принял потенциал этого соединения за точку отсчёта, то есть землю.
 
Схема была собрана на макетке и теперь осциллограф мог осуществлять захват по триггеру и измерять отрицательные напряжения, что делать дальше в плане железа у меня идей не было, поэтому перешёл к софту.
 
На тот момент времени у меня уже было чёткое понимание, что буфер должен быть кольцевой, а размер позаимствовал у DSO 138, то есть 4096 точек.

Для чего столько точек?
Такое количество точек, с помощью прореживания позволяет реализовать некоторые развёртки, которые нельзя реализовать аппаратно. У STM32 длительность преобразования составляет 12.5 цикла, для того чтобы найти период выборок надо к длительности преобразования прибавить значение, которое определяется битовым полем SMPx[2:0]. На данный момент у DSO 138 уменьшили размер буфера до 1024 точек, у меня же осталось 4096.
 
Так, а для чего всё-таки кольцевой буфер?
Кольцевой буфер необходим для изменения соотношения количества пред и пост выборок, на быстрых развёртках. Предвыборки — выборки до срабатывания триггера, поствыборки — выборки после срабатывания триггера. Алгоритм работы следующий, после отрисовки осциллограммы:

  1. накапливаем нужное количество предвыборок;
  2. разрешаем внешние прерывания;
  3. МК начинает выполнение каких-то сторонних задач, до тех пор, пока не придёт сигнал внешнего прерывания.

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

Результат можно посмотреть на видео.

Что мог прототип осциллографа на тот момент?(а что он что-то мог?)
Можно было изменять время развёртки, выбрать тип триггера, выбрать активный фронт, изменять количество пред и пост выборок, изменять уровень триггера, также изменять базовый уровень.
Что касается типа триггера, в режиме авто, всё происходит также как в режиме норм за исключением того, что если в течение 100 миллисекунд не приходит сигнал, вызывающий внешнее прерывание, то происходит прерывание по таймеру и в нём заполняется буфер.
 
Для того чтобы реализовать остальные функции пришлось снова вернуться к железу. И думаю у многих возник вопрос, почему не заюзать всю схему от DSO138?
Всё просто, хотелось чтобы положение аттенюатора переключалось электроникой, а не переключателями и,  главное, чтобы при смене открытого входа на закрытый и наоборот щёлкало реле))
В общем, передо мной стояла задача, найти аналоговую часть, которая бы соответствовала моим требованиям. На самом деле это задача непростая, если бы не одно но…
В один прекрасный момент я вспомнил, что у меня есть осциллограф, а его разработчик около года назад отвечал мне на вопросы по его использованию в skype, в общем, решил ему написать.
После того как рассказал ему о своих планах сделать осциллограф он предложил мне несколько вариантов аналоговой части, я выбрал наиболее мне понятную схему, задал ему имеющиеся у меня вопросы и принялся за реализацию. Через несколько месяцев осциллограф был готов.

В итоге получилось сделать осциллограф обладающий следующими характеристиками:
Напряжение питания: 9 В
Потребляемый ток: 110 мА
Частота сэмплирования: 1 Мвыб/с
Аналоговая полоса пропускания: 0 — 200 КГц
Разрешение по вертикали: 12 бит
Максимальное входное напряжение: 50 В
Чувствительность по вертикали: 10 мВ/дел — 10 В/дел
Время горизонтальной развертки: 10 мкс/дел до 200 мс/дел
Входной импеданс: 1 МОм/20пФ
Режимы входа: DC, AC, земля
Режимы запуска — развертки: авто, нормальный, однократный

img_5167.jpg

img_5182.jpg

Результат можно посмотреть на видео.

© Geektimes