045-SPI. Применение в электронных проектах.

Автор: | 21.06.2010

ТитлЕще один интерфейс, которым можно без особых проблем связать устройство с электронным проектом — это SPI интерфейс.

SPI (Serial Peripheral Interface — последовательный периферийный интерфейс) как и UART довольно старый, простой и популярный интерфейс. Существует множество устройств работающие по этому интерфейсу (микроконтроллеры, EEPROM, ADC, DAC …). Будем и мы его использовать для наших устройств.
В SPI интерфейсе для передачи данных используется три линии: MOSI – передача; MISO – прием; SCK – тактирование. Дополнительно используется линия SS (Slave Select ?) – выбор устройства (если их несколько). SPI — полнодуплексный синхронный интерфейс — передача и прием осуществляется параллельно. Инициирует и тактирует обмен ведущее устройство – master (может быть только одно). Ведомое — slave-устройство (может быть несколько) принимает/отдает байт под действием тактового сигнала.

В микроконтроллерах AVR SPI интерфейс используется двойственно, как интерфейс связи и как интерфейс для последовательного внутрисхемного программирования. В подавляющем большинстве контроллеров SPI интерфейс присутствует (есть исключения в моделях Tiny), но даже если его нет, программно реализовать его не составит труда.

Итак, как устроен SPI интерфейс?
А устроен он до безобразия просто (его то и интерфейсом назвать сложно) — два сдвиговых регистра (master и slave), плюс генератор на стороне master – все! Сдвиговые регистры замкнуты в кольцо по линиям MOSI и MISO, тактовый сигнал от генератора подается на оба сдвиговых регистра (на slave через линию SCK).

Принцип SPI

До начала обмена данные помещаются в сдвиговые регистры, мастер запускает генератор, генератор «отщелкивает» 8 тактов, за эти 8 тактов сдвиговые регистры «меняются» содержимым (мастер получает байт слэйва, а слэйв – мастера) – все!
Если ведомых устройств несколько – обмен осуществляется только с одним, у которого сигнал на SS низкого уровня.

SPI – самый быстрый последовательный интерфейс. При частоте задающего генератора микроконтроллера 20МГц он может осуществлять обмен со скоростью 10 000 000 Бод 
(1.2 мегабайта за секунду)!

Недостатком интерфейса есть то, что инициатором обмена всегда является мастер. Это значит, что если на периферии что-то произошло, мастер об этом узнает только тогда когда сам «надумает» провести сеанс связи (теряется оперативность). Производители AVR нашли способ обойти этот недостаток. Если SPI микроконтроллера сконфигурирован как master ножку SS можно сконфигурировать как на вход, так и на выход. Если ножка сконфигурирована как выход, то она используется для выбора slave-устройства. Но если ножка SS сконфигурирована на вход, то при появлении на этой ножке низкого уровня микроконтроллер автоматически переключит SPI в режим slave и сможет принять данные от другого мастера. После сеанса обмена SPI остается в режиме slave, для перевода его в режим master нужно повторно конфигурировать SPI.

Я думаю, для устройств блога, данный алгоритм переключения master- slave редко будет нужен, поэтому мы будем использовать SPI только как master или только как slave.
Если устройство блога – устройство вывода (например клавиатура), то SPI будет master, а в Вашем проекте SPI будет slave.
Если устройство блога – устройство ввода (например цифровой индикатор), то SPI будет slave, а в Вашем проекте SPI будет master.

Для связи устройства с Вашим проектом через интерфейс SPI нужно:
1 Подключить устройство блога к соответствующим ножкам микроконтроллера.
2 Настроить SPI Вашего контроллера (как master или как slave). Для этого в соответствующие порта ввода/вывода записать определенные значения.
3 Иметь (написать) процедуры приема/передачи сообщений по SPI в Вашей программе.
Теперь рассмотрим подробно каждый пункт:


1 СОЕДИНЕНИЕ УСТРОЙСТВ ЧЕРЕЗ SPI.
Просто соединяем одноименные выводы MOSI-MOSI, MISO-MISO, SCK-SCK, SS-SS. Если соединение только между двумя устройствами, то SS-SS, в принципе, не обязательно, но нужно позаботится о том, чтобы на ножке SS slave-устройства был низкий уровень (притянуть к «земле») иначе устройство не будет участвовать в обмене.

Схема подключения


2 НАСТРОЙКА SPI.

