SPI и с чем его едят
Рассмотрим пример инициализации SPI1 составе STM32F4. Модуль SPI простой в настройке и управлении.
И в основном идентичен во многих сериях микроконтроллеров от STM. Этот код может стать универсальным.
Так как логика управления SPI - это целая маленькая "наука", я ограничусь здесь лишь фрагментами кода,
которые дадут вам понимание, как инициализировать модуль, используя ассемблер.
В моём примере, код использовался на отладке, в которой был подключен экранчик по SPI.
Я загонял ему большие строки, используя DMA. И это наиболее предпочтительный способ передачи
массивов данных. По своей природе, spi довольно медлительный, и нет смысла стоять ждать его, отправляя байт за байтом вручную
или по прерываниям.
Натравите на него DMA и радуйтесь жизни!
Итак, рассмотрим SPI1, висящий на ножках 4, 5, 6, 7 порта А. Первым делом, настраивается порт.
MOV32 R0, GPIOA_BASE
@ Эти ножки супер-скоростные
LDR R1, [R0, GPIO_OSPEEDR]
ORR R1, R1, 3<<8 + 3<<10
ORR R1, R1, 3<<12 + 3<<14
STR R1, [R0, GPIO_OSPEEDR]
@ Ножки будут выполнять альтернативную функцию (это не просто I/O)
LDR R1, [R0, GPIO_MODER]
ORR R1, R1, 2<<8 + 2<<10
ORR R1, R1, 2<<12 + 2<<14
STR R1, [R0, GPIO_MODER]
@ Задать номер альтернативной функции для ножек порта A
LDR R1, [R0, GPIO_AFRL]
ORR R1, 0x55000000
ORR R1, 0x00550000
STR R1, [R0, GPIO_AFRL]
@ Следующий шаг
MOV32 R0, RCC_BASE
@ Затактировать SPI1
LDR R1, [R0, RCC_APB2ENR]
ORR R1, R1, RCC_APB2ENR_SPI1EN
STR R1, [R0, RCC_APB2ENR]
@ Переходим к настройке SPI1
MOV32 R0, SPI1_BASE
@ Разрешить выход slave chip select и передачу при помощи DMA
MOVLo R1, SPI_CR2_SSOE + SPI_CR2_TXDMAEN
STR R1, [R0, SPI_CR2]
@ Запустить SPI1
MOV R1, SPI_CR1_MSTR + SPI_CR1_BR_1 + SPI_CR1_BR_2 + SPI_CR1_SPE
STR R1, [R0, SPI_CR1]
Вот и всё. SPI1 готов к передаче и приёму сообщений.
Что бы данные пошли в порт, просто запишите их в регистр данных.
MOV32 R0, SPI1
STR R1, [R0, SPI_DR]
Принятые данные будут лежать там же. Заберём их:
MOV32 R0, SPI1
LDR R1, [R0, SPI_DR]
Но для работы с массивами данных у нас задекларирован DMA.
Давайте подготовим его.
MOV32 R0, RCC_BASE
@ Вначале затактировать DMA2
LDR R1, [R0, RCC_AHB1ENR]
ORR R1, R1, RCC_AHB1ENR_DMA2EN
STR R1, [R0, RCC_AHB1ENR]
@ Предустановка для DMA2
@ Прерывания не используются
MOV32 R0, DMA2_BASE
@ Сбросить флаги прерываний
MOV32 R1, 0xffffffff
STR R1, [R0, DMA_LIFCR]
STR R1, [R0, DMA_HIFCR]
@ Адрес регистра SPI1
@ Куда будут запихиваться данные
MOV32 R1, SPI1+SPI_DR
STR R1, [R0, DMA_S3PAR]
@ Адрес начала массива данных в ОЗУ
MOV32 R1, ЭКРАННЫЙ_БУФЕР_SPI
STR R1, [R0, DMA_S3M0AR]
Задвинем массив в SPI используя DMA. Делается это так:
MOV32 R0, DMA2_BASE
@ Если флаги не сбросить, dma тупо не будет работать
MOV32 R1, 0xFFFFFFFF
STR R1, [R0, DMA_LIFCR]
STR R1, [R0, DMA_HIFCR]
@ Загрузить количество байтов для передачи
@ Максимум 65535 байт
MOV32 R1, 10000
STR R1, [R0, DMA_S3NDTR]
@ Конфигурация DMA и запуск
MOV32 R1, DMA_SxCR_CHSEL_0 + DMA_SxCR_CHSEL_1 + DMA_SxCR_MINC + DMA_SxCR_DIR_0 + DMA_SxCR_TCIE + DMA_SxCR_EN
STR R1, [R0, DMA_S3CR]
@ Всё, данные пошли в SPI1
Правильная логика работы SPI предусматривает так же своевременную проверку флагов TXE, RXNE и BSY.
Вы можете написать подпрограммы или макросы, которые будут проверять состояние флагов.
А выглядеть они будут примерно так:
@ Ждём в цикле TXE=1 (Transmit buffer Empty)
@ Установка этого флага означает, что буфер передачи пустой
@ И в него можно засунуть следующий байт
MOV32 R0, SPI1+SPI_SR
spi1_txe_wait1:
LDR R1, [R0]
TST R1, SPI_SR_TXE
BEQ spi1_txe_wait1
Следующий код ожидает завершения приёма данных
О чём будет сигнализировать установка флага RXNE в единицу:
MOV32 R0, SPI1+SPI_SR
spi1_rxne_wait1:
LDR R1, [R0]
TST R1, SPI_SR_RXNE
BEQ spi1_rxne_wait1
И ещё один фрагмент, который ждёт полного завершения всех процессов в SPI.
Для этого дождёмся сброса флага BSY.
MOV32 R0, SPI1+SPI_SR
spi1_bsy_wait0:
LDR R1, [R0]
TST R1, SPI_SR_BSY
BNE spi1_bsy_wait0
Вот и всё. Как видите, совсем несложно!
Главное вовремя проверяйте состояние флагов,
прежде чем делать что либо с SPI. А где именно их нужно проверять, пошагово и досконально расписано в референсе на камень.
Добрые люди даже сделали для вас перевод на русский.
Почитайте перевод
локальная копия