Cdc+msc usb composite device на stm32 hal

Содержание

Файловая система FAT

С точки зрения файловой системы каждый носитель данных (жесткий диск, карта памяти) разделен на сектора и кластеры. Сектор — это наименьшее количество байтов, которое может быть записано или прочитано. Обычно размер сектора составляет 512 байт. Файлы сохраняются в пронумерованных кластерах.

Размер кластера зависит от файловой системы и носителя. Каждый кластер полностью выделен для данного файла. Это означает, что даже если файл намного меньше размера кластера, он все равно занимает столько же, сколько один кластер на диске.

Ключевым элементом файловой системы FAT (File Allocation Table) является, в соответствии с ее именем, таблица размещения файлов. Файловая система FAT представлена ​​четырьмя разновидностями, во встроенных системах обычно используется две, в зависимости от размера носителя и требований к приложениям, это будет FAT16 или FAT32.

Рис. 2. Разделение носителя информации в системе FAT

Носитель данных в файловой системе FAT разделен на пять частей, все они показаны на рис. 2. Первая логическая часть носителя данных, расположенная в первом секторе, представляет собой зарезервированную область, которая содержит всю основную информацию о текущем разделе (носителе).

К этой информации относятся, в частности: тип и размер разделов, размер сектора и количество секторов в кластере. За зарезервированной областью находятся таблицы размещения файлов, которые являются основным источником информации о данных, сохраняемых на носителе. Обычно, помимо основной таблицы размещения, есть и ее копия. Четвертая область — это корневой каталог, который создается автоматически при создании файловой системы. Последний, пятый сектор — это область данных.

Внутренняя поддержка Flash

Производитель внедрил в микроконтроллеры STM32 специальный драйвер Flash Program / Erase Controller (FPEC), который можно использовать для изменения содержимого флэш-памяти. В микроконтроллерах STM32 флэш-память организована в виде страниц, размер которых составляет 1 КБ или 2 КБ, в зависимости от общего объема памяти.

Флэш-память находится в адресном пространстве процессора, начиная с адреса 0x08000000. Подробное описание того, как обрабатывать флэш-память в микроконтроллерах STM32, можно найти в документе PM0042 под названием «Программирование флэш-памяти STM32F10xxx», который доступен на веб-сайте STMicroelectronics.

После сброса микроконтроллера FPEC защищает содержимое флэш-памяти от сохранения, поэтому нет возможности случайного повреждения ее содержимого. Чтобы внести какие-либо изменения, вы должны сначала разблокировать память. Это делается путем ввода двух значений в реестр Flash_KEYR:

KEY1 = 0x45670123

а затем

KEY2 = 0xCDEF89AB

С этого момента  могут выполняться операции стирания и записи памяти, пока не произойдет ошибка, которая блокирует доступ к памяти до следующей разблокировки. Алгоритм записи во флэш-память через FPEC показан на рис. 2.

Рис. 2. Алгоритм программирования флэш-памяти в микроконтроллерах STM32

Запись в память производится 2 байтами. Сначала установите бит PG в реестре Flash_CR, информируя контроллер о том, что мы будем программировать. После этого действия вы можете изменить содержимое памяти, что требует ввода 2 байтов по адресу, где они должны быть размещены в памяти. Затем дождитесь окончания этой операции и убедитесь, что запись была произведена правильно, для чего необходимо прочитать записанные 2 байта и убедиться в их правильности — это можно сделать путем сравнения. Если ошибок не нет,  то вы можете записать следующие данные таким же образом. После сохранения всей страницы вы должны удалить бит PG.

Содержимое флэш-памяти в микроконтроллерах STM32 может быть удалено двумя способами: страница за страницей или все ее содержимое. В случае загрузчика память будет удаляться постранично. Постраничный алгоритм удаления показан на рис. 3.

Рис. 3. Алгоритм стирания флэш-памяти в микроконтроллерах STM32 (постраничный)

