Данный урок посвящен последовательному интерфейсу — SPI. Интерфейс позволяет передавать информацию между несколькими устройствами, не обязательно микроконтроллерами. Особенность заключается в том, что имеется одно ведущее устройство и одно или несколько ведомых. Так же интерфейс используется многими программаторами для прошивки микроконтроллеров.
SPI достаточно прост в использовании, рассмотрим пример, в котором используется один ведущий (master — босс :D) и два ведомых микроконтроллера (slave — работяги).
SCK — Тактовый сигнал. Используется для синхронизации данных.
MOSI — передатчик ведущего, приемник ведомого
MISO — приемник ведущего, передатчик ведомого
SS — выбор ведомого
Для того, чтобы ведомый 0 принимал команды, нужно на его вход SS подать логический 0. Тогда он будет знать, что общение идет именно с ним, ведомый 1 будет все игнорировать. Аналогично, чтобы «активировать» ведомого 1, нужно подать на его вход SS логический 0, а на вход SS ведомого 0 — логическую единицу. В таком случае слушать команды будет только ведомый 1.
В отличие от предыдущих уроков, здесь придется создать 2 прошивки. Задача ведущего: активировать ведомого 0, послать команду мигнуть светодиодом, переключиться на 1, снова послать команду мигнуть светодиодом, повторить. Задача ведомых принять команду и мигать светодиодом. Получится одна прошивка для ведущего, вторая для двух ведомых. Создаем проект, на закладке SPI для master настройки слева, для slave справа. Не забудьте поставить галочку SPI Interrupt у slave.
Прошивка для ведущего будет выглядеть так:
#include <mega8.h> #include <spi.h> #include <delay.h> void main(void) { // Input/Output Ports initialization // Port B initialization // Func7=In Func6=In Func5=Out Func4=In Func3=Out Func2=Out Func1=Out Func0=Out // State7=T State6=T State5=0 State4=T State3=0 State2=0 State1=1 State0=0 PORTB=0x02; DDRB=0x2F; PORTC=0x03; DDRC=0x00; // SPI initialization // SPI Type: Master // SPI Clock Rate: 125,000 kHz // SPI Clock Phase: Cycle Half // SPI Clock Polarity: Low // SPI Data Order: MSB First SPCR=0x52; SPSR=0x00; while (1) { PORTB.2=0; //Переключаемся на ведомого 0 spi('1'); //Отсылаем ему 1, чтобы он включил светодиод delay_ms(100); //ждем spi('0'); //Отсылаем ему 0, чтобы он выключил светодиод delay_ms(100); //ждем PORTB.2=1; //Делаем ведомого 0 не активным PORTB.1=0; //Переключаемся на ведомого 1, далее по аналогии с 0 spi('1'); delay_ms(100); spi('0'); delay_ms(100); PORTB.1=1; }; } |
Для ведомых прошивка будет выглядеть так:
#include <mega8.h> interrupt [SPI_STC] void spi_isr(void) { unsigned char data; data=SPDR; //читаем приходящие байты if(data=='1') //если пришла 1, включить светодиод { PORTD=0xFF; } if(data=='0') //если пришел 0, выключить светодиод { PORTD=0x00; } } void main(void) { PORTB=0x00; DDRB=0x10; PORTD=0x00; DDRD=0xFF; // SPI initialization // SPI Type: Slave // SPI Clock Rate: 125,000 kHz // SPI Clock Phase: Cycle Half // SPI Clock Polarity: Low // SPI Data Order: MSB First SPCR=0xC2; SPSR=0x00; // Clear the SPI interrupt flag #asm in r30,spsr in r30,spdr #endasm // Global enable interrupts #asm("sei") while (1) { }; } |
Обратите внимание, что прием происходит в прерывании, т.е. в основном цикле мы можем делать, что угодно, а как только информация придет по SPI, автоматически начнет исполняться код в прерывании.
В результате микроконтроллеры будут весело перемигиваться. Прошивки и файл протеуса тут
а как сделать чтобы ведомый передавал ведомому, или переключать ведомого на ведущего, мне нужно чтобы любой с любым общался ❓
тогда SPI не ваш выбор
ясно, но по-любому можно обратно ведущему передавать, как реализуется примерно?
По поводу переключения режимов мастер-ведущий под воздействием внешнего сигнала, то в spi это возможно. По крайней мере, я так понял даташит 🙂
Если МК в режиме мастер, но вывод SS# настроен как вход, то, пока на нем + питания — МК в режиме мастер, появился 0 — переходит в ведомый. Я с этим не сталкивался в живую никогда, но в даташите вроде как-то так написано 🙂
Есть мнение, чтобы получить данные от слейва, нужно от мастером передать любую хрень слейву. Тогда в регистре SPDR мастера окажутся данные от слейва, но как бы передачу инициирует все равно мастер. Можно просто переконфигурировать мастер в слейва, а слейва в мастера.
Если нужно подключить более двух устройств. Под вывод SS можно использовать любые свободные ноги или обязательно только те которые показаны в примере (порт Б)?
любые
Спасибо за толковые уроки! если возможно напишите урок по беспроводной передаче данных,например с подключением радиомодулей к МК AVR. В CodeVisionAVR.
Подскажите а как с 1й МК передать по очереди значения data1, 2, 3 и записать их по очереди во 2ю МК…в разные переменные.
можно посмотреть статью http://avr-start.ru/?p=2230 там про uart, но для spi суть не поменяется. Например, посылаете символ определяющий 1 переменную, за ним само значение переменной и символ окончания строки *100# — отослали значение переменной 1 равное 100. Для второй переменной символ начала можно изменить, например так %200#. Со стороны приемника смотрите,
if(SPDR==’*’)
{
значит последующие данные пишем в переменную 1, до знака #
}
if(SPDR==’%’)
{
читаем в переменную 2, до знака #
}
Спасибо за статью! Все понятно, все работает.
В ведомом контроллере- как передать данные из прерывания в основной цикл программы? Глобальные переменные почему-то не работают.
сделать глобальную переменную volatile
Подскажите, а по скорости передачи данных какой интерфейс быстрее SPI или UART? я предполагаю что SPI, но из-за малого опыта не могу точно знать. Заранее спасибо.
SPI быстрее
Спасибо огромное! Это было первое, что я сделал на авр. Подскажите, возможно сделать так, чтобы команды от SPI выполнялись с безусловным приоритетом над основной программой? Если можно, подскажите, плиз)
Используйте прерывание по приему, настраивается через CodeWizard в том же разделе галочка SPI Interrupt
Так и сделал, но при передаче команд выполняются обе программы разом, насколько понял(.
Вот код для приёмника)
#include
// SPI interrupt service routine
interrupt [SPI_STC] void spi_isr(void)
{
unsigned char data;
data=SPDR;
if(data==’1′) //если пришла 1, включить светодиод
{
PORTC.1=1;
}
if(data==’0′) //если пришел 0, выключить светодиод
{
PORTC.1=0;
}
if(data==’2′) //если пришла 1, включить светодиод
{
PORTC.2=1;
}
if(data==’3′) //если пришел 0, выключить светодиод
{
PORTC.2=0;
}
if(data==’4′) //если пришла 1, включить светодиод
{
PORTC.3=1;
}
if(data==’5′) //если пришел 0, выключить светодиод
{
PORTC.3=0;
}
if(data==’6′) //если пришла 1, включить светодиод
{
PORTC.4=1;
}
if(data==’8′) //если пришел 0, выключить светодиод
{
PORTC.4=0;
}
}
// Declare your global variables here
void main(void)
{
PORTB=0x3C;
DDRB=0x10;
PORTC=0x00;
DDRC=0x1E;
PORTD=0xFF;
DDRD=0x00;
ACSR=0x80;
SFIOR=0x00;
// SPI initialization
// SPI Type: Slave
// SPI Clock Rate: 125,000 kHz
// SPI Clock Phase: Cycle Half
// SPI Clock Polarity: Low
// SPI Data Order: MSB First
SPCR=0xC1;
SPSR=0x00;
// Clear the SPI interrupt flag
#asm
in r30,spsr
in r30,spdr
#endasm
// Global enable interrupts
#asm(«sei»)
while (1)
{
if(PIND.1==0)
{
PORTC.1=1;
PORTC.3=0;
PORTC.2=0;
PORTC.4=0;
}
if(PIND.2==0)
{
PORTC.1=0;
PORTC.3=0;
PORTC.2=0;
PORTC.4=1;
}
}
}
не совсем понял что вы хотите сделать
Смысл в том, чтобы в отсутствии команд управления выполнялся код внутри бесконечного цикла. Пока он пустой, всё нормально управляется, когда я пытаюсь задать опрос входов, и то, и другое работает плохо
С приходом команды по SPI по идее должна выполняться только эта команда… если я правильно понял). Прошу извинить неграмотность чайника)
У вас есть 2 события влияющие на состояние выхода, обдумайте то, как должен вести себя микроконтроллер, например пока он в прерывании, основной код не выполняется, но прерывание выполняется достаточно быстро, поэтому можно сделать задержку при выходе из прерывания. Если нужно чтобы прерывание не мешало выполнению основного цикла, то можно запрещать прерывания в моменты опроса кнопок. Вся проблема лишь в одном — составить нормальный пошаговый алгоритм, т.е. придумать то как оно должно себя вести.
Всё, разобрался, робот работает)). Управление было через nrf24l01, в этом и была зарыта собака. В протеусе то всё работало… Подкорректировал код и вроде всё путем
Спасибо ещё раз0. Не подскажете, можно как-нибудь магнитный компас от ардуины приспособить как датчик направления без самой ардуины? Может, есть где-то примеры?
может есть 🙂 не знаю
Здравствуйте! Подскажите пожалуйста, какая может быть максимально допустимая протяженность шины SPI? У меня задача, разнести четыре контроллера на расстояние 5-6 м один от другого, а управление и синхронизация должны осуществляться от MASTER. Или посоветуйте, как это реализовать.
Здравствуйте, нужно передавать через приёмопередатчик с 8-битным SPI интерфейсом данные от датчика DS18B20. Данные с датчика получаются в формате float, а он вроде как 32-битный… Как правильно реализовать разбитие 32 бит по 8 на передающей стороне, и сборку на приёмной ? Или как альтернатива- преобразовать float к byte, и передать один байт ? Точность в один градус устроит, да и сборка-разборка отпадает.
посмотрите принцип здесь http://avr-start.ru/?p=2230
здравствуйте подскажите или укажите..на грабли..кваром как у вас на рис(режим 0) с настройками настроил 2 проекта для ведущего и ведомого… в проте ничего лишнего только 2 проца….соединены линии моськи и синхро, на ведомом линию выбора жестко заземлил…в программе переменная тупо отправляется в спи в цикле с делаем стандартной функцией спи(дата)….так вот связка из 2 мег8 работает на ура…связка когда мастер мега 128….слейв 8..да пофиг какая, с мастера приходят некорректные данные…мегу128 мастером кода параметрил так как у вас на рисунке…ставлю в проте мастера из меги8 всё корректно идет…
если не трудно плиз рабочий кусок на спи мастера меги 128 привести…
заранее спс…
наверняка забыли на мк в протеусе установить частоту.
спс за ответ…вчера это были скорей эмоции…ночь наверно…более внимательно посмотрел на то что скомпилировал квар и конечно же…как вы и сказали правильно выставил частоту тактирования в проте…в проекте значения из спи мастера слейв автоматом закидывал в виртуал терминал..ия их от туда и наблюдал…так вот + неправильно был настроен терминал(скорость)…из за некорректно установленной частоты тактирования процессора…наступив на одни грабли..сверху еще и прикрыл себя другими граблями…внимательность и еще раз внимательность…и протеус будет меньше подводить
еще раз спасибо…а уроки у вас зачетные..просто всё изложено.
Подскажите, какая максимальная длина кабеля и какой кабель использовать? Uart насколько я помню (во всяком случае при пк-мк) хорош тем, что разница между 0 и 1 24-30 вольт и наводки с помехами не страшны при длине кабеля 10-15 метров точно. Хотя тут наверное все дело в напряжении и если по uart передавать между контроллерами то длинный кабель не прокатит. Есть у Вас какие то наблюдения касательно длины и типа кабеля при соединении разрозненных устройств ( от 1 метра и более)?
для передачи на длинные расстояния понадобится конверторы RS232 с обвязкой, схема подключения в даташите. например AD232
подскажите, как управлять сдвиговым параллельым регистром
http://avr-start.ru/?p=2656
Есть SD карта и несколько микроконтроллеров.
Возможна ли реализовать им всем доступ к одной карте?
если наставить внешних мультиплексоров, то наверно возможно.
Может и глупый вопрос но все же, возможно ли посредством меги8 и интерфейса SPI прошить флешку из мультимедиаплееров которые прошиваються через программаторы SPI?
Если знать протокол, то вполне вероятно
Когда еще только столкнулся с контроллерами сделал собственный SPI там почти так же только еще одна ножка переключающая из мастера в слейв и обратно и если больше двух приемников надо транзисторы добавлять для усиления иначе не сработает я писал условия при разных частотах 4.0000 мгц и 4.0096 и 3.6864 мгц все работало по наличию напряжения на выводах как только принял подал знак как только подал знак следующий такт приемника и работали все с разными частотами. Передавались данные с максимально возможной скоростью к проводникам подсоединял сопротивления 10 — 22 кОм на минус. Сколько таких сделал никогда не сбивались со своих тактов.
Я его назвал DEMU и DEMI по началу был просто передатчик приемник потом модернизировал протокол 😀