Продолжаем тему программирования шины USB, устройства Host (ведущего), класса MSC (Mass Storage Class) с использованием интерфейса HS (High Speed). Только проект мы будем на данном занятии уже разрабатывать для использования с операционной системой реального времени FREERTOS. При использовании данной системы будут свои тонкости. Поэтому такой урок было принято решение сделать.
В уроке 141 мы уже работали с данным интерфейсом, поэтому нам все равно будет проще, чем изучать всё с нуля.
В качестве подопытной платы мы по-прежнему будем использовать отладочную плату STM32F746G-DISCOVERY, а также тот же самый FLASH-накопитель на 8 гигабайт, которым мы пользовались в уроке 141
Поэтому многие участки кода будут использоваться из этого урока.
Тогда не будем медлить и сразу приступим к проекту.
Проект мы сделаем из проекта урока 111 с именем TASKS_QUEUES и назовём его USB_HS_HOST_MSC_FREERTOS.
Прежде чем открывать проект в Cube MX, удалим из папок Src и Inc все те файлы, которые создавали не мы, а также кроме файла main.c.
Теперь откроем проект в Cube MX и первым делом настроим FreeRTOS, так как в то время, когда мы изучали LWIP при взаимодействии с FREERTOS, мы многому научились.
Установим размер кучи ровно 32 килобайта и выберем другую схему распределения памяти
В разделе Tasks and Queues удалим задачу myTask02
Немного уменьшим стек для задачи по умолчанию
Добавим задачу для вывода строк на дисплей
Подключим остальные два банка памяти SDRAM в настройках FMC
Зайдём в настройки Cortex M7 и добавим туда данные банки в другой регион памяти
Включим шину USB HOST HS
Контакт ULPI DIR переопределим на ножку PC2
Включим USB Host
Также произведём его некоторые настройки, в которых кроме всего прочего увеличим стек на процесс
Включим библиотеку FATFS
Задействуем в ней поддержку длинных имён, а также увеличим максимальную величину сектора и увеличим максимальное количество одновременно открытых файлов
Прежде чем генерировать проект, в файле ff.h в папке репозитория «ДИСК:\Users\Имя пользователя\STM32Cube\Repository\STM32Cube_FW_F7_V1.12.0\Middlewares\Third_Party\FatFs\src» добавим некоторый код, с помощью которого отключим проверку повторных вхождений. У нас таких не ожидается, а с включенными почему-то не работает
Сгенерируем проект для System Workbench, несмотря на предупреждение Cube MX по USB_HOST, и откроем его там. Установим уровень оптимизации в 1, а также уберём при наличии отладочные настройки.
Также вот здесь установим галку, чтобы проект побыстрее собирался
Откроем файл main.c и удалим пользовательскую функцию TaskStringOut вместе с телом.
Также вместе с телом удалим и функцию Task01.
Удалим также глобальный идентификатор этой задачи вместе с идентификаторами остальных пользовательских задач
osThreadId Task01Handle,Task02Handle,Task03Handle,TaskStringOutHandle;
Удалим прототипы функций этих задач
void Task01(void const * argument);
void TaskStringOut(void const * argument);
В структуру для очереди отправки данных на дисплей добавим ещё два поля
1 2 3 4 5 6 7 |
typedef struct struct_out_t { uint32_t tick_count; uint16_t x_pos; uint16_t y_pos; sFONT sfont; char str[60]; } struct_out; |
А структуру для параметров удалим
typedef struct struct_arg_t {
char str_name[10];
uint16_t y_pos;
uint32_t delay_per;
} struct_arg;
Переменные параметров тоже удалим
struct_arg arg01, arg02, arg03;
Добавим глобальный строковый массив
1 2 |
} struct_out; char str_out[32] = {}; |
В функции main() удалим код создания данных задач
strcpy(arg01.str_name,»task1");
strcpy(arg02.str_name,»task2");
strcpy(arg03.str_name,»task3");
arg01.y_pos = 60;
arg02.y_pos = 110;
arg03.y_pos = 160;
arg01.delay_per = 1000;
arg02.delay_per = 677;
arg03.delay_per = 439;
osThreadDef(tskstrout, TaskStringOut, osPriorityBelowNormal, 0, 1280);
TaskStringOutHandle = osThreadCreate(osThread(tskstrout), NULL);
osThreadDef(tsk01, Task01, osPriorityIdle, 0, 128);
Task01Handle = osThreadCreate(osThread(tsk01), (void*)&arg01);
osThreadDef(tsk02, Task01, osPriorityIdle, 0, 128);
Task02Handle = osThreadCreate(osThread(tsk02), (void*)&arg02);
osThreadDef(tsk03, Task01, osPriorityLow, 0, 128);
Task03Handle = osThreadCreate(osThread(tsk03), (void*)&arg03);
В функции задачи по умолчанию StartDefaultTask удалим вот это
uint32_t syscnt;
osThreadList((unsigned char *)str_buf);
HAL_UART_Transmit(&huart1,(uint8_t*)str_buf,strlen(str_buf),0x1000);
HAL_UART_Transmit(&huart1,(uint8_t*)«\r\n»,2,0x1000);
А в бесконечном цикле данной функции оставим только задержку, удалив всё остальное
/* Infinite loop */
for(;;)
{
osDelay(1);
}
В функции TaskStringOut объявим переменную и указатель
1 2 3 |
/* USER CODE BEGIN TaskStringOut */ osEvent event; struct_out *qstruct; |
А в бесконечном цикле обработаем очередь, удалив оттуда задержку
1 2 3 4 5 6 7 8 9 10 11 |
for(;;) { event = osMailGet(strout_Queue, osWaitForever); if (event.status == osEventMail) { qstruct = event.value.p; TFT_SetFont(&qstruct->sfont); sprintf(str_out,"%s", qstruct->str); TFT_DisplayString(qstruct->x_pos, qstruct->y_pos, (uint8_t *)str_out, LEFT_MODE); osMailFree(strout_Queue, qstruct); } |
Подготовим глобальные переменные идентификаторов двух задач для работы с USB
1 2 |
#define LCD_FRAME_BUFFER SDRAM_DEVICE_ADDR osThreadId USB_Task01Handle, USB_Task02Handle; |
Вот это поднимем повыше, а то некрасиво
1 2 |
#define LCD_FRAME_BUFFER SDRAM_DEVICE_ADDR #define MAIL_SIZE (uint32_t) 1 |
Вот эти массивы удалим
char str1[60];
char str_buf[1000]={'\0'};
Подключим переменную состояний программы
1 2 |
osThreadId USB_Task01Handle, USB_Task02Handle; extern ApplicationTypeDef Appli_state; |
Создадим идентификатор очереди для передачи состояний программы между задачами
1 2 |
extern ApplicationTypeDef Appli_state; osMessageQId AppliEvent; |
Создадим перечисление для пользовательских состояний программы, а также переменную данного типа
1 2 3 4 5 6 7 8 9 10 |
osMailQId strout_Queue; typedef enum { MSC_APP_IDLE = 0, MSC_APP_START, MSC_APP_FILE_OPERATIONS, MSC_APP_EXPLORER, MSC_APP_END, } MSC_State; __IO MSC_State state; |
Добавим массивы для регионов кучи
1 2 3 |
char str_out[32] = {}; static __attribute__ ((used,section(".user_heap_stack"))) uint8_t heap_sram1[32*1024]; uint8_t heap_sram2[32*1024]; |
И задействуем их в функции main()
1 2 3 4 5 6 7 8 |
/* USER CODE BEGIN 1 */ HeapRegion_t xHeapRegions[] = { { heap_sram1, sizeof(heap_sram1) }, { heap_sram2, sizeof(heap_sram2) }, { NULL, 0 } }; vPortDefineHeapRegions( xHeapRegions ); |
Уберём все старые надписи с дисплея
TFT_DisplayString(0, 10, (uint8_t *)«Queues», CENTER_MODE);
TFT_SetTextColor(LCD_COLOR_MAGENTA);
TFT_DisplayString(14, 60, (uint8_t *)«Task1:», LEFT_MODE);
TFT_DisplayString(14, 110, (uint8_t *)«Task2:», LEFT_MODE);
TFT_DisplayString(14, 160, (uint8_t *)«Task3:», LEFT_MODE);
Добавим новую шапку и изменим цвет вывода текста
1 2 3 |
TFT_SetTextColor(LCD_COLOR_LIGHTGREEN); TFT_DisplayString(0, 10, (uint8_t *)"USB HS HOST MSC FREERTOS", CENTER_MODE); TFT_SetTextColor(LCD_COLOR_MAGENTA); |
Создадим очередь
1 2 3 |
strout_Queue = osMailCreate(osMailQ(stroutqueue), NULL); osMessageQDef(osqueue, 1, uint16_t); AppliEvent = osMessageCreate(osMessageQ(osqueue), NULL); |
Перейдём в файл usb_host.c и подключим библиотеку FATFS
1 2 |
/* USER CODE BEGIN Includes */ #include "fatfs.h" |
Подключим идентификатор очереди, путь к корневому разделу и переменную структуры файловой системы
1 2 3 4 |
/* Private variables ---------------------------------------------------------*/ extern osMessageQId AppliEvent; extern char USBHPath[4]; extern FATFS USBHFatFS; |
Удалим изменение состояния программы при отключении накопителя, так как мы его изменим сами, но чуть позже
case HOST_USER_DISCONNECTION:
Appli_state = APPLICATION_DISCONNECT;
Вместо этого мы отмонтируем файловую систему и отправим в очередь состояние
1 2 3 4 |
case HOST_USER_DISCONNECTION: osMessagePut(AppliEvent, APPLICATION_DISCONNECT, 0); f_mount(NULL, "", 0); FATFS_UnLinkDriver(USBHPath); |
Аналогично поступим и со следующим кейсом (вариантом)
case HOST_USER_CLASS_ACTIVE:
Appli_state = APPLICATION_READY;
Здесь мы просто в очередь отправим новое состояние программы
1 2 |
case HOST_USER_CLASS_ACTIVE: osMessagePut(AppliEvent, APPLICATION_READY, 0); |
Удалим также изменение состояния и в следующем кейсе
case HOST_USER_CONNECTION:
Appli_state = APPLICATION_START;
А здесь мы только примонтируем нашу файловую систему
1 2 3 |
case HOST_USER_CONNECTION: FATFS_LinkDriver(&USBH_Driver, USBHPath); f_mount(&USBHFatFS, (TCHAR const*) USBHPath, 0); |
Вернёмся в файл main.c и добавим функцию первой задачи
1 2 3 4 5 6 7 |
/* USER CODE BEGIN 4 */ //--------------------------------------------------------------- void USB_Task01(void const * argument) { osEvent event; } //--------------------------------------------------------------- |
А в функции задачи по умолчанию StartDefaultTask мы данную задачу создадим
1 2 3 |
/* USER CODE BEGIN 5 */ osThreadDef(usb_tsk01, USB_Task01, osPriorityNormal, 0, 1024); USB_Task01Handle = osThreadCreate(osThread(usb_tsk01), NULL); |
В функции первой задачи USB_Task01 изменим состояние программы
1 2 |
osEvent event; Appli_state = APPLICATION_IDLE; |
Добавим бесконечный цикл, в котором будем следить за очередью и в зависимости от этого менять состояние программы
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Appli_state = APPLICATION_IDLE; for(;;) { event = osMessageGet(AppliEvent, osWaitForever); if (event.status == osEventMessage) { switch (event.value.v) { case APPLICATION_DISCONNECT: Appli_state = APPLICATION_DISCONNECT; break; case APPLICATION_READY: Appli_state = APPLICATION_READY; break; default: break; } } } |
Выше добавим функцию для второй задачи
1 2 3 4 5 6 |
/* USER CODE BEGIN 4 */ //--------------------------------------------------------------- void USB_Task02(void const * argument) { } //--------------------------------------------------------------- |
Создадим эту задачу в функции для первой задачи USB_Task01
1 2 3 |
osEvent event; osThreadDef(usb_tsk02, USB_Task02, osPriorityNormal, 0, 1024); USB_Task02Handle = osThreadCreate(osThread(usb_tsk02), NULL); |
В функции для второй задачи добавим бесконечный цикл, в котором будем обрабатывать варианты пользовательского состояния программы, не забывая о задержке, иначе рискуем не дать работать остальным задачам
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void USB_Task02(void const * argument) { for(;;) { switch (state) { case MSC_APP_IDLE: state = MSC_APP_START; break; default: break; } osDelay(1); } |
Добавим указатель на переменную структуры для очереди отправки строки на дисплей и локальный строковый массив
1 2 3 4 |
void USB_Task02(void const * argument) { struct_out *qstruct; char str[100]; |
Обработаем состояние программы на отключение носителя. Сделаем это не в теле условия вариантов, а за телом условия
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
default: break; } if (Appli_state == APPLICATION_DISCONNECT) { qstruct = osMailAlloc(strout_Queue, osWaitForever); qstruct->y_pos = 220; qstruct->x_pos = 20; qstruct->sfont = Font24; sprintf(qstruct->str,"%-20s", "Stop"); osMailPut(strout_Queue, qstruct); sprintf(str,"USB-Drive is unmounted\r\n"); HAL_UART_Transmit(&huart1,(uint8_t*)str,strlen(str),0x1000); Appli_state = APPLICATION_IDLE; state = MSC_APP_IDLE; } |
В условии вариантов добавим ещё один кейс, в котором отправим сообщение в терминальную программу о том, что мы вставили FLASH-накопитель в разъём, а также установим другое пользовательское состояние
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
state = MSC_APP_START; break; case MSC_APP_START: if (Appli_state == APPLICATION_READY) { qstruct = osMailAlloc(strout_Queue, osWaitForever); qstruct->y_pos = 220; qstruct->x_pos = 20; qstruct->sfont = Font24; sprintf(qstruct->str,"%-20s", "Start"); osMailPut(strout_Queue, qstruct); state = MSC_APP_FILE_OPERATIONS; } break; |
Добавим ещё три, пока почти пустых варианта (кейса)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
state = MSC_APP_FILE_OPERATIONS; } break; case MSC_APP_FILE_OPERATIONS: if (Appli_state == APPLICATION_READY) { state = MSC_APP_EXPLORER; } break; case MSC_APP_EXPLORER: break; case MSC_APP_END: break; |
Теперь мы можем уже проверить, как наша программа почувствует то, что в плату будет вставлен FLASH-накопитель, а также и то, когда он будет извлечён.
Можно пока вынуть накопитель, запустить терминальную программу, соединиться с виртуальным портом, собрать код и прошить контроллер.
Затем вставим накопитель и через некоторое время должна будет появиться надпись Start на дисплее платы
Извлечём накопитель, и надпись Start поменяется на Stop
Также в терминальной программе мы увидим сообщение, что файловая система отмонтирована
Можно попробовать такие манипуляции по вставке и извлечению накопителя несколько раз, всё будет работать.
В следующей части нашего урока мы попробуем записать и прочитать файлы, а также получить определённую информацию о файловой системе средствами FATFS.
Предыдущий урок Программирование МК STM32 Следующая часть
Отладочную плату можно приобрести здесь STM32F746G-DISCOVERY
Смотреть ВИДЕОУРОК (нажмите на картинку)
Добавить комментарий