Мы начинаем стирать содержимое памяти, устанавливая бит PER, информирующий FPEC в регистре Flash_CR о том, что страница флэш-памяти будет удалена. Затем введите адрес удаляемой страницы в регистр Flash_AR и установите бит STRT в регистр Flash_CR. В этот момент начинается физическое удаление содержимого указанной страницы памяти. На следующем этапе мы ожидаем его завершения, после чего проверяем, была ли страница удалена правильно, читая и проверяя ее содержимое.

Команды, поддерживаемые SD-картами

Каждая команда для отправки на SD-карту состоит из шести байтов. Первый байт всегда является кодом команды, следующие четыре байта являются его аргументом. В конце отправляется байт контрольной суммы CRC.

В то время как сумма CRC проверяется в рабочем режиме с помощью интерфейса SDBus, контрольная сумма игнорируется картой при обмене данными через шину SPI. Только при отправке команды CMD0, переключающей режим работы с SDBus на SPI, требуется байт CRC. Его не нужно рассчитывать каким-либо образом, поскольку он является фиксированным значением и равен 0x95.

В табл. 1 указаны несколько команд при работе с шиной SPI с описанием аргументов. В дополнение к стандартным командам CMD карты SD также могут использовать так называемые прикладные команды (ACMD). Для отправки команды необходимо сначала отправить команду CMD55, информирующую SD-карту о том, что следующей командой будет ACMD.

Табл. 1. Команды, поддерживаемые SD-картами в режиме SPIf

команда описание
CMD0 сбрасывает карту, позволяет включить режим шины SPI
CMD12 принудительное завершение передачи множества блоков данных
CMD16 настройка длины блока данных для чтения / записи
CMD17 чтение блока памяти длины, указанной CMD16
CMD24 запись блока памяти с длиной, указанной CMD16
CMD32 адрес первого удаляемого блока передается в аргументе
CMD33 адрес последнего удаляемого блока передается в аргументе
CMD38 удаляет блоки, обозначенные CMD32 и CMD33

Описание работы USB

  • USB устройства подключаются к Хосту (чаще всего, это — компьютер). Хост — главный, он всем управляет.
  • USB устройство не может быть инициатором передачи данных. То есть, оно сидит и молчит, пока его не спросят. Спросили или прислали данные, — оно ответило или приняло данные и замолчало.
  • USB устройство имеет уникальный идентификатор. Каждое USB устройство может иметь несколько конечных точек, каждая из которых имеет уникальный адрес. Именно через конечные точки передаются данные или команды.
  • USB устройство имеет Дескриптор. Это массив данных, в котором содержится описание устройства. Благодаря дескриптору, операционная система получает информацию о USB устройстве и использует для общения с ним соответствующий драйвер.
  • Хост идентифицирует USB-устройство по ID вендора и ID продукта (Vendor ID — VID и Product ID — PID)

http://microtechnics.ru/osnovy-interfejsa-usb/http://webhamster.ru/mytetrashare/index/mtb0/1410507212bb4zf8gacjhttp://radiokot.ru/circuit/digital/pcmod/63/http://microtechnics.ru/mikrokontroller-stm32-i-usb/http://microtechnics.ru/stm32-peredacha-dannyx-po-usb/

Полярность и фаза тактового сигнала (Clock Polarity and Phase)

В дополнение к настройкам тактирования шины, master и slave должны «договориться» о полярности и фазе тактового сигнала. SPI спецификация подразумевает две настройки CPOL и CPHA соответственно и многие производители микросхем заимствовали данные названия настроек.
Комбинации полярности и фазы тактового сигнала часто ассоциируют с режимами работы SPI, в таблице 1 можно увидеть данные комбинации. Наиболее общие и часто используемые режимы это 0 и 3, большинство ведомых (slave) устройств поддерживают как минимум пару режимов.

Таблица 1: Режимы работы SPI, соответствующие конфигурации CPOL и CPHA

