SPI на других ногах

Как работать с встроенным в AVR SPI`ем будет описано в другой статье. Иногда возникает необходимость синхронного общения по этому протоколу с двумя устройствами (в моем случае это внешний АЦП и внешняя память).

atmega88_p2

И так, для всего этого берем любой контроллер AVR (я взял ATmega168) и определяемся где мы хотим разместить наш ручной SPI. Займет это все 3-4 порта, зависит от необходимости в канале SS. Для моих целей  SS не нужен, т.к. он выбран всегда.

Шаг 1: Определяем порты и частоту

В моем случае:

  • PC3 – выход
  • PC2 – вход
  • PC1 – тактирование

Чтобы не мучиться сразу зададим все дефайнами:

#define MO0 PORTC&=~(1<<PC3)
#define MO1 PORTC|=(1<<PC3) 
#define SCK 8
#define C SCK/2
#define SCK_ON PORTC|=(1<<PC1)
#define SCK_OFF PORTC&=~(1<<PC1)

Соответственно: 1,2  - строки определяют 1/0 на выходе; 5,6 – 1/0 на линии такта; SCK – количество тактов за один период SPI,  если частота кристалла 8МГц, а SPI 2МГц, то это число будет 4. Да и надо помнить, что меньше 2 оно не может быть.

Шаг 2: Определяем главную последовательность

   // Поднимаем такт для режима 4 (для 0 - опускаем линию такта)
   for(bit=0; bit<8;bit++){ // Далее передаем 8 бит данных
     //передаем первый бит входных данных (byte)
     // опускаем линию такта (для режима 0 - поднимаем)
     // считываем бит и записываем в выходной регистр
     // поднимаем линию такта (для режима 0 - опускаем)
   }

Таким образом у нас получится такая вот картина

1234921860_15347_FT18372_zilog_spi_sck_

 

Шаг 3: Расставляем по своим местам

unsigned char SPI (unsigned char byte){
   unsigned char re=0;
   unsigned char bit=0;
   SCK_ON;
   for(bit=0; bit<8;bit++){
      //write
      if (byte&(1<<bit))
         MO1;
      else
         MO0;
      //SCK on
      wait(C);
      SCK_OFF;
      //Read
      re<<=1;
      if(PINC&(1<<PC2))
         re|=(1<<0);//0x01
      else
         re&=~(1<<0);
      // SCK off
      wait(C);
      SCK_ON;
    }
}

 

Готовый проект My SPI под AVRStudio 6