FM радио передатчик из платы Марсоход2

7 мая (25 апреля по старому стилю) 1895 года на заседании Русского физико-химического общества российский физик Александр Степанович Попов выступил с докладом и демонстрацией созданного им первого в мире радиоприемника, осуществив первый сеанс радиосвязи. Началась новая эпоха: радиосвязь, радиовещание, радиолокация, позже телевещание и спутниковая связь. Завтра, 7 мая,  мы отмечаем день радио!

Плата Марсоход2 с проволочной антенной как FM радиопередатчик

Мы сделали свой радиопередатчик, конечно, с использованием ПЛИС Altera Cyclone III на нашей плате Марсоход2. Все, что нужно для этого проекта – это кусок провода, подключенный в качестве антенны к единственному пину IO6 платы.

На этом видео видно, как с помощью программы терминала TeraTerm аудиофайл WAV формата, 22050Гц, 8бит, моно передается через последовательный порт в плату Марсоход2, которая передает его в эфир используя частотную модуляцию на частоте 90МГц. Бытовой приемник улавливает и воспроизводит передаваемую мелодию. Мелодия ловится даже в автомобиле, стоящем во дворе.

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

Скажу честно, идея этого проекта не моя. Сначала я увидел статью на хабре: "Радиопередатчик из Raspberry Pi и куска провода". В этой статье была ссылка на страницу проекта и даже был какой-то исходный код на языке C, реализующий алгоритм передачи радиосигнала через один (!) GPIO выход микропроцессора.

Честно говоря, когда я прочитал ту статью, то сначала решил, что это какая-то первоапрельская шутка. Однако, дата статьи была не 1-е апреля. Да и видеодемонстрация была вполне впечатляющая. К сожалению, на странице проекта не было никакой особой технической информации, чтобы понять как же это работает. Конечно, есть программа на С, но разобраться в ней очень не просто, так как там очень много специфических обращений к каким-то непонятным портам ввода-вывода, DMA, таймеру. Нужна подробная тех. документация именно на чип процессора, используемого в Raspberry Pi. В общем, порассматривал я тот Си-шный код, ничего толком не понял, да и решил делать все сам, как смогу.

В принципе, идея такая. Полезный сигнал из звукового файла нужно промодулировать на некоторой несущей частоте, «излучить» проволочной антенной, подключенной прямо к одному из выходов ПЛИС и на некотором расстоянии принять звуковой сигнал обычным бытовым радиоприемником. Тип модуляции – FM (frequency modulation), частотная модуляция. Частота передачи – где-то от 88Мгц или выше, там где начинаются собственно обычное эфирное радиовещание FM. К сожалению, я не знаю точных параметров этого вещания, не знаю ни занимаемой полосы частот, ни интервала между каналами передачи, ни способа кодирования стерео сигнала и еще не знаю много чего. Что-то нужно искать в интернете, что-то буду просто пробовать и смотреть получается или нет.

Тут нужно сделать одно замечание по поводу качества излучаемого сигнала. Я использую ПЛИС, которая по своей природе является цифровой микросхемой. Значит на выходе ПЛИС, через который я буду выдавать сигнал на проволочную антенну может быть только импульсный сигнал. В предыдущей статье я показал, что спектр прямоугольного периодического сигнала – это набор нечетных гармоник, убывающей амплитуды. Это значит, что теоретически наш передатчик будет передавать одновременно в нескольких диапазонах. Если несущая сигнала будет, например, 100Мгц, то второй, более слабый канал будет на 300Мгц, еще более слабый канал будет на 500Мгц и так далее. Исправить ситуацию наверное мог бы какой-то внешний аналоговый фильтр, но я его специально делать не буду. Все равно его качество оценить будет не просто, а может даже и невозможно в наших условиях. К тому же, у меня есть смягчающее обстоятельство – сама антенна, хоть я и хочу использовать просто кусок провода, уже является довольно избирательным излучателем, а значит и своего рода фильтром. Например, если я хочу вести передачу на частоте 100Мгц, то длина волны будет 300тыс.км/c (скорость света) деленая на 100МГц ~ примерно 3 метра. Четвертьволновый излучатель получается примерно 75 сантиметров. Вот такой кусок провода для 100Мгц передатчика будет наиболее эффективным. Остальные высшие гармоники мало того, что они сами по себе слабее, так еще и антенна для них не подходящая, они будут хуже излучаться.

Итак, значит наша задача – это сделать управляемый генератор пусть и прямоугольных импульсов, но с плавающей частотой. Нам нужна частотная модуляция. Частота выходного генератора управляется в небольших пределах модулирующим звуковым сигналом. Например, решаем, что передавать будем на частоте 100МГц. Пусть это будет нижняя частота передачи, а максимальная частота пусть будет где-то 100,05МГц. Разница от минимальной до максимальной частоты всего 50КГц. Как такое можно вообще синтезировать? Как сделать такой цифровой генератор?

