ITСooky

IT-рецепты съедобные и не очень!

Делаем что-нибудь на FPGA(ПЛИС) на Mojo V3 с SPARTAN-6 (наводящеюся на звук платформу)

дата 10.03.2019

Если коротко то не покупайте Mojo V3(нет, плата мне понравилась, но вот её создатели и производитель чипа…) и не связывайтесь с FPGA от компании XILINX(странная контора это вот точно)! Далее подробно!

Чувствую силу большую я, Верилог только надо учить!


FPGA актив(экстрим)исты предрекают смерть Arduino, и заменой её на FPGA — фанатики, неадекватные люди. Почему FPGA никогда не заменит Arduino — цена, сложность программирования, технически характеристики. Что не так с платой Mojo V3, ну создатели alchitry.com собирали на неё деньги через кикстатер и сейчас бросили её, на сайте её уже не продают. Но её подобрали китайцы, есть на Aliexpress, хватанул за 2245 RUR, китаец видимо ошибся на странице где я купил поставил другой товар, а плату теперь продает за 2536 RUR, а на официальном сайте она стоила 5250 RUR. На Aliexpress так же можно найти шильды для неё.

ОСТОРОЖНО: Плата не выдерживает постоянной эксплуатации, через несколько дней начинает глючить. Что-то с питанием, у меня она всегда подключена к usb писи, последний раз глюкнуло так что комп не грузился, пока не отключил её от usb — так бывает когда usb коротит однако

Так вот создатели сейчас продвигают новую плату, с еще более конским ценником, иии им этого показалось мало они еще и разъемы к ней прикрутили к которым ничего не подключить, хочешь обычных разъемов покупай шильд и паяй туда обычные… ну странные…


Не смотря на то что Mojo V3 начинался на Кикстатере это нифига не опенсурс проект. Платить правда ничего не нужно, если у вас обычный проект (есть какая то плата за лицензию на IP ядра, вообще не понял к чему это). Но надо зарегистрироваться на сайте www.xilinx.com указать свои данные, там просят юридический адрес вашей компании в США и КАНАДЕ это нужно чтобы получить одобрение на скачивание ПО для FPGA Spartan-6 от U.S. Government Export. Указал свой домашний адрес, наверное важно чтобы он совпадал примерно с IP географической привязкой. Пока ждал 10 дней одобрения, написал создателям Mojo, они сказали что если я из Северной Кореи или Ирана то могут и не дать… я не в курсе так-то, надо купить журнал Корея, если все идет по плану мы в этой компании уже должны быть. Одобрили скачку, одобрили лицензию, скачал 6 гигабайт, установил 20 гигабайт, активировал по интернету лицензию, без всего этого Mojo V3 работать не будет.

На плате установлен FPGA Spartan-6 XC6SLX9 и Arduino’вский микроконтролер, он нужен чтобы хранить код, FPGA для этого не приспособлен, при первом включении ардуино в него этот код записывается. В XC6SLX9 9000 штук логический ячеек и 11000 тригеров(это много), из них все строится это хорошо, вот спецификация www.xilinx.com/support/documentation/data_sheets/ds160.pdf. Рабочая температура 0 до 85, я у одного профессора спросил, на ютюбе, это значит что за этими пределами чип начнет работать в разы медленнее, вроде и не страшно но зимой уже таймеры не так работать будут видимо.

Альтернативы есть. Я думаю надо смотреть чтобы — плата была популярной, чтобы для нее были примеры. Mojo V3 не взлетела, любительский мануалов нет, на сайте есть мануалы, но половина уже не работает(и работала ли), форум где можно было спросить заблокирован в России(не специально конечно, а по не компетентности органа), но это ни чего не меняет потому что на форуме никто не отвечает. Так же надо смотреть чтобы легко было с ней работать, прошивать, обычно нужна еще одна коробочка для прошивания.

Вот испанский настоящий опенсурс проект alhambrabits.com. Плата повторяет разъемы Arduino, много примеров на испанском, но ценник тоже конский, и китайцы не копируют. И создатели продвигают способ программирования графический, для детей хорошо, а реального боевого опыта это не дает!

Или вот отечественные разрабы, почти голый FPGA но с примерами и поддержкой, есть даже для малинки шильд marsohod.org, цены российский кхэм.

Еще на али есть вот такой монстр например ALTERA-Cyclone-IV-EP4CE6-FPGA-Development-Kit-Altera-EP4CE-NIOSII-FPGA-Board-and-USB-Blaster-downloader к этой плате все интересное оборудование уже припаяно, нужно только добавить программатор еще, ну и выходит раза в два три дороже чем китайски моджо! Но интересная, но мануалов нет!

Так вот FPGA сейчас это чистое хобби, прекрасно укладывается в понятие о хоббе — слишком дорого, непрактично, и вообщем-то не нужно уже(или еще). Для тех кто перестал получать мазохистское удовольствие от Arduino, да помигать лампочку в FPGA это совсем другая новая боль! Но есть один плюс — если Arduino это все-таки адаптированная, любительская платформа, то на FPGA все изначально надо делать по взрослому, на языках которые стали отраслевыми стандартами. Например на Verilog!

Мигаем лампочкой на Verilog
Должно быть установлено ISE Design Suite по это инструкции alchitry.com/pages/installing-ise
И Mojo IDE по этой инструкции alchitry.com/pages/mojo-ide

Кстати я делал не так как все (у двоих видел) те кто знает ПЛИС мигают через костыль с двоичными числами, ну вот нравится им так, я же честно отсчитываю одну секунду и тогда мигаю!

Создаем проект File>New Project в новом окне ставим галочку Verilog, а то разрабы свой язык продвигают Lucid везде по умолчанию его пихают.

Тут два файла в разделе Source с основным кодом и в разделе Constraints с описанием пинов. Сверху основые иконки: молоток — скомпилировать код, долго и если есть ошибки то они совсем не информативные, пустая стрелка вниз — записать в Mojo временно код, полная стрелка вниз — записать код в постоянную память ардуино, при включении это код скинется в FPGA. Так же важно посматривать в Settings>Serial Port там надо проверять чтобы писал в порт к которому подключена Mojo.

Собираю следующею схему

Зеленый светодиод это дань уважения разрабам Mojo, так же китайцы скопировали почти все(светодиод done не зеленый, а красный почему то — это кстати чисто китайское там красный это статус включено ок) но текст сделали на плате не зеленым, а белым.

Добавляем в разделе Constraints в файл mojo.ucf строку

NET "pin<0>" LOC = P1 | IOSTANDARD = LVTTL;

Это задействует пин 1 к которому подключен светодиод через сопротивление 220 Ом.

Хотим чтобы лампочка мигала раз в секунду. Частота моего Spartan-6 50Mhz, так что чтобы уловить секунду надо досчитать до 50 000 000, это значение надо положить в переменную в которое оно влезет. Чтобы хранить число 50 миллионов надо 26 бит (это будет переменная cnt). Еще одна переменная будет хранит состояние выкл вкл светодиоде, это уменьшаться в один бит (это будет переменная state), один бит и дадим — надо экономить ячейки FPGA. Цикл IF в Verilog, это не то что везде — например там не работает assign, там нельзя сделать подряд если это то а то это(или я пока не понял как это сделать). Все переменные чтоли булевые в Verilog, то есть чтобы state изменить я использую оператор ~ он меняет на обратное значени для бита это 0 на 1 и тп.

Вот весь код который будет в разделе Source в файле mojo_top.v

module mojo_top(
    // 50MHz clock input
    input clk,
    // Input from reset button (active low)
    input rst_n,
    // cclk input from AVR, high when AVR is ready
    input cclk,
    // Outputs to the 8 onboard LEDs
    output[7:0]led,
    //LED pin
    output[0:0]pin,
    // AVR SPI connections
    output spi_miso,
    input spi_ss,
    input spi_mosi,
    input spi_sck,
    // AVR ADC channel select
    output [3:0] spi_channel,
    // Serial connections
    input avr_tx, // AVR Tx => FPGA Rx
    output avr_rx, // AVR Rx => FPGA Tx
    input avr_rx_busy // AVR Rx buffer full
    );

reg [25:0] cnt;
reg [0:0] state;

wire rst = ~rst_n; // make reset active high

// these signals should be high-z when not used
assign spi_miso = 1'bz;
assign avr_rx = 1'bz;
assign spi_channel = 4'bzzzz;
assign led = 8'b0;

always @(posedge clk)
if (cnt == 50000000)
begin
state <= ~state;
cnt <= 0;
end
else
begin
cnt <= cnt + 1;
end

assign pin = state;


endmodule

Здесь все по умолчанию кроме строк 11,25-26,36-47. Компилироваем, записываем иии мигает! Многие на ютюбе на этом останавливается(это к тому что Mojo не взлетело), но мы пойдем дальше!

Тактовая частота SPARTAN 50 мега герц. Это значит что 50 миллинов раз в секунду он что-то делает, не совсем так, то есть он то делает, но это не значит что что-то не может происходить в течении или между одной пятидесяти миллионной секунды.

Когда мы тут делаем цикл

always @(posedge clk)

мы специально этот цикл привязываем к тактовой частоте clk и говорим еще делать его на восходящем сигнале posedge, ну так как тактовая частота, это такт, вверх промежуток, вниз промежуток и опять… интересно а если задать еще один такой цикл и делать его нисходящем negedge это получается в два раза увеличится тактовая частота FPGA по выполнению операций…

