Программирование AVR AVR151: настройка и использование SPI Fri, October 11 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

AVR151: настройка и использование SPI Печать
Добавил(а) microsin   

В этой статье приведен перевод даташита Atmel AVR151 [1], рассказывающего про последовательный порт SPI. Здесь описывается, как настроить и использовать встроенный в микроконтроллер AVR узел Serial Peripheral Interface (SPI) [2]. Большинство AVR уже имеют в своем составе модуль SPI, и могут быть сконфигурированы так, как описано в этом даташите. После небольшого экскурса в теорию SPI будет показано, как конфигурировать SPI AVR в режиме главного устройства (Master Mode) и в режиме подчиненного устройства (Slave Mode).

Рассматриваемые вопросы:

• Назначение выводов порта SPI.
• Системы с несколькими подчиненными устройствами.
• Диаграммы времени SPI.
• Конфликты передач SPI.
• Эмуляция SPI.
• Примеры кода, работающие по принципу опроса.
• Примеры кода, работающие по прерываниям.

[Общее описание SPI]

SPI позволяет осуществить быстрый синхронный обмен данными между AVR и различными периферийными микросхемами, или между несколькими AVR. В большинстве типов AVR интерфейс SPI несет дополнительное назначение - он применяется для внутрисхемного программирования памяти и фьюзов (In System Programming, сокращенно ISP. Дополнительно см. апноут AVR910). Взаимодействие между двумя устройствами SPI всегда происходит как обмен между главным и подчиненным устройством (master device и slave device). В сравнении с различными периферийными устройствами (датчики температуры, микросхемы памяти, расширители портов и т. д.), которые могут работать только в режиме slave, интерфейс SPI микроконтроллера AVR может быть сконфигурирован по выбору программиста как в режим master, так и в режим slave. Режим определяется установкой бита мастера (master bit, MSTR) в регистре управления SPI (SPI control register, SPCR). Для вывода ~SS нужно учесть дополнительные требования по настройке, что будет описано в секции "Системы с несколькими подчиненными устройствами. Работа вывода ~SS". Мастер является управляющей частью системы, и он предоставляет тактовый сигнал, на котором основывается последовательный обмен данными по шине SPI. Подчиненное устройство (slave device) не генерирует сигнал тактов, так что оно не может становиться активным самостоятельно. Устройство slave просто отправляет и принимает данные, только если master генерирует требуемый тактовый сигнал.

Однако master генерирует тактовый сигнал только в момент отправки данных. Это означает, что если нужно прочитать данные из slave, то master должен послать какие-то данные в slave, т. е. чтение и запись по шине SPI происходят одновременно.

Примечание: это может внести путаницу, если используются "пассивные" периферийные устройства наподобие датчиков. Не всегда является четкой концепция необходимости передавать случайные данные только для того, чтобы данные прочитать.

[2.1. Передача данных между Master и Slave]

Взаимодействие между главным и подчиненным устройствами AVR показано на рис. 1. Здесь работают друг с другом два идентичные по типу устройства. Левое устройство сконфигурировано как master, а правое как slave. Сигналы MISO, MOSI и SCK подключены к соответствующим сигналам противоположного устройства (MISO-MISO, MOSI-MOSI, SCK-SCK). Режим (master или slave) определяет, как будут функционировать эти сигналы - как вход или как выход. Поскольку биты сдвигаются в slave (обмен в направлении от главного устройства к подчиненному) и выдвигаются из slave (обмен в направлении от подчиненного устройства к главному) одновременно в одном тактовом цикле, то оба 8-битных сдвиговых регистра приема и передачи будут обрабатываться одновременно. Это означает, что после прохождения 8 импульсов тактов SCK произойдет полный цикл обмена данными в обе стороны.

AVR151 SPI

Рис. 1. Типичная организация обмена данными через SPI.

Примечание: из-за того, что микроконтроллеры AVR нашпигованы различной периферией, и существует множество разновидностей типов AVR с различным набором встроенных периферийных устройств, встречается иногда некоторая путаница в мнемонических обозначениях регистров и сигналов. Это не должно Вас смущать, просто внимательно прочитайте даташит на используемый Вами микроконтроллер AVR.

К примеру, в этом даташите тактовый сигнал SPI обозначается как SCK. Но в то же время у микроконтроллера AT90USB162 под именем SCK фигурирует тактовый сигнал для интерфейса PS/2, а для SPI тактовый сигнал носит другое имя: SCLK.

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

1. Новые байты для отправки не могут быть записаны в регистр данных (SPDR) / регистр сдвига, пока не завершится полный цикл сдвига.
2. Принятые байты записываются в буфер приема немедленно после завершения передачи.
3. Буфер приема должен быть прочитан перед началом следующей передачи, иначе данные будут потеряны.
4. Чтение SPDR вернет данные из буфера приема.

После того, как передача завершится, будет выставлен SPI Interrupt Flag (SPIF) в регистре SPI Status Register (SPSR). Это приведет к срабатыванию соответствующего прерывания, если оно разрешено, и если установлен бит глобального разрешения прерываний. Установка бита SPI Interrupt Enable (SPIE) в регистре SPCR разрешит прерывание от модуля SPI, в то время как установка бита I в регистре SREG глобально разрешает прерывания.

