[STM] STM32F4 SPI tutorial (sample C-program)

Hardware issues, electronic components, schemas, Arduino, STM32, Robots, Sensors
Post Reply
Administrator
Site Admin
Posts: 81
Joined: 26-Feb-2014, 17:54

[STM] STM32F4 SPI tutorial (sample C-program)

Post by Administrator » 26-Mar-2014, 17:21

Интерфейс SPI в STM32. Часть 1
http://easystm32.ru/interfaces/43-spi-interface-part-1

Если вы уже знаете что такое SPI интерфейс и с чем его едят, то вряд-ли вы почерпнете что-то новое из этой статьи. В ней я попробую рассказать основные понятия относящиеся к этому интерфейсу, а в следующей расскажу как использовать модуль SPI встроенный в микроконтроллеры STM32.

Как и любой другой интерфейс, SPI используется для передачи данных от одного устройства к другому. Устройства на шине SPI не равноправны, как правило, присутствует одно главное устройство (Master) и множество подчинённых устройств (Slave). Обычно, в роли мастера выступает микроконтроллер, а подчинёнными устройствами является различная периферия вроде термодатчиков, акселерометров, часов реального времени итд. Мастер не просто так называется мастером, без его ведома ни один слейв не будет предпринимать ни какого обмена данными.

Сама шина SPI физически представляет собой 4 провода:
MOSI – По этому проводу данные идут от Master к Slave устройству
MISO - По этому проводу данные идут от Slave к Master устройству
SCK - Через этот провод, Master передает тактовый сигнал к Slave устройствам
CS – Chip select (или SS – Slave select) – Через этот провод мастер дает понять слейву, что сейчас он шлёт данные именно ему.

Из описания всех четырёх линий можно сделать выводы:
1) SPI – это последовательный интерфейс. Биты данных передаются один за другим
2) SPI - это синхронный интерфейс. Это означает, что передача данных (в любую сторону) происходит только в то время когда мастер генерирует импульсы синхронизации и передает их через провод SCK другим устройствам на шине.
3) К одной шине может подключаться несколько устройств, количество которых теоритически не ограничено.
4) SPI предоставляет возможность обмениваться данными в полнодуплексном режиме. Пока мастер генерирует тактовые импульсы, он может посылать данные на слейв устройство и одновременно принимать их от него же.

Теперь рассмотрим, как устройства подключаются к шине. Стрелочки показывают кто и куда передает сигнал:
spi_connections.png
spi_connections.png (33.59 KiB) Viewed 30387 times
Как видно на рисунке, все линии интерфейса кроме CS, просто объединяются между собой. Для каждого слейв устройства, мастер имеет отдельный выход CS. Для того чтоб обменяться данными со вторым слейв устройством, мастер установит на ножке CS2 низкий логический уровень, а на двух других (CS1 и CS3) – высокий. Таким образом Slave_1 и Slave_3 не будут подавать вообще ни каких признаков жизни, и тем самым не создадут помех общению мастера и Slave_2. Еще раз подчеркну, что активное состояние ноги CS - это логический ноль. У такой схемы есть один недостаток – на 10 слейв устройств, мастер должен иметь 10 отдельных ног для подключения к CS.

Существует другой вариант подключения который называется daisy-chain. При таком включении все устройства соединяются в цепочку и имеют один общий CS. Подробно этот способ включения рассматриваться не будет, в виду того, что используется он достаточно редко.

Как уже упоминалось выше, к одной шине могут быть подключены самые разные slave устройства, некоторые из них достаточно быстрые и могут обмениваться данными с мастером на большой скорости, а некоторые наоборот очень медленные. Это значит, что мастер не должен слишком быстро генерировать тактовые импульсы, в противном случае медленные слейв устройства его не поймут из-за искажения данных. Однако скорость, это еще не все параметры SPI интерфейса, существует также 4 режима SPI. Я обращал внимание, что в даташитах на какое-либо устройство со SPI интерфейсом обычно так и пишут – «это устройство использует режим 2». Насколько это стандартизовано сказать не могу, но видел несколько раз. Если в двух словах описать суть этих режимов, то каждый из них определяет в какой момент (в зависимости от состояния линии SCK) нужно считывать/передавать данные.

