Сегодня поговорим о таком протоколе передачи данных как SPI. Посмотрим какие функции нам предоставлены в рамках SPI HAL в STM32. Подробно о интерфейсе SPI можно прочитать в нашей статье.
SPI HAL модуль
Для начала работы с SPI в HAL содержится структура которую необходимо объявить. Ниже представлена эта структура в сокращенном виде, мы оставили только те параметры на которые необходимо обратить внимание:
typedef struct __SPI_HandleTypeDef { SPI_TypeDef *Instance; /* Адрес регистров SPI по умолчанию */ SPI_InitTypeDef Init; /* Настройка SPI протокола для начала передачи */ uint8_t *pTxBuffPtr; /* Tx-передача SPI. Указатель на буфер данных */ uint16_t TxXferSize; /* Размер буфера Tх данных для передачи по SPI */ __IO uint16_t TxXferCount; /* Счетчик Tx передач SPI */ uint8_t *pRxBuffPtr; /* Rx прием по SPI. Указатель на буфер принимаемых данных */ uint16_t RxXferSize; /* Размер буфера Rx для приема данных по SPI */ __IO uint16_t RxXferCount; /* Счетчик Rx-приема данных */ DMA_HandleTypeDef *hdmatx; /* Значения дескриптора DMA для Tx SPI */ DMA_HandleTypeDef *hdmarx; /* Значения дескриптора DMA для Rx SPI */ HAL_LockTypeDef Lock; /* Блокировка модуля SPI */ __IO HAL_SPI_StateTypeDef State; /* Флаг состояния работы SPI. */ __IO uint32_t ErrorCode; /* Содержит код возникшей ошибки SPI */ } SPI_HandleTypeDef;
Рассмотрим подробнее наиболее важные элементы структуры:
- Instance или экземпляр: является указателем на физический модуль SPI. Для примера, если мы используем третий модуль SPI, то переменная Instance будет равна SPI3.
- Init: эта переменная является указателем на экземпляр структуры SPI_InitTypeDef, которая хранит настройки протокола SPI.
- pTxBuffPtr, pRxBuffPtr: указатели на буферы передачи и приема данных. Их необходим указать, если вы используете SPI в режиме прерываний и в режиме DMA.
- hdmatx, hdmarx: указатели на экземпляры структуры DMA_HandleTypeDef, которые использутся во время работы модуля SPI в режиме DMA.
Настройка протокола SPI
Теперь рассмотрим структуру InitTypeDef. С ее помощью настраивается модуль SPI для приема и передачи данных. Выглядит она следующим образом:
typedef struct { uint32_t Mode; /* Содержит настройки режима работы SPI. */ uint32_t Direction; /* Задает режим направления данных SPI. */ uint32_t DataSize; /* Установка размера передаваемых данных. */ uint32_t CLKPolarity; /* Установка состояния для тактового сигнала. */ uint32_t CLKPhase; /* Установка фронта сигнала для чтения битов */ uint32_t NSS; /* Выбор управления сигналом NSS */ uint32_t BaudRatePrescaler; /* Установка предделителя для настройки скорости передачи. */ uint32_t FirstBit; /* Настройка очередности передачи данных MSB- или LSB. */ uint32_t TIMode; /* Установка режима TI. */ uint32_t CRCCalculation; /* Настройка использования CRC */ uint32_t CRCPolynomial; /* Задает полином, используемый для расчета CRC. */ } SPI_InitTypeDef;
- Mode: настройка режима работы модуля SPI. Выбирается режим ведущего или ведомого устройства. Данные режима описаны константами SPI_MODE_MASTER и SPI_MODE_SLAVE.
- Direction: настройка режима направления для передачи данных. Есть несколько режимов:
- четырех проводной полнодуплексный режим SPI_DIRECTION_2LINES. При таком режиме ведущее устройство и подчиненное принимают и отправляют данные по четырем проводам.
- четырех проводной полудуплексный режим SPI_DIRECTION_2LINES_RXONLY. В этом случае ведущее устройство передает, а ведомое только принимает.
- полудуплексный режим по трем проводам SPI_DIRECTION_1LINE. Ведущее устройство отправляет, ведомое принимает по трем проводам.
- DataSize: настраивает размер передачи данных. Передача может быть побайтно или за раз 2 байта. Принимает значение передаваемых данных SPI_DATASIZE_8BIT или SPI_DATASIZE_16BIT.
- CLKPolarity: настраивает регистр CPOL. Принимает такие значения как SPI_POLARITY_LOW (CPOL = 0) и SPI_POLARITY_HIGH (CPOL = 1).
- CLKPhase: настраивает регистр CPHA. Принимает такие значения как SPI_PHASE_1EDGE (CPHA = 0) и SPI_PHASE_2EDGE (CPHA = 1).
- NSS: здесь мы настраиваем управление сигналом NSS (сигнал выбора устройства). Может управляться программно SPI_NSS_SOFT или аппаратно. Если управление аппаратное, то необходимо настроить вывод на вход или выход, указав значение SPI_NSS_HARD_INPUT или SPI_NSS_HARD_OUTPUT.
- BaudRatePrescaler: настраиваем скорость передачи данных установкой предделителя тактовой частоты шины APB, на которой находится модуль SPI. Принимает следующие значения: SPI_BAUDRATEPRESCALER_1,…, SPI_BAUDRATEPRESCALER_256.
- FirstBit: Определяет очередность передачи данных в рамках одной передачи. Устанавливаем старший байт(полубайт) или младший будет отправлен первым. Может быть SPI_FIRSTBIT_MSB или SPI_FIRSTBIT_LSB.
- TIMode: включение отключение режима TI. Принимает следующие значения SPI_TIMODE_DISABLE или SPI_TIMODE_ENABLE. Режим TI это режим, при котором после установки сигнала CS начинается непрерывная передача данных до момента поднятия сигнала CS. первым отправляется старший бит.
- CRCCalculation и CRCPolynomial: данные настройки относятся к контролю передаваемых данных. Микроконтроллеры STM32 имеют встроенный блок по расчету CRC.
Для того чтобы применить настройки к модулю SPI, необходимо вызвать функцию:
HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi)
и передать указатель на структуру SPI_HandleTypeDef.
Прием и передача данных по SPI в STM32
После того, как мы настроили наш SPI, можно начинать обмен данных между ведущим и ведомым устройством. У нас есть три варианта организации приема передачи:
- Простой опрос;
- Прием и передача с использованием прерываний;
- Прием и передача с использованием DMA;
STM32 SPI HAL в режиме опроса
Когда мы работаем с SPI в режиме опроса для приема/передачи необходимо использовать следующие функции:
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData,uint16_t Size, uint32_t Timeout)
отправляет Size байт на устройство по SPI.
*hspi — указатель на структуру SPI, которая содержит все настройки и дескрипторы SPI. Это может быть например hspi1, hspi2 и ваше объявление.
*pData — указатель а массив данных для отправки
Size — количество байт в массиве данных, которые хотим отправить
Timeout — время ожидания завершения отправки данных на устройство. Если Timeout будет превышен, HAL с генерирует исключение и поместит его код в структуру HAL_StatusTypeDef.
ВНИМАНИЕ! Данная функция применяется в с такими режимами как SPI_DIRECTION_1LINE или SPI_DIRECTION_2LINES.
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData,uint16_t Size, uint32_t Timeout);
Функция получает Size байт по SPI от устройства.
Отличием от предыдущей функции является *pData — здесь это указатель на приемный буфер.
Эта функция применяется во всех режимах Direction.
Если модуль SPI сконфигурирован на работу в полнодуплексном режиме то можно применить такую функцию:
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout);
Данная функция отправляет и принимает данные одновременно. Как вы уже поняли работает только в режиме SPI_DIRECTION_2LINES
STM32 SPI HAL в режиме прерываний
Когда мы используем SPI в режиме прерываний, то для приема передачи у HAL описаны следующие функции:
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);
Их описание идентичны функциям из предыдущего раздела. Для того что бы использовать SPI в режиме прерываний, их необходимо разрешить, вызвав функцию HAL_SPI_IRQHandler() в момент инициализации модуля, после всех настроек.
STM32 SPI DMA
Для обмена данными в режиме DMA, используются следующие функции:
HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData,uint16_t Size); HAL_StatusTypeDef HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData,uint16_t Size); HAL_StatusTypeDef HAL_SPI_TransmitReceive_DMA(SPI_HandleTypeDef *hspi,uint8_t *pTxData, uint8_t *pRxData,uint16_t Size);
Важно знать несколько ограничений в работе SPI через DMA:
Важно знать несколько ограничений в работе SPI через DMA:
- В случае, когда SPI модуль настроен на работу в режиме только приема, мы не можем использовать циклический режим DMA;
- если DMA работает в циклическом режиме, мы не можем использовать расчет контрольной суммы CRC;
- функции остановки, приостановки (HAL_SPI_DMAPause()/HAL_SPI_DMAStop()) работы DMA, можно использовать только в функция CallBack (функции обратного вызова)
В следующей статье мы поговорим как настроить работу SPI HAL в STM32 с использованием CubeMX.