Временная диаграмма показана на рисунке 4 и она дополнительно описана ниже:

  • Если CPOL=0 то основное состояние тактового сигнала находится в 0, таким образом в активном состоянии он переключается на 1, в режиме ожидания в 0.— Для CPHA=0 бит данных захватывается по возрастающему фронту (LOW→HIGH) и отправляется по ниспадающему фронту (HIGH→LOW).— Для CPHA=1 бит данных захватывается по ниспадающему фронту (HIGH→LOW) и отправляется по возрастающему фронту (LOW→HIGH).
  • Если CPOL=1, то основное состояние тактового сигнала в 1, активное состояние в 0, ожидание 1.— Для CPHA=0 бит данных захватывается по ниспадающему фронту (HIGH→LOW) и отправляется по возрастающему фронту (LOW→HIGH).— Для CPHA=1 бит данных захватывается по возрастающему фронту (LOW→HIGH) и отправляется по ниспадающему фронту (HIGH→LOW).

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

Рисунок 4: Временная диаграмма для соответствующих настроек CPOL и CPHA

Прокачиваем драйвер MSC

протокол USB предоставляет такой механизм прямо из коробки

Транзакция SCSI: Read(10) LUN: 0x00 (LBA: 0x00000000, Len: 1)Хост отправляет команду на чтение. Со стороны микроконтроллера вызывается функция MSC_BOT_DataOut()
Команда обрабатывается по цепочке функций MSC_BOT_DataOut() -> MSC_BOT_CBW_Decode() -> SCSI_ProcessCmd() -> SCSI_Read10()
Поскольку драйвер находится в состоянии hmsc->bot_state == USBD_BOT_IDLE, то готовится процедура чтения: проверяются параметры команды, запоминается сколько всего блоков нужно прочитать, после чего передается управление функции SCSI_ProcessRead() с просьбой прочитать первый блок
Функция SCSI_ProcessRead() читает данные в синхронном режиме. Именно тут микроконтроллер занят бОльшую часть времени.
Когда данные получены они перекладываются (с помощью функции USBD_LL_Transmit() ) в выходной буфер конечной точки MSC_IN, чтобы хост мог их забрать
Драйвер переходит в состояние hmsc->bot_state = USBD_BOT_DATA_IN

Транзакция SCSI: Data InХост забирает данные из выходного буфера микроконтроллера пакетами по 64 байта (максимальный рекомендованный размер пакета для USB Full Speed устройств). Все это происходит на самом низком уровне в ядре USB, драйвер MSC в этом не участвует
Когда хост забрал все данные возникает событие Data In. Управление передается в функцию MSC_BOT_DataIn()

Акцентирую Ваше внимание, что эта функция вызывается после реальной отправки данных.
Драйвер находится в состоянии hmsc->bot_state == USBD_BOT_DATA_IN, что означает мы все еще в режиме чтения данных.
Если еще не все заказанные блоки прочитаны – стартуем чтение очередного кусочка и ждем завершения, перекладываем в выходной буфер и ждем пока хост заберет данные. Алгоритм повторяется
Если все блоки прочитаны, то драйвер переключается в состояние USBD_BOT_LAST_DATA_IN для отправки финального статуса команды

Транзакция SCSI: ResponseК этому моменту данные посылки уже отправлены
драйвер лишь получает об этом уведомление в переходит в состояние USBD_BOT_IDLE

  • Транзакция SCSI: Read(10) LUN: 0x00 (LBA: 0x00000000, Len: 1)
    • Микроконтроллер получает команду на чтение, проверяет все параметры, запоминает количество блоков, которые нужно прочитать
    • Микроконтроллер стартует чтение первого блока в асинхронном режиме
    • Выходим из прерывания не дожидаясь окончания чтения
  • Когда чтение закончилось вызывается коллбек
    • Прочитанные данные отправляются в выходной буфер
    • Хост их вычитывает без участия драйвера MSC
  • Транзакция SCSI: Data In
    • Вызывается коллбек функция DataIn(), которая сигнализирует о том, что хост забрал данные и можно делать следующее чтение
    • Запускаем чтение следующего блока. Алгоритм повторяется начиная с обратного вызова о завершении чтения
    • Если все блоки прочитаны – отправляем пакет статуса
  • Транзакция SCSI: Response
    • К этому моменту данные посылки уже отправлены
    • Готовимся к следующей транзакции

Управление сигналом Slave Select

