Связка по SPI на примере контроллеров mega32 и tiny2313

Авторы: 

Маленький пример обмена по SPI (режим 0) между ATmega32(подчиненный) и ATtiny2313(мастер) по схеме предложенной автором авриэла для программирования двух контроллеров через один разъем, код компилировался на AVR Studio 4.16.

У ATmega32 (использованная частота - 7.xxxxМГц) SPI аппаратный:

void SPI_SlaveInit(void)
{
        /* MISO делаем выходом */
        DDRB = (1<<PB6);
        PORTB |= (1<<PB4); /* Инициализируем SPI, Порядок битов - от младшего к старшему, разрешаем прерывание по получению байта */
        SPCR = (1<<SPE)|(1<<DORD)|(1<<SPIE);
        SPDR=0; //данные которые получит мастер
}
ISR(SPI_STC_vect) //обработчик прерывания
{
        SPDR+=1; //в следующий обмен данными с мастером вернем полученное число на 1 больше
}

ATtiny2313 (использованная частота - 8МГц) имеет, скажем так, полуаппаратную реализацию SPI (USI), но при используемом соединении она не может ее использовать в режиме мастера, поэтому сделан программный SPI (выводы MISO и MOSI обозначены правильно лишь для программатора и режима слейв, а в режиме мастера все с точностью до наоборот, об этом сказано в документации, но можно просто не обратить внимания):

void PSPI_Init() //настройка порта
{
        //PB0 - сигнал /SS для слейва, остальное согласно схеме
        PORTB = (1<<PB0)|(1<<PB5); //MOSI и /SS - выставить в 1
        DDRB = (1<<PB5)|(1<<PB7)|(1<<PB0);
}
uint8 PSPI_Send(uint8 val) //процедура приемо-передачи
{
 uint8 tmp;
        PORTB=(val&1)<<PB5; //SCK=0 и отправим бит 0 слейву
        val>>=1; //почистим отправленный бит
        tmp=PINB; //задержка
        tmp=PINB&0x40; //читаем бит
        PORTB|=(1<<PB7); //SCK=1
        tmp<<=1; //подвинем полученный бит на 7-ю позицию
        val|=tmp; //запишем его в переменную       

        //далее все повторим еще 7 раз
        PORTB=(val&1)<<PB5; //1 бит
        val>>=1;
        tmp=PINB;
        tmp=PINB&0x40;
        PORTB|=(1<<PB7);
        tmp<<=1;
        val|=tmp;
        PORTB=(val&1)<<PB5; //2 бит
        val>>=1;
        tmp=PINB;
        tmp=PINB&0x40;
        PORTB|=(1<<PB7);
        tmp<<=1;
        val|=tmp;
        PORTB=(val&1)<<PB5; //3 бит
        val>>=1;
        tmp=PINB;
        tmp=PINB&0x40;
        PORTB|=(1<<PB7);
        tmp<<=1;
        val|=tmp;
        PORTB=(val&1)<<PB5; //4 бит
        val>>=1;
        tmp=PINB;
        tmp=PINB&0x40;
        PORTB|=(1<<PB7);
        tmp<<=1;
        val|=tmp;
        PORTB=(val&1)<<PB5;  //5 бит
        val>>=1;
        tmp=PINB;
        tmp=PINB&0x40;
        PORTB|=(1<<PB7);
        tmp<<=1;
        val|=tmp;
        PORTB=(val&1)<<PB5;  //6 бит
        val>>=1;
        tmp=PINB;
        tmp=PINB&0x40;
        PORTB|=(1<<PB7);
        tmp<<=1;
        val|=tmp;
        PORTB=(val&1)<<PB5;  //7 бит
        val>>=1;
        tmp=PINB;
        tmp=PINB&0x40;
        PORTB|=(1<<PB7);
        tmp<<=1;
        val|=tmp;
        tmp=PINB; //Задержка для слейва, что бы он успел записать последний бит
        tmp=PINB;
        PORTB=(1<<PB0)|(1<<PB5); //MOSI и /SS - выставить в 1
        return val; //полученный байт от слейва
}

ПС. Максимальная скорость обмена составляет около 40Кбайт/сек, что довольно неплохо, но можно и лучше, правда - на ассемблере. При организации обмена данными важно не забывать о слейве, он должен успеть подствить новые данные в регистр SPDR до очередной приемо-передачи.