Окт 062012
 

Вдогонку к статье о модуле  SPI STM32, где рассмотрен порядок работы с ним через регистры, дополняю тему информацией. Здесь будут описаны функции файла spi.c из пакета стандартных библиотек StdPeriph_Lib.

Использование функций стандартных библиотек имеет плюсы и минусы в сравнении с прямой манипуляцией битами регистров. Из плюсов можно выделить более легкий перенос кода между различными семействами STM32, а также существует мнение, что при написании приложения необязательно досконально понимать всю структуру внутренних регистров и порядок инициализации модуля, а использование готовых библиотек при этом сокращает время разработки (плюсом это можно назвать с натяжкой – плохое ориентирование в структуре регистров усложняет для разработчика поиск ошибок, а при обнаружении “багов” может наоборот затормозить процесс написания кода, ведь все равно в итоге придется смотреть содержимое регистров при пошаговой отладке). Еще одним плюсом можно назвать более легкую читаемость кода. Если сам же разработчик взглянет на свою программу через год, то, в случае прямых манипуляций содержимым регистров при отсутствии толковых комментариев, можно долго разбираться с собственным же кодом. Из минусов можно выделить то, что использование чужого кода не всегда дает его понимание, особенно когда все сразу заработало, а разбираться как там все происходит уже нет желания. Тем более, что в теле функции опять же все построено на работе с регистрами. И не всегда там все просто, иногда код функции такой навороченный и избыточный, сделанный “на все случаи жизни”, что во многих случаях бывает излишним. А иногда бывает и наоборот, что функция не совсем корректно выполняет свою задачу и приходится вносить изменения. Можно сказать, что библиотека StdPeriph_Lib еще далека от идеала, в общем и целом ее можно назвать хорошей, но есть в ней и слабые места, хоть их и мало.

Прошу не воспринимать предыдущий текст как высказывание ярого противника готовых библиотек. Использовать библиотечные функции можно и, более того, даже нужно. Но при этом надо понимать работу модуля, все-таки знать структуру его регистров, а также иметь железную уверенность в корректной работе используемых библиотечных функций. И тогда, при написании нового фрагмента кода, вместо того, чтобы в очередной раз манипулировать битами регистров, сверяясь по мануалу с их содержимым, можно использовать готовые и проверенные библиотечные функции. Вот в этом случае как раз и сокращается время разработки. А в случае неправильной работы приложения, в режиме пошаговой отладки можно понаблюдать за изменением содержимого регистров. Если вы ориентируетесь в их структурах, то это не вызовет каких-либо трудностей.

Лично я не обхожусь без пошаговой отладки, наблюдая за битами регистров во время процесса.

В отношении же библиотечных файлов для модуля SPI ничего плохого сказать не могу, все функции здесь довольно лаконичны, ничего лишнего и тормозящего выполнение приложения не содержат.

Функции для работы с интерфейсом SPI находятся в файле spi.c. Все функции можно разделить на несколько групп:

  1. Функции инициализации и конфигурации
  2. Функции передачи и чтения принятых данных
  3. Функции аппаратного вычисления контрольной суммы CRC
  4. Функции управления DMA
  5. Функции управления прерываниями и флагами

Далее будет дано краткое описание каждой функции, ее назначение, параметры и возвращаемое значение.

1. Функции инициализации и конфигурации

Эти функции позволяют настроить нужные режимы модуля SPI: Master или Slave, размерность данных, порядок передачи бит, полярность и фазу тактового сигнала, режима управления NSS, тактовую частоту, полинома для формирования контрольной суммы.

void SPI_I2S_DeInit(SPI_TypeDef* SPIx) – сбрасывает содержимое регистров. Записываются значения, которые заданны по умолчанию после аппаратного сброса микроконтроллера.
Параметры:
SPI_TypeDef* SPIx:
SPIx
(SPI1, SPI2) – выбор модуля SPI
Возвращаемое значение: нет

