SPI: FPGA - Arduino Due

Brainbyter
Offline
Зарегистрирован: 21.12.2017
Уважаемые знатоки-ардуинщики! Просьба не ругать, не бить за столь возможно неверно изложенный текст. Собственно, имеются плата 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;
	}	
}

 

 

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

А если 41 строку заменить вот так :)

Serial.print("типа всё, вот данные: ");
Serial.println(REG_DATA, HEX);

 

Brainbyter
Offline
Зарегистрирован: 21.12.2017

Да вот проблема в том, что этот даже Serial.print("любой_текст") или Serial.println("любой_текст") срабатывают очень много раз в loop(), хотя послан из ПЛИС был всего один байт. Там в окне консоли становится всё заспамлено этими выводами строк из принта... хотя вроде как должно однократно сработать и вывести одну строчку же... Я даже частоту импусьсов клока (SCL) в ПЛИС менял на 1 Герц, чтоб хотя бы раз в секунду тикало...

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

Так срабатывает много раз и выводит правильно или как вы писали в первом сообщении "принт печатал всякую ересь"?

Ересь из-за записи вида "строка" + переменная. Если же выводит то что надо, но много раз то вставьте после 42 строки delay(100500) и посмотрите что там выводится.

Brainbyter
Offline
Зарегистрирован: 21.12.2017

Я даже переменную убрал из принта, просто хотел напечатать одну строку текста по окончанию отработки алгоритма, как бы увидеть завершение, что все импульсы отработали правильно, но когда клок-импульсы приходят - тикает клок, то Serial.println() по непонятным мне условиям постоянно пишет строками, изредка останавливаясь, потом по какому-нить клоку еще начинает спамить в консоль текст до тех пор пока байт не передастся из ПЛИС... а учитывая что я даже частоту поставил 1 Гц, то за 8 клоков там печатается сотня записей...

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Почему пины pinSCL и pinSS не инициализировали ? Считаете - хрен с ними ? :)

Brainbyter
Offline
Зарегистрирован: 21.12.2017

Пардон, в коде в первой строке int не указан, но это я дописывал вручную тут в теме когда писал пост, а в проекте в файле .ino все норм, int есть. Эээ, а вот строчки 12 и 13 - это неправильная инициализация этих двух пинов pinSCL и pinSS? Они ж вроде как настроены на прерывание...

sadman41
Offline
Зарегистрирован: 19.10.2016

Brainbyter пишет:
Эээ, а вот строчки 12 и 13 - это неправильная инициализация этих двух пинов pinSCL и pinSS? Они ж вроде как настроены на прерывание...

http://arduino.ru/Reference/AttachInterrupt принимает на вход не всё подряд.

Brainbyter
Offline
Зарегистрирован: 21.12.2017

sadman41 пишет:

Brainbyter пишет:
Эээ, а вот строчки 12 и 13 - это неправильная инициализация этих двух пинов pinSCL и pinSS? Они ж вроде как настроены на прерывание...

http://arduino.ru/Reference/AttachInterrupt принимает на вход не всё подряд.

Т.е., я выводы не правильно определи? ..а как же тогда надо их назначить, чтоб работали только для вызова одной функции, по RISING?

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Да так же как pinMOSI проиницилизируйте, хуже не будет. Кстати, вы уверены, что на ногах подтяжка не нужна ?

Просто добавьте строки инициализации, а настройку прерываний (12-13) не трогайте.

Brainbyter
Offline
Зарегистрирован: 21.12.2017

Попробую, спасибо! Результат скрином отправлю, что пишет в консоль... Доберусь до дома - проверю)
А подтяжка.. там ведь ПЛИС строго уровни устанавливает. Третьего нет состояния. Когда нет передачи - все уровни в единице стоят

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Дык у плиса могут быть настроики ног в "открытый коллектор", ну или какой нибудь эквивалент такого состояния. То что вы описали может быть при таком раскладе.

Brainbyter
Offline
Зарегистрирован: 21.12.2017

brokly пишет:

Дык у плиса могут быть настроики ног в "открытый коллектор", ну или какой нибудь эквивалент такого состояния. То что вы описали может быть при таком раскладе.

Ага...это только у не используемых ног они в третьем состоянии, в настройках компилятора под плис. Под рукой не было осциллографа, поэтому пришлось сделать частоту SCL - 1 Гц, чтоб на светодиодах на макетке посмотреть клоки, SS уровень и выставляемые данные на MOSI. Светодиоды как надо отрабатывают состояния пинов из ПЛИС...

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Ну теперь проверьте с настройкой ног ардуины.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Brainbyter, посмотрел я на Ваш код, IMHO он работает в точности так, как написан.

Вот попытайтесь сформулировать словами, что у Вас делает функция read_bit(). Она ведь делает совсем не то, что можно было бы подумать, исходя из ее названия.

Brainbyter
Offline
Зарегистрирован: 21.12.2017