Следующая таблица показывает, что это за режимы и чем они отличаются друг от друга. Во всех 4-х режимах, мастер посылает один и тот же байт (0x93). Желтая линия это SCK, а синяя – MOSI.
0 0 0 CPOL CPHA
mode_0.jpg
mode_0.jpg (96.94 KiB) Viewed 30387 times
Выборка по переднему нарастающему фронту
1 0 1
2 1 0
3 1 1
(картинки осциллографа в оригинальной статье)

Как видно из таблицы, номер режима состоит из двух бит – CPOL и CPHA. Бит CPOL определяет, в каком состоянии будет находиться нога SCL в то время когда ничего не передается. Если CPOL=0 то в режиме простоя на ноге низкий логический уровень. Это означает, что передний фронтом будет считаться переход из 0 в 1 (а задним фронтом соответственно наоборот из 1 в 0). Если CPOL=1 то в режиме простоя на ноге высокий логический уровень. Это означает, что передний фронтом будет считаться переход из 1 в 0 (а задним фронтом соответственно наоборот из 0 в 1). Бит CPHA определяет по какому фронту нужно производить выборку 0 – по переднему фронту, 1 – по заднему фронту. Собственно это всё и показывает таблица сверху. Кстати, примечательно, что точно так же эти два бита называются в регистре настройки SPI у микроконтроллеров STM32 и AVR.

Следующий немаловажный параметр – порядок следования бит. Обычно, первым передается старший бит, но иногда бывает наоборот, если этого не учесть, то слейв и мастер не найдут общий язык. Количество бит может изменяться, обычно это 8 бит, иногда бывает больше. С теоритическими основами покончено.

Administrator
Site Admin
Posts: 81
Joined: 26-Feb-2014, 17:54

Re: [STM] STM32F4 SPI tutorial (sample C-program)

Post by Administrator » 09-Apr-2014, 20:23

Интерфейс SPI в STM32. Часть 2
http://easystm32.ru/interfaces/45-spi-interface-part-2

В микроконтроллерах STM32F407Vxxx существуют аж целых три аппаратных SPI интерфейса. В других менее крутых контроллерах SPI модули не имеют существенных отличий, разве что их там поменьше. Чтоб не запутаться на каких ногах вашего контроллера висит SPI, рекомендую воспользоваться программой MicroXplorer. Если SPI-ноги контроллера уже заняты, то следует напомнить о такой замечательной вещи как remap (позволяет перенести SPI на другие ноги). Посмотреть что и куда ремапится можно так же при помощи этой программы. У моего контроллера все SPI интерфейсы расположены на этих ногах:
STM32F407V.jpg
STM32F407V.jpg (152.38 KiB) Viewed 30385 times
Для упрощения работы со всей периферией микроконтроллера, (таймеры, UART, SPI итд) компания ST придумала библиотеку под названием stdperiph_lib. Раньше я почему-то старался не использовать её, но сейчас я осознал, что с её использованием код становится более понятным и читаемым + улучшается переносимость кода с одного STM32 контроллера на другой. Из-за этого, начиная с этой статьи, я буду использовать эту библиотеку для инициализации периферии. Чтоб понять как использовать SPI, крайне желательно ознакомиться со всеми регистрами через которые происходит взаимодействие с ним. Их как обычно достаточно много, но если разобраться, то ничего особо сложного в них нет. По идее библиотека stdperiph_lib как раз и предназначена для того, чтоб избавить программиста от необходимости напрямую взаимодействовать с регистрами настройки периферии, но я считаю что нужно иметь хотя-бы примерное представление о том, что и как она настраивает.

Регистр SPI_CR1

BIDIMODE - если этот бит установлен, то прием и передача данных осуществляются по одному проводу (не считая SCK). При этом, как я понял, MOSI вывод мастера подключается к MISO слейва. Экзотический режим на мой взгляд :)

BIDIOE - этот бит используется в однопроводном режиме. Если он установлен - SPI модуль только передает данные. Если сброшен - то принимает.

CRCEN - включает аппаратный модуль расчёт контрольной суммы. 0 - выкл, 1 - вкл. Изменять состояние этого бита можно только когда SPI модуль выключен (бит SPE=0).

