Предисловие
Данный перевод сделан мною для группы переводчиков, которые осуществляют перевод книги Mastering STM32 (v0.21). К сожалению, ребята завязли в рутине и мы вряд ли в скором времени увидим перевод полностью, поэтому буду выкладывать свои куски перевода.
Описание SPI
Serial Peripheral Interface (SPI) — это последовательный, синхронный, полнодуплексный протокол передачи данных между главным (master) контроллером (обычно микроконтроллером или другими устройством с программируемой функциональностью) и несколькими ведомыми (slave) устройствами. Как мы увидим далее, SPI позволяет передавать данные как в полнодуплексном, так и в полудуплексном режиме. Спецификация SPI стандарт в протоколах передачи данных и была разработана в конце 70-х компанией Motorola и на данный широко используется как протокол передачи данных для многих цифровых микросхем. В отличие от протокола I2C, SPI не задает жестких условий в протоколе передачи данных по шине, давая ведомым (slave) устройствам полную свободу в структуре сообщений обмена данными.
Рисунок 1: Архитектура типовой SPI шины.
Типовая шина SPI содержит 4 сигнала, как показано на рисунке 1, даже, если возможно управлять некоторыми SPI устройствами, используя лишь 3 сигнала (в таком случае мы говорим о 3-проводном SPI).
- SCK: сигнал, используемый для генерации тактовой частоты синхронизации передачи данных по SPI. Генерируется master устройством и это означает, что каждая передача по шине SPI всегда начинается по инициативе master устройства. В отличие от I2C SPI более быстрый интерфейс передачи данных и тактовая частота обычно в районе нескольких мегагерц. В наше время вполне обычно найти SPI устройства со скоростью обмена данными от 100 МГц и выше. Более того, протокол SPI позволяет работать в одной шине на различных скоростях в одно и то же время.
- MOSI: расшифровывается как Master Output Slave Input и используется для передачи данных от главного (master) устройства к ведомому (slave). В отличие от I2C, где для обмена между устройствами используется лишь один провод, в SPI предусмотрено две линии передачии данных.
- MISO: расшифровывается как Master Input Slave Output и используется для передачи данных от ведомого (slave) устройства к главному (master).
- SSn: Slave Select используется для адресации устройств в шине SPI, где „n“ — количество линий адресации. В отличие от I2C, SPI не использует адреса ведомых (slave) устройств, а использует физические линии адресации, которые устанавливаются в низкий логический уровень для выбора устройства. В типовой SPI шине только одно ведомое (slave) устройство может быть активно в одно время путем установки линии SS в низкий уровень. В этом причина того, что в одной и той же шине могут быть устройства с разной скоростью передачи данных.
Имея две раздельные шины данных, MOSI и MISO, SPI по сути является полнодуплексным интерфейсом передачи данных, таким образом ведомое (slave) устройство может отдавать данные главному (master) пока одновременно принимает данные от него же. В одноранговых шинах (когда одно главное (master) и одно ведомое (slave) устройства), сигнал SS может не использоваться, лишь достаточно его подтянуть к «земле» резистором на несколько килоом, линии MISO/MOSI соединяются в одно общую линию Slave In/Slave Out (SISO). В таком случае мы говорим о двухпроводном SPI, хотя по существу он конечно же трехпроводной.
Рисунок 2: Как передаются данные по шине SPI в полнодуплексном режиме
Каждая транзакция в шине начинается с подачи тактирования в линию SCK в соответствии с допустимой тактовой частотой ведомого (slave) устройства. В то же время главное (master) устройство устанавливает LOW уровень на линии SS и передача данных начинается. Обычно обмен данными подразумевает использование двух регистров (в основном 8-битные, хотя некоторые slave устройства поддерживают 16-битный размер слова), одного в главном (master) устройстве и один в ведомом (slave). Данные побитно сдвигаются в сдвиговом регистре, начиная со старшего бита, пока младший бит сдвигается в этот же самый регистр. В это же самое время, данные от ведомого (slave) устройства сдвигаются в младший бит регистра данных. Когда все биты регистра будут сдвинуты в одну и другую сторону обмен данными будет осуществлен. Если необходимо передать более одного слова данных, сдвиговые регистры обнуляются и процесс повторяется. Обмен данными может продолжаться сколько угодно большое количество циклов тактового генератора. Когда обмен завершен master выключает тактовый сигнал и линия SS возвращается в исходное состояние высокого логического уровня.
Рисунок 2 показывает процесс обмена данными в полнодуплексном режиме, рисунок 3 же демонстрирует процесс в полудуплексном режиме.
Рисунок 3: Как передаются данные по шине SPI в полудуплексном режиме
Полярность и фаза тактового сигнала (Clock Polarity and Phase)
В дополнение к настройкам тактирования шины, master и slave должны «договориться» о полярности и фазе тактового сигнала. SPI спецификация подразумевает две настройки CPOL и CPHA соответственно и многие производители микросхем заимствовали данные названия настроек.
Комбинации полярности и фазы тактового сигнала часто ассоциируют с режимами работы SPI, в таблице 1 можно увидеть данные комбинации. Наиболее общие и часто используемые режимы это 0 и 3, большинство ведомых (slave) устройств поддерживают как минимум пару режимов.
Таблица 1: Режимы работы SPI, соответствующие конфигурации CPOL и CPHA
Временная диаграмма показана на рисунке 4 и она дополнительно описана ниже:
- Если CPOL=0 то основное состояние тактового сигнала находится в 0, таким образом в активном состоянии он переключается на 1, в режиме ожидания в 0.
— Для CPHA=0 бит данных захватывается по возрастающему фронту (LOW→HIGH) и отправляется по ниспадающему фронту (HIGH→LOW).
— Для CPHA=1 бит данных захватывается по ниспадающему фронту (HIGH→LOW) и отправляется по возрастающему фронту (LOW→HIGH). - Если CPOL=1, то основное состояние тактового сигнала в 1, активное состояние в 0, ожидание 1.
— Для CPHA=0 бит данных захватывается по ниспадающему фронту (HIGH→LOW) и отправляется по возрастающему фронту (LOW→HIGH).
— Для CPHA=1 бит данных захватывается по возрастающему фронту (LOW→HIGH) и отправляется по ниспадающему фронту (HIGH→LOW).
Вот и все, CPHA=0 подразумевает, что выборка идет по первому фронту импульса тактовой частоты, CPHA=1 же подразумевает, что выборка осуществляется по второму фронту импульса тактовой частоты вне зависимости от того возрастающий ли фронт или спадающий. Необходимо помнить, что при CPHA=0 данные должны быть стабильны за пол цикла до начала первого импульса тактовой частоты.
Рисунок 4: Временная диаграмма для соответствующих настроек CPOL и CPHA
Управление сигналом Slave Select
Как говорилось ранее, ведомое устройство в SPI шине не имеет адреса, по которому его можно было бы идентифицировать, но оно начинает обмен данными и продолжает его пока сигнал Slave Select (SS) находится в низком логическом уровне LOW. STM32 микроконтроллеры предоставляют два режима управления сигналом SS или NSS, как он называется в документации ST. Давайте проанализируем их.
- Программный режим NSS (NSS software mode): сигнал SS управляется из программы и используется любой свободный GPIO пин.
- Аппаратный режим NSS (NSS hardware mode): для работы с сигналом SS используется специальный IO пин, который управляется SPI интерфейсом. В данном режиме доступны две конфигурации NSS выхода:
— NSS выход включен: данная настройка используется только, когда устройство работает в режиме master. NSS сигнал устанавливается в LOW, когда master начинает передачу и остается в уровне LOW пока SPI отключен. Стоит заметить, что этот режим возможно использовать, когда лишь одно ведомое (slave) устройство в шине и ее SS пин подключен к сигнальной линии NSS.
— NSS выход отключен: эта конфигурация допускает использование режима multi-master для устройств, работающих в режиме master. Для ведомых (slave) устройств NSS пин работает, как простой вход, т.е. slave выбран, когда NSS в LOW.
TI режим SPI
SPI интерфейс в микроконтроллерах STM32 поддерживает TI режим, когда микроконтроллер работает в режиме master и сигнал NSS настроен на аппаратный режим работы. В режиме TI полярность и фаза тактового сигнала SPI автоматически устанавливаются в соответствии с требованиями протокола Texas Instruments вне зависимости от того какие настройки были выбраны до этого. Управление сигналом NSS также специфично для протокола TI, NSS сигнал совершает серию импульсов в конце каждого передаваемого байта (с LOW уровня в HIGH в начале передачи младшего бита и с HIGH в LOW в начале передачи старшего бита). Для более подробной информации можете обратиться в референс мануалу соответствующего микроконтроллера.
Таблица 2: SPI периферия всех 16-ти плат Nucleo
Наличие SPI интерфейсов в микроконтроллерах STM32
В зависимости от семейства и корпуса, микронтроллеры STM32 могут иметь до шести независимых SPI интерфейсов. Таблица 2 демонстрирует наличие интерфейсов всех плат Nucleo, рассматриваемых в рамках этой книги.
Для каждого SPI интерфейса в таблице 2 показаны пины MOSI, MISO и SCK. Темными строками выделены альтернативные пины, которые могут быть использованы благодаря соответствующей разводке платы. Для примера, микроконтроллер STM32F401RE может использовать SPI1 с пинами PA7, PA6 и PA5, но возможно использовать также пины PB5, PB4 и PB3 как альтернативные с помощью ремаппинга. Стоит заметить также, что SPI1 использует одни и те же пины во всех микроконтроллерах STM32 в корпусах LQFP-64, что сделано для pin-to-pin совместимости различных семейств микроконтроллеров STM32.
Теперь мы готовы рассмотреть как использовать CubeHAL API для программирования данного интерфейса.
HAL_SPI модуль
Для программирования SPI HAL определяет С структуру SPI_HandleTypeDef:
Давайте проанализируем наиболее важные поля данной структуры.
- Instance: указатель на дескриптор SPI, который мы будем использовать. Например, SPI1 — это дескриптор первого SPI интерфейса.
- Init: объект C структуры SPI_InitTypeDef, которая используется для настройки интерфейса, далее мы рассмотрим ее более подробно.
- ptxBuffPtr, pRxBuffPtr: указатели на временные буферы для принимаемых и отправляемых данных SPI. Они используются, когда SPI работает в режиме прерывания и не могут быть изменены из программы пользователя.
- hdmatx, hdmarx: указатели на объекты DMA_HandleTypeDef структуры, используемой, когда SPI работает в режиме с DMA.
Настройка SPI производится с использованием объекта структуры SPI_InitTypeDef:
- Mode: параметр, определяющий в каком режиме, master или slave работает SPI интерфейс. Может принимать два значения SPI_MODE_MASTER и SPI_MODE_SLAVE.
- Direction: параметр, задающий работу SPI интерфейса либо в 4-проводном режиме (2 отдельные линии для входных и выходных данных соответсвенно), либо в 3-проводном режиме (одна I/O линия). Может принимать следующие значения: SPI_DIRECTION_2LINES или полнодуплексный 4-проводной режим; SPI_DIRECTION_2LINES_RXONLY или полудуплексный 4-проводной режим; SPI_DIRECTION_1LINE или полудуплексный 3-проводной режим.
- DataSize: параметр, задающий размер данных, передаваемых по шине SPI. Может принимать два следующих значения: SPI_DATASIZE_8BIT и SPI_DATASIZE_16BIT.
- CLKPolarity: настройка SCK CPOL, принимает значения SPI_POLARITY_LOW (CPOL=0) и SPI_POLARITY_HIGH (CPOL=1).
- CLKPhase: настройка фазы тактового сигнала SCK, принимает также два значения SPI_PHASE_1EDGE (CPHA=0) и SPI_PHASE_2EDGE (CPHA=1).
- NSS: параметр, задающий поведение линии NSS. Принимает значения SPI_NSS_SOFT для программного управления NSS, SPI_NSS_HARD_INPUT и SPI_NSS_HARD_OUTPUT для настройки NSS сигнала в аппаратном режиме.
- BaudRatePrescaler: делитель частоты шины APB, определяет максимальную тактовую частоту линии SCK. Может принимать значения SPI_BAUDRATEPRESCALER_2, SPI_BAUDRATEPRESCALER_4,…,SPI_BAUDRATEPRESCALER_256.
- FirstBit: параметр, определяющий порядок передачи данных по шине SPI: SPI_FIRSTBIT_MSB или SPI_FIRSTBIT_LSB.
- TIMode: включение/выключение режима поддержки протокола TI. Значения: SPI_TIMODE_DISABLE или SPI_TIMODE_ENABLE.
- CRCCalculation и CRCPolynomial: во всех микроконтроллерах STM32 SPI периферия может аппаратно генерировать CRC контрольную сумму. Значение CRC может передаваться последним байтом в режиме передачи или автоматическая проверка на ошибку CRC может быть произведена с последним принятым байтом данных. Значение CRC вычисляется с использованием нечетного программируемого многочлена на каждом бите. Вычисление происходит на определяемом параметрами CPHA и CPOL фронте тактовой частоты каждой выборки. Вычисленное CRC проверяется автоматически в конце блока данных. Когда появляется ошибка между вычисленным значением CRC от принятых данных и CRC, переданной от передающего устройства, устанавливается состояние ошибки. CRC не доступно в режиме работы SPI с DMA в циклическом режиме. Для более подробной информации рекомендуется ознакомиться с референс мануалом на конкретный микроконтроллер.
Как обычно, для конфигурации SPI мы используем функцию:
HAL_StatusTypeDef HAL_SPI_Init (SPI_HandleTypeDef * hspi);
которая принимает в качестве входного параметра указатель на объект структуры SPI_HandleTypeDef, рассмотренной выше.
Обмен сообщениями с использованием SPI
Как только SPI интерфейс сконфигурирован, мы можем начать обмен данными с ведомыми (slave) устройствами. Так как SPI спецификации не описывает конкретный протокол для передаваемых данных, то и нет особой разницы между master и slave при настройке через CubeHAL. Разница лишь в параметре режимов в структуре SPI_InitTypeDef.
Как обычно, CubeHAL предоставляет три режима настройки шины SPI: опрос, прерывание и режим DMA.
Для отправки нескольких байт в режиме опроса необходимо использовать функцию:
HAL_StatusTypeDef HAL_SPI_Transmit (SPI_HandleTypeDef * hspi, uint8_t * pData, uint16_t Size, uint32_t Timeout);
Параметры, принимаемые этой функцией не нуждаются в описании, т. к. они идентичны уже изученым ранее интерфейсам, например UART. Эта функция может быть использована, если SPI настроен как SPI_DIRECTION_1LINE или SPI_DIRECTION_2LINES. Для приема нескольких байт в режиме опроса мы используем функцию:
HAL_StatusTypeDef HAL_SPI_Receive (SPI_HandleTypeDef * hspi, uint8_t * pData, uint16_t Size, uint32_t Timeout);
Эта функция может использоваться в любом из трех режимов Direction.
Если ведомое (slave) устройство поддерживает полнодуплексный режим, то соответственно можно использовать функцию:
HAL_StatusTypeDef HAL_SPI_TransmitReceive (SPI_HandleTypeDef * hspi, uint8_t * pTxData, uint8_t * pRxData, uint16_t Size, uint32_t Timeout);
которая отправляет несколько байт, одновременно принимая ответ. Функция может быть использована только, если параметр Direction настроен на SPI_DIRECTION_2LINES.
Для обмена данными с использованием прерываний CubeHAL предоставляет следующие три функции:
HAL_StatusTypeDef HAL_SPI_Transmit_IT (SPI_HandleTypeDef * hspi, uint8_t * pData, uint16_t Size); HAL_StatusTypeDef HAL_SPI_Receive_IT (SPI_HandleTypeDef * hspi, uint8_t * pData, uint16_t Size); HAL_StatusTypeDef HAL_SPI_TransmitReceive_IT (SPI_HandleTypeDef * hspi, uint8_t * pTxData, uint8_t * pRxData, uint16_t Size);
CubeHAL также позволяет передавать данные с DMA и функции похожи на три, представленные выше, лишь с префиксом _DMA в конце.
Используя прерывания или DMA, мы должны быть готовы к тому, что в конце передачи мы получим ассинхронный запрос об этом. Это значит, что мы должны принять этот запрос и обработать его, разрешив и настроив контроллер прерывания NVIC и определив функцию HAL_SPI_IRQHandler() обработчик прерывания. Возможно шесть различных вызовов перечисленных в таблице 3.
Когда SPI настроен на работу с DMA в циклическом режиме, можно использовать несколько функий для паузы, продолжения и отмены циклического режима отправки/приема DMA:
HAL_StatusTypeDef HAL_SPI_DMAPause (SPI_HandleTypeDef * hspi); HAL_StatusTypeDef HAL_SPI_DMAResume (SPI_HandleTypeDef * hspi); HAL_StatusTypeDef HAL_SPI_DMAStop (SPI_HandleTypeDef * hspi);
Также в данном режиме существуют следующие ограничения:
- Циклический режим DMA не может быть использован, если SPI настроен исключительно на прием;
- В циклическом режиме с DMA не доступна функция аппаратного расчета CRC;
- Когда используются функции паузы/остановки они могут вызваны только в обработчике функции обратного вызова, т.е. в прерывании.
В этой главе не будет рассмотрено практическое применение SPI, но в последующих главах мы будем использовать SPI для программирования Ethernet контроллера.
Максимальная частота тактирования при использовании CubeHAL
Частота SCK производна от PCLK путем деления на соответствующий предделитель. Этот предделитель принимает значения от 21 до 28. Тем не менее, как уже несколько раз ранее было замечено, CubeHAL добавляет неизбежный расход ресурсов на управление периферией. И это также применимо и к SPI. Фактически, использование CubeHAL не дает возможности получить все возможные частоты SPI с различными режимами работы.
Инженеры ST четко прописали это в документации на CubeHAL. Если вы откроете файл stm32XXxx_hal_-spi.c, то в районе 60 строки вы можете увидеть две таблицы, в которых прописаны максимально-допустимые скорости передачи данных в зависимости от параметра Direction и способа обмена данными по SPI (опрос, прерывание или DMA).
Для примера, в микроконтроллерах STM32F4 мы можем получить частоту SCK, равную fPCLK/8, если SPI интерфейс работает в режиме ведомого (slave) и мы программируем с использованием прерываний в CubeHAL.
Использование CubeMX для настройки SPI
Для использования CubeMX в первую очеред необходимо включить необходимый SPI интерфейс. Далее выбрать необходимый режим работы, как показано на рисунке 5. Далее, необходимо настроить поведение сигнала NSS. После настройки этих двух параметров можно продолжить конфигурацию во вкладке Configuration CubeMX.
Рисунок 5: Как выбрать режим работы SPI в CubeMX
Если статья показалась вам полезной подписывайтесь на мой канал в telegram, чтобы не пропустить анонсы новых статей и другую интересную информацию для разработчиков.
Для тех, кого интересует остальная часть перевода можете пройти по ссылке на гуглдокс https://docs.google.com/spreadsheets/d/1MXv-D5R7S1SIvx4F1FpBppbqPUgDvh7wYG05F3LXf_0/edit#gid=0
По ссылке нет остальной части. Как получить?
Эх, ребята из группы перевода удалили зачем-то аннотации статей и видимо забросили перевод совсем. Можете рассчитывать на те части, что переводил я https://blog.radiotech.kz/category/perevod/ Если хотите, я могу вас добавить в группу telegram, которая занималась переводом, но там уже давно нет активности?
Я написал в группу, чтобы узнать куда делся файл, как ответят я отпишу тут.
https://docs.google.com/spreadsheets/d/1MXv-D5R7S1SIvx4F1FpBppbqPUgDvh7wYG05F3LXf_0/edit#gid=0 восстановили таблицу, кто-то стер оказывается недавно.
Как использовать HAL для управления SPI но на произвольных ногах? Может кто то допиливал библиотеки под это.
Дописывать что-то в HAL категорически не рекомендуется. А что мешает переопределить ноги в cubemx?
Под SPI определен один, иногда два набора, жестко заданных ног.
Ну я и имел в виду сделать remap на те выводы, которые доступны для SPI. Или вам нужен софтовый SPI?
Да, я так и написал. «Как использовать HAL..» Функции HAL могут работать только с объектами привязанными стандартным образом. Ну например вся работа идет через объект SPI_HandleTypeDef hspi;. И сам этот объект нельзя привязать к произвольным ногам.
Полный русский перевод книги. Просьба, распространять дальше
https://disk.yandex.ru/i/0-M9a5lC_NdQGw
Супер. Спасибо за ваши усилия.