6 августа 2012 г.

USI в режиме SPI

USI (Universal Serial Interface) - это последовательный интерфейс, который позволяет передавать данные от ведущего устройства к ведомому и обратно с большой скоростью. USI может работать в трехпроводном и в двухпроводном режимах. Двухпроводной режим напоминает I2C, а трехпроводной - SPI, однако в последнем отсутствует реализация функции Slave Select, которую, впрочем, можно запилить самостоятельно.



Работа USI в трехпроводном режиме

На шине есть ведущее (Master) и ведомое (Slave) устройства, которые обмениваются данными своих регистров USIDR побитово, при этом Master задает тактирование. Интерфейс состоит из трех линий: DO (data output), DI (data input), USCK (USI clock ):


На каждом такте USCK из регистра USIDR одного устройства "выдавливается" один старший битик, который записывается в младший разряд аналогичного регистра другого устройства. Таким образом, через восемь тактов оказывается, что устройства обменялись данными, и выставляется соответствующий флаг (а если хотите, то и прерывание).

Подключение сдвигового регистра 74HC595 к USI

Вернемся к нашим баранам Для экспериментов с USI обратимся к нашему любимому сдвиговому регистру 74HC595, который выступал в качестве рабочей лошадки в проектах с 7-сегментниками (1, 2, 3, 4, 5) и матрицами (1, 2).

Возьмем микроконтроллер с USI, пусть это будет Attiny45, и посмотрим на его распиновку:

Я пометил ноги 5, 6 и 7 - они имеют функции USI. Сдвиговый регистр все же требует защелкивающего строба, который ранее реализовывался нами в виде линии SS. Сделаем его тупо на PB4. 

Подключим 
DO (6 нога Attiny) к DS (14 нога 74HC595), 
USCK (7 нога Attiny) к SHCP (11 нога 74HC595), 
PB4 (3 нога Attiny) к STCP (12 нога 74HC595),
как показано на рисунке:

DI не подключаем никуда, потому что принимать нам и нечего. Я не стал на схеме рисовать светодиоды и резисторы - они занимают много места.

В живую, конечно, выглядит страшно, тем более, что у меня там еще 6 проводов от программатора:

Описание регистров USI

У процессора есть четыре регистра, ответственных за USI:
USIDR - USI Data Register - Регистр данных USI,
USIBR - USI Buffer Register - Регистр буфера USI,
USISR - USI Status Register - Регистр состояния USI,
USICR - USI Control Register - Управляющий регистр USI.

7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit 
USIDRMSB





LSB
Этот регистр содержит данные, пересылаемые по USI. До сеанса пересылки он содержит отправляемый данные, а после - принимаемые.

7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit 
USIBRMSB





LSB
Этот регистр содержит копию данных из USIDR, доступную только для чтения.

7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit 
USISRUSISIFUSIOIFUSIPFUSIDCUSICNT3USICNT2USICNT1USICNT0
Четыре старших бита регистра - это флаги, а четыре младших хранят значение счетчика USI.

USISIF - Start Condition Interrupt Flag - флаг условия старта. Изменение сигнала на линии SCK поднимет этот флаг. Опустить флаг можно, только записав 1 в бит USISIF. Флаг может сгенерировать прерывание, если выставлен бит USISIE в регистре USICR. Это прерывание выведет процессор из любого режима сна.
USIOIF - Counter Overflow Interrupt Flag - флаг переполнения счетчика. Поднимается, когда счетчик досчитает от 15 до 0. Опустить флаг можно, только записав 1 в бит USIOIF. Флаг может сгенерировать прерывание, если выставлен бит USIOIE в регистре USICR. Это прерывание выведет процессор из режима Idle.
USIPF - Stop Condition Flag - флаг условия остановки. Если выбран двухпроводной режим, этот флаг поднимается, когда обнаруживается условие остановки. Опустить флаг можно, записав 1 в этот бит. Этот сигнал используется при арбитраже двухпроводной шины. 
USIDC - Data Output Collision Flag - флаг коллизии вывода данных. Этот флаг поднимается, когда 7-й бит регистра USIDR отличается от физического значения пина. Этот флаг работает только в двухпроводном режиме и используется для арбитража шины. 
USICNT[3:0] - Counter Value - значение счетчика. Значение увеличивается на 1 каждый раз, когда генерируется строб детектором внешнего источника или софтверно, с помощью битов USICLK или USITC. Источник задается битами USICS[1:0] в регистре USICR. 