[2.2. Выводы SPI]

SPI стандартно состоит из 4 сигнальных линий [2]. Это такты сдвига (shift clock, SCK), выходной сигнал главного устройства (Master Out Slave In MOSI), входной сигнал главного устройства (Master In Slave Out, MISO) и сигнала выборки подчиненного устройства с активным уровнем лог. 0 (Slave Select, ~SS). Когда интерфейс SPI разрешен, то направление данных выводов MOSI, MISO, SCK и ~SS автоматически назначаются в соответствии с таблицей ниже.

Таблица 2-1. Переназначение режима работы (вход или выход) выводов AVR в режиме SPI.

Сигнал Направление в режиме Master Направление в режиме Slave
MOSI Определяется программистом Вход
MISO Вход Определяется программистом
SCK Определяется программистом Вход
~SS Определяется программистом Вход

В таблице показано, что автоматически конфигурируются только входные сигналы (т. е. только входы не требуется специально настраивать через регистр DDRx). Но выходные сигналы должны быть специально настроены вручную, под управлением firmware микроконтроллера. Это нужно для устранения конфликтов и случайного повреждения выходного драйвера порта.

[2.3. Системы с несколькими подчиненными устройствами. Работа вывода ~SS]

Сигнал Slave Select (~SS) играет центральную роль в конфигурировании SPI. В зависимости от режима работы порта SPI AVR, вывод ~SS может использоваться для активирования или активизации устройств. Вывод ~SS можно сравнить с сигналом chip select (CS), у которого есть несколько дополнительных возможностей.

Если вывод ~SS сконфигурирован как вход в режиме master, то вывод ~SS должен удерживаться в состоянии лог. 1, чтобы гарантировать, что устройство будет работать как master SPI. Лог. 0 на входе ~SS переключит SPI в режим slave, и аппаратура SPI будет выполнять следующие действия:

1. Бит главного устройства (master bit, MSTR) в регистре SPI Control Register (SPCR) очищается, и система SPI становится подчиненной. Направление работы выводов (вход или выход) переключается в соответствии с таблицей 2-1.

2. Установится SPI Interrupt Flag (SPIF) в SPI Status Register (SPSR). Если разрешено прерывание SPI, и глобально разрешены прерывания, то выполнится обработчик прерывания SPI.

Такое странное на первый взгляд функционирование может быть полезным, когда в системе есть несколько главных устройств - чтобы избежать попытку одновременного захвата управления шиной SPI сразу двумя устройствами master. Если же вывод ~SS сконфигурирован как выход, то он может использоваться как обычный порт ввода/вывода общего назначения (GPIO), который никак не связан с системой SPI.

Примечание: в случаях, когда AVR сконфигурирован в режиме master, и он не может гарантировать состояние лог. 1 вывода ~SS между двумя передачами, может быть проверен бит состояния MSTR перед записью нового передаваемого байта. Как только бит MSTR был очищен низким уровнем сигнала ~SS, бит MSTR снова должен быть установлен кодом firmware, чтобы опять разрешить работу SPI в режиме master.

В режиме slave вывод ~SS всегда работает как вход. Когда ~SS удерживается в лог. 0, то SPI активизируется, и MISO становится выходом, если это было ранее сконфигурировано пользователем. Все другие выводы SPI остаются работать как входы. Когда ~SS в лог. 1, то все выводы SPI становятся входами, и SPI деактивируется, это означает, что он не будет принимать поступающие данные. В таблице 2-2 показан обзор функционирования вывода ~SS.

Примечание: в режиме slave логика SPI будет сброшена, как только уровень вывода ~SS был переведен в состояние лог. 1. Так что если вывод ~SS получил снаружи лог. 1 во время передачи, то передача и прием SPI немедленно остановятся, и данные могут считаться потерянными.

Таблица 2-2. Обзор функций вывода ~SS.

Режим Конфигурация ~SS Лог. уровень на ~SS Описание
Slave Всегда вход 1 Slave деактивирован (не выбран)
0 Slave активен (выбран)
Master

Вход 1 Master активен (выбран)
0 Master деактивирован, переключен в режим slave
Выход 1 Master активен (выбран)
0

Как показано в таблице 2-2, вывод ~SS в режиме slave всегда работает как вход. Низкий уровень активирует работу SPI, а высокий деактивирует. В системе с одним главным и несколькими подчиненными устройствами (Single Master Multiple Slave), где AVR сконфигурирован как master, вывод ~SS конфигурируется как выход, и возможная схема подключения показана на рис. 2-1. Количество подчиненных устройств, которое может быть подключено к этому главному AVR, ограничено только количеством свободных выводов GPIO, на которых можно программно устанавливать сигнал выборки соответствующего подчиненного устройства.

AVR151 Multi Slave System

Рис. 2-1. Пример подключения нескольких подчиненных устройств к одному главному устройству AVR.