У SPI есть четыре режима работы (0, 1, 2, 3). Режим работы зависит от того, по какому фронту тактового сигнала осуществляется обмен. Кроме того можно настроить какой бит (старший или младший) передается первым. Для обмена это не имеет никакого значения – результат будет одинаковым (режимы работы нужны для подстройки SPI к устройствам разных производителей). Master и slave должны работать в одном режиме.
Для устройств блога будем применять следующие настройки SPI:
Режим – 0
Старший бит передается первым
Частота тактирования — по обстоятельствам (для SPI частота не столь важна, так как тактирование происходит от одного источника).
Для примера возьмем микроконтроллер ATmega8 с внутренним задающим генератором на 8МГц. Настроим управляющие порта ввода-вывода контроллера и в различных средах программирования.

Algorithm Builder.
Создаем проект (Файл/Новый). Выбираем микроконтроллер и частоту задающего генератора в Опции/Опции проекта… (ATmega8, внутренний задающий генератор на 8МГц). В панели инструментов жмем кнопочку «S» – настройщик управляющих регистров» выбираем SPI.
Настройки для master (для связи с устройством вывода):

Настройка мастера в Algoritm Builder

Настройки для slave (для связи с устройством ввода):

Настройки slave SPI в Algorithm Builder

Жмем «ОК». Готово – SPI проинициализирован и готов к работе.

Ассемблер. Можно поковыряться в даташите на контроллер, можно взять кусок асма из скомпиленного проекта на С, можно перевести мнемокоманды Algorithm Builder — выбираем, что нравится.
Настройки для master (для связи с устройством вывода):

init-SPI-master:
	sbi	0x17,0x02
	sbi	0x17,0x05
	sbi	0x17,0x03
	ldi	r16,0x00
	out	0x0E,r16
	ldi	r16,0xD0
	out	0x0D,r16

Настройки для slave (для связи с устройством ввода):

init-SPI-slave:
	sbi	0x17,0x04
	ldi	r16,0x00
	out	0x0E,r16
	ldi	r16,0xC0
	out	0x0D,r16

CodeVisionAVR. Для генерации настроек SPI, нажимаем на значок шестеренку (CodeWisardAVR) на панели инструментов. В открывшемся окошке сначала выбираем вкладку Chip в ней выбираем микроконтроллер и устанавливаем частоту, с которой будет работать задающий генератор. Далее выбираем и заполняем вкладку SPI в соответствии с нужными характеристиками.

Настройки для master (для связи с устройством вывода):

// Port B initialization
// Func7=In Func6=In Func5=Out Func4=In Func3=Out Func2=Out Func1=In Func0=In
// State7=T State6=T State5=0 State4=T State3=0 State2=0 State1=T State0=T
PORTB=0x00;
DDRB=0x2C;
 
// SPI initialization
// SPI Type: Master
// SPI Clock Rate: 2000,000 kHz
// SPI Clock Phase: Cycle Half
// SPI Clock Polarity: Low
// SPI Data Order: MSB First
SPCR=0xD0;
SPSR=0x00;

Настройки для slave (для связи с устройством ввода):

// Port B initialization
// Func7=In Func6=In Func5=In Func4=Out Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=0 State3=T State2=T State1=T State0=T
PORTB=0x00;
DDRB=0x10;
 
// SPI initialization
// SPI Type: Slave
// SPI Clock Rate: 2000,000 kHz
// SPI Clock Phase: Cycle Half
// SPI Clock Polarity: Low
// SPI Data Order: MSB First
SPCR=0xC0;
SPSR=0x00;

Сохраняем сгенерированный проект (File\Generate, Save and Exit). Создан проект со всеми нужными установками для SPI. В проекте инициализируется и другая периферия (зачастую не нужная). После создания проекта его можно подкорректировать – удалить все не нужное.


3 СОЗДАНИЕ ПРОЦЕДУР ОБРАБОТКИ SPI.

Работа с SPI в микроконтроллере организована очень просто. Чтобы начать обмен данными в мастер-контроллере достаточно записать передаваемый байт в регистр SPDR – обмен начнется автоматически. По завершению обмена вызовется процедура обработки прерывания, в ней считываем из SPDR полученный байт. Со слэйв-контроллером еще проще по окончании обмена вызовется процедура обработки прерывания, в ней считываем из SPDR полученный байт.