Так вот за гранью одной пятидесяти миллионной секунды тоже что-то происходит, например мы нажимаем кнопку и до того как её обработает на следующем такте кто-нибудь возникает что-то что может даже не уложится в 1 или 0, да в самом начале после 1 может и ноль еще проскачит — это кажется называется дребезжанием. Осмелюсь прикинуть если скорость электрона (а ток это движение электронов от плюса к минусу на схемах, хотя на самом деле они бегут от минуса плюсу по физике) близка к скорости света 300 000 км в секунду, то в 1/50М он пробегает 6 метров, а SPARTAN выполнен по нормам 45 нанометров, то одну ячейку за 1/50М секунды могло пробежать 275 миллиардов раз, неустановленное количество электронов. И это реально так… вот в коде, нажатие на кнопку описано как

wire rst = ~rst_n; // make reset active high

слово wire значит что он напрямую подключен к кнопке и при нажатие будет значение бежать вот 275 миллиардов раз в 1/50M, скорей всего к следующему такту все устаканится, а что если нет, придется надеятся на следующий

Но если бы мы это значение задали как(другое)

reg [0:0] state;

То оно бы уже было привязано к тактовой частое и задалаваось один раз в 1/50M… по сути это вообще не важно… но интересно, и этому есть даже название триггеры тут и цикл триггер, и регистровые значения

А также когда мы присваиваем значение <= то это асинхронный способ мы никого не ждем, а если = то ждем не идем дальше пока значение не присвоится…

Кнопка для счета до 255 с отображением в двоичном виде

Как это будет работать — нажимаем кнопку, мигает светодиод и число увеличивается на 1. Число отображается 8-ю встроенным светодиодами. Так как светодиодов 8 то число 8-ми битное значит не может быть больше 255. Кнопка rst обнуляет счет.

Собираю такую схему

Вот так это выглядит

Проблема кнопки в том что нажатий фиксируется миллионы в секунду, и могут проскакивать отжатия — так что чтобы уловить нажатие человеческим пальцем — считаем нажатия, потом считаем отжатия. Если отжатий больше 5000000 значит кнопка точно уже не нажата, если нажатий больше 1000000 значит точно было нажатие, обнуляем все значения.

Что понравилось в программирование Mojo (или это заслуга Verilog) пины можно группировать под одним именем и в случае со светодиодами просто на led посылать цифру и они загорятся визуализируя цифру в двоичном исчисление.

Добавляем в разделе Constraints в файл mojo.ucf строку

NET "pin<0>" LOC = P1 | IOSTANDARD = LVTTL;
NET "button" LOC = P2 | IOSTANDARD = LVCMOS33;

Вот весь код который будет в разделе Source в файле mojo_top.v

module mojo_top(
    // 50MHz clock input
    input clk,
    // Input from reset button (active low)
    input rst_n,
    // cclk input from AVR, high when AVR is ready
    input cclk,
    // Outputs to the 8 onboard LEDs
    output[7:0]led,
    //LED
    output[0:0]pin,
    // Button
    input button,
    // AVR SPI connections
    output spi_miso,
    input spi_ss,
    input spi_mosi,
    input spi_sck,
    // AVR ADC channel select
    output [3:0] spi_channel,
    // Serial connections
    input avr_tx, // AVR Tx => FPGA Rx
    output avr_rx, // AVR Rx => FPGA Tx
    input avr_rx_busy // AVR Rx buffer full
        );

reg [25:0] presson;
reg [25:0] pressoff;
reg [7:0] cnt;
reg [0:0] state;

wire rst = ~rst_n; // make reset active high

// these signals should be high-z when not used
assign spi_miso = 1'bz;
assign avr_rx = 1'bz;
assign spi_channel = 4'bzzzz;

always @(posedge clk)
if (button == 0)
begin
pressoff <= pressoff + 1;
state <= 0;
if (rst == 1)
begin
cnt <= 0;
end
if (pressoff == 5000000)
begin
pressoff <= 0;
if (presson > 1000000)
begin
presson <= 0;
cnt <= cnt + 1;
if (cnt == 256)
begin
cnt <= 0;
end
end
end
end
else
begin
presson <= presson + 1;
state <= 1;
end


assign pin = state;
assign led = cnt;

endmodule

Цикл if требует особого обращения, не понял как сделать подряд несколько if, но можно сделать один if а в него засунуть уже несколько.

Посылаем число на USB-Serial converter на Verilog
Вот тут начинается настоящая боль, то что на Arduino делается в две строчку, тут надо вот… пару лекций посмотреть

Тут правда на другом языке пишет VHDL, и про UART, но UART в принципе так же работает как RS232 и TTL, вольты только отличаются — на само понимание процесса передается понятно. Тут ведь как в FPGA ничего нужно в комплекте не идет(как в микроконтроллере) но можно все что угодно собрать… если знаешь как…

Пример для Mojo v3 от разрабов успешно не работает alchitry.com/blogs/tutorials/hello-world, не собирается, они вообще инструкции не умеют писать! Конкртено эта вообще вышла через 5 лет после выпуска платы…

ВАЖНО: Долго ничего не работало, как выяснилось из-за не контакта — проводки плохо входили в макетку. Если есть возможность все втыкайте в макетку

У нас UART TTL он вообще то сигнал один считать за 5V, а MojoV3 на пины выдает только 3,3V, но у меня USB-Serial converter’ом будет Arduino Nano Китайский на ch340, он с вольтами сам разбирается как-то

Далее «простой» скрипт. Надо знать как работает передача сигналов, в простое надо подавать 1, чтобы передать 8 бит надо сначала стартануть передать 0, а в конце закончить передать 1. Символы передаются в кодировке ASCII, чтобы передать i надо передать число 105 (чтобы передать число 105 надо поаслать три байта, три числа 49 48 53) в бинарном виде 01101001. При передаче надо их перевернуть потому что существует некая least significant bit правило, не понял как оно работает, поэтому посылаю 10010110. Скорорсть MojoV3 50 мега герц, а скорость у нас будет 115200 герц, чтобы работать на этой скорости будем считать до 434, и менять значение сигналов. Два цикла always у них особенность нельзя переменные одинаковые использовать в двух сразу(это что-то электромеханическое) поэтому вводятся третьи и перебрасываются за их пределами. А вот оператор case работает нормально в циклах со значениями из других, 0 это просто 1-10 это передача, так же там же присваивает цифра переменной ledstate нужно было для диагностики — будут мигать на каждом шаге светодиоды встроенные. Так же есть кнопочка rst только после нажатия на неё посылается сигнал.

Добавляем в разделе Constraints в файл mojo.ucf строку

NET "tx_mojo" LOC = P50 | IOSTANDARD = LVTTL;

Вот весь код который будет в разделе Source в файле mojo_top.v

 
module mojo_top(
    // 50MHz clock input
    input clk,
    // Input from reset button (active low)
    input rst_n,
    // cclk input from AVR, high when AVR is ready
    input cclk,
    // Outputs to the 8 onboard LEDs
   output[7:0]led,
    //tx pin
   output tx_mojo,
    // AVR SPI connections
    output spi_miso,
    input spi_ss,
    input spi_mosi,
    input spi_sck,
    // AVR ADC channel select
    output [3:0] spi_channel,
    // Serial connections
    input avr_tx, // AVR Tx => FPGA Rx
    output avr_rx, // AVR Rx => FPGA Tx
    input avr_rx_busy // AVR Rx buffer full
    );
 
reg [25:0] cnt=0;
reg [0:0] sendTxYes=0;
reg [7:0] sendTxDo=0;
reg [7:0] ledstate=0;
reg [7:0] led_out=0;
reg tx, tx_out;
reg [25:0] presson;
reg [25:0] pressoff;
 
wire rst = ~rst_n; // make reset active high
 
// these signals should be high-z when not used
assign spi_miso = 1'bz;
assign avr_rx = 1'bz;
assign spi_channel = 4'bzzzz;

assign tx_mojo = tx_out;
assign led = led_out;
 
always @(posedge clk)
if (cnt == 434)
//2604 for 19200 boud rate
//434 for 115200
begin
cnt <= 0;
if (rst == 0)
begin
pressoff <= pressoff + 1;
if (pressoff == 50000)
begin
pressoff <= 0;
if (presson > 10000)
begin
presson <= 0;
sendTxYes = 1; 
end 
end
end
else
begin
presson <= presson + 1;
end
if (sendTxYes == 1)
begin
sendTxDo = sendTxDo + 1;
if (sendTxDo == 11)
begin
sendTxYes = 0;
sendTxDo = 0;
end
end
end else begin
cnt <= cnt + 1;
end

always @(*) begin
tx_out <= tx; 
led_out <= ledstate;
case (sendTxDo)
0: begin
tx = 1;
ledstate = 0;
end
1: begin
tx = 0;
ledstate = 1;
end
2: begin
//start sending "i"
tx = 1; 
ledstate = 2;
end
3: begin
tx = 0;
ledstate = 3;
end
4: begin
tx = 0; 
ledstate = 4;
end
5: begin
tx = 1; 
ledstate = 5;
end
6: begin
tx = 0;
ledstate = 6; 
end
7: begin
tx = 1; 
ledstate = 7;
end
8: begin
tx = 1; 
ledstate = 8;
end
9: begin
tx = 0; 
ledstate = 9;
end
10: begin
// stop sending
tx = 1;
ledstate = 10;
end
endcase
end
endmodule