Возможность подключить несколько устройств в одной и той же шине SPI основывается на факте, что в любой момент времени могут быть активны и передавать данные только 2 устройства SPI - только одно master и только одно slave. Сигнальные линии MISO, MOSI и SCK всех остальных slave-устройств остаются в третьем состоянии (сконфигурированными как входы с высоким входным сопротивлением и отключенными pullup-резисторами). Ошибочная реализация (например, если 2 подчиненных устройства будут активизированы одновременно) может привести к замыканию друг на друга выходов CMOS и сквозным токам, чего следует избегать. Резисторы от 1 до 10 кОм могут быть подключены последовательно с выводами SPI, чтобы предотвратить перегрузку выходных буферов CMOS при ошибочном замыкании выходов друг на друга, когда на этих выходах присутствуют разные логические уровни. Однако имейте в виду, что это может повлиять на максимально возможную скорость передачи данных из-за паразитной емкости сигналов шины SPI.

Однонаправленные устройства SPI требуют только сигнал тактирования и один из сигналов данных. Какой именно сигнал данных - MISO или MOSI - будет использоваться, зависит от предназначения устройства. Простые сенсоры, к примеру, просто отправляют данные (как S2 на рис. 2-1), в то время как внешний DAC просто принимает данные (как S3 на рис. 2-1).

[2.4. Диаграммы времени SPI]

У SPI есть 4 режима работы SPI, от 0 до 3 (Mode 0 .. Mode 3). Эти режимы по существу управляют методом, как данные вдвигаются или выдвигаются через SPI. Конфигурирование осуществляется двумя битами в SPI control register (SPCR). Полярность тактов указывается управляющим битом CPOL, которым выбирается активный лог. 1 или лог. 0 уровень для тактов. Управляющий бит фазы тактов CPHA выбирает один из двух фундаментально разных формата передачи. Чтобы гарантировать правильный обмен данными между master и slave, оба устройства должны работать в одинаковом режиме. Это может потребовать переконфигурирования master, чтобы соответствовать требованиям разных периферийных устройств slave (когда один master работает через одну шину SPI с несколькими устройствами slave).

Возможные комбинации битов CPOL и CPHA задают различные режимы SPI как это показано в таблице 2-3. Поскольку тут нет четкого стандарта, и эти режимы называются по-разному в разной литературе, то к конфигурированию SPI следует подойти с особым вниманием.

Таблица 2-3. Конфигурирование режима тактирования SPI.

Mode CPOL CPHA Перепад SCK для сдвига Перепад SCK для захвата
0 0 0 1 -> 0 0 -> 1
1 0 1 0 -> 1 1 -> 0
2 1 0 0 -> 1 1 -> 0
3 1 1 1 -> 0 0 -> 1

Полярность тактов (состояние CPOL) не оказывает значительного эффекта на формат передачи. Смена состояния этого бита только лишь приводит к инвертированию сигнала тактов (активный высокий уровень становится активным низким, и низкий уровень состояние ожидания меняется на высокий, и наоборот). Установка фазы (состояние CPHA) напротив, выбирает один из двух разных режимов тактирования данных, которые подробнее будут рассмотрены в следующих двух разделах. Поскольку сигналы MOSI, MISO устройств master и slave напрямую соединены друг с другом, то диаграммы времени показывают форму сигналов сразу о обоих устройств master и slave. Сигнал ~SS является входом выборки у подчиненного устройства. Вывод ~SS устройства master не показан на диаграммах. Он не активен при лог. 1 на ~SS (если ~SS был сконфигурирован как вход), или выход ~SS конфигурируется как выходной порт, тогда он может управляться программно и работать как обычный порт GPIO.

[2.5. CPHA=0 CPOL=0 (Mode 0) и CPHA=0 CPOL=1 (Mode 1)]

Форма сигналов SPI, когда CPHA=0, показана на рис. 2-2. Для сигнала SCK показано 2 варианта формы: когда CPOL=0 и когда CPOL=1.

AVR151 SPI Transfer Format CPHA 0

Рис. 2-2. Формат передачи SPI, когда CPHA=0.

Примечание: состояние * не определено, но обычно самый старший бит (MSB) принятого символа. MSB означает Most Significant Bit (самый старший бит), LSB означает Least Significant Bit (самый младший бит).

Когда SPI сконфигурирован в режиме slave, передача начинается по спаду сигнала ~SS. Это активирует аппаратуру SPI slave-устройства, и значение MSB в выводимом байте, сохраненном в регистре данных (SPDR), выводится на сигнал MISO. Действительная передача начнется, когда устройство master запишет в своем программном обеспечении содержимое регистра SPDR. Это приведет к генерации тактового сигнала. В случаях, когда CPHA=0, сигнал SCK остается в 0 для первой половины первого цикла SCK. Это гарантирует стабильность сигнала на входах как master, так и slave. Данные на входных сигнальных линиях будут прочитаны по переходу сигнала SCK из неактивного состояния в активное (нарастание сигнала, если CPOL=0, и спад сигнала, если CPOL=1). Переход сигнала SCK из активного состояния в неактивное (спад сигнала, если CPOL=0, и нарастание, если CPOL=1) приведет к сдвигу данных на 1 бит, так что следующий биты будет выведен на сигналы MOSI и MISO.

