Помните как некто cnlohr запустил передачу ТВ сигнала на ESP8266?
Недавно мне попалось к просмотру это видео, стало интересно как это возможно и выяснил что автор видео разогнал частоту I2S до телевизионного диапазона, а затем с помощью DMA генерировал AM сигнал. Мне захотелось повторить это, но или прошивка криво собирается, или ESP модуль оказался неподходящий. Запустить передачу телесигнала не получалось.
Затем я вспомнил что STM32 умеет выводить свой тактовый сигнал на один из пинов.
Современные микроконтроллеры могут работать на частотах в сотни МГц, они попадают в диапазон работы FM приемников и аналоговых телевизоров. Практически все они имеют возможность вывода своей тактовой частоты на один из пинов. В микроконтроллерах STM32 эта функция называется Master Clock Output.
Если выбрать источником тактирования PLL, частоту на выходе можно менять в широких пределах. Так-же выход MCO можно включить и выключить простым переключением режима пина в регистре GPIO. Это побудило меня к экспериментам над возможностями формирования радиосигналов с помощью микроконтроллера.
В наличии была отладочная плата с микроконтроллером STM32F407. Его максимальная частота ядра равна 168МГц, а максимальная частота переключения GPIO равна 84мгц.
Для начала нужно было понять на какую частоту настраивать MCO чтобы его мог поймать телевизор. Поиск привел меня на страницу с таблицей частот всех тв каналов. Наибольшую частоту ядра без превышения максимальной частоты переключения GPIO можно достичь выбрав 3 канал.
Частота ядра при этом будет равна 154.5МГц, а тактовый выход необходимо поделить на 2, чтобы получить искомые 77.25МГц.
Чтобы долго не изучать мануалы, для генерации инициализационного кода был использован cubeMX. В нем настроил PLL на частоту 154.5МГц, вывод MCO1 сделал с источником от PLL предделителем на 2. К выводу PA8 подсоединил кусок провода.
Скомпилировал проект, залил прошивку в плату и экран на телевизоре стал темным. Это означает что телевизор понимает импровизированную несущую как сигнал.
Дальше было определение насколько быстро можно включать/выключать тактовый сигнал чтобы создать подобие амплитудной модуляции. В бесконечном цикле включая и выключая тактирование с плавным уменьшением задержек удалось получить сначала горизонтальные, а затем и вертикальные полосы на кинескопе.
Но как вывести на него изображение?
Если осуществлять управление с помощью ядра, практически всё его время будет использовано только для переключения тактового сигнала и любое прерывание будет сбивать тайминги. Поэтому единственным способом управления осталось использовние DMA с буфером.
Чтобы не тратить ресурсы ядра на запуск DMA каждый кадр, последнее можно настроить на работу в кольцевом режиме. Для того чтобы данные переносились со строго заданной скоростью, запуск передачи нужно осуществлять по событию от таймера.
В cubeMX это выглядит так:
В ходе эксперимента выяснилось что к регистрам GPIO возможен и побайтовый доступ как самим ядром, так и через DMA, что позволило тратить всего 1 байт на пиксель:
В начальной поставке библиотеки HAL, адресом назначения DMA является регистр таймера ARR. Пришлось написать функцию, позволяющую задать произвольный адрес назначения. Этим адресом является биты [16:23] регистра GPIOA->MODER.
Так-как DMA имеет 16 битный счетчик элементов, размер буфера ограничен в 64кб. Но можно настроить DMA на работу с двойным буфером, что позволяет увеличить количество элементов в 2 раза.
В стандарте PAL/SECAM видеосигнал имеет частоту кадров равную 25гц и 625 строк на кадр. В случае отказа от черезстрочной развертки остается 312 строк изображения с частотой полей 50гц. При максимальном размере буфера в 128кб получится: 131072/312 = 420 «пикселей» на строку.
Значит фреймбуфер получится размером 312x410.
Чтобы телевизор понял где находится начало кадра и начало каждой строки, необходимо формировать синхроимпульсы. Поскольку в эфире сигнал передается с негативной полярностью, эти импульсы будут соответствовать максимальной амплитуде сигнала. Поскольку управление сигнала возможно лишь дискретно, для создания уровня черного используется ШИМ.
Перед запуском передачи происходит заполнение фреймбуфера синхроимпульсами, а уровень черного обеспечивается заполнением ШИМ в 25%.
На этом этапе записью значений в frameBuf можно формировать изображение на экране телевизора.
Графическая библиотека была портирована из демо проекта от другой платы discovery. С ее помощью можно генерировать различные графические примитивы и символы. Так-же был портирован когда-то мной написанный клон игры Space Invaders и 3D шары из проекта от ESP8266.
Результат получился следующий:
По фотографиям экрана видны диагональные полосы, полученные в результате формирования «уровня черного».
Что если управлять тактовым выходом не просто включая и выключая его, а менять значение регистра OSPEEDR? Этим регистром управляется крутизна фронта переключения вывода. Было интересно, возможно ли меняя его значение создать на экране телевизора больше чем 2 градации яркости.
Написал код, который в бесконечном цикле перебирает 5 вариантов:
MCO выключен через MODER
OSPEEDR = 0
OSPEEDR = 1
OSPEEDR = 2
OSPEEDR = 3
На экране появилось 4 полосы с разной яркостью. Состояние с минимальной крутизной фронтов и выключенным совсем MCO никак не отличается для телевизора.
Использование регистра OSPEED вместо MODER позволило увеличить четкость изображения.
Также пытался использовать I2S, но безуспешно.
Выше примерно скорости 20 Мбит/с при дальнейшем увеличении частоты тактирования интерфейса, появляется нестабильность в работе. А на «стабильных» частотах если принимать гармоники сигнала, изображение едва отличимо от шума. SPI1 может работать на частотах выше, но качество сигнала тоже остается плохим.
Видео демонстрации работы прилагаю, код на гитхабе.
В STM32 в регистре RCC_CR есть биты HSITRIM, отвечающие за подстройку частоты HSI генератора.
Если настроить PLL со входом от HSI и выходной частотой, попадающей в FM диапазон, можно получить радиосигнал, который будет приниматься FM приемником. Модуляция осуществляется изменением значения битов HSITRIM.
Доказательство работоспособности показано в этом видео. Исходники тоже прилагаются.
p.s. Да, код ужасный, но как proof of concept сгодится
Недавно мне попалось к просмотру это видео, стало интересно как это возможно и выяснил что автор видео разогнал частоту I2S до телевизионного диапазона, а затем с помощью DMA генерировал AM сигнал. Мне захотелось повторить это, но или прошивка криво собирается, или ESP модуль оказался неподходящий. Запустить передачу телесигнала не получалось.
Затем я вспомнил что STM32 умеет выводить свой тактовый сигнал на один из пинов.
Введение
Современные микроконтроллеры могут работать на частотах в сотни МГц, они попадают в диапазон работы FM приемников и аналоговых телевизоров. Практически все они имеют возможность вывода своей тактовой частоты на один из пинов. В микроконтроллерах STM32 эта функция называется Master Clock Output.
Если выбрать источником тактирования PLL, частоту на выходе можно менять в широких пределах. Так-же выход MCO можно включить и выключить простым переключением режима пина в регистре GPIO. Это побудило меня к экспериментам над возможностями формирования радиосигналов с помощью микроконтроллера.
В наличии была отладочная плата с микроконтроллером STM32F407. Его максимальная частота ядра равна 168МГц, а максимальная частота переключения GPIO равна 84мгц.
Для начала нужно было понять на какую частоту настраивать MCO чтобы его мог поймать телевизор. Поиск привел меня на страницу с таблицей частот всех тв каналов. Наибольшую частоту ядра без превышения максимальной частоты переключения GPIO можно достичь выбрав 3 канал.
Частота ядра при этом будет равна 154.5МГц, а тактовый выход необходимо поделить на 2, чтобы получить искомые 77.25МГц.
Начало экспериментов
Чтобы долго не изучать мануалы, для генерации инициализационного кода был использован cubeMX. В нем настроил PLL на частоту 154.5МГц, вывод MCO1 сделал с источником от PLL предделителем на 2. К выводу PA8 подсоединил кусок провода.
Настройки тактирования в CubeMX
Скомпилировал проект, залил прошивку в плату и экран на телевизоре стал темным. Это означает что телевизор понимает импровизированную несущую как сигнал.
Дальше было определение насколько быстро можно включать/выключать тактовый сигнал чтобы создать подобие амплитудной модуляции. В бесконечном цикле включая и выключая тактирование с плавным уменьшением задержек удалось получить сначала горизонтальные, а затем и вертикальные полосы на кинескопе.
Результат программного управления MCO
Но как вывести на него изображение?
Использование DMA
Если осуществлять управление с помощью ядра, практически всё его время будет использовано только для переключения тактового сигнала и любое прерывание будет сбивать тайминги. Поэтому единственным способом управления осталось использовние DMA с буфером.
Чтобы не тратить ресурсы ядра на запуск DMA каждый кадр, последнее можно настроить на работу в кольцевом режиме. Для того чтобы данные переносились со строго заданной скоростью, запуск передачи нужно осуществлять по событию от таймера.
В cubeMX это выглядит так:
Настройки таймера
В ходе эксперимента выяснилось что к регистрам GPIO возможен и побайтовый доступ как самим ядром, так и через DMA, что позволило тратить всего 1 байт на пиксель:
#define GPIOA_MODER_8_11 (((uint8_t*)(&(GPIOA->MODER)))[2])
#define MCO_ON() (GPIOA_MODER_8_11) = 2
#define MCO_OFF() (GPIOA_MODER_8_11) = 0
В начальной поставке библиотеки HAL, адресом назначения DMA является регистр таймера ARR. Пришлось написать функцию, позволяющую задать произвольный адрес назначения. Этим адресом является биты [16:23] регистра GPIOA->MODER.
Так-как DMA имеет 16 битный счетчик элементов, размер буфера ограничен в 64кб. Но можно настроить DMA на работу с двойным буфером, что позволяет увеличить количество элементов в 2 раза.
// Изначальная функция, которая принимает в качестве аргумента лишь источник данных, а назначением является регистр TIM->ARR (регистр предзагрузки)
// HAL_StatusTypeDef HAL_TIM_Base_Start_DMA(TIM_HandleTypeDef *htim, uint32_t *pData, uint16_t Length);
// Добавлен аргумент - адрес назначения данных
HAL_StatusTypeDef HAL_TIM_Base_Start_DMA(TIM_HandleTypeDef *htim, uint32_t *pData, uint32_t *pDest, uint16_t Length);
// Запуск в режиме двойного буфера
HAL_StatusTypeDef HAL_TIM_Base_Start_DMA_DoubleBuffer(TIM_HandleTypeDef *htim, uint32_t *pData, uint32_t *pData2, uint32_t *pDest, uint16_t Length);
Формирование кадра
В стандарте PAL/SECAM видеосигнал имеет частоту кадров равную 25гц и 625 строк на кадр. В случае отказа от черезстрочной развертки остается 312 строк изображения с частотой полей 50гц. При максимальном размере буфера в 128кб получится: 131072/312 = 420 «пикселей» на строку.
Значит фреймбуфер получится размером 312x410.
Чтобы телевизор понял где находится начало кадра и начало каждой строки, необходимо формировать синхроимпульсы. Поскольку в эфире сигнал передается с негативной полярностью, эти импульсы будут соответствовать максимальной амплитуде сигнала. Поскольку управление сигнала возможно лишь дискретно, для создания уровня черного используется ШИМ.
Перед запуском передачи происходит заполнение фреймбуфера синхроимпульсами, а уровень черного обеспечивается заполнением ШИМ в 25%.
Заполнение буфера кадра
// заполнение синхроимпульсами
void init_fb()
{
// кадровый синхроимпульс находится в начале буфера,
// чтобы прерывание конца передачи DMA (Vsync) приходилось
// на начало обратного хода луча
for (int i=0;i<24;i++)
for (int j=0;j<(WIDTH);j++)
frameBuf[i][j] = 2;
// строчный синхроимпульс
for (int i=0;i<312;i++)
for (int j=(WIDTH - 30);j<(WIDTH);j++)
frameBuf[i][j] = 2;
}
// приведение буфера кадра к уровню черного
void clear_fb()
{
for (int i=24;i<312;i++)
{
// смещение начала строки
int offset = (i * WIDTH);
// ядро cortex-m4 позволяет работать с невыровненными данными,
// 32 битный доступ позволяет ускорить работу
uint32_t* data_ptr = (uint32_t*)(&((uint8_t*)(frameBuf))[offset]);
// диагональные линии менее заметны на экране чем вертикальные
uint32_t pattern = 0x02020202;
pattern &= ~( 2 << (((i)%4)*8) );
for (int j=0;j<(390);j+=4)
{
*(data_ptr++) = pattern;
}
}
}
На этом этапе записью значений в frameBuf можно формировать изображение на экране телевизора.
Графическая библиотека была портирована из демо проекта от другой платы discovery. С ее помощью можно генерировать различные графические примитивы и символы. Так-же был портирован когда-то мной написанный клон игры Space Invaders и 3D шары из проекта от ESP8266.
Результат получился следующий:
Полученный результат
По фотографиям экрана видны диагональные полосы, полученные в результате формирования «уровня черного».
Эксперименты с созданием промежуточных уровней сигнала
Что если управлять тактовым выходом не просто включая и выключая его, а менять значение регистра OSPEEDR? Этим регистром управляется крутизна фронта переключения вывода. Было интересно, возможно ли меняя его значение создать на экране телевизора больше чем 2 градации яркости.
Написал код, который в бесконечном цикле перебирает 5 вариантов:
MCO выключен через MODER
OSPEEDR = 0
OSPEEDR = 1
OSPEEDR = 2
OSPEEDR = 3
На экране появилось 4 полосы с разной яркостью. Состояние с минимальной крутизной фронтов и выключенным совсем MCO никак не отличается для телевизора.
Полосы с градациями яркости
Использование регистра OSPEED вместо MODER позволило увеличить четкость изображения.
Изображение при использовании модуляции с помощью OSPEEDR
Также пытался использовать I2S, но безуспешно.
Выше примерно скорости 20 Мбит/с при дальнейшем увеличении частоты тактирования интерфейса, появляется нестабильность в работе. А на «стабильных» частотах если принимать гармоники сигнала, изображение едва отличимо от шума. SPI1 может работать на частотах выше, но качество сигнала тоже остается плохим.
Видео демонстрации работы прилагаю, код на гитхабе.
Как насчет частотной модуляции?
В STM32 в регистре RCC_CR есть биты HSITRIM, отвечающие за подстройку частоты HSI генератора.
Если настроить PLL со входом от HSI и выходной частотой, попадающей в FM диапазон, можно получить радиосигнал, который будет приниматься FM приемником. Модуляция осуществляется изменением значения битов HSITRIM.
Доказательство работоспособности показано в этом видео. Исходники тоже прилагаются.
p.s. Да, код ужасный, но как proof of concept сгодится
agalakhov
Надо только LC ФНЧ или полосовой фильтр на выход добавить, иначе оно будет мусорить гармониками на чужих диапазонах.
rus084 Автор
Надо попробовать
Ато в ДМВ диапазоне штук 5 гармоник можно принять крутя ручку настройки.
Еще есть странность: ЖК телевизор ни в какую не принимает этот сигнал ни в каком диапазоне
Gengenid
Цифровые приемники тв сигнала намного чувствительнее к качеству модулятора.
Они даже с настоящими модуляторами, к примеру, старых игровых приставок, не всегда работают.
Vort123
Чётные и нечётные поля должны немного отличаться друг от друга в области вертикальных синхроимпульсов.
Это может быть критично для приёма сигнала ЖК телевизором.
Хорошее описание нужной последовательности импульсов есть в статье Batsocks — Monochrome Composite Video (на сайте Мартина есть неточности, тут картинки более правильные).
rus084 Автор
Возможно дело в том что я не генерирую уравнивающие импульсы в области кадрового синхроимпульса. В этой статье лучше описано как их формировать
Javian
Видимо ожидает частоту кадров равную не 25гц, а 50 Гц.
mistergrim
А где вы 25 Гц увидели?
Javian
В фразе "Формирование кадра
В стандарте PAL/SECAM видеосигнал имеет частоту кадров равную 25гц и 625 строк на кадр. ".
Насколько я помню у кадровых синхроимпульсов частота 50 Гц.
drWhy
Чересстрочная развёртка, два полукадра.
Javian
Телевизор ожидает импульсы 50 Гц. По сильному мерцанию «Видео демонстрации работы прилагаю» (на минимальной скорости воспроизведения Youtube видны искажения) создается впечатление, что телевизор выдает собственные синхроимпульсы, когда не получает настоящих и в этот момент экран черный.
rus084 Автор
Нет, сигнал генерируется непрерывно. Частота кадров если быть точным у меня равна 49,126Гц и не меняется.
Моменты когда на видео экран темнеет видны только на камеру. Это из-за того что кинескоп сильно мерцает и в 1 кадр видео может не попасть целый кадр телевизора.
На этом видео чуваки сняли телевизор на высокоскоростную камеру, где видно что в каждый момент времени светится только маленькая часть экрана
hidoba
Офтоп, но вспоминается программа, которая заставляла старые мониторы играть музыку через радиосигнал
www.erikyyy.de/tempest
xakep666
Прошу прощения за минус (промахнулся).
Через подобный эффект (https://ru.wikipedia.org/wiki/TEMPEST) можно не просто генерировать радиоволны, а иногда даже считывать то, что монитор показывает. Нам такие опыты демонстрировали в институте.
Выглядит это примерно вот так https://www.youtube.com/watch?v=V7DMUUNZSm4
romanetz_omsk
Неубедительно как-то, на расстоянии 20 сантиметров от ЭЛТ монитора, в котором сигналы усиливаются до сотен вольт. Хотел бы я посмотреть, как с 300 метров (за забором объекта) улавливать сигналы ЖК-монитора с разрешением 1920*1080, подключенного через hdmi. Только это из области ненаучной фантастики.
crustal
Логичней IMHO было бы не напрямую на микроконтроллере генерировать радиосигнал, а добавить хотя бы простой генератор синусоиды, частота которого управляется напряжением DAC микроконтроллера. Плюс управлять амплитудой генерируемого сигнала с помощью второго DAC.
Эх, кучу книжек по электронике прошлого века вспомнили, но пока никто еще не упомянул Айсберга с его серией "… — та це ж дуже просто!" (На русском было не достать в наших краях, а по украински шпрехал чуть более, чем никто, поэтому купить можно было) «Радио?.. Это очень просто!» в оригинале на французском переиздавалось, если помню правильно, более 50 раз. Здесь было бы уместно вспомнить его «Телевидение?.. Это очень просто!»
rus084 Автор
Весь смысл в том чтобы генерировать радиосигнал только средствами микроконтроллера без внешних генераторов.
Просто композитный сигнал — не интересно, его даже на pic16 можно сгенерировать
begin_end
Чтобы не нарушать концепцию «без внешних генераторов» можно было бы на одном и том же МК формировать композитный сигнал, выводить чистую тактовую-несущую и на 1-2 транзисторах замодулировать. Заодно это подусилит уровень да и фильтр можно будет повесить, чтоб без гармоник.
Мне кажется, что результат будет в разы лучше и по цветности и по разрешению.
Sdima1357
Можно и на второй гармонике попробовать передавать.
drWhy
Впечатляюще.
Фабрис Беллард сгенерировал сигнал DVB-T в 2005 году, но он использовал видеокарту ATI Radeon 9200SE, чей PLL позволял генерировать частоты вплоть до 400 МГц.
Уговорить мелкоконтроллер, конечно, сложнее.
R0SAB
Прикольный эксперимент.
Народ в комментах пишет про гармоники, но тут актуальнее проблема другая — при таком формировании сигнала большое количество мусора будет и в соседнем канале, из-за чего я бы постеснялся такое излучать и завел в телевизор только коаксиалом. Даже если формировать не радиосигнал, а гораздо более низкочастотный видео--.
Тоже экспериментировал с нищим формированием радиосигналов, но для коротковолновых целей и на ПЛИС.
Взял свой самый мелкий четвертый циклон, сделал в нем сигма-дельта ЦАП, который фапчей разогнал до 200..300 МГц клока и выход вывел на I/O, после которого RC фильтр.
Сделал два синтезатора синуса с клоком 50 МГц: один на 14 МГц, второй на 1 кГц. Перемножил их, получив т.о. положенный для измерения IMD3 двухтональный сигнал, и завел его в сигма-дельта ЦАП с входной разрядностью 10 бит.
Глянул осциллографом — красиво. Дал на спектроанализатор в виде RTL SDR — а там 55 дБ динамики по IMD3 и тоны чистенькие, аж на слух приятные.
Дальше — больше. В своем DDC трансивере за неимением параллельного звукового цапа и нежеланием вникать в сложные кодеки, сделал сигма-дельта АЦП для оцифровки микрофона, использовав в нем порог переключения логики в качестве компаратора (да и весь модулятор — просто инвертирующий D-триггер с интегратором на внешней RC цепи, а фильтр — просто счетчик).
В такой конфигурации при клоке 50 МГц и разрядности счетчика 10 бит получил частоту дискретизации 48 кГц и реальную динамику около 45 дБ. Этот АЦП прекрасно трудится в трансивере. Хорош предельной простотой и реализуемостью прямо в ПЛИС, без специальных микросхем.
Это я к чему подвожу:
Получается, что звуковой АЦП и ЦАП для работы на КВ можно сделать на одной лишь ПЛИС.
И, не используя при этом специальных микросхем АЦП и ЦАП, получить весьма добротный полностью цифровой передатчик, который удовлетворяет требованиям к любительской аппаратуре для работы на КВ.
Если нужно лезть выше — тут только формирование НЧ квадратурного сигнала с последующим преобразованием вверх. Иначе чистый спектр не получить.
И вот смотрю я на попытки сделать подобное на МК — понимаемо только если это из любопытства и спортивного интереса.
Для реальных же цифровых ВЧ вещей уже годятся только плис, с их идеальным реалтаймом, более высокими рабочими частотами и параллельностью вычислений.
Самый мелкий четвертый циклон от Альтеры на простейшей плате стoит баксов 25 на Али. А возможностей для качественной быстрой ЦОС несравнимо больше.
Попробуйте, не пожалеете.
drWhy
Существуют быстродействующие ЦАП прямого синтеза, лет пятнадцать назад один такой мог сформировать 1 ГГц полосы, т.е. сразу весь диапазон кабельного телевидения с лишком.
Вероятно, ПЛИС всё же дешевле и менее дефицитна.
Sun-ami
Можно попробовать сделать АМ, складывая сигнал с MCO и модулирующий сигнал, полученный на простейшем ЦАП из резисторов сопротивлением R, 2*R, 4*R, ..., подключенных к GPIO, на встроенном в МК операционнике, так, чтобы сигнал на выходе операционника зашкаливал за питание. Тогда высокочастотная составляющая сигнала будет промодулирована, а внешние элементы — только резисторы и конденсаторы.
Sdima1357
Вроде на 407 нет операционника?
Sun-ami
Да, на F407 нет операционников, это я вообще, в тему «как сгенерировать аналоговый ТВ сигнал с помощью STM32». Это можно попробовать сделать, к примеру, на STM32H750.
rus084 Автор
в F4 серии нету встроенного операционника.
Способ, который вы предложили создаст сигнал на выходе, больше похожий на ШИМ. Тогда лучше используя 3 потока DMA и 2 таймера получить полноценный ШИМ сигнал на выходе MCO.
Первый таймер синхронизирован со вторым. По переполнению первого, через DMA в регистр CCR второго загружается новое значение ШИМ. По переполнению второго таймера в GPIO загружается выключение MCO, по событию сравнения одного из каналов загружается включение MCO.
Sun-ami
Нет, мой способ не похож на ШИМ, он позволяет модулировать MCO с максимальной частотой для для GPIO+DMA, и получить при этом до 100 градаций яркости. А если перенастраивать DMA с двойной буферизацией на ходу, можно не ограничиваться 128Кпикселями. А еще можно подумать о том, чтобы вместо DMA+GPIO использовать с таким самодельным ЦАП встроенный контроллер дисплея STM32.
rus084 Автор
Да, вы правы, действительно модулирует
схема на falstad
Sun-ami
Да, я имел в виду такую схему и эффект. После неё хорошо бы ещё полосовой фильтр на втором встроенном операционнике поставить, чтобы убрать гармоники MCO и видеочастоты.
Sun-ami
У этих операционников частота единичного усиления маловата — у STM32H750 гарантируется лишь 4Мгц, типичная — 7,3МГц. Это значит, что для радиочастот коэффициент усиления может быть только очень низким — 0,03..0,05 на каскад. Но для прямого подключения к антенному входу телевизора этого хватит.
VT100
Настолько низким, что фильтр (если пытаться сделать активный) не будет работать. Да и вообще — вряд-ли встроенный ОУ будет работать хоть как-то ожидаемо...