void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct) – инициализация значениями, заданными пользователем в параметрах функции.
Параметры:
SPI_TypeDef* SPIx:
SPIx (SPI1, SPI2) – выбор модуля SPI
SPI_InitTypeDef* SPI_InitStruct:
SPI_Direction (SPI_Direction_2Lines_FullDuplex, SPI_Direction_2Lines_RxOnly, SPI_Direction_1Line_Rx, SPI_Direction_1Line_Tx) – режимы: двунаправленный/однонаправленный обмен данными по 1 или 2 линиям, только передача или только прием данных
SPI_Mode (SPI_Mode_Master, SPI_Mode_Slave) – ведущий или ведомый
SPI_DataSize (SPI_DataSize_16b, SPI_DataSize_8b) – размерность 8 бит/16 бит
SPI_CPOL (SPI_CPOL_Low, SPI_CPOL_High) – полярность тактового сигнала
SPI_CPHA (SPI_CPHA_1Edge, SPI_CPHA_2Edge) – фаза тактового сигнала
SPI_NSS (SPI_NSS_Soft, SPI_NSS_Hard) – аппаратное/программное управление NSS
SPI_BaudRatePrescaler (SPI_BaudRatePrescaler_2 … SPI_BaudRatePrescaler_256) – коэффициент деления для формирования тактовой частоты
SPI_FirstBit (SPI_FirstBit_MSB, SPI_FirstBit_LSB) – направление передачи: старшим (MSB) или младщим (LSB) битом вперед
SPI_CRCPolynomial – полином для вычисления контрольной суммы
Возвращаемое значение: нет

void SPI_StructInit(SPI_InitTypeDef* SPI_InitStruct) — инициализация SPI типовой структурой, в этой функции параметры уже предопределены заранее. Записывает готовые шаблонные значения в регистры. Используется в том случае, когда желаемые параметры настройки модуля соответствуют этим “заготовкам”, чтобы лишний раз не прописывать все эти значения, а задать их все сразу вызовом этой функции.Здесь задаются следующие режимы работы:
SPI_Direction = SPI_Direction_2Lines_FullDuplex – двунаправленный обмен по 2 линиям
SPI_Mode = SPI_Mode_Slave – режим подчиненного устройства
SPI_DataSize = SPI_DataSize_8b – размерность данных 8 бит
SPI_CPOL = SPI_CPOL_Low – низкий уровень на тактовой линии в режиме ожидания
SPI_CPHA = SPI_CPHA_1Edge – стробирование данных происходит по первому перепаду тактового сигнала
SPI_NSS = SPI_NSS_Hard – аппаратное управление NSS
SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2 – тактовая частота равна Fpclk/2
SPI_FirstBit = SPI_FirstBit_MSB – первым передается старший бит
SPI_CRCPolynomial = 7 – полином CRC
Параметры этой функции уже заданы по умолчанию, возвращаемого значения нет.

void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState) – включение/выключение SPI.
Параметры:
SPI_TypeDef* SPIx:
SPIx (SPI1, SPI2) – выбор модуля SPI
FunctionalState NewState:
NewState (ENABLE или DISABLE) – включить/выключить модуль
Возвращаемое значение: нет

Следующие функции из этой же группы позволяют производить выборочную настройку отдельных режимов. Возможно, когда в системе несколько устройств с различными настройками, может потребоваться изменять отдельные параметры интерфейса, не затрагивая остальных настроек, что позволяет исключить повторную инициализацию модуля в полном объеме.

void SPI_DataSizeConfig(SPI_TypeDef* SPIx, uint16_t SPI_DataSize) – размерность передаваемых данных.
Параметры:
SPI_TypeDef* SPIx:
SPIx (SPI1, SPI2) – выбор модуля SPI
uint16_t SPI_DataSize:
SPI_DataSize (SPI_DataSize_16b, SPI_DataSize_8b) – размерность 16 бит/8 бит
Возвращаемое значение: нет

void SPI_BiDirectionalLineConfig(SPI_TypeDef* SPIx, uint16_t SPI_Direction) – выбор направления передачи данных в двунаправленном режиме.
Параметры:
SPI_TypeDef* SPIx:
SPIx (SPI1, SPI2) – выбор модуля SPI
uint16_t SPI_Direction:
SPI_Direction (SPI_Direction_Tx, SPI_Direction_Rx) – только передача/только прием
Возвращаемое значение: нет

void SPI_NSSInternalSoftwareConfig(SPI_TypeDef* SPIx, uint16_t SPI_NSSInternalSoft) – внутренняя установка NSS, функция устанавливает/сбрасывает бит SSI регистра SPI_CR1. Объяснения в предыдущей статье о модуле SPI.
Параметры:
SPI_TypeDef* SPIx:
SPIx (SPI1, SPI2) – выбор модуля SPI
uint16_t SPI_NSSInternalSoft
SPI_NSSInternalSoft (SPI_NSSInternalSoft_Set, SPI_NSSInternalSoft_Reset) – бит SSI установлен/сброшен
Возвращаемое значение: нет

void SPI_SSOutputCmd(SPI_TypeDef* SPIx, FunctionalState NewState) – разрешает или запрещает использование вывода NSS в качестве выхода.
Параметры:
SPI_TypeDef* SPIx:
SPIx (SPI1, SPI2) – выбор модуля SPI
FunctionalState NewState:
NewState (ENABLE или DISABLE) – разрешение/запрет использовать вывод NSS как выход
Возвращаемое значение: нет

