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