CRCNEXT - если этот бит установлен, то после следующей передачи байта данных, будет отправлена контрольная сумма.

DFF - если бит сброшен, то SPI модуль передает/принимает данные по 8 бит, в противном случаее передается/принимается сразу по 16 бит. Изменять состояние этого бита можно только когда SPI модуль выключен (бит SPE=0).

RXONLY - если используется 2-х проводной режим (см бит BIDIMODE) то установка этого бита запрещает передачу, SPI модуль работает только на приём

Перед описанием следующих двух бит, нужно отметить одну интересную особенность вывода NSS (он же SS он же CS). Если SPI модуль настроен в режиме слейва, то он может получать сигнал с ноги NSS или же программно. Если бит SSM сброшен, то сигнал SS будет считываться с ноги NSS, а если он установлен, то состояние ноги NSS игноирируется. В таком случае для управления сигналом SS возлагается на бит SSI. Бит установлен - есть сигнал SS, в противном случае нет. Если же SPI модуль работает в режиме мастера, то ногу NSS нужно подтянуть к питанию или включить программное управление (SSM=1) и установить бит SSI. В противном случае - SPI модуль подумает, что появился новый мастер и сам станет слейвом. Этот момент для меня был не совсем очевиден и я потратил много времени чтоб разобраться.

LSBFIRST - задает порядок передачи бит:
0 - сначала передается старший бит
1 - сначала передается младший бит

SPE - выключает/выключает SPI модуль

BR2,BR1,BR0 - задают скорость приема/передачи (частоту SCK). Частота тактирования модуля SPI делится на число, которое задается комбинацией этих трех бит.

Изменять состояние этих бит можно только когда SPI модуль выключен (бит SPE=0).

MSTR - если бит установлен - SPI модуль является мастером, иначе слейвом.

CPOL - полярность сигнала SCK. (см. табличку из прошлой статьи).

CPHA - фаза сигнала SCK (см. табличку из прошлой статьи).

Регистр SPI_CR2
Spoiler:
TXEIE - разрешает прерывание, когда буфер передачи пуст (всё передалось)

RXNEIE - разрешает прерывание, когда буфер заполнен данными и их можно забирать.

ERRIE - разрешает прерывание в случае возникновения ошибки. Их всего три, чтоб разобраться какая возникла, нужно смотреть состояние бит в регистре статуса. Об этом чуть ниже.

FRF - Frame format, не разбирался что это такое.

0 - SPI Motorola mode

1 - SPI TI mode

По умолчанию там ноль, без надобности не трогать :)

SSOE – Если этот бит выставлен, то SPI модуль (в режиме мастера разумеется) сам управляет выводом NSS. Т.е. как я понял перед началом передачи выставляет ноль на этом выводе, а после завершения – выставляет единицу. Возможно это не правильно, у меня так и не получилось ничего с этим битом сделать.

TXDMAEN - разрешает/запрещает запрос DMA по завершению передачи

RXDMAEN - разрешает/запрещает запрос DMA по завершению приема
Статусный регистр SPI_SR
Spoiler:
FRE - Frame error flag, пока не совсем понятно что это за флаг ошибки, он используется когда SPI модуль работает в режиме «TI mode» (бит FRF=1). Еще он относится к интерфейсу I2S, о нем будет отдельная статья. (не путать с I2C)

BSY - если этот бит установлен, значит модуль SPI сейчас занят передачей данных.

OVR - бит выставляется в том случае, если в SPI модуль поступили новые данные и перетерли старые которые не были прочитаны.

MODF - выставляется в том случае если мастер внезапно перестал быть мастером. Такое возможно когда нога мастера NSS настроена как вход и на неё поступил сигнал низкого уровня.

CRCERR - ошибка контрольной суммы

UDR – флаг не используется в режиме SPI

TXE - Передача данных завершилась

RXNE - Приём данных завершен
Регистр SPI_DR
Представляет собой 16-ти битный регистр данных. На самом деле регистров два – один для передачи, а другой для приёма, но работа с ними осуществляется через один регистр SPI_DR. Если мы что-то в него пишем, то запись данных производится в регистр для передачи. Если читаем, то данные считываются из регистра для приёма данных.

Регистр SPI_CRCPR
Сюда записывают некоторое число, которое как-то должно повлиять на расчет контрольной суммы. По умолчанию там записано число 7.