После того, как будут завершены 8 импульсов тактов, цикл передачи байта будет завершен. В обоих устройствах (master и slave) установится SPI interrupt flag (SPIF), и принятый байт попадет в буфер приема.

[2.6. CPHA=1 CPOL=0 (Mode 2) и CPHA=1 CPOL=1 (Mode 3)]

Форма сигналов SPI, когда CPHA=1, показана на рис. 2-3. Для сигнала SCK показано 2 варианта формы: когда CPOL=0 и когда CPOL=1.

AVR151 SPI Transfer Format CPHA 1

Рис. 2-3. Формат передачи SPI, когда CPHA=1.

Примечание: состояние * не определено, но обычно самый младший бит (LSB) ранее переданного символа. MSB означает Most Significant Bit (самый старший бит), LSB означает Least Significant Bit (самый младший бит).

Как и в предыдущих двух случаях (описанными в 2.5), спад уровня на ~SS выбирает и актирует устройство slave. В сравнении с двумя предыдущими случаями (описанными в 2.5), где CPHA=0, здесь передача не начинается, и в этот момент MSB не выводится устройством slave.

Действительная передача начинается, когда программное обеспечение устройства master запишет байт в SPDR, и начнется генерация сигнала тактов SCK. Первый переход сигнала из неактивного состояния в активное (нарастание сигнала, если CPOL=0, и спад сигнала, если CPOL=1) приведет к выводу MSB байта (который был записан в SPDR) как устройством master, так и устройством slave. Как показано на рис. 2-3, здесь нет задержки в половину такта SCK, как это было в режимах Mode 0 и Mode 1. Сигнал SCK немедленно меняет свой уровень с началом первого такта SCK. Данные на входных сигналах будут прочитаны по переходу сигнала SCK из активного состояния в неактивное (спад сигнала, если CPOL=0, и нарастание, если CPOL=1).

После того, как будут завершены 8 импульсов тактов, цикл передачи байта будет завершен. В обоих устройствах (master и slave) установится SPI interrupt flag (SPIF), и принятый байт попадет в буфер приема.

2.6.1. Что нужно учесть на высоких скоростях обмена

Устройства, которые работают на высоких тактовых частотах, и модули SPI, которые способны к работе на скоростях, доходящих до половины периода системной тактовой частоты, должны использоваться так, чтобы для передатчика и приемника было более точное соответствие тактирования. На следующих двух диаграммах показана форма сигналов для AVR в режиме master и режиме slave для SPI Mode 0 и Mode 1. Точные значения отображаемых интервалов времени зависят от различных типов устройств, и не обсуждаются в этом апноуте. Однако принцип функционирования всех устройств один и тот же, так что для них всех нужно принимать во внимание следующие соображения.

AVR151 SPI Timing Master Mode

Рис. 2-4. Диаграммы времени Master.

Минимальные требования к интервалам тактового сигнала показаны на рисунках метками "1" и "2". Значение "1" указывает период SCK, в то время как "2" указывает времена высокого / низкого уровня тактового сигнала. Максимальное время нарастания и спада сигнала SCK указано меткой "3". Это первые 3 интервала времени AVR master, которые должны быть проверены на соответствие требованиям slave-устройства.

Время установки (Setup time) "4" и время удержания (Hold time) "5" являются важными интервалами, потому что они указывают требования к AVR, определяемые подключением к slave-устройству. Эти интервалы определяют, сколько времени должно пройти до тактового перепада slave, после чего должны быть готовы допустимые выходные данные, и сколько времени после тактового перепада эти данные должны удерживаться в допустимом состоянии.

Что если время Setup и Hold достаточно велики, чтобы slave походило к требованиям AVR, подойдет ли AVR к требованиям slave?

Время "6" (Out к SCK) указывает минимальное время, когда AVR имеет готовые действительные выходные данные до появления тактового перепада. Это время можно сравнить с временем Setup "4" slave-устройства.

The time "7" (SCK к Out) задает максимальное время, после которого AVR выводит следующий бит данных, а время "8" (SCK к Out high) указывает минимальное время, когда последний бит данных сигнала MOSI в верном значении после того как SCK был установлен обратно к состоянию ожидания (idle state).

AVR151 SPI Timing Slave Mode

Рис. 2-5. Диаграммы времени Slave.

Принцип формирования диаграмм в режиме slave тот же самый, как он был ранее описан для режима master. Это потому, что при переключении ролей между master и slave требования к интервалам также инвертируются. Минимальные интервалы режима master теперь становятся максимальными интервалами, и наоборот.

[2.7. Конфликты в передаче SPI]