Как говорилось ранее, ведомое устройство в SPI шине не имеет адреса, по которому его можно было бы идентифицировать, но оно начинает обмен данными и продолжает его пока сигнал Slave Select (SS) находится в низком логическом уровне LOW. STM32 микроконтроллеры предоставляют два режима управления сигналом SS или NSS, как он называется в документации ST. Давайте проанализируем их.

  • Программный режим NSS (NSS software mode): сигнал SS управляется из программы и используется любой свободный GPIO пин.
  • Аппаратный режим NSS (NSS hardware mode): для работы с сигналом SS используется специальный IO пин, который управляется SPI интерфейсом. В данном режиме доступны две конфигурации NSS выхода:— NSS выход включен: данная настройка используется только, когда устройство работает в режиме master. NSS сигнал устанавливается в LOW, когда master начинает передачу и остается в уровне LOW пока SPI отключен. Стоит заметить, что этот режим возможно использовать, когда лишь одно ведомое (slave) устройство в шине и ее SS пин подключен к сигнальной линии NSS.— NSS выход отключен: эта конфигурация допускает использование режима multi-master для устройств, работающих в режиме master. Для ведомых (slave) устройств NSS пин работает, как простой вход, т.е. slave выбран, когда NSS в LOW.

Заключительные замечания

Загрузчик должен быть сохранен в памяти микроконтроллера так же, как и любая другая программа (с использованием интерфейса JTAG или через USART). После сохранения во флэш-памяти загрузчик сразу готов к использованию.

Чтобы использовать его для правильного запуска пользовательского приложения, вам необходимо выполнить в нем несколько операций. Во-первых, это перемещение приложения пользователя во флэш-памяти с адреса от 0x8000000 до 0x8002000, то есть на 8 КБ вверх (например, путем изменения адреса ссылки начала памяти в сценарии компоновщика). Во-вторых — удалить настройку вектора прерывания пользовательского приложения, как это делает загрузчик.

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

В прикрепляемом файле вам нужно только подключить соответствующую библиотеку ST (CMSIS), определяющую показатели структуры регистров устройств (RCC, BKP, и т. д.)

Пример 2. Пример заголовочного файла для вызова загрузчика

#ifndef BOOTLOADER_H_ \
#define BOOTLOADER_H_
// адрес таблицы векторов прерываний загрузчика
#define BOOTLOADER_VECTOR_ADDR 0x8000000
// определения, вставленные из файлов библиотеки ST
/ * --------- PWR регистрирует битовый адрес в области псевдонима ---------- * /
#define PWR_OFFSET (PWR_BASE - PERIPH_BASE)
/ * --- CR Register --- * /
/ * Псевдоним слова адреса бита DBP * /
#define CR_OFFSET (PWR_OFFSET + 0x00)
#define DBP_BitNumber 0x08
#define CR_DBP_BB (PERIPH_BB_BASE + (CR_OFFSET * 32) + (DBP_BitNumber * 4))
// структура, описывающая начало стандартного вектора прерывания
typedef struct {
uint32_t SP;
void (* RESET_ISR) (void);
void (* NMIExc) (void);
void (* HardFaultExc) (void);
void (* MemManageExc) (void);
void (* BusFaultExc) (void);
void (* UsageFaultExc) (void);
void (* RESRV1) (void);
void (* RESRV2) (void);
void (* RESRV3) (void);
void (* RESRV4) (void);
void (* SVC) (void);
BOOTLOADER_CM3_ISR_TABLE};
// определение указателя на таблицу прерываний загрузчика
#define BOOTLOADER_TAB ((BOOTLOADER_CM3_ISR_TABLE *) BOOTLOADER_VECTOR_ADDR)
// макрос для вызова загрузчика путем перехода к нему
#define BOOTLOADER_CALL_BY_JUMP () (BOOTLOADER_TAB- & SVC) ()
// макрос, служащие для вызова загрузчика через запись сигнатуры
// к первому резервному регистру данных и сбросу процессора
#define BOOTLOADER_CALL_BY_RESET () {\
RCC-> APB1ENR | = RCC_APB1Periph_BKP | RCC_APB1Periph_PWR; \
RCC-> APB1RSTR & amp; = ~ ((uint32_t) (RCC_APB1Periph_BKP | RCC_APB1Periph_PWR)); \\
* (vu32 *) CR_DBP_BB = (u32) 1; \
BKP-> DR1 = 0x159D; \
SCB-> AIRCR = (SCB-> AIRCR & 0xFFFF) | (0x5FA & lt; & lt; 16) | (1 & lt; & lt; 0); \
}
#endif / * BOOTLOADER_H _ * /

