Данный урок посвящен последовательному интерфейсу — 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
#include
#include
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
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, автоматически начнет исполняться код в прерывании.
В результате микроконтроллеры будут весело перемигиваться. Прошивки и файл протеуса тут





В атмеге8 есть выбор msb/lsb. Смотрите биты DORD
Спасибо, Админ ! Нашел DORD и не только в Meге8.
Приведенный пример, к сожалению, не даёт ответа на простой и естественный вопрос: как передать от slave данные к master?
Если поставить в прерывании spi(x), то контроллер просто зависнет, как в реале, так и в протеусе. Если в том же прерывании попробовать записать SPDR = data, как советуют на некоторых форумах, то это попросту не работает.
Не подскажете, как решить очевидную задачу, которая не очевидна?
По умолчанию он шлет FF, то есть попросту единицу.