Передача аналогового тв сигнала с помощью STM32

    Помните как некто 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 подсоединил кусок провода.

    Настройки тактирования в 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 сгодится
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 30

      0

      Надо только LC ФНЧ или полосовой фильтр на выход добавить, иначе оно будет мусорить гармониками на чужих диапазонах.

        0

        Надо попробовать
        Ато в ДМВ диапазоне штук 5 гармоник можно принять крутя ручку настройки.
        Еще есть странность: ЖК телевизор ни в какую не принимает этот сигнал ни в каком диапазоне

          0

          Цифровые приемники тв сигнала намного чувствительнее к качеству модулятора.
          Они даже с настоящими модуляторами, к примеру, старых игровых приставок, не всегда работают.

            +3

            Чётные и нечётные поля должны немного отличаться друг от друга в области вертикальных синхроимпульсов.
            Это может быть критично для приёма сигнала ЖК телевизором.
            Хорошее описание нужной последовательности импульсов есть в статье Batsocks — Monochrome Composite Video (на сайте Мартина есть неточности, тут картинки более правильные).

              0

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

              0
              Видимо ожидает частоту кадров равную не 25гц, а 50 Гц.
                0
                А где вы 25 Гц увидели?
                  0

                  В фразе "Формирование кадра


                  В стандарте PAL/SECAM видеосигнал имеет частоту кадров равную 25гц и 625 строк на кадр. ".
                  Насколько я помню у кадровых синхроимпульсов частота 50 Гц.

                    0
                    Чересстрочная развёртка, два полукадра.
                      0
                      Телевизор ожидает импульсы 50 Гц. По сильному мерцанию «Видео демонстрации работы прилагаю» (на минимальной скорости воспроизведения Youtube видны искажения) создается впечатление, что телевизор выдает собственные синхроимпульсы, когда не получает настоящих и в этот момент экран черный.
                        +1

                        Нет, сигнал генерируется непрерывно. Частота кадров если быть точным у меня равна 49,126Гц и не меняется.
                        Моменты когда на видео экран темнеет видны только на камеру. Это из-за того что кинескоп сильно мерцает и в 1 кадр видео может не попасть целый кадр телевизора.
                        На этом видео чуваки сняли телевизор на высокоскоростную камеру, где видно что в каждый момент времени светится только маленькая часть экрана


                        Анимация замедленной съемки кинескопа
            +2
            Офтоп, но вспоминается программа, которая заставляла старые мониторы играть музыку через радиосигнал
            www.erikyyy.de/tempest
              +1

              Прошу прощения за минус (промахнулся).
              Через подобный эффект (https://ru.wikipedia.org/wiki/TEMPEST) можно не просто генерировать радиоволны, а иногда даже считывать то, что монитор показывает. Нам такие опыты демонстрировали в институте.
              Выглядит это примерно вот так https://www.youtube.com/watch?v=V7DMUUNZSm4

                0

                Неубедительно как-то, на расстоянии 20 сантиметров от ЭЛТ монитора, в котором сигналы усиливаются до сотен вольт. Хотел бы я посмотреть, как с 300 метров (за забором объекта) улавливать сигналы ЖК-монитора с разрешением 1920*1080, подключенного через hdmi. Только это из области ненаучной фантастики.

              +1
              Логичней IMHO было бы не напрямую на микроконтроллере генерировать радиосигнал, а добавить хотя бы простой генератор синусоиды, частота которого управляется напряжением DAC микроконтроллера. Плюс управлять амплитудой генерируемого сигнала с помощью второго DAC.

              Эх, кучу книжек по электронике прошлого века вспомнили, но пока никто еще не упомянул Айсберга с его серией "… — та це ж дуже просто!" (На русском было не достать в наших краях, а по украински шпрехал чуть более, чем никто, поэтому купить можно было) «Радио?.. Это очень просто!» в оригинале на французском переиздавалось, если помню правильно, более 50 раз. Здесь было бы уместно вспомнить его «Телевидение?.. Это очень просто!»
                +3

                Весь смысл в том чтобы генерировать радиосигнал только средствами микроконтроллера без внешних генераторов.
                Просто композитный сигнал — не интересно, его даже на pic16 можно сгенерировать

                  +3
                  Чтобы не нарушать концепцию «без внешних генераторов» можно было бы на одном и том же МК формировать композитный сигнал, выводить чистую тактовую-несущую и на 1-2 транзисторах замодулировать. Заодно это подусилит уровень да и фильтр можно будет повесить, чтоб без гармоник.
                  Мне кажется, что результат будет в разы лучше и по цветности и по разрешению.
                0
                Можно и на второй гармонике попробовать передавать.
                  0
                  Впечатляюще.
                  Фабрис Беллард сгенерировал сигнал DVB-T в 2005 году, но он использовал видеокарту ATI Radeon 9200SE, чей PLL позволял генерировать частоты вплоть до 400 МГц.
                  Уговорить мелкоконтроллер, конечно, сложнее.
                    +5
                    Прикольный эксперимент.
                    Народ в комментах пишет про гармоники, но тут актуальнее проблема другая — при таком формировании сигнала большое количество мусора будет и в соседнем канале, из-за чего я бы постеснялся такое излучать и завел в телевизор только коаксиалом. Даже если формировать не радиосигнал, а гораздо более низкочастотный видео--.

                    Тоже экспериментировал с нищим формированием радиосигналов, но для коротковолновых целей и на ПЛИС.
                    Взял свой самый мелкий четвертый циклон, сделал в нем сигма-дельта ЦАП, который фапчей разогнал до 200..300 МГц клока и выход вывел на I/O, после которого RC фильтр.
                    Сделал два синтезатора синуса с клоком 50 МГц: один на 14 МГц, второй на 1 кГц. Перемножил их, получив т.о. положенный для измерения IMD3 двухтональный сигнал, и завел его в сигма-дельта ЦАП с входной разрядностью 10 бит.
                    Глянул осциллографом — красиво. Дал на спектроанализатор в виде RTL SDR — а там 55 дБ динамики по IMD3 и тоны чистенькие, аж на слух приятные.

                    Дальше — больше. В своем DDC трансивере за неимением параллельного звукового цапа и нежеланием вникать в сложные кодеки, сделал сигма-дельта АЦП для оцифровки микрофона, использовав в нем порог переключения логики в качестве компаратора (да и весь модулятор — просто инвертирующий D-триггер с интегратором на внешней RC цепи, а фильтр — просто счетчик).
                    В такой конфигурации при клоке 50 МГц и разрядности счетчика 10 бит получил частоту дискретизации 48 кГц и реальную динамику около 45 дБ. Этот АЦП прекрасно трудится в трансивере. Хорош предельной простотой и реализуемостью прямо в ПЛИС, без специальных микросхем.

                    Это я к чему подвожу:
                    Получается, что звуковой АЦП и ЦАП для работы на КВ можно сделать на одной лишь ПЛИС.
                    И, не используя при этом специальных микросхем АЦП и ЦАП, получить весьма добротный полностью цифровой передатчик, который удовлетворяет требованиям к любительской аппаратуре для работы на КВ.
                    Если нужно лезть выше — тут только формирование НЧ квадратурного сигнала с последующим преобразованием вверх. Иначе чистый спектр не получить.

                    И вот смотрю я на попытки сделать подобное на МК — понимаемо только если это из любопытства и спортивного интереса.
                    Для реальных же цифровых ВЧ вещей уже годятся только плис, с их идеальным реалтаймом, более высокими рабочими частотами и параллельностью вычислений.
                    Самый мелкий четвертый циклон от Альтеры на простейшей плате стóит баксов 25 на Али. А возможностей для качественной быстрой ЦОС несравнимо больше.
                    Попробуйте, не пожалеете.
                      0
                      Существуют быстродействующие ЦАП прямого синтеза, лет пятнадцать назад один такой мог сформировать 1 ГГц полосы, т.е. сразу весь диапазон кабельного телевидения с лишком.
                      Вероятно, ПЛИС всё же дешевле и менее дефицитна.
                      0
                      Можно попробовать сделать АМ, складывая сигнал с MCO и модулирующий сигнал, полученный на простейшем ЦАП из резисторов сопротивлением R, 2*R, 4*R, ..., подключенных к GPIO, на встроенном в МК операционнике, так, чтобы сигнал на выходе операционника зашкаливал за питание. Тогда высокочастотная составляющая сигнала будет промодулирована, а внешние элементы — только резисторы и конденсаторы.
                        0
                        Вроде на 407 нет операционника?
                          +2
                          Да, на F407 нет операционников, это я вообще, в тему «как сгенерировать аналоговый ТВ сигнал с помощью STM32». Это можно попробовать сделать, к примеру, на STM32H750.
                          0

                          в F4 серии нету встроенного операционника.
                          Способ, который вы предложили создаст сигнал на выходе, больше похожий на ШИМ. Тогда лучше используя 3 потока DMA и 2 таймера получить полноценный ШИМ сигнал на выходе MCO.
                          Первый таймер синхронизирован со вторым. По переполнению первого, через DMA в регистр CCR второго загружается новое значение ШИМ. По переполнению второго таймера в GPIO загружается выключение MCO, по событию сравнения одного из каналов загружается включение MCO.

                            +1
                            Нет, мой способ не похож на ШИМ, он позволяет модулировать MCO с максимальной частотой для для GPIO+DMA, и получить при этом до 100 градаций яркости. А если перенастраивать DMA с двойной буферизацией на ходу, можно не ограничиваться 128Кпикселями. А еще можно подумать о том, чтобы вместо DMA+GPIO использовать с таким самодельным ЦАП встроенный контроллер дисплея STM32.
                              0

                              Да, вы правы, действительно модулирует
                              схема на falstad


                              скрин схемы
                                0
                                Да, я имел в виду такую схему и эффект. После неё хорошо бы ещё полосовой фильтр на втором встроенном операционнике поставить, чтобы убрать гармоники MCO и видеочастоты.
                                  +1
                                  У этих операционников частота единичного усиления маловата — у STM32H750 гарантируется лишь 4Мгц, типичная — 7,3МГц. Это значит, что для радиочастот коэффициент усиления может быть только очень низким — 0,03..0,05 на каскад. Но для прямого подключения к антенному входу телевизора этого хватит.
                                    0

                                    Настолько низким, что фильтр (если пытаться сделать активный) не будет работать. Да и вообще — вряд-ли встроенный ОУ будет работать хоть как-то ожидаемо...

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                          Самое читаемое