Algorithm Builder.
Передача. Как для master’a, так и для slave передаваемый байт нужно записать в регистр SPDR. Для master’a запись байта в SPDR инициирует обмен.
Прием. Как для master’a, так и для slave после принятия байта вызовется прерывание, в котором и обрабатывается посылка.

Процедура обработки прерывания

Если во время передачи байта будет попытка записи в регистр SPDR — возникнет конфликт записи. Поэтому в программе нужно позаботится о том, чтобы следующий байт передавался по окончании приема предыдущего. Наилучшим решением этой проблемы будет следующий алгоритм: первый байт-начало пакета байт передается из тела программы, а остальные передаются из прерывания окончании передачи по SPI. Таким образом основная программа «освобождается» от работы по передаче строки байт и в тоже время не может возникнуть конфликт записи, так как прерывание вызывается о окончании обмена.

Так как мы планируем передавать единичные байты по SPI, приведенный выше алгоритм мы использовать не будем.

Ассемблер.
Код предельно прост. Для передачи пишем в SPDR, полученный байт читаем из SPDR в теле прерывания.

CodeVisionAVR. CodeWisardAVR кроме инициализации создаст и процедуру обработки прерывания. Ничего дополнительного не потребуется.

// прием/передача данных по SPI в теле прерывания
interrupt [SPI_STC] void spi_isr(void)
{
unsigned char data;
data=SPDR;
}
 
// подготовка/передача данных по SPI в теле программы
unsigned char data;
SPDR=data;

Вот, вроде бы, и все, что хотелось сказать по SPI. 
Интерфейс довольно простой и не должен вызывать проблем при его использовании.

(Visited 2 786 times, 1 visits today)

