SPI: FPGA - Arduino Due
- Войдите на сайт для отправки комментариев
Чт, 21/12/2017 - 10:38
Уважаемые знатоки-ардуинщики! Просьба не ругать, не бить за столь возможно неверно изложенный текст. Собственно, имеются плата Arduino Due, плата с FPGA Cyclone II. С Ардуино никогда не работал, из железа опыт только с ПЛИС, ну и старые наши дубовые логические микросхемы. Вот, необходимо наладить между двумя платами интерфейс SPI. Мастером выступает ПЛИСина, слэйв - Ардуино.Задействованы ввего три вывода: SCL, SS, MOSI. Выбор устройства: активный низкий уровень SS, по спаду клока выставляю бит данных из регисра, по фронту RISING - Адруино должна читать бит на проводе MOSI. В посылке 8 бит, 8 клоков, младшим битом вперед.
Почитав в общем мануал-подсказку из коробки с Ардуино и изучив режимы выводов у платы, по логике, я пин MOSI определил как INPUT, SCL и SS - проинициализировал как attachInterrupt.
В результате сопряжения ПЛИС правильно формирует импульсы, осцилограф и светодиоды это показали =), а вот чёто Ардуино не сработало по прерываниям и в отладчике в мониторе творилось такооооое, принт печатал всякую ересь, только не данные. Что я делаю не так, направьте мну в нужное русло? И да, я пока еще не дошёл до изучения библиотеки SPI для Ардуино... хотелось пока просто реализовать чисто логику устройства на пинах по прерываниям, так сказать первая тестовая программа по ловле клоков =). Заранее спасибо!
const pinSS = 22; const int pinSCL = 24; const int pinMOSI = 26; volatile byte REG_DATA = 0x00; // сдвиговый регистр, собтвенно, байт данных volatile boolean flag_read_bit = false; volatile boolean flag_end_tx = false; void setup() { Serial.begin(9600); pinMode(pinMOSI, INPUT); attachInterrupt(pinSCL, read_bit, RISING); attachInterrupt(pinSS, end_tx, RISING); } void read_bit() // должно срабатывать каждый RISING клок... { if (digitalRead(pinSS)== LOW) { if (flag_read_bit == false) flag_read_bit = true; } } void end_tx() // должно сработать по RISING, в конце передачи { if (flag_end_tx == false) flag_end_tx = true; } void loop { if (flag_read_bit == true) { bitWrite(REG_DATA, 7, digitalRead(pinMOSI)); // записал в старший разряд REG_DATA >>= 1; // сдвиг вправо на 1 разряд flag_read_bit = false; } if (flag_end_tx) { Serial.println("типа всё, вот данные: " + REG_DATA); flag_end_tx = false; } }
А если 41 строку заменить вот так :)
Да вот проблема в том, что этот даже Serial.print("любой_текст") или Serial.println("любой_текст") срабатывают очень много раз в loop(), хотя послан из ПЛИС был всего один байт. Там в окне консоли становится всё заспамлено этими выводами строк из принта... хотя вроде как должно однократно сработать и вывести одну строчку же... Я даже частоту импусьсов клока (SCL) в ПЛИС менял на 1 Герц, чтоб хотя бы раз в секунду тикало...
Так срабатывает много раз и выводит правильно или как вы писали в первом сообщении "принт печатал всякую ересь"?
Ересь из-за записи вида "строка" + переменная. Если же выводит то что надо, но много раз то вставьте после 42 строки delay(100500) и посмотрите что там выводится.
Я даже переменную убрал из принта, просто хотел напечатать одну строку текста по окончанию отработки алгоритма, как бы увидеть завершение, что все импульсы отработали правильно, но когда клок-импульсы приходят - тикает клок, то Serial.println() по непонятным мне условиям постоянно пишет строками, изредка останавливаясь, потом по какому-нить клоку еще начинает спамить в консоль текст до тех пор пока байт не передастся из ПЛИС... а учитывая что я даже частоту поставил 1 Гц, то за 8 клоков там печатается сотня записей...
Почему пины pinSCL и pinSS не инициализировали ? Считаете - хрен с ними ? :)
Пардон, в коде в первой строке int не указан, но это я дописывал вручную тут в теме когда писал пост, а в проекте в файле .ino все норм, int есть. Эээ, а вот строчки 12 и 13 - это неправильная инициализация этих двух пинов pinSCL и pinSS? Они ж вроде как настроены на прерывание...
http://arduino.ru/Reference/AttachInterrupt принимает на вход не всё подряд.
http://arduino.ru/Reference/AttachInterrupt принимает на вход не всё подряд.
Т.е., я выводы не правильно определи? ..а как же тогда надо их назначить, чтоб работали только для вызова одной функции, по RISING?
Да так же как pinMOSI проиницилизируйте, хуже не будет. Кстати, вы уверены, что на ногах подтяжка не нужна ?
Просто добавьте строки инициализации, а настройку прерываний (12-13) не трогайте.
Попробую, спасибо! Результат скрином отправлю, что пишет в консоль... Доберусь до дома - проверю)
А подтяжка.. там ведь ПЛИС строго уровни устанавливает. Третьего нет состояния. Когда нет передачи - все уровни в единице стоят
Дык у плиса могут быть настроики ног в "открытый коллектор", ну или какой нибудь эквивалент такого состояния. То что вы описали может быть при таком раскладе.
Дык у плиса могут быть настроики ног в "открытый коллектор", ну или какой нибудь эквивалент такого состояния. То что вы описали может быть при таком раскладе.
Ага...это только у не используемых ног они в третьем состоянии, в настройках компилятора под плис. Под рукой не было осциллографа, поэтому пришлось сделать частоту SCL - 1 Гц, чтоб на светодиодах на макетке посмотреть клоки, SS уровень и выставляемые данные на MOSI. Светодиоды как надо отрабатывают состояния пинов из ПЛИС...
Ну теперь проверьте с настройкой ног ардуины.
Brainbyter, посмотрел я на Ваш код, IMHO он работает в точности так, как написан.
Вот попытайтесь сформулировать словами, что у Вас делает функция read_bit(). Она ведь делает совсем не то, что можно было бы подумать, исходя из ее названия.
Всем спасибо за подсказки! Особенно навели мысли про уровни на линиях... Но вот я балда, не учёл одного, что питались платы то от разных источников. Нужно было либо земли объединить, либо от одного источника питать и земля была бы общая, что я и сделал - от адаптера запитал все две платы =). Всё работает!
Функция read_bit() - ага, я сначала думал там в ней прописать вот это:
но прочитал, что по прерыванию функции должны быть короткие, быстро выполнимые, поэтому решил просто завести булевский флажок в теле функции, а в цикле loop() опрашивать и сбрасывать его, и выполнять сдвиг и чтение с MOSI =).
Единственный момент... я хотел выдавать на шину MOSI данные с частотой 2,5 Мгц, а что-то как-то не получается: биты не успевают защёлкиваться в Ардуине то, выводятся нули, т. е., срабатывает нарастающий фронт от SS, а клоки не успевают. В итоге методом проб установил частоту SCL с ПЛИС 150 кГц примерно, что не есть хорошо. Но данные защёлкиваются и в мониторе принтуются =).
Функция read_bit() - ага, я сначала думал там в ней прописать вот это:
но прочитал, что по прерыванию функции должны быть короткие, быстро выполнимые, поэтому решил просто завести булевский флажок и в цикле loop() опрашивать и сбрасывать его =).
Ну да.
Только все хорошо в меру.
Для чего Вы используете прерывания?
Чтобы успеть прочитать информацию, пока она еще есть на пинах.
Теперь Вы чесно ловите прерывание, взводите флаг, что пора читать данные, и ... не спеша пытаетесь прочесть их из loop(). И никто не гарантирует, что пока loop() сподобится прочитать данные, сигнал на ножках не изменится.
Единственный момент... я хотел выдавать на шину MOSI данные с частотой 2,5 Мгц, а что-то как-то не получается: биты не успевают защёлкиваться в Ардуине то, выводятся нули, т. е., срабатывает нарастающий фронт от SS, а клоки не успевают. В итоге методом проб установил частоту SCL с ПЛИС 150 кГц примерно, что не есть хорошо. Но данные защёлкиваются и в мониторе принтуются =).
Вы используете довольно медленную функцию digitalRead(), которая на Due выполняется примерно 1.2 мкс. Если считать, что между фронтом CLK и чтением должно проходить не более четверти периода, то целый период оказывается порядка 5 мкс, т.е. максимальная частота SPI ограничена значением примерно в 200 кГц. Ни о каких 2.5 МГц здесь речь не идет. Естественно, это все при условии, что чтение происходит в прерывании, а не в loop().
В общем, переносите чтение в прерывание, само чтение осуществляйте не digitalRead(), а гораздо более быстрой командой чтения из порта, данные заносите не bitWrite(), а нормальными битовыми операциями, а флаг готовности выставляйте только после того, как байт принят целиком. А вообще - лучше для принятых данных организовать кольцевой буфер, иначе в тот момент, пока Вы выводите что-то в Serial либо управляете другой периферией, можете пропустить целую кучу символов.
Cпасибо! А Serial так то вообще мне не нужен, эт для отладки посмотреть мне. А сами данные планирую потом в ФИФО кидать... да и между посылками из ПЛИС большие интервалы будут. А как еще можно считать с пина, кроме как digitalRead() ? =).
Я же написал - читать из порта. Смотрите дэйташит http://www.atmel.com/Images/Atmel-11057-32-bit-Cortex-M3-Microcontroller-SAM3X-SAM3A_Datasheet.pdf
Доступ к отдельным пинам имеет смысл делать через bit-banding.
но прочитал, что по прерыванию функции должны быть короткие...
Однако "короткие" без фанатизма, в вашем случае в прерывании вы должны считать бит, сохранить его в буфере, при необходимости переключиться на следующий байт буфера. Что касается совета про медленное чтение пина, он очень правильный, но в вашем случае сам пин у вас объявлен "жестко", дефайном, поэтому компилятор сделает и так самую быструю процедуру чтения. Все нужные ему данные про бит и порт он подберет на этапе компиляции. Если же имеется в виду именно кортековский битбандинг, то я не знаю использует ли его компилятор в этом случае, думаю скорее да.
На самом деле бытующее мнение, что digitalRead медленный, верно от части, как правило когда в данно месте нога представлена переменной.
Хм, спасибо за разъяснения! Буду курить даташит тогда, почитаю про bit-banding :)
Что касается совета про медленное чтение пина, он очень правильный, но в вашем случае сам пин у вас объявлен "жестко", дефайном, поэтому компилятор сделает и так самую быструю процедуру чтения. Все нужные ему данные про бит и порт он подберет на этапе компиляции. Если же имеется в виду именно кортековский битбандинг, то я не знаю использует ли его компилятор в этом случае, думаю скорее да.
На самом деле бытующее мнение, что digitalRead медленный, верно от части, как правило когда в данно месте нога представлена переменной.
Ну да, "компилятор все сделает сам!"
Не будет такого.
И что использовать - битбандинг или нет, зависит не от компилятора, а от того, кто написал функцию.
Короче: чтение из порта посредством digitalRead() - порядка 100 тактов. Кто-нибудь всерьез считает, что быстрее не сделать никак?
Ну да, "компилятор все сделает сам!"
Не будет такого.
И что использовать - битбандинг или нет, зависит не от компилятора, а от того, кто написал функцию.
Короче: чтение из порта посредством digitalRead() - порядка 100 тактов. Кто-нибудь всерьез считает, что быстрее не сделать никак?
Не собираюсь вступать в полемику. Проверяйте сами. И не забывайте, что речь идет о DUE.