Я потратил безрезультатно несколько дней, пытаясь сделать какой-то работающий прототип. Перепробовал множество разных технологий и методов. Никак не мог понять, как же мне промодулировать несущую частоту полезным сигналом и как мне менять частоту выходного генератора. Пытался перестраивать внутренний PLL ПЛИС, пытался синтезировать модулированный сигнал на относительно низкой частоте, а потом умножать его в PLL. Ничего не получалось. В какой-то момент времени ситуация стала напоминать мне ту, что описана в фантастическом рассказе Раймонда Джоунса, «Уровень шума». Там ученым продемонстрировали фильм об антигравитационном устройстве якобы созданном одним человеком. Этот человек продемонстрировал работу летающего устройства, но оно взорвалось вместе с изобретателем. Теперь ученым нужно заново изобрести эту «антигравицапу». Вот и мне тогда стало казаться, что мне нужно сделать что-то невозможное.

Как говорится, «ларчик просто открывался». Решение оказалось довольно простым. Нужно взять какую-то довольно высокую частоту, например, 500МГц и потом делить ее на 5, чтобы получить 100МГц, или делить на дробный делитель, скажем, на 4,9998, чтобы получить близкую к 100МГц, но все же несколько другую частоту: 100004000Гц. То есть нужно сделать дробное деление частоты.

Ну на 5 делить довольно просто, счетчиком. А вот как делить на дробный делитель? На самом деле цифровыми методами мы можем сделать только иммитацию дробного деления. Скажем так: будем делить исходную высокую частоту 500МГц почти всегда на 5, но иногда, редко, на 4. Тогда средний поток импульсов при вставке «деление на 4» будет повышаться. Чем больше вставочек «деление на 4», тем выше в среднем выходная частота. Вот иллюстрация такого метода деления:

метод цифрового деления частоты
В формуле M – это количество последовательных делений на 5 перед вставочкой «деление на 4». Для изображенной на рисунке последовательности коэффициент деления получается Q = 19/4 = 4,75.

Понятно, что если взять за исходную частоту не 500МГц, а 1000Гц и делать деление почти всегда на 10 и только иногда на 9, то «качество» или точность выходной последовательности можно будет вычислить гораздо лучше. Однако, и 500МГц делить в ПЛИС не очень просто. Сказать по правде я до сих пор не понимаю, как это сделано в том проекте для Raqspberry Pi. Ведь 500МГц – это на самом деле очень высокая частота. Не так-то просто ее разделить даже на 5.

Если вернуться к написанной выше формуле, то из нее получается мы можем рассчитать все необходимые нам коэффициенты M для разных излучаемых частот. Например, упрощая ситуацию, считаем, что мы будем передавать восьмибитный звук, моно. Пусть занимаемый нами диапазон частот будет от 100МГц до 100050000Гц – то есть полоса частот 50КГц. Делим весь интервал 50КГц на 256 отрезков. Вычисляем все 256 частот и соответствующие им коэффициенты M. Для этого вычисления я написал программу на Си:


#include "stdafx.h"

#define BASE_FREQ 500000000
#define CARRIER 100000000
#define BAND 50000

int _tmain(int argc, _TCHAR* argv[])
{
    double freq_wish;
    double divider;
    double m;

    printf("WIDTH = 16;\n");
    printf("DEPTH = %d;\n",256);
    printf("ADDRESS_RADIX = HEX;\n");
    printf("DATA_RADIX = HEX;\n");
    printf("CONTENT BEGIN\n");

    for(int i=0; i<256; i++)
    {
        freq_wish = CARRIER + BAND*i/256;
        divider = BASE_FREQ / freq_wish;
        m=(divider-4)/(5-divider);
        //printf("freq %f div %f %f %d\n",freq_wish,divider,m,(int)(m+0.5));
        printf("%04X: %04X;\n",i,(int)(m+0.5));
    }

    printf("END\n");

    return 0;
}


Эта программа сразу напечатает текстовый файл в формате *.MIF (Memory Initialization File). В среде проектирования  Altera Quartus II мы сможем создать блок постоянной памяти ROM, который будет инициализирован этими табличными значениями. Таким образом, получая из звукового файла байт, мы для него по таблице сразу получим коэффициент M. Коэффициент M показывает сколько раз нужно делить 500МГц на 5, чтобы потом один раз разделить на 4.

Собственно переменный делитель – это главная часть нашего устройства. Расскажу о нем подробнее. Не знаю, не перемудрил ли я здесь, но надеюсь, что мое техническое решение оказалось правильным.