В Ubuntu консоли запускаю
sudo screen /dev/ttyUSB0 115200
И жму rst на Mojo и в окне появляется i жму еще появляется еще и так далее в одну строку, потому что переход на другую строку это тоже отдельный сигнал. Вот так все «просто» пока.

Начинают появляется странности Verilog пока не ватальные, но вот плисовцы никто не пишет так

 cnt <= cnt + 1;

Вводят еще одну переменную, у меня однако работает.

Следующий код такой же просто перебираем цифры от 33 до 127 и смотрим какие это символы в ACSII, так же вынес кнопку, раньше она работала на частоте скорости передачи теперь на полных 50мГц.

Добавляем в разделе Constraints в файл mojo.ucf строку

NET "tx_mojo" LOC = P50 | IOSTANDARD = LVTTL;

Вот весь код который будет в разделе Source в файле mojo_top.v

module mojo_top(
    // 50MHz clock input
    input clk,
    // Input from reset button (active low)
    input rst_n,
    // cclk input from AVR, high when AVR is ready
    input cclk,
    // Outputs to the 8 onboard LEDs
   output[7:0]led,
    //tx pin
   output tx_mojo,
    // AVR SPI connections
    output spi_miso,
    input spi_ss,
    input spi_mosi,
    input spi_sck,
    // AVR ADC channel select
    output [3:0] spi_channel,
    // Serial connections
    input avr_tx, // AVR Tx => FPGA Rx
    output avr_rx, // AVR Rx => FPGA Tx
    input avr_rx_busy // AVR Rx buffer full
    );
   
reg [25:0] cnt=0;
reg [0:0] sendTxYes=0;
reg [7:0] sendTxDo=0;
reg [7:0] ledstate=0;
reg [7:0] led_out=0;
reg tx, tx_out;
reg [25:0] presson;
reg [25:0] pressoff;
reg [7:0] data=33;
   
wire rst = ~rst_n; // make reset active high
   
// these signals should be high-z when not used
assign spi_miso = 1'bz;
assign avr_rx = 1'bz;
assign spi_channel = 4'bzzzz;
  
assign tx_mojo = tx_out;
assign led = led_out;
   
always @(posedge clk)
begin
if (rst == 0)
begin
pressoff <= pressoff + 1;
if (pressoff == 5000000)
begin
pressoff <= 0;
if (presson > 1000000)
begin
presson <= 0;
sendTxYes = 1; 
end
end
end else begin
presson <= presson + 1;
end
 
if (cnt == 434)
//2604 for 19200 boud rate
//434 for 115200
begin
cnt <= 0;
if (sendTxYes == 1)
begin
sendTxDo = sendTxDo + 1;
if (sendTxDo == 11)
begin
sendTxDo = 1;
data = data + 1;
if (data == 127)
begin
data = 33;
sendTxYes = 0;
sendTxDo = 0;
end
end
end
end else begin
cnt <= cnt + 1;
end
end
  
always @(*) begin
tx_out <= tx; 
led_out <= ledstate;
case (sendTxDo)
0: begin
tx = 1;
ledstate = 0;
end
1: begin
tx = 0;
ledstate = 1;
end
2: begin
//start sending something
tx = data[0]; 
ledstate = 2;
end
3: begin
tx = data[1];
ledstate = 3;
end
4: begin
tx = data[2]; 
ledstate = 4;
end
5: begin
tx = data[3]; 
ledstate = 5;
end
6: begin
tx = data[4];
ledstate = 6; 
end
7: begin
tx = data[5]; 
ledstate = 7;
end
8: begin
tx = data[6]; 
ledstate = 8;
end
9: begin
tx = data[7]; 
ledstate = 9;
end
10: begin
// stop sending
tx = 1;
ledstate = 10;
end
endcase
end
endmodule

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

Все прекрасно но, чтобы вывести число например 1234 на порт, нельзя его просто послать, надо послать 1, потом 2, потом 3, потом 4… Я уверен что должен быть простой способ разделить число на числа, но я не нашел, придумла свой, далее результат моего мозгового дождя, или мозгового града, не помню как это называется но это было прям уф

Во первых Verilog падла, работает точне не работает как ему вздумается, вот например вот этот код в одном месте программы не работает:

data_mult=1000;
<...>
data_fun = data_temp - (data_mult*data_des);

а так работает

data_fun = data_temp - (1000*data_des);

и вот это работает

data_bit = 3;
<...>
data_big[data_bit]=data_des;

А почему? А потому что говорили же что Verilog это не язык программирования, это язык описания электронных схем!!! Ваше чудо что он хоть как то работает — ну правда его написали 20 лет назад, тогда и ПЛИСы все были другие и сейчас каждый по совему их делает ЧУДО что есть хоть какой-то язык… их всего два ксатати, и второму тоже 20 лет!!!

Пытался все делать изящно, экономя строки, лишний раз не прогоняя операции… должен был догадаться, когда узнал что в Verilog нет оператора возведения в степень, что надо делать все как можно проще, а то он просто не будет работать, даже если все успешно скомпилируется и засунется в Mojo!

Так вот, по нажатию кнопки начинается передача и увеличивается число на 25(для наглядности). Передача начинается с обработки числа — разбиением оного и дет подстчет сколько тысяч, сотен, десятков и единиц. Число от 0 до 9999, если больше выдает 0000. Считаю вычитанием. Дале цифры числаконвертятс в занчение ASCII и посылаются!

Добавляем в разделе Constraints в файл mojo.ucf строку

NET "tx_mojo" LOC = P50 | IOSTANDARD = LVTTL;

Вот весь код который будет в разделе Source в файле mojo_top.v

В программе ниже есть глюки с выводом, но они подчищенные в программе модуле(ниже) используйте модуль

module mojo_top(
    // 50MHz clock input
    input clk,
    // Input from reset button (active low)
    input rst_n,
    // cclk input from AVR, high when AVR is ready
    input cclk,
    // Outputs to the 8 onboard LEDs
   output[7:0]led,
    //tx pin
   output tx_mojo,
    // AVR SPI connections
    output spi_miso,
    input spi_ss,
    input spi_mosi,
    input spi_sck,
    // AVR ADC channel select
    output [3:0] spi_channel,
    // Serial connections
    input avr_tx, // AVR Tx => FPGA Rx
    output avr_rx, // AVR Rx => FPGA Tx
    input avr_rx_busy // AVR Rx buffer full
    );
  
reg [25:0] cnt=0;
reg [0:0] sendTxYes=0;
reg [7:0] sendTxDo=0;
reg [7:0] ledstate=0;
reg [7:0] ledstate2=0;
reg [7:0] led_out=0;
reg tx, tx_out;
reg [25:0] presson;
reg [25:0] pressoff;

reg [25:0] data;
reg [25:0] data_src;
reg [25:0] data_big [0:3];
reg signed [3:0] data_cnt=0;
reg [3:0] data_case;

  
reg [0:0] DataPrepareYes=0;
reg [3:0] data_p_cnt=1;
reg [25:0] data_temp;
reg signed [25:0] data_fun;
reg signed [25:0] data_des=9;
reg [0:0] data_fin=0;
reg [7:0] data_bit=0;  

wire rst = ~rst_n; // make reset active high
  
// these signals should be high-z when not used
assign spi_miso = 1'bz;
assign avr_rx = 1'bz;
assign spi_channel = 4'bzzzz;
 
assign tx_mojo = tx_out;
assign led = led_out;
  
always @(posedge clk)
begin

led_out[7:4] <= ledstate2;

if (rst == 0)
begin
pressoff <= pressoff + 1;
if (pressoff == 5000000)
begin
pressoff <= 0;
if (presson > 1000000)
begin
presson <= 0;
if (DataPrepareYes == 0)
begin
DataPrepareYes = 1;
data_temp=data_src;
data_src=data_src + 25;
end

end
end
end else begin
presson <= presson + 1;
end

//start prepare data
if (DataPrepareYes == 1)
begin
if (data_temp >= 10000)
begin
data_p_cnt = 5;
data_cnt = 3;
data_big[3]=0;
data_big[2]=0;
data_big[1]=0;
data_big[0]=0;
data_src=0;
end


case (data_p_cnt)
1: begin
//settings
data_bit = 3;
//do
data_fun = data_temp - (1000*data_des);
if (data_fun > 0)
begin
if (data_des == 0)
begin
if (data_fin == 0)
begin
data_cnt = data_bit -1;
data_p_cnt = data_p_cnt + 1;
data_des=9;
end else begin
data_big[data_bit]=0;
data_des=9;
data_p_cnt = data_p_cnt + 1;
end
end else begin
data_big[data_bit]=data_des;
data_temp = data_temp - (1000*data_des);
data_des=9;
if (data_fin == 0)
begin
data_cnt = data_bit;
data_fin = 1;
end
data_p_cnt = data_p_cnt + 1;
data_des=9;
end
end

if (data_fun < 0)
begin
data_des = data_des - 1;
end

if (data_fun == 0)
begin
if (data_des == 0)
begin
if (data_fin == 0)
begin
data_p_cnt = data_p_cnt + 1;
data_des=9;
end else begin
data_big[data_bit] = 0;
data_des=9;
data_p_cnt = data_p_cnt + 1;
end
end else begin
data_big[data_bit]=data_des;
data_temp = data_temp - (1000*data_des);
data_des=9;
if (data_fin == 0)
begin
data_cnt = data_bit;
data_fin = 1;
end
data_p_cnt = data_p_cnt + 1;
data_des=9;
end
end
end

