Если коротко то не покупайте 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, приходилось говорит датчики в спину, а они так фиговые!!!
Всё! Всё работает! Фигово но работает!
Добавить комментарий