Коллизия записи происходит, если записан SPDR, когда передача еще не завершилась. Поскольку этот регистр имеет только одиночную буферизацию для направления передачи, то запись в SPDR приведет к записи данных непосредственно в регистр сдвига SPI. Поскольку эта операция записи повредила бы данные текущей передачи, генерируется ошибка коллизии записи (write-collision error) путем установки бита WCOL в регистре SPSR. Запись при этом не будет выполнена, и текущая передача продолжится без помех.

Ошибка коллизии записи обычно происходит в устройстве slave, потому что slave никак не управляет моментом, когда master начнет передачу. Однако master знает, когда происходит передача. Так что master не должен генерировать ошибки коллизии записи, хотя логика SPI может детектировать эти ошибки как в режиме master, так и в режиме slave.

[2.8. Эмуляция SPI]

Когда SPI эмулируется с использованием эмулятора аппаратуры ICE200, имейте в виду, что в действительности периферийные устройства в этом эмуляторе не останавливаются на точке останова (break point), а продолжают работать с той скоростью, которая была сконфигурирована.

Когда SPI эмулируется с использованием ICEPRO, то интервалы времени будут менее точны, чем если бы использовался реальный микроконтроллер сам по себе. Причина в том, что у ICEPRO больше длина цепей сигналов - это та цена, которую мы вынуждены заплатить за возможность апгрейда и получение гибкости в использовании.

[2.9. Настройка SPI]

Конфигурирование SPI в режиме master будет показано в двумя разными способами. Первый пример покажет реализацию обмена через SPI, когда текущее состояние передачи получают опросом флагов прерывания (polling). Второй пример покажет, как реализовать обмен данными с управлением потоком по прерываниям.

Обмен данными между двумя устройствами AVR будет показан в виде отправки текста "Text String" от устройства master к устройству slave. Принятые символы будут сравнены с ожидаемыми, и результат проверки будет выведен в порт D. Эти результаты хорошо подходят к для реализации на 2 платах разработчика наподобие STK500.

Во всех примерах, показанных здесь, SPI конфигурируется в Mode 0, и MSB передается первым. Это достигается сбросом в 0 битов CPOL, CPHA и DORD в регистре SPCR. В том же регистре устанавливается бит SPE, разрешающий работу SPI. Тактовая частота SCK задается CK/4 в первом примере (управление потоком по опросу) и CK/16 в коде второго примера (управление потоком по прерываниям).

Таблица 2-4. Настройка тактирования SPI микроконтроллера AT90USB162 битами SPI2X (регистр SPSR), и битами SPR1, SPR0 (регистр SPCR). Частота CK и fosc это тактовая частота ядра микроконтроллера.

SPI2X SPR1 SPR0 Тактовая частота SCK
0 0 0 fosc/4
0 0 1 fosc/16
0 1 0 fosc/64
0 1 1 fosc/128
1 0 0 fosc/2
1 0 1 fosc/8
1 1 0 fosc/32
1 1 1 fosc/64

Для сравнения конфигураций SPI в разных примерах нужно обратить внимание на установку бита выбора Master / Slave (MSTR) и бита разрешения прерывания SPI Interrupt Enable (SPIE).

Примечания:

1. Поскольку оба примера показывают передачу между одним master и одним slave, то не требуется проверять, установлен ли бит MSTR перед тем, как master начнет новую передачу. Этот код должен быть добавлен в приложение, где используется несколько устройств master.

2. Хотя установка битов выбора тактовой частоты (Clock Rate Select bits) не дает никакого эффекта в режиме slave, нужно удостоверится, что системная частота (CK) slave-устройства как минимум в 4 раза выше тактовой частоты SPI (SCK).

3. Очистка ожидающих прерываний SPI осуществляется пустым доступом к SPSR и SPDR.

Вместе с этим апноутом поставляются 2 файла исходного кода на языке C.

Чтобы запустить код, настройте 2 платы STK500, как это показано на рис. 2-6. Код написан для ATmega162, но может быть скомпилирован для любых AVR, у которых есть аппаратный SPI и порты PORTA, PORTB и PORTD.

AVR151 test hardware setup

Рис. 2-6. Настройка аппаратуры.

2.9.1. Пример 1 - обмен данными через SPI по принципу опроса

2.9.1.1. На стороне Master:

Если не используются прерывания, то нужно сконфигурировать только лишь аппаратуру SPI и выводы сигналов обмена. В этом примере важно настроить вывод ~SS как выходной порт. Это делается перед тем, как будет разрешена работа SPI в режиме master. Разрешение SPI, когда ~SS сконфигурирован как вход, приведет к тому, что SPI немедленно переключится в режим slave, если к выводу ~SS будет приложен низкий уровень. В режиме slave вывод ~SS всегда конфигурируется как вход (см. рис. 2-7). Использование опроса дает самый быстрый обмен данными, поэтому в режиме master такой принцип работы используется чаще всего.

AVR151 SPI Polled master initialization and transmission

Рис. 2-7. Master, работающий по опросу - инициализация и передача.

2.9.1.2. На стороне Slave:

