SPI STM32.

SPI STM32.

Принцип работы протокола рассматривать не будем, он был рассмотрен тут, перейдём сразу к возможностям модуля SPI у STM32:
  • полнодуплексная синхронная передача с использованием 3 линий
  • симплексная синхронная передача с использованием двух линий, возможность использования двунаправленной линии для передачи данных
  • 8 или 16 битный формат фрейма
  • работа в режиме Master или Slave
  • в режиме Master 8 значений прескалера скорости передачи(fPCLK/2 max)
  • в режиме Slave максимальное значение скорости fPCLK/2 max
  • программное и аппаратное управление выводом NSS, возможность динамического переключения Master/Slave
  • полярность и фаза тактового сигнала программируется
  • порядок следования битов программируется(MSB-first или LSB-first)
  • флаг приёма и передачи, с возможностью прерывания при их возникновении
  • флаг занятости шины SPI
  • Аппаратное вычисление CRC для надёжного обмена данными:
    - значение CRC может передаваться последним байтом при передаче
    - автоматическая проверка CRC для последнего принятого байта

  • возможность возникновения прерываний по флагам CRC error, overrun и Master mode fault
  • 1-байтовый буфер DMA для передачи и приёма: запросы Tx и Rx


Для передачи данных может использоваться четыре вывода:

Miso(Master In / Slave Out data) — в зависимости от режима вывод может быть настроен как вход или выход, вход в режиме Master и выход в режиме Slave

Mosi(Master Out / Slave In data) – тут наоборот, выход в режиме Master и вход в режиме Slave

SCK – тактовый сигнал, генерируемый Master.

NSS – вывод предназначен для выбора подчинённого устройства в режиме Slave, при подаче на его вход логического ноля, также с помощью этого вывода можно переключать режимы SPI (Master/Slave), в мультимастерной шине.

Итак, вывод NSS работает не совсем так, как привычный CS у AVR, и в режиме Master для выбора ведомого необходимо использовать GPIO.

В режиме Slave сигнал(Сhip Select) можно получить с вывода NSS или программно, зависит это от значения бита SSM, если он сброшен, то считывается состояние вывода NSS, иначе определяющим является состояние бита SSI. Для приёма данных в таком случае(при программном управлении) необходимо установить SSM = 1, SSI = 0.

В режиме Master, вывод необходимо подтянуть к питанию или включить программную подтяжку (SSM =1, SSI = 1). В случае программной подтяжки вывод можно использовать как обычный GPIO.

Возможен ещё один вариант SSM = 0, SSOE = 0, вывод NSS настроен как вход с подтяжкой к питанию, при появлении на нём низкого уровня, SPI модуль подумает что появился новый Master и сам станет Slave, такой вариант используется в мультимастерной шине.

Так выглядит подключение одного Master к Slave.
SPI STM32.

Шина SPI представляет собой два последовательно соединённых между собой сдвиговых регистра и по каждому тактовому импульсу данные в регистрах сдвигаются. То есть по каждому тактовому импульсу один бит уходит из регистра, остальные биты сдвигаются к выходу, а на входе появляется свободное место, его и занимает вновь пришедший бит.

Ниже представлена блочная диаграмма модуля SPI.
SPI STM32.

На диаграмме видно, что модуль состоит из двух регистров у правления и регистра статуса, давайте рассмотрим какой бит в регистрах за что отвечает. Хотелось отметить, что изменять состояние большинства битов можно только, когда SPI выключен(SPE = 0).

Регистр управления SPI_CR1:

BIDIMODE(Bidirectional data mode enable) — если записать в этот бит единицу данные будут передаваться в двух направлениях по одной линии(однопроводной режим), если ноль —по двум линиям. При двунаправленной передаче master использует вывод MOSI, а slave вывод MISO.

BIDIOE(Output enable in bidirectional mode) — этот бит используется для настройки однопроводного режима, 1 — данные только передаются, 0 — данные только принимаются.

CRCEN(Hardware CRC calculation enable) — запись единицы в этот бит, разрешает аппаратный подсчёт CRC, 0 – запрещает.

CRCNEXT(CRC transfer next) – если этот бит установлен, это говорит о том следующей будет передаваться контрольная сумма.

DFF(Data frame format) – этот бит определяет формат фрейма, 0 — 8 бит, 1 — 16 бит.

RXONLY(Receive only) – этот бит используется в двухпроводном режиме, его установка в единицу запрещает передачу и модуль SPI работает только на приём, иначе на приём и передачу.

SSM(Software slave management) — программное управление NSS, когда этот бит установлен вместо уровня на входе NSS контролируется состояние бита SSI, если сброшен — контролируется состояние вывода NSS.

SSI(Internal slave select) — этот бит имеет силу только при установленном SSM, его значение заменяет состояние на выводе NSS.

LSBFIRST(Frame format) — определяет формат фрейма, 1 — младшим битом вперёд, 0 — старшим битом вперёд.

SPE(SPI enable) — единица в этом бите включает модуль SPI, 0 – выключает.

BR[2:0](Baud rate control) — данные биты определяют скорость передачи данных.
SPI STM32.

MSTR(Master selection) — определяет режим работы, 1 —Master, 0 — Slave.

CPOL(Clock polarity) — определяет полярность в режиме ожидания, 0 — низкий уровень в режиме ожидания, 1 — высокий уровень в режиме ожидания.

CPHA(Clock phase) – задаёт фазу тактового сигнала, 0 — выборка данных производится по переднему фронту сигнала синхронизации, 1 — выборка данных производится по заднему фронту сигнала синхронизации.