2: begin
//settings
data_bit = 2;
//do
data_fun = data_temp - (100*data_des);
if (data_fun > 0)
begin
if (data_des == 0)
begin
if (data_fin == 0)
begin
data_des=9;
data_p_cnt = data_p_cnt + 1;
end else begin
data_big[data_bit]=0;
data_des=9;
data_p_cnt = data_p_cnt + 1;
end
end else begin
data_big[data_bit]=data_des;
data_temp = data_temp - (100*data_des);
data_des=9;
if (data_fin == 0)
begin
data_cnt = data_bit;
data_fin = 1;
end
data_p_cnt = data_p_cnt + 1;
data_des=9;
end
end

if (data_fun < 0)
begin
data_des = data_des - 1;
end

if (data_fun == 0)
begin
if (data_des == 0)
begin
if (data_fin == 0)
begin
data_p_cnt = data_p_cnt + 1;
data_des=9;
end else begin
data_big[data_bit] = 0;
data_des=9;
data_p_cnt = data_p_cnt + 1;
end
end else begin
data_big[data_bit]=data_des;
data_temp = data_temp - (100*data_des);
data_des=9;
if (data_fin == 0)
begin
data_cnt = data_bit;
data_fin = 1;
end
data_p_cnt = data_p_cnt + 1;
data_des=9;
end
end
end


3: begin
//settings
data_bit = 1;
//do
data_fun = data_temp - (10*data_des);
if (data_fun > 0)
begin
if (data_des == 0)
begin
if (data_fin == 0)
begin
data_des=9;
data_p_cnt = data_p_cnt + 1;
end else begin
data_big[data_bit]=0;
data_des=9;
data_p_cnt = data_p_cnt + 1;
end
end else begin
data_big[data_bit]=data_des;
data_temp = data_temp - (10*data_des);
data_des=9;
if (data_fin == 0)
begin
data_cnt = data_bit;
data_fin = 1;
end
data_p_cnt = data_p_cnt + 1;
data_des=9;
end
end

if (data_fun < 0)
begin
data_des = data_des - 1;
end

if (data_fun == 0)
begin
if (data_des == 0)
begin
if (data_fin == 0)
begin
data_p_cnt = data_p_cnt + 1;
data_des=9;
end else begin
data_big[data_bit] = 0;
data_des=9;
data_p_cnt = data_p_cnt + 1;
end
end else begin
data_big[data_bit]=data_des;
data_temp = data_temp - (10*data_des);
data_des=9;
if (data_fin == 0)
begin
data_cnt = data_bit;
data_fin = 1;
end
data_p_cnt = data_p_cnt + 1;
data_des=9;
end
end
end


4: begin
//settings
data_bit = 0;
//do
data_fun = data_temp - (data_des);
if (data_fun > 0)
begin
if (data_des == 0)
begin
if (data_fin == 0)
begin
data_des=9;
data_p_cnt = data_p_cnt + 1;
data_des=9;
end else begin
data_big[data_bit]=0;
data_des=9;
data_p_cnt = data_p_cnt + 1;
end
end else begin
data_big[data_bit]=data_des;
data_temp = data_temp - (data_des);
data_des=9;
if (data_fin == 0)
begin
data_cnt = data_bit;
data_fin = 1;
end
data_p_cnt = data_p_cnt + 1;
data_des=9;
end
end

if (data_fun < 0)
begin
data_des = data_des - 1;
end

if (data_fun == 0)
begin
if (data_des == 0)
begin
if (data_fin == 0)
begin
data_p_cnt = data_p_cnt + 1;
data_des=9;
end else begin
data_big[data_bit] = 0;
data_des=9;
data_p_cnt = data_p_cnt + 1;
end
end else begin
data_big[data_bit]=data_des;
data_temp = data_temp - (data_des);
data_des=9;
if (data_fin == 0)
begin
data_cnt = data_bit;
data_fin = 1;
end
data_p_cnt = data_p_cnt + 1;
data_des=9;
end
end
end

5: begin
data_temp=0;
data_des=9;
data_fin=0;
sendTxYes = 1;
data_p_cnt = 1;
DataPrepareYes = 0; 
end
endcase
end
//end prepare data 

if (cnt == 434)
//2604 for 19200 boud rate
//434 for 115200
begin
cnt <= 0;
if (sendTxYes == 1)
begin
data_case = data_big[data_cnt];
case (data_case)
0: begin
data = 48;
end
1: begin
data = 49;
end
2: begin
data = 50;
end
3: begin
data = 51;
end
4: begin
data = 52;
end
5: begin
data = 53;
end
6: begin
data = 54;
end
7: begin
data = 55;
end
8: begin
data = 56;
end
9: begin
data = 57;
end
endcase
sendTxDo = sendTxDo + 1;
if (sendTxDo == 11)
begin
sendTxDo = 1;
data_cnt = data_cnt - 1;
if (data_cnt < 0)
begin
sendTxDo = 12;
end
end
if (sendTxDo == 32)
begin
sendTxYes = 0;
sendTxDo = 0;
end
end
end else begin
cnt <= cnt + 1;
end

end


 
always @(*) begin
tx_out <= tx; 
led_out[3:0] <= ledstate;
case (sendTxDo)
0: begin
tx = 1;
ledstate = 0;
end
1: begin
tx = 0;
ledstate = 1;
end
2: begin
//start sending "i"
tx = data[0]; 
ledstate = 2;
end
3: begin
tx = data[1];
ledstate = 4;
end
4: begin
tx = data[2]; 
ledstate = 8;
end
5: begin
tx = data[3]; 
ledstate = 1;
end
6: begin
tx = data[4];
ledstate = 2; 
end
7: begin
tx = data[5]; 
ledstate = 7;
end
8: begin
tx = data[6]; 
ledstate = 4;
end
9: begin
tx = data[7]; 
ledstate = 8;
end
10: begin
// stop sending
tx = 1;
ledstate = 10;
end
12: begin
// nextline star send
tx = 0;
end
13: begin
tx = 0;
end
14: begin
tx = 1;
end
15: begin
tx = 0;
end
16: begin
tx = 1;
end
17: begin
tx = 0;
end
18: begin
tx = 0;
end
19: begin
tx = 0;
end
20: begin
tx = 0;
end
21: begin
//stop
tx = 1;
end
22: begin
// nextline put to begin curs0r star send
tx = 0;
end
23: begin
tx = 1;
end
24: begin
tx = 0;
end
25: begin
tx = 1;
end
26: begin
tx = 1;
end
27: begin
tx = 0;
end
28: begin
tx = 0;
end
29: begin
tx = 0;
end
30: begin
tx = 0;
end
31: begin
//stop
tx = 1;
end
endcase
end
endmodule

Тяжело это далось, но результат… какой результат! Я всего лишь хотел подключить датчик(хорошо что аналоговый, надеюсь мозгового дождя опять не потребуется) и посмотреть что из него лезет, а для этого придется эту программу превратить в подключаемый модуль!

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

input [0:0] send_start,

и в always назачены внутренней переменной

always @(posedge clk)
begin
DataPrepareYes = send_start;

В верхнем коде в mojo_top надо её просто описать

reg [0:0] send_yes;

С выходящими данными сложнее, в модуле надо

output send_end,

Потом надо её назначит внутренней переменной

assign send_end = sendTxYes;

В верхнем коде в mojo_top надо её просто описать

output send_busy,

и сделать кабелем

output send_busy,

теперь в модуле будет работать связь

output send_busy,

Сам код модуля вот, в проект его добавляем через в списке кодов Source > New source создаю файд Verilog под названием tx_9999.v

module tx_9999 (
    input clk,  // clock
    input [0:0] send_start,
    output send_end,
    input signed [25:0] send_data,
    
    //tx pin
   output tx_mojo
   
    );
  
reg [25:0] cnt=0;
reg [6:0] sendTxDo=0;
reg [0:0] sendTxYes;
reg [0:0] DataPrepareYes;
reg tx, tx_out;
reg [25:0] data;
reg [25:0] data_big [0:4];
reg signed [3:0] data_cnt;
reg [6:0] data_case;
reg [4:0] data_p_cnt=1;
reg signed [25:0] data_temp;
reg signed [25:0] data_fun;
reg signed [25:0] data_des=9;
reg [0:0] data_load=0;
reg [0:0] data_fin=0;
reg [0:0] data_min=0;
reg [7:0] data_bit=0;  


 
assign tx_mojo = tx_out;
assign send_end = sendTxYes;

  
always @(posedge clk)
begin
DataPrepareYes = send_start;
//start prepare data
if (DataPrepareYes == 1 && sendTxYes == 0)
begin
if (data_load == 0)
begin
data_temp = send_data;
data_load = 1;
data_des=9;
data_fin=0;
data_bit=0;
data_p_cnt = 1;

if (data_temp > 9999 || data_temp < -9999 )
begin
data_p_cnt = 5;
data_cnt = 4;
data_big[4]=0;
data_big[3]=0;
data_big[2]=0;
data_big[1]=0;
data_big[0]=0;
end else begin
if (data_temp < 0)
begin
data_temp = data_temp*(-1);
data_min = 1;
end else begin
data_min=0;
end
end 
end