Этот файл определяет базовый адрес вектора прерывания загрузчика, который совпадает с адресом начала флэш-памяти. Затем был определено начало стандартной для этих микроконтроллеров структуры вектора прерываний.

Необходимо обсудить, что находится внутри макроса BOOTLOADER_CALL_BY_RESET. Первые две строки отвечают за включение часов для контроллера резервного копирования и его выключение, третья строка отвечает за разблокировку записи в регистры резервного копирования (она поступает из библиотеки stm32f10x_pwr).

В четвертой строке необходимо ввести значение 0x159D в первый регистр данных резервного копирования, которое загрузчик будет искать в этом реестре после сброса. Пятая строка отвечает за программный сброс микроконтроллера. Это включает установку бита VECTRESET (самый младший бит) в регистре управления прерываниями и сбросом приложения. Это один из регистров контроллера NVIC, определенных в спецификации ядра Cortex-M3. После ввода в нулевой бит 1 с одновременным вводом в биты 16 … 31 сигнатуры 0x5FA, происходит сброс микроконтроллера.

Тестер транзисторов / ESR-метр / генератор
Многофункциональный прибор для проверки транзисторов, диодов, тиристоров…

Подробнее

Главная идея нашего USB Mass Storage Bootloader

bin0x08002800binbinHEXbin-файлddWin32DiskImagerST-LINK Utilitybin

Команда для заливки на Mass Storage bin-файла под Ubuntu:

Итак, наш USB Mass Storage, который теперь играет роль Bootloader-а, прошивку уже обновляет. Теперь осталось доделать USB Mass Storage пример таким образом, чтобы он мог передавать управление залитой программе. Я это сделал с помощью кнопки. Когда мы подключаем микроконтроллер к компьютеру USB кабелем, и при этом нога PB1 замкнута на землю, контроллер стартует как USB Mass Storage устройство и можно заливать прошивку. Если во время старта PB1 на землю не замкнута, а висит в воздухе, или подключена к +, микроконтроллер передает управление залитой прошивке.
Вот как выглядит код нашего Bootloader-а (доработанного примера USB Mass Storage):

https://github.com/avislab/STM32F103/tree/master/Example_Bootloader

Модуль FatFs

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

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

Одним из таких общедоступных инструментов является модуль FatFs, задачей которого является создание моста между физическим уровнем (носителем данных) и приложением, работающим на микроконтроллере. Подробную информацию о FatFs можно найти на сайте автора. Роль модуля FatFs показана на рис. 3.

Рис. 3. Расположение модуля FatFs в программном проекте

Рис. 4. Структура файлов FatFs

Сам модуль FatFs написан на языке C. Файлы, необходимые для правильной работы FatFs, показаны на рис. 4 в виде дерева, скопированного из проекта с использованием файловой системы FAT. Теоретически для правильной работы модуля FatFs требуется наличие часов реального времени (RTC) во встроенной системе. Это требование можно легко обойти, введя фиксированные значения вместо даты и времени.

Обмен сообщениями с использованием SPI

Как только SPI интерфейс сконфигурирован, мы можем начать обмен данными с ведомыми (slave) устройствами. Так как SPI спецификации не описывает конкретный протокол для передаваемых данных, то и нет особой разницы между master и slave при настройке через CubeHAL. Разница лишь в параметре режимов в структуре SPI_InitTypeDef.
Как обычно, CubeHAL предоставляет три режима настройки шины SPI: опрос, прерывание и режим DMA.

Для отправки нескольких байт в режиме опроса необходимо использовать функцию:

HAL_StatusTypeDef HAL_SPI_Transmit (SPI_HandleTypeDef * hspi, uint8_t * pData, uint16_t Size, uint32_t Timeout);