Всем спасибо за подсказки! Особенно навели мысли про уровни на линиях... Но вот я балда, не учёл одного, что питались платы то от разных источников. Нужно было либо земли объединить, либо от одного источника питать и земля была бы общая, что я и сделал - от адаптера запитал все две платы =). Всё работает!

Функция read_bit() - ага, я сначала думал там в ней прописать вот это:

 DATA_REG >>= 1;
 bitWrite(DATA_REG, 7, digitalRead(pinMOSI));

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

Единственный момент... я хотел выдавать на шину MOSI данные с частотой 2,5 Мгц, а что-то как-то не получается: биты не успевают защёлкиваться в Ардуине то, выводятся нули, т. е., срабатывает нарастающий фронт от SS, а клоки не успевают. В итоге методом проб установил частоту SCL с ПЛИС 150 кГц примерно, что не есть хорошо. Но данные защёлкиваются и в мониторе принтуются =).

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Brainbyter пишет:

Функция read_bit() - ага, я сначала думал там в ней прописать вот это:

 DATA_REG >>= 1;
 bitWrite(DATA_REG, 7, digitalRead(pinMOSI));

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

Ну да.

Только все хорошо в меру.

Для чего Вы используете прерывания?

Чтобы успеть прочитать информацию, пока она еще есть на пинах.

Теперь Вы чесно ловите прерывание, взводите флаг, что пора читать данные, и ... не спеша пытаетесь прочесть их из loop(). И никто не гарантирует, что пока loop() сподобится прочитать данные, сигнал на ножках не изменится.

Цитата:

Единственный момент... я хотел выдавать на шину MOSI данные с частотой 2,5 Мгц, а что-то как-то не получается: биты не успевают защёлкиваться в Ардуине то, выводятся нули, т. е., срабатывает нарастающий фронт от SS, а клоки не успевают. В итоге методом проб установил частоту SCL с ПЛИС 150 кГц примерно, что не есть хорошо. Но данные защёлкиваются и в мониторе принтуются =).

Вы используете довольно медленную функцию digitalRead(), которая на Due выполняется примерно 1.2 мкс. Если считать, что между фронтом CLK и чтением должно проходить не более четверти периода, то целый период оказывается порядка 5 мкс, т.е. максимальная частота SPI ограничена значением примерно в 200 кГц. Ни о каких 2.5 МГц здесь речь не идет. Естественно, это все при условии, что чтение происходит в прерывании, а не в loop().

В общем, переносите чтение в прерывание, само чтение осуществляйте не digitalRead(), а гораздо более быстрой командой чтения из порта, данные заносите не bitWrite(), а нормальными битовыми операциями, а флаг готовности выставляйте только после того, как байт принят целиком. А вообще - лучше для принятых данных организовать кольцевой буфер, иначе в тот момент, пока Вы выводите что-то в Serial либо управляете другой периферией, можете пропустить целую кучу символов.

 

Brainbyter
Offline
Зарегистрирован: 21.12.2017

Cпасибо! А Serial так то вообще мне не нужен, эт для отладки посмотреть мне. А сами данные планирую потом в ФИФО кидать... да и между посылками из ПЛИС большие интервалы будут. А как еще можно считать с пина, кроме как digitalRead() ? =). 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Я же написал - читать из порта. Смотрите дэйташит http://www.atmel.com/Images/Atmel-11057-32-bit-Cortex-M3-Microcontroller-SAM3X-SAM3A_Datasheet.pdf

Доступ к отдельным пинам имеет смысл делать через bit-banding.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Brainbyter пишет:

но прочитал, что по прерыванию функции должны быть короткие...

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

На самом деле бытующее мнение, что digitalRead медленный, верно от части, как правило когда в данно месте нога представлена переменной.

Brainbyter
Offline
Зарегистрирован: 21.12.2017

Хм, спасибо за разъяснения! Буду курить даташит тогда, почитаю про bit-banding :)

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

brokly пишет:

Что касается совета про медленное чтение пина, он очень правильный, но в вашем случае сам пин у вас объявлен "жестко", дефайном, поэтому компилятор сделает и так самую быструю процедуру чтения. Все нужные ему данные про бит и порт он подберет на этапе компиляции. Если же имеется в виду именно кортековский битбандинг, то я не знаю использует ли его компилятор в этом случае, думаю скорее да.

На самом деле бытующее мнение, что digitalRead медленный, верно от части, как правило когда в данно месте нога представлена переменной.

Ну да, "компилятор все сделает сам!"

Не будет такого.

И что использовать - битбандинг или нет, зависит не от компилятора, а от того, кто написал функцию.

Короче: чтение из порта посредством digitalRead() - порядка 100 тактов. Кто-нибудь всерьез считает, что быстрее не сделать никак?

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

andriano пишет:

Ну да, "компилятор все сделает сам!"

Не будет такого.

И что использовать - битбандинг или нет, зависит не от компилятора, а от того, кто написал функцию.

Короче: чтение из порта посредством digitalRead() - порядка 100 тактов. Кто-нибудь всерьез считает, что быстрее не сделать никак?

Не собираюсь вступать в полемику. Проверяйте сами. И не забывайте, что речь идет о DUE.