Для конфигурирования AVR в режиме SPI slave нет нужды придерживаться какого-либо определенного порядка при инициализации регистров. Вывод MISO настраивается как выход, и все остальные выводы автоматически настраиваются как входы, когда разрешается работа SPI (см. таблицу 2-8). Для конфигурирования SPI AVR в режиме slave нужно сбросить в 0 бит MSTR. В этом случае биты выбора тактовой частоты SPR0 и SPR1 не оказывают никакого влияния, потому что происходит синхронный обмен данными по тактам master-устройства.

Все другие настройки регистра конфигурации SPI (SPCR) будут такими же, как и в режиме master. Это важно для гарантии успешного обмена данными между двумя устройствами.

AVR151 SPI Polled slave initialization and reception

Рис. 2-8. Slave, работающий по опросу - инициализация и прием.

2.9.2. Пример 2 - обмен через SPI под управлением прерываний

Обмен данными по прерываниям в режиме master имеет смысл использовать, только если генерируемая тактовая частота SCK получается большим делителем системной частоты (наподобие 64 или 128). В этом случае процессор может делать что-то еще, вместо того чтобы тупо ждать момента, когда можно передать/принять очередной байт (собственно в этом и заключается достоинство метода работы по прерываниям). В режиме slave, когда устройство не знает, когда именно начнется передача, в реализации под управлением прерываний понижение тактовой частоты SPI требуется для того, чтобы успеть среагировать на начало передачи, и избежать ошибок коллизий записи.

2.9.2.1. На стороне Master:

Инициализация SPI происходит точно так же, как и инициализация master в первом примере. Как и раньше, в режиме master сначала настраивается вывод ~SS как выход, и только потом можно разрешить работу SPI. Прерывание SPI разрешается установкой бита SPIE в регистре SPCR.

AVR151 SPI Interrupt controlled master initialization and transmission

Рис. 2-9. Master, работающий по прерываниям - инициализация и передача.

2.9.2.2. На стороне Slave:

Устройство slave не знает, когда master запустит новый обмен данными. Прерывания - отличная возможность реагировать на события, когда заранее не известно, когда они произойдут, и именно таким способом реализована работа SPI в режиме slave.

В этом примере основная программа (тело функции main) занимается оповещениями об ошибках передачи и событиях завершения передачи.

AVR151 SPI Interrupt controlled slave initialization and reception

Рис. 2-10. Slave, работающий по прерываниям - инициализация и прием.

[Исходный код примеров]

Atmel предоставила для апноута AVR151 примеры исходного кода для IAR. Исходный код примеров можно скачать с сайта Atmel, либо по ссылке [3] (с комментариями в коде, переведенными на русский язык).

//***************************************************************************
// A P P L I C A T I O N   N O T E   F O R   T H E   A V R   F A M I L Y
//
// Number               : AVR151
// File Name            : "avr151_Master.c"
// Title                : Настройка и использование SPI
// Date                 : 00.09.20
// Version              : 1.0
// Target MCU           : Любой AVR, имеющий на борту SPI
//
// ОПИСАНИЕ
// Этот апноут показывает, как разрешить и использовать встроенный в микроконтроллер
//  аппаратный интерфейс SPI в режиме Master.
//
// Этот файл содержит код всех примеров. Для переключения между примерами Вам
//  нужно будет вызывать соответствующие подпрограммы (см. ниже комментарии в основной
//  программе, т. е. в функции main).
//
// Modified 2004-10-25 RAA
//**************************************************************************
#include "inavr.h"
#include "ioavr.h"
 
char*    TextString    = "AVR communicating via the SPI"+0x00;
char*    PtrToStrChar;           // Указатель на определенный символ в строке
char     ClearToSend   = 1;      // Флаг завершения отправки строки.
 