7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit 
USICRUSISIEUSIOIEUSIWM1USIWM0USICS1USICS0USICLKUSITC

USISIE - Start Condition Interrupt Enable - разрешение прерывания при условии старта. Установка этого бита в 1 разрешает прерывание детектором условия старта. Прерывание будет генерироваться, если флаги USISIE и Global Interrupt Enable Flag подняты. 
USIOIE - Counter Overflow Interrupt Enable - разрешение прерывания при переполнении счетчика. Прерывание будет генерироваться, если флаги USIOIE и Global Interrupt Enable Flag подняты. 
USIWM[1:0] - Wire Mode - режим шины. Эти два бита задают режим использования шины.

USIWM1
USIWM0
Описание
0
0
USI отключен.
0
1
Трехпроводной режим. Используются пины DO, DI и USCK.
1
0
Двухпроводной режим. Используются пины SDA (DI) и SCL (USCK).
Линия USCL удерживается низкой, когда обнаруживается условие старта. Сброс флага USISIF отпускает линию.
1
1
Двухпроводной режим. Используются пины SDA (DI) и SCL (USCK).
Линия USCL удерживается низкой, когда обнаруживается переполнение счетчика. Сброс флага USIOIF отпускает линию.
USICS[1:0] - Clock Source Select - выбор источника тактирования. Сброс битов USICS[1:0] разрешает софтверное стробирование. При этом запись 1 в бит USICLK тактирует регистр USIDR и счетчик. При использовании внешнего источника (USICS1 = 1), USICLK не используется как строб, а задает режим тактирования от внешнего источника или от USITC.

USICS1
USICS0
USICLK
Источник тактирования
Источник тактирования счетчика
0
0
0
Нет
Нет
0
0
1
Софтверный строб (USICLK)
Софтверный строб (USICLK)
0
1
X
Timer/Counter0 CNC
Timer/Counter0 CNC
1
0
0
Внешний, положительный фронт
Внешний, оба фронта
1
1
0
Внешний, отрицательный фронт
Внешний, оба фронта
1
0
1
Внешний, положительный фронт
Софтверный строб (USITC)
1
1
1
Внешний, отрицательный фронт
Софтверный строб (USITC)
USICLK - Clock Strobe - Тактирующий строб. Запись 1 в этот бит заставляет регистр USIDR совершить сдвиг и инкрементирует счетчик, если выбрано софтверное тактирование (USICS[1:0] = 0). Если выбран внешний источник тактирования (USICS1 = 1), то функция USICLK меняется - запись 1 в этот бит выбирает строб USITC в качастве источника тактирования счетчика. 
USITC - Toggle Clock Port Pin - переключение пина порта тактирования. Запись 1 в этот бит переключает значение USCK/SCL с 0 на 1 или с 1 на 0. Если выбран внешний источник тактирования (USICS1 = 1) и USICLK установлен в 1, то запись в USITC тактирует счетчик. 

Пишем код

//Подключаем самую нужную библиотеку io
#include </usr/lib/avr/include/avr/io.h>
//Подключаем библиотеку, которая обеспечивает функцию delay
#include </usr/lib/avr/include/util/delay.h>

int main(void){
  //Настраиваем нужные пины на выходы
  DDRB = (1 << DDB1) | (1 << DDB2) | (1 << DDB3) | (1 << DDB4);
  //Бесконечный цикл
  while(1){
    //Пробегаем по номерам светодиодов
    for (int i=0;i<8;i++){
      //В качестве данных для USI указываем номер светодиода
      USIDR=(1<<i);
      //Опускаем флаг переполнения таймера
      USISR|=(1<<USIOIF);
      //Отпускаем защелку регистра 74HC595
      PORTB&=(0<<PORTB4);
      //Пока не поднялся флаг переполнения счетчика
      while((USISR&(1<<USIOIF))==0){
        //Выставляем трехпроводной режим;
        //Выбираем внешний источник тактирования, с положительным фронтом;
        //Выставляем тактирование по стробу USITC;
        //Собственно, генерируем сам строб USITC
        USICR=(1<<USIWM0)|(1<<USICS1)|(1<<USICLK)|(1<<USITC);
      }
      //Защелкиваем данные в регистре 74HC595
      PORTB|=(1<<PORTB4);
      //Пауза 100 миллисекунд
      _delay_ms(100);
    }
  }
  //main возвращает 0, как всегда
  return 0;
}

Наслаждаемся работой