Регистр управления SPI_CR2 :

TXEIE(Tx buffer empty interrupt enable) — единица в этом бите разрешает прерывание, когда буфер передачи пуст.

RXNEIE(RX buffer not empty interrupt enable) — единица в этом бите разрешает прерывание когда буфер не пуст.

ERRIE(Error interrupt enable) — единица в этом бите разрешает прерывание при возникновении ошибки, о которой сигнализируют биты CRCERR, OVR, MODF.

SSOE(SS output enable) — единица в этом бите, разрешает использовать вывод NSS в качестве выхода и модуль SPI сам управляет выводом NSS, но управляет ним по следующему алгоритму: при включении модуля SPI(SPE = 1) на NSS появляется низкий уровень и сохраняется пока модуль не будет выключен(SPE = 0), что в большинстве случаев не подходит, поэтому для этих целей использую GPIO, а на NSS в режиме Master программно выставляю единицу(SSM = 1, SSI = 1).

TXDMAEN(Tx buffer DMA enable) — разрешает/запрещает формировать запрос к DMA при появлении флага TXE(по завершении передачи).

RXDMAEN(Rx buffer DMA enable) — разрешает/запрещает формировать запрос к DMA при появлении флага RXE(по завершении приёма).


Регистр статуса SPI_SR:

BSY(Busy flag) — флаг занятости, предназначен для отслеживания состояния сдвигового регистра, устанавливается аппаратно во время обмена данными, а точнее с момента когда данные из регистра SPI_DR поступают в сдвиговый регистр, сбрасывается при опустошении сдвигового регистра, то есть когда все данные ушли в линию MOSI.
Производители не советуют проверять его при приёме и передаче, вместо этого советуют пользоваться флагами TXE и RXNE.

OVR(Overrun flag) – устанавливается при переполнении буфера, когда новые данные перезаписали старые, которые ещё не были прочитаны.

MODF(Mode fault) — устанавливается если в режиме Master на вход NSS поступает сигнал низкого уровня, и он становится Slave. Сбросить его можно с помощью специальной последовательности.

CRCERR(CRC error flag) – флаг ошибки контрольной суммы.

TXE(Transmit buffer empty) – устанавливается когда буфер передачи(регистр SPI_DR) пуст, очищается при загрузке данных

RXNE(Receive buffer not empty) — устанавливается когда приёмный буфер содержит данные, очищается при считывании данных.

SPI_DR(Data register) – регистр данных, фактически он состоит из двух буферов: приёма и передачи, но мы работаем только с регистром DR, поэтому для отправки данных пишем в регистр DR, для приёма читаем его же.

SPI_CRCPR(CRC polynomial register) – содержит полином для расчёта CRC, после сброса его значение равно 0x0007.

SPI_RXCRCR(Rx CRC register) – содержит вычисленную CRC принятых данных.

SPI_TXCRCR(Tx CRC register) – содержит вычисленную CRC передаваемых данных.

Инициализация модуля из рабочего проекта.

#define		 CS_LOW 	GPIOB->BSRR = GPIO_BSRR_BR10;
#define 	         CS_HIGH 	GPIOB->BSRR = GPIO_BSRR_BS10;

void Spi_Init(void)
{
//включаем тактирование порта B и альтернативных функций 
	RCC->APB2ENR  |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN;   
	
//13(SCK) и 15(MOSI) вывод - альтернативная функция  push pull, 14(MISO) вывод - Input floating, 10(CS) вывод - выход, push-pull
	GPIOB->CRH &= ~(GPIO_CRH_CNF13_0 | GPIO_CRH_CNF15_0 | GPIO_CRH_CNF10_0);  
	GPIOB->CRH |= GPIO_CRH_CNF13_1 | GPIO_CRH_CNF15_1;  	
	GPIOB->CRH |= GPIO_CRH_MODE10_0 | GPIO_CRH_MODE13_1 |	GPIO_CRH_MODE15_1;

//включаем тактирование SPI2
	RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;   
	
        SPI2->CR1 |= SPI_CR1_BR;                //Baud rate = Fpclk/256
        SPI2->CR1 &= ~SPI_CR1_CPOL;             //Polarity cls signal CPOL = 0;
        SPI2->CR1 &= ~SPI_CR1_CPHA;             //Phase cls signal    CPHA = 0;
        SPI2->CR1 |= SPI_CR1_DFF;               //16 bit data
        SPI2->CR1 &= ~SPI_CR1_LSBFIRST;         //MSB will be first
        SPI2->CR1 |= SPI_CR1_SSM | SPI_CR1_SSI;  //Software slave management & Internal slave select
	
        SPI2->CR1 |= SPI_CR1_MSTR;              //Mode Master
        SPI2->CR1 |= SPI_CR1_SPE;                //Enable SPI2
}

uint16_t Spi_Write_Data(uint16_t data)
{
        //ждём пока опустошится Tx буфер
	while(!(SPI2->SR & SPI_SR_TXE));
	//активируем Chip Select
	CS_LOW   
        //отправляем данные     
	SPI2->DR = data;  

        //ждём пока придёт ответ
	while(!(SPI2->SR & SPI_SR_RXNE));
        //считываем полученные данные
	data = SPI2->DR;  
	//деактивируем Chip Select
	CS_HIGH   
        //возвращаем то, что прочитали
       return data;  
}


Такой способ обмена данными нашёл на форуме st.com, в нём есть некоторые вещи, которые надо бы изменить, как минимум установить порог ожидания флагов, а то мало ли что может случиться. Но пока всё работает, решил не трогать.
комментарии
2