// Обработчик прерывания для режима Master (для управления потоком по прерываниям)
#pragma vector=SPI_STC_vect
__interrupt void ISR_SPI (void)
{
   PtrToStrChar++;               // Указать на следующий символ в строке,
   if (*PtrToStrChar != 0)       //  если это еще не конец строки.
   {
      SPDR  = *PtrToStrChar;     // Отправить символ через SPI.
   }
   else
   {
      ClearToSend = 1;           // Если дошли до конца строки, разрешить
                                 //  отправку следующей строки.
}
 
// Подпрограмма инициализации режима Master с управлением потоком по опросу.
void Init_Master (void)
{
   volatile char IOReg;
   // Настроить PB4(/SS), PB5(MOSI), PB7(SCK) как выходы:
   DDRB    = (1 << PB4)|(1 << PB5)|(1 << PB7);
   // Разрешить SPI в режиме Master Mode с тактовой частотой SCK = CK/4:
   SPCR    = (1 << SPE)|(1 << MSTR);
   IOReg   = SPSR;               // очистить бит SPIF в регистре SPSR
   IOReg   = SPDR;
}
 
// Подпрограмма инициализации режима Master с управлением потоком по прерываниям.
void Init_Master_IntContr (void)
{
   volatile char IOReg;
   // Настроить PB4(/SS), PB5(MOSI), PB7(SCK) как выходы:
   DDRB  = (1 << PB4)|(1 << PB5)|(1 << PB7);
   // Разрешить прерывание SPI и режим SPI Master с тактовой частотой SCK = CK/16:
   SPCR  = (1 << SPIE)|(1 << SPE)|(1 << MSTR)|(1 << SPR0);
   IOReg   = SPSR;               // Очистить бит SPIF в регистре SPSR.
   IOReg   = SPDR;
   __enable_interrupt();         // Глобально разрешить прерывания.
 }
 
// Подпрограмма отправки режима Master (для работы по опросу)
void Master_Send (void)
{
   PtrToStrChar  = TextString;        // Установить указатель на начало строки.
   while   (*PtrToStrChar != 0)       // Цикл "если не конец строки", то ..
   {
      SPDR  = *PtrToStrChar;          // .. отправить символ через SPI,
      while (!(SPSR & (1 << SPIF)));    // ждать завершения отправки символа,
      PtrToStrChar++;                 // перейти к следующему символу в строке.
   }
}
 
// Подпрограмма отправки режима Master (для работы по прерываниям)
void Master_Send_IntContr (void)
{
   if (ClearToSend == 1)   // Если передача не идет, то ..
   {                  
      PtrToStrChar  = TextString;   // .. установить указатель на начало строки,
                                    // запустить новую передачу путем отправки
      SPDR = *PtrToStrChar;         // первого символа новой строки,
      ClearToSend = 0;              // заблокировать инициацию новых передач.
   }
}
 
void main (void)
{
   char KeyPressed = false;
   DDRD  = 0xFF;
   PORTD = 0x00;
   // Для Примера 1 раскомментируйте следующую строку:
   Init_Master ();               // инициализация для работы по опросу
   // Для Примера 2 раскомментируйте следующую строку:
   // Init_Master_IntContr();      // инициализация для работы по прерываниям
   while (1)
   {
      if (PINA != 0xFF)
      {
         if (KeyPressed == false)
         {
            KeyPressed = true;
            PORTD = ~PORTD; // Переключить в противоположное состояние выходы
                            //  порта D (туда подключены светодиоды).
            // Для Примера 1 раскомментируйте следующую строку:
            Master_Send ();
            // Для Примера 2 раскомментируйте следующую строку:
            // Master_Send_IntContr();
         }
      }
      else
         KeyPressed = false;
   }
}

//***************************************************************************
// A P P L I C A T I O N   N O T E   F O R   T H E   A V R   F A M I L Y
//
// Number               : AVR151
// File Name            : "avr151_Master.c"
// Title                : Настройка и использование SPI
// Date                 : 00.09.20
// Version              : 1.0
// Target MCU           : Любой AVR, имеющий на борту SPI
//
// ОПИСАНИЕ
// Этот апноут показывает, как разрешить и использовать встроенный в микроконтроллер
//  аппаратный интерфейс SPI в режиме Slave.
//
// Этот файл содержит код всех примеров. Для переключения между примерами Вам
//  нужно будет вызывать соответствующие подпрограммы (см. ниже комментарии в основной
//  программе, т. е. в функции main).
//
// Modified 2004-10-25 RAA
//**************************************************************************
#include "inavr.h"
#include "ioavr.h" 
 
#define  Error   0x01
#define  Success 0x02
 
char     TransmitState = 0x00;
char*    TextString    = "AVR communicating via the SPI"+0x00;
 
// Обработчик прерывания для режима Slave (для управления потоком по прерываниям)
#pragma vector=SPI_STC_vect
__interrupt void ISR_SPI (void)
{
   if (SPDR != *TextString)         // Проверка на ошибки при передаче
   {
      TransmitState = Error;        // Ошибка!
      SPCR &= ~(1 << SPIE);         // Запрет прерываний SPI.
   }
   else 
   {
      TextString++;                 // Перейти к следующему символу,
      if (*TextString == 0)         //  проверить на конец строки
      {
         TransmitState = Success;   // Прием строки успешно завершен.
         SPCR &= ~(1 << SPIE);      // Запрет прерываний SPI.
      }
   }
}
 
// Подпрограмма инициализации режима Slave с управлением потоком по опросу.
void Init_Slave (void)
{
   volatile char IOReg;
   // Настройка PB6(MISO) как выхода:
   DDRB    = (1 << PB6);
   // Разрешить SPI в режиме Slave с тактовой частотой SCK = CK/4:
   SPCR    = (1 << SPE);
   IOReg   = SPSR;         // Очистить бит SPIF в регистре SPSR.
   IOReg   = SPDR;
   DDRD    = 0xFF;         // Настроить разряды порта D как выходы.
}
 
// Подпрограмма инициализации режима Slave с управлением потоком по прерываниям.
void Init_Slave_IntContr (void)
{
   volatile char IOReg;
   // Настройка PB6(MISO) как выхода:
   DDRB    = (1 << PB6);
   // Разрешить прерывание SPI и разрешить SPI в режиме Slave с тактовой 
   //  частотой SCK = CK/4:
   SPCR     = (1 << SPIE)|(1 << SPE);
   IOReg    = SPSR;        // Очистить бит SPIF в регистре SPSR.
   IOReg    = SPDR;
   DDRD     = 0xFF;        // Настроить разряды порта D как выходы.
   __enable_interrupt();   // Глобально разрешить прерывания.
}
 
// Подпрограмма приема в режиме Slave (с работой по прерываниям)
void Slave_Receive_IntContr (void)
{
   while (1)
   {
      switch (TransmitState)
      {// Индикация ошибки или успешного завершения приема светодиодами,
       //  подключенными к порту D:
      case 0x00:
         PORTD = 0x00;
         break;
      case Error:
         PORTD = 0xF0;
         break;
      case Success:
         PORTD = 0xAA;
         break;
      }
   }
}
 
// Подпрограмма приема в режиме Slave (с работой по опросу)
void Slave_Receive (void)
{
   // Цикл, пока не дойдем до конца строки, или не произойдет ошибка:
   while ((*TextString != 0) && (TransmitState != Error))
   {
      while (!(SPSR & (1 << SPIF)));  // Ожидание момента приема символа.
      // Проверка: правильно ли принят символ:
      if (SPDR  != *TextString)
         TransmitState = Error;
      else
         TextString++;              // Перейти к следующему символу
      // output result transmission status on Port D 
      if (TransmitState == Error)
         PORTD = 0xF0;
      else
         PORTD = 0xAA;
      while (1);
}
 
void main (void)
{
   // Для Примера 1 раскомментируйте следующие две строки:
   Init_Slave();
   Slave_Receive();
   // Для Примера 2 раскомментируйте следующие две строки:
   // Init_Slave_IntContr();
   // Slave_Receive_IntContr();
}

[Ссылки]

1. AVR151: Setup And Use of The SPI site:atmel.com.
2. Интерфейс SPI.
3150219AVR-apnouts.zip - архив с апноутами и исходным кодом.
4. ATmega328: SPI.

 

Комментарии  

 
+2 #4 protv 12.05.2022 21:17
Перечитываю данную фразу, и ни как не могу понять почему при низком делителе не рекомендуют использовать прерывания. "Обмен данными по прерываниям в режиме master имеет смысл использовать, только если генерируемая тактовая частота SCK получается большим делителем системной частоты (наподобие 64 или 128). В этом случае процессор может делать что-то еще, вместо того чтобы тупо ждать момента, когда можно передать/принять очередной байт."

Частота кварца 14.7456 МГц, микруха памяти по даташиту держит до 20 МГц. То есть что бы работать на прерывания нужно принудительно и основательно занижать частоту шины?

microsin: занижать тактовую частоту master SCK в случае использования для обмена прерываний может понадобиться только в том случае, если нужны интенсивные вычисления на фоне длительного интенсивного обмена через SPI. Понятно почему - при высокой частоте обмена процессор будет вынужден почти все время находиться в обработчике прерывания SPI, и на основной поток вычислений времени не останется.
Цитировать
 
 
0 #3 Александр 18.03.2021 09:25
Цитирую Alexey:
microsin: для разрешения подобных конфликтов как раз и придуманы сигналы выборки ~SS. Если на выводе ~SS не активный уровень (лог. 1), то подчиненное устройство переводит свой выход MISO в третье (отключенное) состояние.

Я правильно понял, для корректной реализации работы с несколькими slave устройствами желательно применять тот или иной демультиплексор из 74 логики, где управляемый контроллером SPI сигнал ~SS подается на вход, а выбор "клиента" осуществляется ногами?

microsin: вы все поняли правильно, но мне очень сложно придумать ситуацию, когда такой демультиплексор (дешифратор) может понадобиться. Обычно подчиненных устройств на шине SPI немного, и никакой демультиплексор не нужен - выборками непосредственно может управлять главное устройство шины, управляющий микроконтроллер .
Цитировать
 
 
0 #2 Владимир 28.12.2019 20:08
Некоторые девайсы, напр. синтезатор 7001 или DDS AD9850 просят передачи байта начиная с младшего бита. Есть ли у AVR SPI опция реверса, или байт нужно предварительно оборачивать программно? Если нет, то проще передать байт программно через любую ногу не заморачиваясь с SPI. Если кто прочтет, прошу ответить в личку.

microsin: регистр управления SPCR, бит Bit 5 – DORD: Data Order.
Цитировать
 
 
0 #1 Alexey 06.07.2018 22:06
Если два устройства Slave ждут обмена с одним Master одновременно, их шины MISO объединены и выдают (неважно, 1 или 0) в ожидании, а во время обмена MISO одного вынужден ослаблять (мешать) MISO другого. Получается нельзя ставить устройство Slave на ожидание обмена, включив MISO на выход, если их несколько, а только MISO на вход. Тогда в примерах - ошибка.

microsin: для разрешения подобных конфликтов как раз и придуманы сигналы выборки ~SS. Если на выводе ~SS не активный уровень (лог. 1), то подчиненное устройство переводит свой выход MISO в третье (отключенное) состояние.
Цитировать
 

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


Защитный код
Обновить

Top of Page