045-SPI. Применение в электронных проектах.: 29 комментариев

  1. fkvfaydar

    Круто!Но для меня пока слишком сложно…

  2. GetChiper Автор записи

    Все очень просто!
    Несмотря на кучу букв, весь текст выше сводится к нескольким строчкам.
    Инициализация:
    SPCR=0xD0; (0xC0 — для slave)
    SPSR=0x00;
    Отправить байт:
    SPDR=data;
    Принять байт:
    data=SPDR;

  3. alexandershahbazov

    Еще не попорбовал с помощью SPI вывести на 2-х разрядный 7-сегментник .
    Выводил программно через 74НС164 но без SPI . Сдержала статья на gaw.ru
    в которой есть строки :
    «Когда используется только одна внешняя ИС, может возникнуть соблазн исключения и линии SS за счет жесткой установки низкого уровня на входе выбора подчиненной микросхемы. Такое решение крайне нежелательно и может привести к сбоям или вообще невозможности передачи данных, т.к. вход выбора микросхемы служит для перевода ИС в её исходное состояние и иногда инициирует вывод первого бита данных.»
    Рад был бы услышать ваше мнение .

  4. alexandershahbazov

    Потом там в CodeVisionAVR прерывание по SPI происходит по какому
    событию чтобы например можно было отправить байт .

  5. GetChiper Автор записи

    alexandershahbazov :

    … Такое решение крайне нежелательно и может привести к сбоям или вообще невозможности передачи данных, т.к. вход выбора микросхемы служит для перевода ИС в её исходное состояние и иногда инициирует вывод первого бита данных.» …

    C 74НС164 такое не грозит — в нем просто нет какого входа!
    А вообще, микросхем использующий SPI множество и может быть такое, что перепад сигнала SS используется для «защелкивания» данных или еще для чего. В даташите на используемую микросхему об этом можно прочитать и выяснить можно вешать SS намертво на землю или нет.

  6. GetChiper Автор записи

    alexandershahbazov :

    Потом там в CodeVisionAVR прерывание по SPI происходит по какому
    событию чтобы например можно было отправить байт .

    Прерывание происходит по факту окончания обмена.
    Если это мастер, то после записи байта в SPDR микроконтроллер автоматически начинает обмен со слейвом. После передачи последнего бита вызовется прерывание (в прерывании можно сразу прочитать принятый байт из SPDR, и отправить следующий записав в тот же SPDR).
    Если это слейв, то прерывание вызовется после окончания передачи от мастера.

  7. GaDs

    Вопрос появился, а как можно передать не 1байт 0xFF, а 2байта 0xFСFF?
    Если написать SPDR=0xFСFF, то в протеусе придёт только FF 🙁

  8. GaDs

    Реализовал так:
    if (SendData >= 256)
    {
    SPDR = (SendData>>8);
    delay_us(2);
    }
    SPDR = SendData;

    Верно ли? и можно ли ускорить:)?

  9. GetChiper Автор записи

    Можно применить готовую библиотеку, там посылки идут через буфер.
    Или если делать напрямую (записывать байты в регистр UDR), то перед записью проверяешь UDRE на ноль (пока ноль — идет передача — ждешь, если не ноль отсылаешь следующий байт).

  10. GaDs

    А можно поподробней?

  11. GetChiper Автор записи

    Упс! В предыдущем комменте описывал работу с UART.

    С SPI удобно работать в прерываниях, а именно, отправили первый байт, занимаемся своими делами. По окончании передачи байта вызовется прерывание, в нем отправляем следующий байт и так далее.

    Если работать без прерываний то после отправки первого байта контролируем состояние бита SPIF регистра SPSR. Как только он установился в 1 — байт передан, можно отправлять следующий. Перед отправкой следующего байта нужно обнулить этот бит (обнуляется он автоматически если прочитать регистр SPSR перед записью нового байта в SPDR).

  12. anatoliy

    Есть у мя мысля, что USART в синхронном режиме это SPI.
    В наличии передача,приём,клок. Читаю датасшид. Вижу что похож. Но про синхронный режим там 2 слова. Негде не вычитал. Используется ли в синхронном режиме старт-стопные посылки?

  13. GetChiper Автор записи

    Во многих AVR модуль USART «официально» переключается в режим SPI. Соответственно и работает как обычный SPI (без дополнительных битов USART).

  14. Bonio

    Подскажите пожалуйста, как проверить выходят ли байты из микроконтроллера?
    Я отсылаю данные в spi, пытаюсь вешать светодиод на SCK и MOSI, а он вобще ничего не индицирует. Нормально ли это? передаються ли байты?

  15. Bonio

    @Bonio
    А все, сам разобрался. Я пины на выход забыл настроить)

  16. Ypir

    Доброй ночи! Не подскажите как можно принять по spi 13 чисел(числа 0х30 и 0х31) и в зависимости от пришедшего числа на определенных ножках портов подавался уровень 0 или 1, например если первое число 0х31 то на PIND.5 подается питание. Пробовал написать сам, не получается записать пришедшие числа в массив для обработки, работало только с одним числом.

  17. GetChiper Автор записи

    В прерывании окончания приема байта по SPI, забираешь байт анализируешь его и выставляешь уровень на нужной ножке.

  18. Ypir

    Разобрался с приемом, проблема была глупая))) Предлагаю добавить, дабы не возникало проблем у совсем новичков (как я), конструкцию такого вида:
    // прием/передача данных по SPI в теле прерывания
    interrupt [SPI_STC] void spi_isr(void)
    {
    asm(«cli»);
    unsigned char data;
    data=SPDR;
    asm(«sеi»);
    }

  19. dmibr

    Здравствуйте. Подскажите возможно ли сделать устройство записи/воспроизведения звука(речи)с микрофона в микросхему spi-flash памяти en25t80 (её размер 1Мб)на базе широко распространённого микроконтроллера, например, atmega8-16 attiny2313?

  20. dmibr

    У меня просто есть эта память и стоит у нас раз в 10 дешевле карты, сейчас микросхемы серии 25хх широко применяются в бытовой технике.

  21. GetChiper Автор записи

    Где-то в статье по говорилке уже обсуждался этот вопрос. Пришли ко мнению что для домашних условий все-же лучше SD-карта (в виду простоты ее использования, наполнения файлами, замены и т.д.).

  22. radiokarp

    Доброго времени суток. Подскажите пожалуйста, как в Algorithm Builder отправить массив по SPI? И как вообще с массивами работать?

  23. GetChiper Автор записи

    В Билдере нет средств работы с массивами данных, Вам придется свои данные выдавать в SPI побайтно (соответственно и работать с массивом придется побайтно).

  24. radiokarp

    как наиболее просто это сделать?

  25. begun

    здравствуйте дорогой автор саита!
    хочу выразить вам свою благодарность за то, что вы рассказываете все на очень понятном для читателя языке. Вы пишите для нас ,а не ради приличия как многие другие., и это у вас хорошо получается.
    так держать и все вперед и вперед.
    Честь и хвола вам

Добавить комментарий