case (data_p_cnt)
1: begin
//settings
data_bit = 3;
//do
data_fun = data_temp - (1000*data_des);
if (data_fun > 0)
begin
if (data_des == 0)
begin
if (data_fin == 0)
begin
data_des=9;
data_p_cnt = data_p_cnt + 1;
end else begin
data_big[data_bit]=0;
data_des=9;
data_p_cnt = data_p_cnt + 1;
end
end else begin
data_big[data_bit]=data_des;
data_temp = data_temp - (1000*data_des);
data_des=9;
data_p_cnt = data_p_cnt + 1;
if (data_fin == 0)
begin
data_cnt = data_bit + 1;
if (data_min == 1)
begin
data_big[data_cnt]=45;
end else begin
data_cnt = data_bit;
end
data_fin = 1;
end

end
end

if (data_fun < 0)
begin
data_des = data_des - 1;
end

if (data_fun == 0)
begin
if (data_des == 0)
begin
if (data_fin == 0)
begin
data_p_cnt = data_p_cnt + 1;
data_des=9;
end else begin
data_big[data_bit] = 0;
data_des=9;
data_p_cnt = data_p_cnt + 1;
end
end else begin
data_big[data_bit]=data_des;
data_temp = data_temp - (1000*data_des);
data_des=9;
if (data_fin == 0)
begin
data_cnt = data_bit + 1;
if (data_min == 1)
begin
data_big[data_cnt]=45;
end else begin
data_cnt = data_bit;
end
data_fin = 1;
end
data_p_cnt = data_p_cnt + 1;
data_des=9;
end
end
end

2: begin
//settings
data_bit = 2;
//do
data_fun = data_temp - (100*data_des);
if (data_fun > 0)
begin
if (data_des == 0)
begin
if (data_fin == 0)
begin
data_des=9;
data_p_cnt = data_p_cnt + 1;
end else begin
data_big[data_bit]=0;
data_des=9;
data_p_cnt = data_p_cnt + 1;
end
end else begin
data_big[data_bit]=data_des;
data_temp = data_temp - (100*data_des);
data_des=9;
if (data_fin == 0)
begin
data_cnt = data_bit + 1;
if (data_min == 1)
begin
data_big[data_cnt]=45;
end else begin
data_cnt = data_bit;
end
data_fin = 1;
end
data_p_cnt = data_p_cnt + 1;
data_des=9;
end
end

if (data_fun < 0)
begin
data_des = data_des - 1;
end

if (data_fun == 0)
begin
if (data_des == 0)
begin
if (data_fin == 0)
begin
data_p_cnt = data_p_cnt + 1;
data_des=9;
end else begin
data_big[data_bit] = 0;
data_des=9;
data_p_cnt = data_p_cnt + 1;
end
end else begin
data_big[data_bit]=data_des;
data_temp = data_temp - (100*data_des);
data_des=9;
if (data_fin == 0)
begin
data_cnt = data_bit + 1;
if (data_min == 1)
begin
data_big[data_cnt]=45;
end else begin
data_cnt = data_bit;
end
data_fin = 1;
end
data_p_cnt = data_p_cnt + 1;
data_des=9;
end
end
end


3: begin
//settings
data_bit = 1;
//do
data_fun = data_temp - (10*data_des);
if (data_fun > 0)
begin
if (data_des == 0)
begin
if (data_fin == 0)
begin
data_des=9;
data_p_cnt = data_p_cnt + 1;
end else begin
data_big[data_bit]=0;
data_des=9;
data_p_cnt = data_p_cnt + 1;
end
end else begin
data_big[data_bit]=data_des;
data_temp = data_temp - (10*data_des);
data_des=9;
if (data_fin == 0)
begin
data_cnt = data_bit + 1;
if (data_min == 1)
begin
data_big[data_cnt]=45;
end else begin
data_cnt = data_bit;
end
data_fin = 1;
end
data_p_cnt = data_p_cnt + 1;
data_des=9;
end
end

if (data_fun < 0)
begin
data_des = data_des - 1;
end

if (data_fun == 0)
begin
if (data_des == 0)
begin
if (data_fin == 0)
begin
data_p_cnt = data_p_cnt + 1;
data_des=9;
end else begin
data_big[data_bit] = 0;
data_des=9;
data_p_cnt = data_p_cnt + 1;
end
end else begin
data_big[data_bit]=data_des;
data_temp = data_temp - (10*data_des);
data_des=9;
if (data_fin == 0)
begin
data_cnt = data_bit + 1;
if (data_min == 1)
begin
data_big[data_cnt]=45;
end else begin
data_cnt = data_bit;
end
data_fin = 1;
end
data_p_cnt = data_p_cnt + 1;
data_des=9;
end
end
end

4: begin
//settings
data_bit = 0;
//do
data_fun = data_temp - (data_des);


if (data_fun < 0)
begin
data_des = data_des - 1;
end

if (data_fun == 0)
begin
if (data_des == 0)
begin
if (data_fin == 0)
begin
data_p_cnt = data_p_cnt + 1;
data_des=9;
data_cnt = data_bit;
data_big[data_bit] = 0;
end else begin
data_big[data_bit] = 0;
data_des=9;
data_p_cnt = data_p_cnt + 1;
end
end else begin
data_big[data_bit]=data_des;
data_temp = data_temp - (data_des);
data_des=9;
if (data_fin == 0)
begin
data_cnt = data_bit + 1;
if (data_min == 1)
begin
data_big[data_cnt]=45;
end else begin
data_cnt = data_bit;
end
data_fin = 1;
end
data_p_cnt = data_p_cnt + 1;
data_des=9;
end
end
end

5: begin
data_des=9;
data_fin=0;
data_min=0;
sendTxYes = 1;
end
endcase
end
//end prepare data 

if (cnt == 434)
//2604 for 19200 boud rate
//434 for 115200
begin
cnt <= 0;
if (sendTxYes == 1)
begin

data_case = data_big[data_cnt];
case (data_case)
0: begin
data = 48;
end
1: begin
data = 49;
end
2: begin
data = 50;
end
3: begin
data = 51;
end
4: begin
data = 52;
end
5: begin
data = 53;
end
6: begin
data = 54;
end
7: begin
data = 55;
end
8: begin
data = 56;
end
9: begin
data = 57;
end
45: begin
data = 45;
end
endcase
sendTxDo = sendTxDo + 1;
if (sendTxDo == 11)
begin
sendTxDo = 1;
data_cnt = data_cnt - 1;
if (data_cnt < 0)
begin
sendTxDo = 12;
end
end
if (sendTxDo == 32)
begin
sendTxYes = 0;
sendTxDo = 0;
data_load = 0;
end
end
end else begin
cnt <= cnt + 1;
end

end


 
always @(*) begin
tx_out <= tx; 

case (sendTxDo)
0: begin
tx = 1;

end
1: begin
tx = 0;

end
2: begin
tx = data[0]; 

end
3: begin
tx = data[1];

end
4: begin
tx = data[2]; 

end
5: begin
tx = data[3]; 

end
6: begin
tx = data[4];

end
7: begin
tx = data[5]; 

end
8: begin
tx = data[6]; 

end
9: begin
tx = data[7]; 

end
10: begin
// stop sending
tx = 1;

end
12: begin
// nextline star send
tx = 0;
end
13: begin
tx = 0;
end
14: begin
tx = 1;
end
15: begin
tx = 0;
end
16: begin
tx = 1;
end
17: begin
tx = 0;
end
18: begin
tx = 0;
end
19: begin
tx = 0;
end
20: begin
tx = 0;
end
21: begin
//stop
tx = 1;
end
22: begin
// nextline put to begin curs0r star send
tx = 0;
end
23: begin
tx = 1;
end
24: begin
tx = 0;
end
25: begin
tx = 1;
end
26: begin
tx = 1;
end
27: begin
tx = 0;
end
28: begin
tx = 0;
end
29: begin
tx = 0;
end
30: begin
tx = 0;
end
31: begin
//stop
tx = 1;
end
endcase
end
endmodule

Для чтения аналогового сигнала добавлю в схему TSR 3296W-103, потенциометр многооборотный керметный 10кОм 0.5Вт ±10% 30 ±2 оборотов. На схеме он не так выглядит, крутилка маленькая в правом нижнем углу к ноге под ней плюс, дальше на лево аналоговый вывод, и GND. Кручу по часовой значения уменьшаются. Внимание кручение продолжается и когда ноль и когда в обратную сторону уже 1024, просто если крутите и ничего не происхожит — крутите дальше… или в обратную сторону. (подумалось тут — потенциометр простой способ передавать в ПЛИС цифры)

ПЛИС сам аналоговые пины не читает, для этого на Mojo стоит ADC который выдает 10 битовые значения от 0 до 1024, меряет напряжение(3,3V) и делит его на 1024 частей. Чтобы его задействовать надо скачать базовый проект от создателей Mojo. В своей проект из базового надо добавить через Source > New Source файлы avr_interface.v cclk_detector.v serial_rx.v serial_tx.v spi_slave.v почти все!

Добавляем в разделе Constraints в файл mojo.ucf строку

NET "tx_mojo" LOC = P50 | IOSTANDARD = LVTTL;

Вот весь код который будет в разделе Source в файле mojo_top.v