2. Функции передачи и чтения принятых данных

uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) – функция чтения принятых данных из буфера приемника Rx buffer.
Параметр:
SPI_TypeDef* SPIx:
SPIx (SPI1, SPI2) – выбор модуля SPI
Возвращаемое значение: принятые данные.

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data) – функция записи значения в буфер Tx buffer для последующей передачи.
Параметры:
SPI_TypeDef* SPIx
SPIx (SPI1, SPI2)– выбор модуля SPI
uint16_t Data
Dataданные, которые необходимо передать. Формат 16-разрядный.
Возвращаемое значение: нет

3. Функции аппаратного вычисления контрольной суммы CRC

void SPI_CalculateCRC(SPI_TypeDef* SPIx, FunctionalState NewState) – разрешение или запрет подсчета CRC для передаваемых данных.
Параметры:
SPI_TypeDef* SPIx:
SPIx (SPI1, SPI2) – выбор модуля SPI
FunctionalState NewState:
NewState (ENABLE или DISABLE) – разрешено/запрещено
Возвращаемое значение: нет

void SPI_TransmitCRC(SPI_TypeDef* SPIx) – передача контрольной суммы.
Параметры:
SPI_TypeDef* SPIx:
SPIx (SPI1, SPI2) – выбор модуля SPI
Возвращаемое значение: нет

uint16_t SPI_GetCRC(SPI_TypeDef* SPIx, uint8_t SPI_CRC) – функция возвращает значение принятой или переданной контрольной суммы CRC.
Параметры:
SPI_TypeDef* SPIx:
SPIx (SPI1, SPI2) – выбор модуля SPI
uint8_t SPI_CRC:
SPI_CRC (SPI_CRC_Tx, SPI_CRC_Rx) – регистр переданной/принятой контрольной суммы
Возвращаемое значение: контрольная сумма

uint16_t SPI_GetCRCPolynomial(SPI_TypeDef* SPIx) – функция возвращает значение полинома CRC из регистра.
Параметры:
SPI_TypeDef* SPIx:
SPIx (SPI1, SPI2) – выбор модуля SPI
Возвращаемое значение: полином CRC

4. Функции управления DMA

void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState) – разрешает/запрещает формирование запроса DMA от буферов приемника или передатчика.
Параметры:
SPI_TypeDef* SPIx:
SPIx (SPI1, SPI2) – выбор модуля SPI
uint16_t SPI_I2S_DMAReq:
SPI_I2S_DMAReq (SPI_I2S_DMAReq_Tx, SPI_I2S_DMAReq_Rx) – выбор источника формирования запроса DMA: Tx buffer или Rx buffer (установка бит TXE или RXE генерирует запрос DMA).
FunctionalState NewState:
NewState (ENABLE или DISABLE) – разрешено/запрещено
Возвращаемое значение: нет

5. Функции управления прерываниями и флагами

void SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState) — разрешение/запрет генерации прерываний при установке флагов TXE, RXNE, ERR (маски прерываний).
Параметры:
SPI_TypeDef* SPIx:
SPIx (SPI1, SPI2) – выбор модуля SPI
uint8_t SPI_I2S_IT:
SPI_I2S_IT (SPI_I2S_IT_TXE, SPI_I2S_IT_RXNE, SPI_I2S_IT_ERR) – выбор источника прерывания. TXE – буфер передатчика пуст, RXNE – буфер приемника содержит принятые данные, ERR – произошла одна из нескольких ошибок.
FunctionalState NewState:
NewState (ENABLE или DISABLE) – разрешение/запрет генерации прерывания от выбранного источника
Возвращаемое значение: нет

FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG) – проверка статуса флагов TXE, RXNE, BSY, OVR, MODF, CRCERR.
Параметры:
SPI_TypeDef* SPIx:
SPIx (SPI1, SPI2) – выбор модуля SPI
uint16_t SPI_I2S_FLAG:
SPI_I2S_FLAG (SPI_I2S_FLAG_TXE, SPI_I2S_FLAG_RXNE, SPI_I2S_FLAG_BSY, SPI_I2S_FLAG_OVR, SPI_I2S_FLAG_MODF, SPI_I2S_FLAG_CRCERR) – выбор флага для чтения его состояния
Возвращаемое значение: состояние флага (SET или RESET)

void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG) – очистка флага CRCERR.
Параметры:
SPI_TypeDef* SPIx:
SPIx (SPI1, SPI2) – выбор модуля SPI
uint16_t SPI_I2S_FLAG:
SPI_I2S_FLAG – для данной функции только флаг CRCERR
Возвращаемое значение: нет

