SPI STM32.
Принцип работы протокола рассматривать не будем, он был рассмотрен тут, перейдём сразу к возможностям модуля SPI у STM32:
Для передачи данных может использоваться четыре вывода:
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 представляет собой два последовательно соединённых между собой сдвиговых регистра и по каждому тактовому импульсу данные в регистрах сдвигаются. То есть по каждому тактовому импульсу один бит уходит из регистра, остальные биты сдвигаются к выходу, а на входе появляется свободное место, его и занимает вновь пришедший бит.
Ниже представлена блочная диаграмма модуля SPI.
На диаграмме видно, что модуль состоит из двух регистров у правления и регистра статуса, давайте рассмотрим какой бит в регистрах за что отвечает. Хотелось отметить, что изменять состояние большинства битов можно только, когда 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) — данные биты определяют скорость передачи данных.
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 передаваемых данных.
Инициализация модуля из рабочего проекта.
Такой способ обмена данными нашёл на форуме st.com, в нём есть некоторые вещи, которые надо бы изменить, как минимум установить порог ожидания флагов, а то мало ли что может случиться. Но пока всё работает, решил не трогать.
- полнодуплексная синхронная передача с использованием 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 представляет собой два последовательно соединённых между собой сдвиговых регистра и по каждому тактовому импульсу данные в регистрах сдвигаются. То есть по каждому тактовому импульсу один бит уходит из регистра, остальные биты сдвигаются к выходу, а на входе появляется свободное место, его и занимает вновь пришедший бит.
Ниже представлена блочная диаграмма модуля SPI.
На диаграмме видно, что модуль состоит из двух регистров у правления и регистра статуса, давайте рассмотрим какой бит в регистрах за что отвечает. Хотелось отметить, что изменять состояние большинства битов можно только, когда 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) — данные биты определяют скорость передачи данных.
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, в нём есть некоторые вещи, которые надо бы изменить, как минимум установить порог ожидания флагов, а то мало ли что может случиться. Но пока всё работает, решил не трогать.
Похожие статьи