Смотрите, вот простой делитель на 5, написанный на Verilog HDL:


reg [2:0] cnt;

always @(posedge clk)
  if(cnt==4)
     cnt <= 0;
  else
    cnt <= cnt + 1;


 Когда этот код будет синтезирован в схему для ПЛИС получится 3 регистра на входе данных которых будет комбинаторная логика. Вот что показывает Altera Netlist Viewer:

цифровой делитель частоты на 5
Быстродействие такого счетчика будет зависить от быстродействия комбинаторной логики на входе триггеров. Хотелось бы сделать счетчик максимально надежным, работающим на высокой частоте и поменьше ограничений со стороны быстродействия логических функций. Как бы уменьшить их влияние?

Я решил, что более быстродействующий вариант делителя получится если соединить в кольцо 5 триггеров и по сигналу сброса только в один из них записать единицу, а в остальные записать нули. Вот так:


reg r0,r1,r2,r3,r4;

always @(posedge clk or negedge nreset)
   if(~nreset)
    begin
        r0 <= 1;
        r1 <= 0;
        r2 <= 0;
        r3 <= 0;
        r4 <= 0;
    end
    else
    begin
        r0 <= r1;
        r1 <= r2;
        r2 <= r3;
        r3 <= r4;
        r4 <= r0;
    end

assign out = r0;


 Эквивалентная схема получится вот такой:

цифровой делитель частоты на 5
Почувствуйте разницу, здесь нет ни одной комбинаторной функции, только регистры. Единственная записанная в триггерах логическая единица будет вращаться по кольцу с каждым тактом делимой частоты. Я думаю и надеюсь такой делитель на 5 сможет работать на очень высокой частоте.

Конечно, не все так просто. Ведь нужно не всегда делить на 5. Иногда нужно делить на четыре. Иногда кольцо придется временно «укорачивать» исключая регистр r4 из кольца. Так что логические функции, может какие-то мультиплексоры, в нашей схеме все равно появятся, но хотя бы их будет поменьше.

В итоге, модуль делителя на языке Verilog HDL получился вот такой:


module divider(
    input wire clk,
    input wire nreset,
    input wire [15:0]in_factor,
    output wire nclk
);

reg r0, r1, r2, r3, r4;

reg [15:0]factor=0;
reg [15:0]fcnt;
reg short;
reg short_f;

always @(posedge clk or negedge nreset)
begin
    if(~nreset)
        begin
            r0 <= 0;
            r1 <= 0;
            r2 <= 0;
            r3 <= 0;
            r4 <= 1;
        end
    else
        begin
            r0 <= r1;
            r1 <= r2;
            r2 <= r3;
            if(short)
                r3 <= r0;
            else
                r3 <= r4;
            if(~short)
                r4 <= r0;
        end
end

assign nclk = r0;

always @(posedge clk or negedge nreset)
    if(~nreset)
    begin
        fcnt <= 0;
        short <= 0;
        short_f <= 0;
    end
    else
    begin
        if(r0)
            fcnt <= short ? 0 : fcnt +1;
        if(r2)
            short <= (fcnt == factor);
            
        if(r0)
            short_f <= short;
        
        if(r0 & short_f)
            factor <= in_factor;
    end

endmodule


Здесь входной сигнал in_factor – это и есть количество последовательных делений на 5, следующее деление будет на 4. Сигнал short – как раз маркирует исключительную ситуацию – деление на 4 вместо деления на 5.

Чтобы посмотреть детально как же этот модуль работает можно написать тестбенч для модуля на языке Verilog HDL.