ITStatus SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT) – чтение состояния флагов прерываний.
Параметры:
SPI_TypeDef* SPIx:
SPIx (SPI1, SPI2) – выбор модуля SPI
uint8_t SPI_I2S_IT:
SPI_I2S_IT (SPI_I2S_IT_TXE, SPI_I2S_IT_RXNE, SPI_I2S_IT_OVR, SPI_I2S_IT_MODF, SPI_I2S_IT_CRCERR) – выбор флага для чтения его состояния
Возвращаемое значение: состояние флагов прерываний (SET или RESET)

void SPI_I2S_ClearITPendingBit(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT) – очистка флага прерывания CRCERR, ожидающего обработки.
Параметры:
SPI_TypeDef* SPIx:
SPIx (SPI1, SPI2) – выбор модуля SPI
uint8_t SPI_I2S_IT:
SPI_I2S_IT — для данной функции вызывается только очистка флага необработанного прерывания, сгенерированного при возникновении ошибки CRCERR
Возвращаемое значение: нет

Как можно заметить, многие функции и их параметры предназначены для работы не только с интерфейсом SPI, но и с I2S, поскольку названия  функций и параметров начинаются со SPI_I2S… Из числа функций, рассмотренных здесь, исключены те функции, которые предназначены только для интерфейса I2S. Как отмечалось в предыдущей статье, интерфейс I2S предназначен для работы с потоком аудиоданных, и в данный момент не рассматривается.

Другие статьи:

  11 Responses to “STM32. Стандартные библиотеки для модуля SPI”

  1. Хотельсь бы узнать о расширенных функциях таймеров, с применением внешних сигналов тактирования и безсофтовым управлением PIN-ами.

  2. непонятность с SPI:
    в ф-ю SPI_I2S_SendData(); приходят данные (0xF0), это видно на скрине
    http://radikal.ru/F/i054.radikal.ru/1301/93/bf8a606ea3ba.jpg

    а после след. шага эти данные в регистр DR не записываются
    http://radikal.ru/F/s017.radikal.ru/i436/1301/ea/90445d950de4.jpg

    о почему так?

    • Все нормально, регистр DR уже пустой к следующему шагу, передача завершилась. Отладчик «шагает» по коду не через один такт, шаг в данном случае намного больше. То что происходит аппаратно, отладчик не может отследить.

  3. Спасибо.
    В общем, затык у меня с SPI- данные с модуля не принимаю. Пытаюсь разобраться. А как бы просмотреть содержимое данных, что в регистре SPI?

  4. пример не Ваш. Но в тему этой статьи, где я у себя использую библиотеку эту.
    Так у меня данные не принимаются, или принимаются и не выводятся. Вот и хочу посмотреть- что там приходит. А как понять закрыть окно и снова попробовать? Не совсем понял.

  5. Может и не связано с этим окном. У меня так было, когда после приема или передачи была проверка флага, а регистр DR очищался, когда было открыто окно с этим регистром, видимо потому-что дебаггер читал содержимое регистра,а он после чтения автоматически очищается. Я когда в окне открывал другой регистр какой-нибудь, программа не висла. Может дело и не в этом.
    Можно попробовать отладить SPI отдельно, закольцевав один модуль SPI на другой. Чтобы убедиться что все работает. Читать данные в переменную, следить за ней, а не регистрами SPI. Потом перенести код в этот проект.
    Кстати, там W5100? Я тоже недавно модуль с этой микросхемой получил, но еще не разбирался.

  6. Да, модуль W5100. Готов подсказать что и как там. Пока что пингуется, но данные в него шлю и не принимаю. Или не шлю) Не понятно.
    Даже не знаю, может подождать, пока статью про него напишете ))))

    • Ну проверить лучше всего с помощью второго модуля SPI, если нет осциллографа или лог. анализатора. Еще надо попробовать различные варианты сочетаний полярности и фазы сигнала CLK, т.е. бит CPOL и CPHA в регистре SPI_CR1. У приемника может быть предусмотрен всего один режим работы, с определенной полярностью и фазой сигнала. У меня так AD9834 не получалось запустить, вроде выставил этот режим по даташиту, а заработало совсем в другом режиме. В библиотечных функциях это параметры
      SPI_CPOL (SPI_CPOL_Low, SPI_CPOL_High) и
      SPI_CPHA (SPI_CPHA_1Edge, SPI_CPHA_2Edge).
      Статья по W5100 если и будет, то не скоро, я еще вообще с этим модулем не разбирался. Но буду знать к кому обращаться. Заказывал
      сразу 2 модуля, еще и на ENC2860.