Регистр SPI_TXCRCR
Сюда записывается контрольная сумма, которая посчиталась для передаваемых данных.

Регистр SPI_RXCRCR
Сюда записывается контрольная сумма, которая посчиталась для принятых данных.

Примеры кода
Теперь настало время для кода. Для первых трёх примеров можно использовать плату STM32F4DISCOVERY. Четвертый пример требует для запуска второй контроллер или еще одну отладочную платку STM32VLDISCOVERY. Для начала рассмотрим простой пример, коего будет достаточно в 95% всех случаев. Пример показывает как настроить SPI1 в режиме мастера и отправить данные. В бесконечном цикле происходит передача одного и того же байта (0x93).

Code: Select all

#include "stm32f4xx.h" 
#include "stm32f4xx_gpio.h" 
#include "stm32f4xx_rcc.h" 
#include "stm32f4xx_spi.h" 
 
int main(void)
{ 
  GPIO_InitTypeDef GPIO_InitStructure; 
  SPI_InitTypeDef SPI_InitStructure; 

  // Тактирование модуля SPI1 и порта А 
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); 
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); 

  // настройка порта А
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; 
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; 
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5; 
  GPIO_Init(GPIOA, &GPIO_InitStructure); 

  // СНАЧАЛА НАСТРАИВАЕМ ПОРТ, ПОТОМ ПЕРЕНАПРАВЛЯЕМ!
  // Настраиваем ноги SPI1 для работы в режиме альтернативной функции 
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1); 
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1); 
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1); 

 
  //Заполняем структуру с параметрами SPI модуля 
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //полный дуплекс 
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // передаем по 8 бит 
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; // Полярность и 
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // фаза тактового сигнала 
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // Управлять состоянием сигнала NSS программно 
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32; // Предделитель SCK 
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // Первым отправляется старший бит 
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // Режим - мастер 
  SPI_Init(SPI1, &SPI_InitStructure); //Настраиваем SPI1 
  SPI_Cmd(SPI1, ENABLE); // Включаем модуль SPI1.... 
 
  // Поскольку сигнал NSS контролируется программно, установим его в единицу 
  // Если сбросить его в ноль, то наш SPI модуль подумает, что 
  // у нас мультимастерная топология и его лишили полномочий мастера. 
  SPI_NSSInternalSoftwareConfig(SPI1, SPI_NSSInternalSoft_Set); 

  while(1)
  { 
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == SET) {}//ждём освобождения буфера
    SPI_I2S_SendData(SPI1, 0x93); //Передаем байт 0x93 через SPI1 
    //while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET) {} //У автора проверяется флаг BUSY  - не айс.
  } 
} 
Если кроме байт данных нужно еще и отсылать контрольную сумму, то можно сделать это так как написано ниже. В инициализации SPI модуля почти ничего не поменялось:

Code: Select all

#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_spi.h"

int main(void) {
  GPIO_InitTypeDef GPIO_InitStructure;
  SPI_InitTypeDef SPI_InitStructure;
  // Тактирование модуля SPI1 и порта А
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

  // Опять у автора нехорошо было написано! Сначала надо Порт настроить, потом AF пинов! 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  // Настраиваем ноги SPI1 для работы в режиме альтернативной функции
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);

  //Заполняем структуру с параметрами SPI модуля
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //полный дуплекс
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // передаем по 8 бит
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; // Полярность и
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // фаза тактового сигнала
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // Управлять состоянием сигнала NSS программно
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32; // Предделитель SCK
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // Первым отправляется старший бит
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // Режим - мастер
  SPI_InitStructure.SPI_CRCPolynomial = 0x07; // Задаем значение параметра CRCPolynomial
  SPI_CalculateCRC(SPI1,ENABLE); //Включаем расчёт CRC
  SPI_Init(SPI1, &SPI_InitStructure); //Настраиваем SPI1
  SPI_Cmd(SPI1, ENABLE); // Включаем модуль SPI1....
  // Поскольку сигнал NSS контролируется программно, установим его в единицу
  // Если сбросить его в ноль, то наш SPI модуль подумает, что
  // у нас мультимастерная топология и его лишили полномочий мастера.
  SPI_NSSInternalSoftwareConfig(SPI1, SPI_NSSInternalSoft_Set);
  SPI_I2S_SendData(SPI1, 0xAA); 
  while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);
  SPI_I2S_SendData(SPI1, 0xBB); 
  while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);
 
  SPI_TransmitCRC(SPI1); //сразу после последней передачи данных
  // отправить контрольную сумму которая рассчиталась из трех переданых байт
  // 0xAA ,0xBB, 0xCC и некого числа которое называется CRCPolynomial (7 по дефолту) 
  // Алгоритм по которому она считается мне неизвестен, пока не вникал.
 
  SPI_I2S_SendData(SPI1, 0xCC); //Передаем последний байт и контрольную сумму
  while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET); // Не лучший вариант...
 
  return 0;
}
 