`timescale 1ns / 1ps

module testbench;

reg clk500;
initial clk500 = 1'b0;
always
    #1 clk500 = ~clk500;

wire nclk;
reg  nreset;

divider div(
    .clk(clk500),
    .nreset(nreset),
    .in_factor(3),
    .nclk(nclk)
);

initial
begin

    $dumpfile("out.vcd");
    $dumpvars(0,testbench);
    nreset = 0;
    #10;
    nreset = 1;
    #1000;
    $finish;
end

endmodule


Я симулирую с помощью opensource пакета Icarus Verilog и смотрю на сигналы в программе GtkWave.

симуляция проекта Verilog, просмотр сигналов в GtkWave

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

А вот вообще весь процесс частотной модуляции можно посмотреть, если написать другой тестбенч. Полный текст тестбенча tb2.v lздесь приводить не буду - он есть в составе проекта, который можно будет взять на нашем сайте. В этом тестбенче будем синтезировать звук в виде пилы. Частота выборок в оцифрованном звуковом сигнале у нас будет 22050Гц, что является стандартом для PCM WAV файлов.

Симуляция проекта verilog для FM radio

Здесь видно, что вместе с повышением уровня модулирующего звукового сигнала плотность импульсов сигнала short растет.  То есть растет количество вставок «деление на 4» в общий процесс обычного деления частоты на 5.

Если же посмотреть подробнее временную диаграмму, то окажется, что на один период звуковой частоты может приходиться всего несколько вставочек short:

Симуляция проекта верилог для FM radio

Теперь расскажу про более простую часть нашего проекта – приемник последовательного порта. На плате Марсоход2 установлена двухканальная микросхема FTDI. Первый канал используется для JTAG, для загрузки проекта в плату. Второй канал можно использовать для приема или передачи через последовательный порт. Оба интерфейса и JTAG и COM-порт являются виртуальными, реализованными поверх физического USB2.0 соединения. Впрочем, это все делается в основном средствами самой микросхемы FTDI.

Наша задача – использовать последовательный порт.
Приемник последовательного порта я уже много раз делал для других наших проектов. Вставил уже готовый модуль из какого-то старого проекта, что-то подправил, в нем, установил скорость приема 230400 бит в секунду. Это одна из стандартных скоростей передачи для последовательных портов. Почему выбрал ее?

Очень просто. Байт при простой последовательной передаче выдается как последовательность: старт бит, потом последовательно 8 бит данных младшими вперед, затем стоп бит. Итого 10 бит на байт. Значит скорость потока получится 23040байт/сек. Это почти настоящая стандартная скорость в WAV файлах. Правильная частота сэмплирования звукового файла 22050Гц. Ну бывает еще 44100Гц, но я такие файлы не беру в расчет, мне они не подходят.

Если запрограммировать последовательный порт на передачу с двумя стоп битами, то на один байт будет приходиться уже 11 бит. В этом случае скорость потока получится 230400/11=20945Гц. Ну тоже почти подходит. Обычный человек на слух врядли различит повышение или понижение на пару тонов. Если же вам принципиально важно сделать звуковой поток с нормальной скоростью, то переконвертируйте ваш аудио файл в нужную частоту. Один из самых удобных редакторов аудио файлов – audacity. Эта программа позволяет конвертировать форматы аудио, делать моно из стерео и много других полезных функций.

Хорошо, что микросхема FTDI имеет внутри FIFO. Несмотря на пакетную передачу данных по шине USB данные из последовательного порта микросхемы приходят в ПЛИС равномерным потоком с постоянной скоростью, что очень важно для нашего проекта.

Итак, top модуль проекта в среде Altera Quartus II будет выглядеть вот так:

Схема проекта Altera Quartus II "FM радио" в ПЛИС платы Марсоход2

Модуль PLL создает 2 частоты:
- 100МГц для приемника последовательного порта и таблицы коэффициентов.  
- 450Мгц для радиопередатчика. Да, сначала я хотел делать свою трансляцию на 100МГц, для этого мне нужна была бы частота 500МГц и потом ее нужно было бы делить на 5. Но почему-то на 500МГц сразу не заработало, я понизил частоту проекта до 450МГц и теперь трансляция идет на 90МГц – ну то же FM диапазон. Обрадованный работающим прототипом уже не стал пытаться поднять частоту, но думаю это возможно.
 
Модуль serial, написан на Verilog, принимает байты из последовательного порта на скорости 230400 бит в секунду.

Модуль tbl_rom преобразует по таблице байт принятый из ком порта в другое, зараннее рассчитанное значение коэффициента деления (точнее число последовательных делений на 5, после которых идет одно деление на 4).

Модуль divider, написан на Verilog. Он собственно выполняет частотную модуляцию несущей. Делит 450МГц на 5 или другой дробный делитель.

Еще одно важное замечание. После загрузки проекта в плату нажмите на плате кнопочку KEY0 – она выполняет важный сброс схемы делителя. Когда схема успешно запустилась радиоприемник находит несущую частоту 90МГц и перестает шуметь.

После этого программой TeraTerm открываете последовательный порт на скорости 230400 бод и посылаете аудио файл формата WAV, 8 бит, моно. Радио приемник начинает воспроизводить мелодию.

Дальность действия передатчика совсем не большая.

car radio 90МГц

Как видно из демонстрационного видео в автомобиле в 15 метрах от передатчика музыка еще играет. Но стоит отъехать чуть дальше и она становится все тише и потом совсем замолкает.

Весь проект Altera Quartus II для платы Мерсоход2 можно взять на нашем сайте в разделе загрузки или прямо здесь:

В проект включен и демонстрационный звуковой файл и программа на C для MS Visual Studio для генерации таблицы коэффициентов.

 


Добавить комментарий