Некоторое время назад автор этих строк взялся разрабатывать компактный регистратор одно-полярного аналогового сигнала в пределах 3 вольт с максимально возможной скоростью считывания и минимально возможными затратами и размерами. В список минимально возможных затрат я вписал также и свою головную боль и выбрал хорошо знакомый мне STM32F303. Это, напомню, Cortex-M4 на 72 мегагерца от известной компании, со встроенными 12 разрядными, довольно шустрыми, аналого-цифровыми преобразователями (АЦП или ADC, кому как нравится) и с CAN интерфейсом на борту.
Таких небольших регистраторов требовалось несколько десятков. А наличие у микроконтроллера корпуса о 48-ми ногах вселяло надежду на то, что получится назвать этот считыватель компактным. Интерфейс CAN, с которым у меня уже тогда были хорошие отношения, давал мне возможность объединить весь этот джаз в достаточно удобной манере.
Скорость, с которой, в конце концов, все это заработало, оказалась вполне подходящей, чтобы заявить о работоспособности выбранного подхода. Удалось добиться пол микросекундного шага дискретизации. Головной боли и ассемблера избежать не удалось, но кто об этом сейчас вспоминает?
Однако, некоторый осадок остался. Осталась мысль, что, в отношении быстродействия, выжато было не все.
И вот, совсем недавно появляется серия STM32G4 с тактовой частотой до 170 мегагерц с вариантом в маленьком корпусе, и с почти такими же шустрыми АЦП на борту в количестве пяти штук. Надо было что-то делать.
Над душой теперь никто не стоял и можно было не беспокоиться о сроках и планах.
Действительно, если об этом не думать, то можно получить удовольствие даже от работы. Но, честно говоря, о времени думать пришлось. Думать долго о времени малом (а что, поэтично).
Напрашивалась мысль, что надо начать немного с другого конца. То есть, надо исходить из минимально возможного времени выборки АЦП, которое, если исходить из описания, занимает 2.5 такта и составляет на 40 МГц (160 МГц / 4) 62.5 наносекунды. Тогда сам собой напрашивается шаг дискретизации в 100 наносекунд. Число круглое и, главное, довольно маленькое и красивое, почему бы и не попробовать?
К тому же, появилась в продаже и была куплена подходящая для экспериментов плата NUCLEO-G474RE, к которой разумно было добавить дополнительную макетную плату с двумя двухрядными разъемами, чтобы припаивать всякие провода и детали к макетной плате и не портить основную. Вот как это выглядит в готовом виде.
Там, снизу, есть несколько проводков и резистор с конденсатором, уж поверьте мне на слово.
Теперь надо подать подходящий электрический сигнал сразу на все четыре АЦП, затем запускать их друг за другом с постоянным шагом (сначала, при отладке, не очень коротким). Почему четыре? В соответствии с описанием, на одно преобразование каждый АЦП тратит 15 тактов или 0.025*15=375 наносекунд (почти 400). Поэтому при шаге 100 потребуется конвейер из четырех АЦП.
В качестве входного сигнала использовалась RC цепь, на которую подавалось напряжение просто от ножки контроллера. Этой ножкой я назначил управлять таймер TIM5 в режиме одиночных импульсов.
Минимальная схема соединений выглядела как показано на рисунке ниже.
Были задействованы ADC1-ADC4. Разрядность можно было снизить для некоторого повышения быстродействия, но 12 разрядов перевесили, поскольку не хотелось терять в точности измерений. Для запуска каждого АЦП в нужном порядке и с требуемым шагом используются три таймера (TIM2, TIM3, TIM4) и еще один таймер (TIM1) служит для синхронизации вышеуказанных трех. На рисунке 2 ниже показаны сигналы, вокруг которых все строится.
Зеленые стрелки показывают фронты импульсов, по которым происходит запуск соответствующих преобразователей ADC1-ADC4.
Чтобы получить задуманные 100 наносекунд пришлось снизить тактовую частоту до 160 мегагерц, чтобы все там делилось нацело как следует. Сначала шаг был установлен значительно медленнее, 4 микросекунды, чтобы спокойно проверить работу таймеров, используя прерывания, выходные порты и осциллограф. Затем по прерываниям был отлажен и заработал прямой доступ к памяти. В конечном счете только одно прерывание обрабатывается — это прерывание окончания работы от четвертого (последнего) канала прямого доступа к памяти. На рисунке ниже показаны соединения участвующих в процессе аппаратных блоков, а также четыре выходных буфера памяти.
Из четырех буферов памяти (на рисунке — справа) отсчеты собираются программно в один буфер результата.
В этом контроллере, в отличие от STM32F303, есть блок коммутации запросов (DMAMUX), который позволяет перенаправлять огромное количество запросов от периферийных устройств всего на 16 каналов DMA1 и на 16 каналов DMA2. В справочном руководстве (Reference manual) выходные запросы DMAMUX отсчитываются от 0, а входы каналов DMA блоков отсчитываются от 1. Не стал менять, оставил как в первоисточнике.
В качестве генератора одиночного импульса используется таймер TIM5. В нем предусмотрен удобный режим одиночного импульса. Удобство заключается в возможности установки времени до начала импульса и установки длительности самого импульса. Этот импульс получился таким как показано ниже на картинке, любезно предоставленной осциллографом, взятым напрокат.
Из осциллограммы видно, что подъем импульса длится 10 микросекунд, значит он должен вместить порядка 100 отсчетов.
Проект делался в IAR 8.4 вручную (то есть, без Куба и Шара), но, надеюсь, будет понятен самым разным конфессиям. Его можно посмотреть здесь.
Вот картинка содержимого отдельного буфера ADC1 и собранного из четырех источников буфера результата в районе начала входного импульса.
А вот содержимое этих буферов в районе пика импульса (там, где значение доходит до 2508).
Как видим, на участок от начала импульса до его вершины потрачен 196-95=101 отсчет.
Значит частота дискретизации составляет 10 мегагерц. Заработало на такой скорости не сразу.
Для того, чтобы добиться этого пришлось останавливать процессор пред началом работы прямого доступа к памяти (DMA1). Хорошо, что у Cortex-M4 есть специальная ассемблерная инструкция WFI (Ждать прерывания). Если этого не сделать то процессор будет мешаться под ногами у DMA и занимать шину обращениями в память, которые вполне могут подождать.
Если увеличить шаг отсчетов до 200 наносекунд, то они перестанут толкаться и заживут мирно и счастливо.
Если вовлечь в работу компаратор COMP4 и соединить его положительный вход (порт PB0) с входным сигналом, а затем использовать ЦАП (DAC1) и соединить его выход (CH1) с отрицательным входом компаратора (внутри контроллера), то получим пороговое устройство с регулируемым порогом. Прерывания от срабатывания компаратора позволят запускать общий тактовый таймер TIM1 и получить ждущий режим, как в осциллографе.