Найти тему
Просто Радиолюбитель

"Заготовка" для "осциллографа" приставки к телевизору (на базе stm32f401, часть 1, видеовыход)

Ранее я писал о "приобщении" к микроконтроллерам stm32, и о проблемах, вызванных браком платы на основе stm32f401ccu6 (статья). Точнее "мертвыми" выводами контроллера (В15, A8-A11), и в связи с этим отсутствием USB порта и вытекающими неудобствами. Будем считать, что Вы имеете способ программировать контроллер тем или иным способом.

Основная цель проведенной работы описанной в данной статье для меня - более или менее разобраться с программированием контроллеров stm32 с помощью "родного" stm32CubeIDE, дающего больше возможностей в использовании ресурсов контроллера, и упрощающего отладку кода. Все таки stm32 в ArduinoIDE - "за уши" притянутый инструмент. Нет смысла писать "мигалку" диодом - поэтому ставим более интересную задачу: получить простенький "осциллограф" с выходом видеосигнала для просмотра на маленьком телевизоре (недавно писал статью о переделке на светодиодную подсветку 7" "Vitek", статья). Похожая идея с реализацией аналоговой входной части осциллографа описана здесь.

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

Примеры реализации формирования видеосигнала можно глянуть в этой статье, интересный проект и пример кода формирования радиочастотного модулированного сигнала описан здесь, а здесь еще и цвет добавился, правда VGA. Ну а за основу был взят проект от команды Аrtekit (здесь статья, код). Не возможно не упомянуть проект от Tomakichi.

Итак, видеосигнал. На этот счет есть ГОСТ 7845-92. На рисунке 1 и 2 показан строчный и кадровый импульс, с указанными длительностями периодов. У нас 625 строк, два поля, частота кадровых импульсов - 50 Гц, частота строчных импульсов - 15625 Гц, размах - 1 В. Итого период следования строчных импульсов - 64 мкс, кадровых - 20 мс.

Итак, алгоритм такой: таймер (TIM3), канал 1 (PWM, PA6) - строчные синхроимпульсы, канал 3 (PWM, PB0) - гасящий импульс строк, по завершению импульса прерывание, запускающее вывод информации в строке (SPI3 (PB5), чтение массива кадра (fb) с помощью DMA1 stream 5). Таймер строчных импульсов является "мастером" для таймера кадровых импульсов (TIM2). Таймер TIM2 ведомый (считает 312 импульсов с таймера TIM3): канал 2 (PA1) - кадровый синхроимпульс, канал 3 (PA2) - гасящий кадровый импульс, по окончании вызывает прерывание, дает разрешение на вывод строк через SPI. Алгоритм не блещет изяществом - но работоспособен. Будем заполнять один полукадр - 312 линий, по горизонтали разрешение зависит от частоты SPI (9 мГц) и длительности выборки (длины строки в байтах) из массива кадра. Учитывая "съеденные" полями гашения кадрового импульса строки, и приемлемую длительность выводимой строчной информации разрешение получилось 432 по горизонтали, 280 строк по вертикали.

На рисунках ниже приведен процесс создания проекта в stm32CubeIDE версии 1.9 (с сайта stm скачать из России не получится, но достаточно уже выложено ссылок на сторонние ресурсы, такие stm мелкие пакостники, хотя тенденция замечена года так с 2010 - то ti закроет из России доступ к "белым страницам", то AD модели не дает, то ni требует подтверждений для обновлений......)

В конце диалога создания проекта стартует stm32CubeMX.

Может, не стоило так подробно - простите, это мои первые опыты написание статей такого рода - про программирование. Настройка тактовых частот и PLL в следующей серии картинок.

Здесь я выбираю частоту 72 МГц - максимальная частота для АЦП 36 мГц, то есть если выставить максимум 84 мГц, для АЦП можно будет установить делитель только на 4.... Или я еще что-то не понял в разделе настройки тактовой частоты периферии... Если у Вас есть замечания - пишите.

Настройка таймера строчных синхроимпульсов (TIM3)

Период строчных синхроимпульсов для таймера считается так: 72мГц/15625Гц=4608 (пишем 4607, поле counter period не может быть равным 0, поэтому там всегда есть 1), остальные длительности импульсов для каналов - считаются подобно. Гасящий строчный импульс имеет меньшую длину - дело в том, что вывод SPI организован с использованием HAL, а вызов функции HAL занимает время, по моим подсчетам порядка 5,7 мкс с момента вызова до момента появления сигнала на ножке контроллера (осциллограмма на Рисунке 3). Причем, если делать через LL (low level, регистры) - все быстрее (около 400 нсек). За удобство нужно платить.

Рисунок 3. Задержка HAL функции SPI_DMA. Зеленый - запускающий импульс, желтый - SPI.
Рисунок 3. Задержка HAL функции SPI_DMA. Зеленый - запускающий импульс, желтый - SPI.

Настройки таймера кадровых импульсов TIM2 приведены на рисунках ниже.

Стоит обратить внимание на поля Slave Mode и Trigger. ITR2 - как раз флаг таймера TIM3 (поле Update Event) о начале работы. Соответственно, далее будем считать относительно таймера строчных импульсов (TIM3).

Изменяя длительность строчных импульсов гашения можно подстроится под "центр" экрана телевизора.

Настройка SPI на рисунках ниже.

Вот здесь появляется одна проблема. В век ЖК мониторов и ТВ хорошо бы попадать в пиксели. И если эта проблема остро стоит для монитора, для телевизора она не настолько критична, тем более, что маленький жк телевизор, который у меня с разрешением матрицы - 480 на 234.... и скалер телевизора работает по достаточно хитрому алгоритму - полукадр "затеняет" четные строки, второй полукадр - нечетные - в сумме картинка вроде как и ничего при 625 строках (т.е. если "попали" в строку - все четко, не попали - две строки серые). С горизонтальным разрешением примерно такая же картина. На телевизорах с большим разрешением и "аналоговыми, кинескопными" - изображение нормальное.

Настройки периферии в CubeMX закончены, можно открыть main.c, при этом будет предложено генерировать код проекта по произведенным настройкам в CubeMX.

Теперь надо все привести к общему знаменателю. В качестве дружеского совета - свой код пишем только между комментариями /* USER CODE BEGIN */ и /* USER CODE END */, иначе при следующей генерации кода CubeMX все затрет....

Я не буду писать про определение переменных, кто заинтересуется, скачает проект и сам все увидит, замечу только, что массив области кадра fb[][] необходимо выравнивать. Про выравнивание хорошо описано в этой статье. Еще одно замечание - так как "ноги" проекта растут из кода от Artekit - соответственно много заимствований.

Все действо происходит в функциях-обработчиках прерываний, собранных в файле stm32f4xx_it.c (создается автоматически).

Обработка прерываний. Файл stm32f4xx_it.c
Обработка прерываний. Файл stm32f4xx_it.c

ТIM2 по окончании кадрового импульса гашения вызывает прерывание, меняем verflag, разрешая (if verflag ==1) по окончании строчного импульса гашения (TIM3) передачу SPI. Функция HAL_SPI_Transmit_DMA читает одну строку из области памяти массива fb[][]. Каждый следующий раз адрес сдвигается на значение VTOTAL (в байтах, длина "строки" плюс поля), то есть на одну строку. Когда выведены VID_VSIZE строк, verflag = 0, вывод запрещен до прохождения следующего кадрового гасящего импульса.

В main.c запустим таймеры, нужные каналы с разрешением вызова прерываний.

main.c
main.c

Ну и добавим функцию test заполнения массива fb[][] и очистки экрана:

main.c
main.c

Теперь сформируем видеовыход (Рисунок 4). Конечно, надо бы складывать сигналы на буферном каскаде, с возможностью регулировки уровней, да еще и убрать строчные синхроимпульсы с импульса кадровой развертки (можно программно формировать, но как-то я еще не домыслил, опыта не хватает в написании кода для контроллеров), да еще и добавить гальваническую развязку, но для первого опыта будем делать проще.

Рисунок 4. Схема подключения.
Рисунок 4. Схема подключения.

Результат на экране телевизора:

"Заливка" экрана.
"Заливка" экрана.

Видеосигнал:

Теперь добавим файлы из проекта Artekit, позволяющие выводить графические примитивы и текст.

Дерево файлов проекта
Дерево файлов проекта

gdi - вывод графических примитивов, mth - расчет синуса и косинуса для gdi (окружность), demo - демонстрация от artekit, вызывается в цикле while main.c (demoInit();). Код доработан для данного проекта.

Неожиданно тяжело оказалось камерой моего планшета качественно сфотографировать экран маленького телевизора, прошу прощения за не качественные фото. Результат работы demoInit():

Код проекта для скачивания (файл osc_tv_out1.zip).

О осциллографе написано в следующей статье.

Синусоида 100 кГц.
Синусоида 100 кГц.

Спасибо за проявленный интерес и Ваше терпение, время. Подписывайтесь, оставляйте комментарии - приятно видеть, что маленький труд автора замечен.