SPI_InitStructure.SPI_CRCPolynomial = 0x07; // Задаем значение параметра CRCPolynomial
SPI_CalculateCRC(SPI1,ENABLE); //Включаем расчёт CRC
...
SPI_TransmitCRC(SPI1); //сразу после последней передачи данных

Administrator
Site Admin
Posts: 81
Joined: 26-Feb-2014, 17:54

Re: [STM] STM32F4 SPI tutorial (sample C-program)

Post by Administrator » 09-Apr-2014, 21:35

http://eliaselectronics.com/stm32f4-tut ... -tutorial/

This example code initializes the SPI1 peripheral and configures the needed pins.
I have written this up some time ago and just forgot about actually publishing it.
As always the code can be found over at GitHub, too.

Code: Select all

#include <stm32f4xx.h>
#include <stm32f4xx_spi.h>

// this function initializes the SPI1 peripheral
void init_SPI1(void){
    
    GPIO_InitTypeDef GPIO_InitStruct;
    SPI_InitTypeDef SPI_InitStruct;
    
    // enable clock for used IO pins
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    
    /* configure pins used by SPI1
     * PA5 = SCK
     * PA6 = MISO
     * PA7 = MOSI
     */
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // connect SPI1 pins to SPI alternate function
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
    
    // enable clock for used IO pins
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
    
    /* Configure the chip select pin
       in this case we will use PE7 */
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOE, &GPIO_InitStruct);
    
    GPIOE->BSRRL |= GPIO_Pin_7; // set PE7 high
    
    // enable peripheral clock
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    
    /* configure SPI1 in Mode 0 
     * CPOL = 0 --> clock is low when idle
     * CPHA = 0 --> data is sampled at the first edge
     */
    SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // set to full duplex mode, seperate MOSI and MISO lines
    SPI_InitStruct.SPI_Mode = SPI_Mode_Master;     // transmit in master mode, NSS pin has to be always high
    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; // one packet of data is 8 bits wide
    SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;        // clock is low when idle
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;      // data sampled at first edge
    SPI_InitStruct.SPI_NSS = SPI_NSS_Soft | SPI_NSSInternalSoft_Set; // set the NSS management to internal and pull internal NSS high
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; // SPI frequency is APB2 frequency / 4
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;// data is transmitted MSB first
    SPI_Init(SPI1, &SPI_InitStruct); 
    
    SPI_Cmd(SPI1, ENABLE); // enable SPI1
}

/* This funtion is used to transmit and receive data 
 * with SPI1
 *             data --> data to be transmitted
 *             returns received value
 */
uint8_t SPI1_send(uint8_t data){

    SPI1->DR = data; // write data to be transmitted to the SPI data register
    while( !(SPI1->SR & SPI_I2S_FLAG_TXE) ); // wait until transmit complete
    while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); // wait until receive complete
    while( SPI1->SR & SPI_I2S_FLAG_BSY ); // wait until SPI is not busy anymore
    return SPI1->DR; // return received data from SPI data register
}

int main(void){
    
    uint8_t received_val = 0;
    
    init_SPI1();
    
    while(1){
        
        GPIOE->BSRRH |= GPIO_Pin_7; // set PE7 (CS) low
        SPI1_send(0xAA);  // transmit data
        received_val = SPI1_send(0x00); // transmit dummy byte and receive data
        GPIOE->BSRRL |= GPIO_Pin_7; // set PE7 (CS) high
    }
}   

Post Reply