Параметры, принимаемые этой функцией не нуждаются в описании, т. к. они идентичны уже изученым ранее интерфейсам, например UART. Эта функция может быть использована, если SPI настроен как SPI_DIRECTION_1LINE или SPI_DIRECTION_2LINES. Для приема нескольких байт в режиме опроса мы используем функцию:

HAL_StatusTypeDef HAL_SPI_Receive (SPI_HandleTypeDef * hspi, uint8_t * pData, uint16_t Size, uint32_t Timeout);

Эта функция может использоваться в любом из трех режимов Direction.
Если ведомое (slave) устройство поддерживает полнодуплексный режим, то соответственно можно использовать функцию:

HAL_StatusTypeDef HAL_SPI_TransmitReceive (SPI_HandleTypeDef * hspi, uint8_t * pTxData, uint8_t * pRxData, uint16_t Size, uint32_t Timeout);

которая отправляет несколько байт, одновременно принимая ответ. Функция может быть использована только, если параметр Direction настроен на SPI_DIRECTION_2LINES.
Для обмена данными с использованием прерываний CubeHAL предоставляет следующие три функции:

HAL_StatusTypeDef HAL_SPI_Transmit_IT (SPI_HandleTypeDef * hspi, uint8_t * pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_Receive_IT (SPI_HandleTypeDef * hspi, uint8_t * pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_TransmitReceive_IT (SPI_HandleTypeDef * hspi, uint8_t * pTxData, uint8_t * pRxData, uint16_t Size);

CubeHAL также позволяет передавать данные с DMA и функции похожи на три, представленные выше, лишь с префиксом _DMA в конце.

Используя прерывания или DMA, мы должны быть готовы к тому, что в конце передачи мы получим ассинхронный запрос об этом. Это значит, что мы должны принять этот запрос и обработать его, разрешив и настроив контроллер прерывания NVIC и определив функцию HAL_SPI_IRQHandler() обработчик прерывания. Возможно шесть различных вызовов перечисленных в таблице 3.

Когда SPI настроен на работу с DMA в циклическом режиме, можно использовать несколько функий для паузы, продолжения и отмены циклического режима отправки/приема DMA:

HAL_StatusTypeDef HAL_SPI_DMAPause (SPI_HandleTypeDef * hspi);
HAL_StatusTypeDef HAL_SPI_DMAResume (SPI_HandleTypeDef * hspi);
HAL_StatusTypeDef HAL_SPI_DMAStop (SPI_HandleTypeDef * hspi);

Также в данном режиме существуют следующие ограничения:

  • Циклический режим DMA не может быть использован, если SPI настроен исключительно на прием;
  • В циклическом режиме с DMA не доступна функция аппаратного расчета CRC;
  • Когда используются функции паузы/остановки они могут вызваны только в обработчике функции обратного вызова, т.е. в прерывании.

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

Программатор

В предыдущей части мы познакомились с несколькими вариантами программатора ST-Link. В данном примере в качестве программатора я буду использовать отладочную плату stm32f4discovery, просто потому, что она у меня есть. Для того, чтобы использовать эту отладочную плату в качестве программатора, нужно сделать 2 вещи:

  1. Снять перемычки, соединяющие линии программирования встроенного ST-Link-а с микроконтроллером, распаянным на плате
  2. С помощью гребенки, на которую выведена шина SWD, подключить сигнальные линии программатора к внешнему микроконтроллеру

На картинке, приведенной далее, показана распиновка разъема SWD:

На китайских отладочных платах с микроконтроллером stm32f103c8 имеется соответствующий разъем, через который можно загрузить прошивку в МК. Эти платы выглядят примерно так:

Пины на разъеме программирования подписаны как GND, CLK, DIO, 3V3. Соединение с программатором выполняется вот таким образом:

  • GND — GND
  • CLK — SWCLK
  • DIO — SWDIO

У меня макеты выглядят вот так:

В данном случае отладочная плата с stm32f103c8 припаяна к «решету», на котором соединена с остальными компонентами на обратной стороне проводом в изоляции. Таким же проводом выполнено подключение программатора к отладочной плате:

HAL_SPI модуль

Для программирования SPI HAL определяет С структуру SPI_HandleTypeDef:

Давайте проанализируем наиболее важные поля данной структуры.

  • Instance: указатель на дескриптор SPI, который мы будем использовать. Например, SPI1 — это дескриптор первого SPI интерфейса.
  • Init: объект C структуры SPI_InitTypeDef, которая используется для настройки интерфейса, далее мы рассмотрим ее более подробно.
  • ptxBuffPtr, pRxBuffPtr: указатели на временные буферы для принимаемых и отправляемых данных SPI. Они используются, когда SPI работает в режиме прерывания и не могут быть изменены из программы пользователя.
  • hdmatx, hdmarx: указатели на объекты DMA_HandleTypeDef структуры, используемой, когда SPI работает в режиме с DMA.

Настройка SPI производится с использованием объекта структуры SPI_InitTypeDef:

  • Mode: параметр, определяющий в каком режиме, master или slave работает SPI интерфейс. Может принимать два значения SPI_MODE_MASTER и SPI_MODE_SLAVE.
  • Direction: параметр, задающий работу SPI интерфейса либо в 4-проводном режиме (2 отдельные линии для входных и выходных данных соответсвенно), либо в 3-проводном режиме (одна I/O линия). Может принимать следующие значения: SPI_DIRECTION_2LINES или полнодуплексный 4-проводной режим; SPI_DIRECTION_2LINES_RXONLY или полудуплексный 4-проводной режим; SPI_DIRECTION_1LINE или полудуплексный 3-проводной режим.
  • DataSize: параметр, задающий размер данных, передаваемых по шине SPI. Может принимать два следующих значения: SPI_DATASIZE_8BIT и SPI_DATASIZE_16BIT.
  • CLKPolarity: настройка SCK CPOL, принимает значения SPI_POLARITY_LOW (CPOL=0) и SPI_POLARITY_HIGH (CPOL=1).
  • CLKPhase: настройка фазы тактового сигнала SCK, принимает также два значения SPI_PHASE_1EDGE (CPHA=0) и SPI_PHASE_2EDGE (CPHA=1).
  • NSS: параметр, задающий поведение линии NSS. Принимает значения SPI_NSS_SOFT для программного управления NSS, SPI_NSS_HARD_INPUT и SPI_NSS_HARD_OUTPUT для настройки NSS сигнала в аппаратном режиме.
  • BaudRatePrescaler: делитель частоты шины APB, определяет максимальную тактовую частоту линии SCK. Может принимать значения SPI_BAUDRATEPRESCALER_2, SPI_BAUDRATEPRESCALER_4,…,SPI_BAUDRATEPRESCALER_256.
  • FirstBit: параметр, определяющий порядок передачи данных по шине SPI: SPI_FIRSTBIT_MSB или SPI_FIRSTBIT_LSB.
  • TIMode: включение/выключение режима поддержки протокола TI. Значения: SPI_TIMODE_DISABLE или SPI_TIMODE_ENABLE.
  • CRCCalculation и CRCPolynomial: во всех микроконтроллерах STM32 SPI периферия может аппаратно генерировать CRC контрольную сумму. Значение CRC может передаваться последним байтом в режиме передачи или автоматическая проверка на ошибку CRC может быть произведена с последним принятым байтом данных. Значение CRC вычисляется с использованием нечетного программируемого многочлена на каждом бите. Вычисление происходит на определяемом параметрами CPHA и CPOL фронте тактовой частоты каждой выборки. Вычисленное CRC проверяется автоматически в конце блока данных. Когда появляется ошибка между вычисленным значением CRC от принятых данных и CRC, переданной от передающего устройства, устанавливается состояние ошибки. CRC не доступно в режиме работы SPI с DMA в циклическом режиме. Для более подробной информации рекомендуется ознакомиться с референс мануалом на конкретный микроконтроллер.

Как обычно, для конфигурации SPI мы используем функцию:

HAL_StatusTypeDef HAL_SPI_Init (SPI_HandleTypeDef * hspi);

которая принимает в качестве входного параметра указатель на объект структуры SPI_HandleTypeDef, рассмотренной выше.