module mojo_top(
    // 50MHz clock input
    input clk,
    // Input from reset button (active low)
    input rst_n,
    // cclk input from AVR, high when AVR is ready
    input cclk,
    // Outputs to the 8 onboard LEDs
 //   output[7:0]led,
    // AVR SPI connections
    output spi_miso,
    input spi_ss,
    input spi_mosi,
    input spi_sck,
    // AVR ADC channel select
    output [3:0] spi_channel,
    // Serial connections
    input avr_tx, // AVR Tx => FPGA Rx
    output avr_rx, // AVR Rx => FPGA Tx
    input avr_rx_busy, // AVR Rx buffer full
  
  output send_busy,  
  output tx_pin
    );

reg [0:0] send_yes;
reg signed [25:0] data_q;
reg signed [25:0] data_mid=0;
reg [25:0] data_mid_cnt;
reg [3:0] channel = 4'd0;

reg [25:0] cnt;

wire tx_pin;
wire send_busy;
wire rst = ~rst_n; // make reset active high

wire new_sample;
wire [9:0] sample;
wire [3:0] sample_channel;

// these signals should be high-z when not used
assign spi_miso = 1'bz;
assign avr_rx = 1'bz;
assign spi_channel = 4'bzzzz;

//assign led[0:0] = send_yes;
//assign led[7:1] = send_busy;

always @(posedge clk)
begin
cnt <= cnt + 1;
if (new_sample && sample_channel == 4'd0) // valid sample
  begin 
   data_q = sample;
end

if (cnt == 1000000)
begin
cnt <=0;
if (send_busy == 0)
begin
send_yes = 1; 
end else begin
send_yes = 0;
end
end
if (send_busy == 1)
begin
send_yes = 0; 
end
end



avr_interface avr_interface (
    .clk(clk),
    .rst(rst),
    .cclk(cclk),
    .spi_miso(spi_miso),
    .spi_mosi(spi_mosi),
    .spi_sck(spi_sck),
    .spi_ss(spi_ss),
    .spi_channel(spi_channel),
    .tx(avr_rx),
    .rx(avr_tx),
    .channel(channel),
    .new_sample(new_sample),
    .sample(sample),
    .sample_channel(sample_channel),
    .tx_data(8'h00),
    .new_tx_data(1'b0),
    .tx_busy(),
    .tx_block(avr_rx_busy),
    .rx_data(),
    .new_rx_data()
  );

tx_9999 send_data (
    .clk(clk),
    .send_data(data_q),
    .send_start(send_yes),
    .send_end(send_busy),
    .tx_mojo(tx_pin)
  );

endmodule

Обратите внимание описание пина для для модуля выводы числа происходит в mojo_top.v а в самом модуле просто кидается дата на tx_mojo.

Дата с аналогово входа хватается этим кодом

if (new_sample && sample_channel == 4'd0) // valid sample
  begin 
   data_q = sample;
end

Перед этим он задан, еще в другом месте

reg [3:0] channel = 4'd0;

Передача начинается 50 раз в секунду, хотя она не начинается если модуль вывода уже чего то передает или обрабатывает — это важно чтобы не было хаоса

if (cnt == 1000000)
begin
cnt <=0;
if (send_busy == 0)
begin
send_yes = 1; 
end else begin
send_yes = 0;
end
end
if (send_busy == 1)
begin
send_yes = 0; 
end
end

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

Собираю схему, в коде менять ничего не собирался (да картинка кривоватая Gimp стал глючить, выделение не работает)

Закачал код и датчик начал выдавать всякую фигню, причем на появление звука реагировал, но приэтом и ноль выскакивал и чопопало.

Микрофон на плате CZN-15E посмотрел его спецификацию eva.fing.edu.uy/file.php/585/materiales/HD/CZN-15E.pdf ничего не понял, но появилась мысль. Я думал раз он аналоговый, то он может выводит в реальном времени(с любой частотой) значение, это и есть в моем понимание аналоговость. Но нет, выдает и адекватные значения и всякую фигню. Реализовал в коде — брать 1000 значений с микрофна и выбирать наибольшее его дальше и передовать.

Вот код

module mojo_top(
    // 50MHz clock input
    input clk,
    // Input from reset button (active low)
    input rst_n,
    // cclk input from AVR, high when AVR is ready
    input cclk,
    // Outputs to the 8 onboard LEDs
 //   output[7:0]led,
    // AVR SPI connections
    output spi_miso,
    input spi_ss,
    input spi_mosi,
    input spi_sck,
    // AVR ADC channel select
    output [3:0] spi_channel,
    // Serial connections
    input avr_tx, // AVR Tx => FPGA Rx
    output avr_rx, // AVR Rx => FPGA Tx
    input avr_rx_busy, // AVR Rx buffer full
  
  output send_busy,  
  output tx_pin
    );

reg [0:0] send_yes;
reg signed [25:0] data_q;
reg signed [25:0] data_mid=0;
reg [25:0] data_mid_cnt;
reg [3:0] channel = 4'd0;

reg [25:0] cnt;

wire tx_pin;
wire send_busy;
wire rst = ~rst_n; // make reset active high

wire new_sample;
wire [9:0] sample;
wire [3:0] sample_channel;

// these signals should be high-z when not used
assign spi_miso = 1'bz;
assign avr_rx = 1'bz;
assign spi_channel = 4'bzzzz;

//assign led[0:0] = send_yes;
//assign led[7:1] = send_busy;

always @(posedge clk)
begin
cnt <= cnt + 1;
if (new_sample && sample_channel == 4'd0) // valid sample
  begin
   if (sample > data_mid)
   begin
   data_mid = sample;
   end
   data_mid_cnt = data_mid_cnt + 1;
   if (data_mid_cnt == 1000)
   begin
   data_q = data_mid;
   data_mid_cnt = 0;
   data_mid = 0;
   end
  
end



if (cnt == 1000000)
begin
cnt <=0;
if (send_busy == 0)
begin
send_yes = 1; 
end else begin
send_yes = 0;
end

end

if (send_busy == 1)
begin
send_yes = 0; 
end
end


avr_interface avr_interface (
    .clk(clk),
    .rst(rst),
    .cclk(cclk),
    .spi_miso(spi_miso),
    .spi_mosi(spi_mosi),
    .spi_sck(spi_sck),
    .spi_ss(spi_ss),
    .spi_channel(spi_channel),
    .tx(avr_rx),
    .rx(avr_tx),
    .channel(channel),
    .new_sample(new_sample),
    .sample(sample),
    .sample_channel(sample_channel),
    .tx_data(8'h00),
    .new_tx_data(1'b0),
    .tx_busy(),
    .tx_block(avr_rx_busy),
    .rx_data(),
    .new_rx_data()
  );

tx_9999 send_data (
    .clk(clk),
    .send_data(data_q),
    .send_start(send_yes),
    .send_end(send_busy),
    .tx_mojo(tx_pin)
  );

endmodule

Теперь все ок, на речь регирует, чуствительность какая то заторможенная — если завывать то сначала срабатывает а потом падает, хотя я продолжаю выть.

Еще «чуть-чуть» и уже можно будет начать делать то ради чего, вот это все… осталось научиться двигать двигатель, еще один квест не на жизнь а на плис!

У меня Tower Pro 9g SG90 на него надо посылать PWM сигнал, и меня его частоту можно управлять двигателем. Изучаю alchitry.com/blogs/tutorials/servos. Оценили простоту и понятносять? 🙂

Чтобы мотор стоял в положение ноль надо послать 1 на 1,5 мс потом 20мс посылать 0 и так далее
Чтобы мотор ехал на лево в конец надо послать 1 на 1 мс потом 20мс посылать 0 и так далее
Чтобы мотор ехал в другую сторону в конец надо послать 1 на 2 мс потом 20мс посылать 0 и так далее

Битовые счетчики конечно нереально крутые и экономят попугаев в плисе, но они совершенно не для начального понимания процесса. Так что буду честно считать, 0,5 мс это от 50 Mhz будет 25 000.

Собираю схему у SG90 земля коричневая, питание красное, дата оранжевый

Добавляем в разделе Constraints в файл mojo.ucf строку

NET "servo_mojo" LOC = P1 | IOSTANDARD = LVTTL;

Вот весь код который будет в разделе Source в файле mojo_top.v

module mojo_top(
    // 50MHz clock input
    input clk,
    // Input from reset button (active low)
    input rst_n,
    // cclk input from AVR, high when AVR is ready
    input cclk,
    // Outputs to the 8 onboard LEDs
    output[7:0]led,
    // servo
    output servo_mojo,
    // AVR SPI connections
    output spi_miso,
    input spi_ss,
    input spi_mosi,
    input spi_sck,
    // AVR ADC channel select
    output [3:0] spi_channel,
    // Serial connections
    input avr_tx, // AVR Tx => FPGA Rx
    output avr_rx, // AVR Rx => FPGA Tx
    input avr_rx_busy // AVR Rx buffer full
    );

wire rst = ~rst_n; // make reset active high

reg [25:0] cnt=0;
reg [0:0] servo;
reg [2:0] servorun=0;
reg [1:0] servoway=0;
reg [25:0] presson;
reg [25:0] pressoff;

// these signals should be high-z when not used
assign spi_miso = 1'bz;
assign avr_rx = 1'bz;
assign spi_channel = 4'bzzzz;

assign led = 8'b0;
assign servo_mojo = servo;

always @(posedge clk)
begin
cnt <= cnt + 1;
//button
if (rst == 0)
begin
pressoff <= pressoff + 1;
if (pressoff == 50000)
begin
pressoff <= 0;
if (presson > 10000)
begin
presson <= 0;
servoway <= servoway + 1;
end
end
end
else
begin
presson <= presson + 1;
end

if (servoway >= 3)
begin
servoway <= 0;
end
//end button

case (servorun)
0: begin
servo <= 0;
if (cnt >= 1000000)
begin
cnt <= 0;
case (servoway)
0: begin
servorun <= 1;
end
1: begin
servorun <= 2;
end
2: begin
servorun <= 3;
end
endcase
end
end
1: begin
servo <= 1;
if (cnt >= 75000)
begin
cnt <= 0;
servorun <= 0;
end
end
2: begin
servo <= 1;
if (cnt >= 50000)
begin
cnt <= 0;
servorun <= 0;
end
end
3: begin
servo <= 1;
if (cnt >= 100000)
begin
cnt <= 0;
servorun <= 0;
end
end
endcase
end


endmodule

Программа начинаятся с посылки мотору команды иди в ноль, нажимаем кнопку, говорит иди в минус -90 градусов, еще нажатие в 90, еще нажатие в ноль и тд.

Так как все прсивоения переменных асинхронные, то условие по servoway надо вынести за кнопку

С этим кодом мотор будет потрескивать, в первую очередь потому что если мы не снимаем сигнал он рабоатет на удержание этой позиции, а из-за своего качества (все таки два бакса за моторчик) он плохо определяет свою позицию край и пытается докручивать и пр. По хорошему надо двигать мтор и отключать сигнал.

Спустя какое то время…
Подобрал частоту которая больше всего подходит для моего подключения для волтажа моджо. Если смотреть на мотор на его положение в 90 градусов, то слева будет ноль для этого впромежутке 20мс или миллион тактов моджо, надо подавать единицу 35000 тактов, чтобы ушел в 90 75000, что в 180 11500. Получается каждый градус 444 такта всего 8000, мотор кстати крутится и дальше до 230 градусов.

Дальнейший код по нажатию прыгает с 0 до 90 до 180 и обратно, при включении встает в 90. Оформлен в виде модуля. Схема и пины как у предыдущего кода.

для sg90.v код такой

module sg90 (
    input clk,  // clock
    input rst,  // reset
    output servo_mojo,
  //  output moto_busy_m,
// input [0:0] moto_start_m,
 input [25:0] servoway_m
    );

//wire rst = ~rst_n; // make reset active high


reg [25:0] cnt;
reg [25:0] servo_cnt=35000;
reg [25:0] servo_cnt_data;
reg [0:0] servo;
reg [2:0] servorun=0;
//reg [0:0] servoOn;


// these signals should be high-z when not used
assign servo_mojo = servo;
//assign moto_busy_m = servoOn;


always @(posedge clk)
begin
cnt = cnt + 1;

//if (moto_start_m == 1)
//begin
case (servorun)
0: begin
servo = 0;
servo_cnt_data = 35000 + servoway_m * 444;
servorun = 1;


end
1: begin
servo = 0;

if (cnt >= 1000000)
begin
cnt = 0;

if (servo_cnt > servo_cnt_data)
begin
servo_cnt = servo_cnt - 444;
servorun = 2;
if (servo_cnt < servo_cnt_data)
begin
//stop
//servoOn = 0;
servorun = 0;
servo_cnt = servo_cnt_data;
end
end else begin
servo_cnt = servo_cnt + 444;
servorun = 2;
if (servo_cnt > servo_cnt_data)
begin
//stop
//servoOn = 0;
servorun = 0;
servo_cnt = servo_cnt_data;
end
end


end


end
2: begin
//servoOn = 1;
servo = 1;
if (cnt >= servo_cnt)
begin
//cnt <= 0;
servorun = 1;
end

end

endcase
//end

end


endmodule

И основной код который будет в разделе Source в файле mojo_top.v

module mojo_top(
    // 50MHz clock input
    input clk,
    // Input from reset button (active low)
    input rst_n,
  
    // cclk input from AVR, high when AVR is ready
    input cclk,
    // Outputs to the 8 onboard LEDs
    output[7:0]led, 
   output servo_mojo_pin,
    // AVR SPI connections
    output spi_miso,
    input spi_ss,
    input spi_mosi,
    input spi_sck,
    // AVR ADC channel select
    output [3:0] spi_channel,
    // Serial connections
    input avr_tx, // AVR Tx => FPGA Rx
    output avr_rx, // AVR Rx => FPGA Tx
    input avr_rx_busy // AVR Rx buffer full
    );

wire rst = ~rst_n; // make reset active high

wire servo_mojo_pin;

reg [25:0] presson;
reg [25:0] pressoff;
reg [25:0] servoway=90;
reg [7:0] ledb;

// these signals should be high-z when not used
assign spi_miso = 1'bz;
assign avr_rx = 1'bz;
assign spi_channel = 4'bzzzz;

assign led = ledb;


always @(posedge clk)
begin
//moto button
if (rst == 0)
begin
pressoff = pressoff + 1;
if (pressoff == 50000)
begin
pressoff = 0;

if (presson > 10000)
begin
presson = 0;

servoway = servoway + 90;
ledb = 1;
if (servoway > 181)
begin
servoway = 0;
ledb = 2;
end

end 


end
end else begin
presson = presson + 1;
ledb = 0;

end
//end moto button

end

sg90 move_moto (
    .clk(clk),
    .servoway_m(servoway),
    .servo_mojo(servo_mojo_pin)
  );

endmodule

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

А в этом коде используется модуль отсылки текста и движения мотора. Только добавлен потенциометр(см выше где было про потенциометр какие файлы надо добавить в соурс) чтобы можно было менять значения и смотреть как двигается мотор.

Схемка

В дожно быть плюс к обычному

NET "tx_pin" LOC = P50 | IOSTANDARD = LVTTL;
NET "servo_mojo_pin" LOC = P1 | IOSTANDARD = LVTTL;

Код который в разделе Source в файле mojo_top.v

module mojo_top(
    // 50MHz clock input
    input clk,
    // Input from reset button (active low)
    input rst_n,
    // cclk input from AVR, high when AVR is ready
    input cclk,
    // Outputs to the 8 onboard LEDs
 //   output[7:0]led,
    // AVR SPI connections
    output spi_miso,
    input spi_ss,
    input spi_mosi,
    input spi_sck,
    // AVR ADC channel select
    output [3:0] spi_channel,
    // Serial connections
    input avr_tx, // AVR Tx => FPGA Rx
    output avr_rx, // AVR Rx => FPGA Tx
    input avr_rx_busy, // AVR Rx buffer full
  
  output send_busy,  
  output tx_pin,
  output servo_mojo_pin
    );

reg [0:0] send_yes;

reg signed [25:0] data_q;
reg signed [25:0] data_s;
reg signed [25:0] data_mid=0;
reg [25:0] data_mid_cnt;
reg [3:0] channel = 4'd0;

reg [25:0] cnt;
reg [25:0] presson;
reg [25:0] pressoff;
reg [7:0] servoway;
wire tx_pin;
wire servo_mojo_pin;
wire send_busy;
wire rst = ~rst_n; // make reset active high


wire new_sample;
wire [9:0] sample;
wire [3:0] sample_channel;

// these signals should be high-z when not used
assign spi_miso = 1'bz;
assign avr_rx = 1'bz;
assign spi_channel = 4'bzzzz;

//assign led[0:0] = send_yes;
//assign led[7:1] = send_busy;

always @(posedge clk)
begin
cnt <= cnt + 1;
if (new_sample && sample_channel == 4'd0) // valid sample
  begin 
   servoway = sample;
end

if (cnt == 1000000)
begin
cnt <=0;
if (send_busy == 0)
begin
send_yes = 1; 
end else begin
send_yes = 0;
end
end
if (send_busy == 1)
begin
send_yes = 0; 
end


end



avr_interface avr_interface (
    .clk(clk),
    .rst(rst),
    .cclk(cclk),
    .spi_miso(spi_miso),
    .spi_mosi(spi_mosi),
    .spi_sck(spi_sck),
    .spi_ss(spi_ss),
    .spi_channel(spi_channel),
    .tx(avr_rx),
    .rx(avr_tx),
    .channel(channel),
    .new_sample(new_sample),
    .sample(sample),
    .sample_channel(sample_channel),
    .tx_data(8'h00),
    .new_tx_data(1'b0),
    .tx_busy(),
    .tx_block(avr_rx_busy),
    .rx_data(),
    .new_rx_data()
  );

sg90 move_moto (
    .clk(clk),
    .servoway_m(servoway),
    .servo_mojo(servo_mojo_pin)
  );
  
tx_9999 send_data (
    .clk(clk),
    .send_data(servoway),
    .send_start(send_yes),
    .send_end(send_busy),
    .tx_mojo(tx_pin)
  );

endmodule

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

Предыстория: Работал я тут в одной мега корпорации в айти отделе. Всегда удивляло что покупали всякую мега фигню за очень дорого, на столько дорого и на столько фигню что тру айтишник встал бы и сказал «Да я тоже самое, сделаю из говна и палок за цену в сто раз меньше», но ни кто не вставал — потому что чтобы работать в мега корпорации в айти отделе надо убить в себе айтишника! И вот пример такой фигни — Polycom VCS система видео конференции см фото!

8 тыыыысяч, королева их, ФУНТОВ, и это только часть системы — две камеры, микрофон для прицеливания (не для разговора) и всё. Одна камера держит крупный план, когда вторая наводится, по веритикали и горизонтали, на говоряшего. Буду делать тоже самое, да, только, у меня будет навидиться только по горизонтали, могбы попробовать и по вертикали только палок у меня мало, другого достаточно!

Теория: Скорость звука в воздухе 331 метра в секунду. Частота Mojo 50 миллинов, за одну пятидясяти миллионную секунды звук проходит 6,6 тысячных миллиметра. Если у нас два звуко детектора на расстоянии 30 см, то один из них, который ближе к источника звука, сработает раньше, гораздо раньше.

Собираю схему из трех детекторов. Также собирал из двух, но срабатывает плохо, из трех тоже плохо срабатывает но это уже претензи к детекторам звука, вот если бы на их месте были цифровые микрофоны…

вот схема, сразу подключил двигатель

Фото в сборе, arduino nano не причем её просто тяжко выдирать из макетки

Добавляем в разделе Constraints в файл mojo.ucf строку

NET "soundright" LOC = P21 | IOSTANDARD = LVCMOS33;
NET "soundmid" LOC = P16 | IOSTANDARD = LVCMOS33;
NET "soundleft" LOC = P14 | IOSTANDARD = LVCMOS33;

Вот весь код который будет в разделе Source в файле mojo_top.v

module mojo_top(
    // 50MHz clock input
    input clk,
    // Input from reset button (active low)
    input rst_n,
    // cclk input from AVR, high when AVR is ready
    input cclk,
    // Outputs to the 8 onboard LEDs
    output[7:0]led,
    // AVR SPI connections
    output spi_miso,
    input spi_ss,
    input spi_mosi,
    input spi_sck,
    // sound detect
    input soundright,
    input soundleft,
    input soundmid,
    // AVR ADC channel select
    output [3:0] spi_channel,
    // Serial connections
    input avr_tx, // AVR Tx => FPGA Rx
    output avr_rx, // AVR Rx => FPGA Tx
    input avr_rx_busy // AVR Rx buffer full
    );
 
reg [7:0] r_led;
reg [7:0] l_led;
reg [7:0] m_led;
reg [32:0] cnt;
reg [32:0] cnt_sound;
reg [1:0] silence = 0;
 
wire rst = ~rst_n; // make reset active high
//wire soundright_n = soundright;
//wire soundleft_n = soundleft;
//wire soundmid_n = soundmid;


 
// these signals should be high-z when not used
assign spi_miso = 1'bz;
assign avr_rx = 1'bz;
assign spi_channel = 4'bzzzz;
assign led[7:5] = r_led;
assign led[4:3] = m_led;
assign led[2:0] = l_led;

 
always @(posedge clk)
begin

if (silence == 1)
begin
if (soundright  == 0)
begin
if (soundleft  == 0)
begin
if (soundmid  == 0)
begin
cnt_sound = cnt_sound + 1;
if (cnt_sound >= 5000000)
begin
silence = 0;
end
end else begin
cnt_sound = 0;
end
end else begin
cnt_sound = 0;
end
end else begin
cnt_sound = 0;
end
end

if (silence == 0)
begin

if (soundleft == 1)
begin
l_led = 7;

silence = 1;
end else begin
l_led = 0;
end
if (soundright == 1)
begin
r_led = 7;

silence = 1;
end else begin
r_led = 0;
end
if (soundmid == 1)
begin
m_led = 3;

silence = 1;
end else begin
m_led = 0;
end

end

end

endmodule

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

Пытался сделать более сложно улавливание, фиксировать прохождение звуковой волны слева на право, но работало плохо.

Теперь главный код

Добавляем в разделе Constraints в файл mojo.ucf строку

NET "soundright" LOC = P21 | IOSTANDARD = LVCMOS33;
NET "soundmid" LOC = P16 | IOSTANDARD = LVCMOS33;
NET "soundleft" LOC = P14 | IOSTANDARD = LVCMOS33;
NET "servo_mojo_pin" LOC = P1 | IOSTANDARD = LVTTL;

У нас будет вот такой модуль для мотора вот он sg90.v — здесь добавлен статус занят или нет мотор, пока занят звук не ловится. И в другом месте считается частота, должно разгрузить при простое.

module sg90 (
    input clk,  // clock
    input rst,  // reset
    output servo_mojo,
    output servo_busy_m,
  //  output moto_busy_m,
// input [0:0] moto_start_m,
 input signed [8:0] servoway_m
    );

//wire rst = ~rst_n; // make reset active high


reg [32:0] cnt;
reg [25:0] servo_cnt=45000;
reg [25:0] servo_cnt_data;
reg [0:0] servo;
reg [2:0] servorun=0;
reg [0:0] servoOn;


// these signals should be high-z when not used
assign servo_mojo = servo;
assign servo_busy_m = servoOn;
//assign moto_busy_m = servoOn;


always @(posedge clk)
begin
cnt = cnt + 1;

//if (moto_start_m == 1)
//begin
case (servorun)
0: begin
servo = 0;
servo_cnt_data = 35000 + servoway_m * 444;
servorun = 1;


end
1: begin
servo = 0;

if (cnt >= 1000000)
begin
cnt = 0;

if (servo_cnt == servo_cnt_data)
begin
//stop
servoOn = 0;
servorun = 0;
end else begin
servorun = 2;
end

end



end
2: begin
servoOn = 1;
servo = 1;
if (cnt >= servo_cnt)
begin
if (servo_cnt > servo_cnt_data)
begin
servo_cnt = servo_cnt - 222;
if (servo_cnt < servo_cnt_data)
begin
//stop
servoOn = 0;
servorun = 0;
servo_cnt = servo_cnt_data;
end else begin
servorun = 1;
end
end


if (servo_cnt < servo_cnt_data)
begin
servo_cnt = servo_cnt + 222;
if (servo_cnt > servo_cnt_data)
begin
//stop
servoOn = 0;
servorun = 1;
servo_cnt = servo_cnt_data;
end else begin
servorun = 0;
end
end
end

end

endcase
//end

end


endmodule

При первом включение мотор дергается но становится на центр.

главный код mojo_top.v

module mojo_top(
    // 50MHz clock input
    input clk,
    // Input from reset button (active low)
    input rst_n,
    // cclk input from AVR, high when AVR is ready
    input cclk,
    // Outputs to the 8 onboard LEDs
    output[7:0]led,
    // AVR SPI connections
    output spi_miso,
    input spi_ss,
    input spi_mosi,
    input spi_sck,
    // sound detect
    input soundright,
    input soundleft,
    input soundmid,
     output servo_mojo_pin,
   output servo_busy,
 
    
  
  
    // AVR ADC channel select
    output [3:0] spi_channel,
    // Serial connections
    input avr_tx, // AVR Tx => FPGA Rx
    output avr_rx, // AVR Rx => FPGA Tx
    input avr_rx_busy // AVR Rx buffer full
    );
 
reg [7:0] r_led;
reg [7:0] l_led;
reg [7:0] m_led;
reg [32:0] cnt_sound;
reg [1:0] silence = 0;



 


reg signed [8:0] servoway = 90;
wire servo_mojo_pin;

wire rst = ~rst_n; // make reset active high
//wire soundright_n = soundright;
//wire soundleft_n = soundleft;
//wire soundmid_n = soundmid;


 
// these signals should be high-z when not used
assign spi_miso = 1'bz;
assign avr_rx = 1'bz;
assign spi_channel = 4'bzzzz;
assign led[7:5] = r_led;
assign led[4:3] = m_led;
assign led[2:0] = l_led;

 
always @(posedge clk)
begin

if (servo_busy == 0)
begin
if (silence == 1)
begin
if (soundright  == 0)
begin
if (soundleft  == 0)
begin
if (soundmid  == 0)
begin
cnt_sound = cnt_sound + 1;
if (cnt_sound >= 500000)
begin
silence = 0;
cnt_sound = 0;
end
end else begin
cnt_sound = 0;
end
end else begin
cnt_sound = 0;
end
end else begin
cnt_sound = 0;
end
end
end else begin
r_led = 0;
m_led = 0;
l_led = 0;
silence =1;
end

if (silence == 0)
begin
if (soundleft == 1)
begin
r_led = 0;
m_led = 0;
l_led = 7;
servoway = servoway + 5;
if (servoway > 150)
begin
servoway = 150;
end
silence = 1;

end
if (soundright == 1)
begin
r_led = 7;
m_led = 0;
l_led = 0;
servoway = servoway - 5;
if (servoway < 30)
begin
servoway = 30;
end
silence = 1;

end
if (soundmid == 1)
begin
r_led = 0;
m_led = 3;
l_led = 0;
silence = 1;
end
end



end

sg90 move_moto (
    .clk(clk),
    .servoway_m(servoway),
    .servo_busy_m(servo_busy),
    .servo_mojo(servo_mojo_pin)
  );
  
  

endmodule

Углы ограничены 30 на 150, а то когда было 0 на 180, приходилось говорит датчики в спину, а они так фиговые!!!

Всё! Всё работает! Фигово но работает!

Битва за Моджо на планете Спартан6 в системе Плис — продолжается!


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

Ваш адрес email не будет опубликован. Обязательные поля помечены *