Всем доброго дня! Возвращаемся к экспериментам с микроконтроллерами STM32 и сегодня продолжим обсуждать связь контроллера и компьютера при помощи интерфейса USB. Раньше мы разобрались со стандартом USB в целом, а также с приемом данных от хоста (ПК), вот ссылки на эти статьи: раз и два. Так вот, теперь пришло время разобраться с передачей данных в обратном направлении, то есть от USB-устройства (в нашем случае это отладочная плата STM32F3Discovery) к хосту.
Итак, приступаем. За основу я возьму проект из предыдущей статьи, в который уже включены все необходимые файлы из всех используемых библиотек.
Время традиционной вставки: поскольку компания STMicroelectronics прекратила поддержку библиотеки SPL, которая использовалась в этом курсе, я создал новый, посвященный работе уже с новыми инструментами, так что буду рад видеть вас там - STM32CubeMx. Кроме того, вот глобальная рубрика по STM32, а также несколько статей на смежную тему из нового курса:
Мы собираемся передавать данные хосту, поэтому нас сегодня интересуют в первую очередь транзакции типа IN. За транзакции OUT и IN, как вы помните, у нас отвечают обработчики соответствующих прерываний, а именно:
void EP1_IN_Callback (void) void EP3_OUT_Callback(void)
Но тут все не так просто и банально, как в случае с приемом данных от хоста. Тогда мы просто поместили функцию, принимающую данные в обработчик EP3_OUT_Callback(), и после этого всего лишь анализировали эти данные. С транзакцией IN так не получится...
Для того, чтобы наша программа улетела на обработчик EP1_IN_Callback(), необходимо как то сообщить хосту, что мы готовы передавать данные. Для этого нам нужно обратиться к обработчику SOF_Callback(). Что это за функция? А это понятно из ее названия (SOF - Start Of Frame) - это обработчик Start Of Frame пакета. И именно в этой функции мы должны "запустить" отправку хосту. После этого хост будет регулярно запрашивать данные через транзакции IN, а нам остается только успевать скармливать ему новые байты.
Давайте теперь все это опробуем на примере. Поставим себе такую задачу - будем анализировать принятые данные и если мы увидим, что хост прислал нам байт 0х55 - то отправим ему парочку байт в ответ.
Начнем с повторения того, что было в предыдущей статье, именно с обработки транзакций типа OUT:
/***************************************************************************************/ void EP3_OUT_Callback(void) { uint16_t USB_Rx_Cnt; USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, USB_Rx_Buffer); if (USB_Rx_Buffer[0] == 0x55) { needToSend = 1; } SetEPRxValid(ENDP3); } /***************************************************************************************/
Принимаем данные и, если видим наш байт 0х55, то выставляем переменную-флаг needToSend в единицу. Кстати, не забываем объявить эту самую переменную:
uint8_t needToSend = 0;
Теперь идем в файл hw_config.c. И в нем нам, между прочим, тоже понадобится переменная needToSend, поэтому объявляем ее и в этом файле:
extern uint8_t needToSend;
Ищем обработчик пакета Start Of Frame, о котором мы уже вспоминали в начале статьи и находим его в файле usb_endp.c:
/***************************************************************************************/ void SOF_Callback(void) { static uint32_t FrameCount = 0; if(bDeviceState == CONFIGURED) { if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL) { /* Reset the frame counter */ FrameCount = 0; /* Check the data to be sent through IN pipe */ Handle_USBAsynchXfer(); } } } /***************************************************************************************/
Не задерживаемся тут и отправляемся в функцию Handle_USBAsynchXfer(). Туда то и поместим вызов функции для отправки данных хосту:
/***************************************************************************************/ void Handle_USBAsynchXfer (void) { if (needToSend == 1) { uint8_t tempData = 0x12; USB_SIL_Write(EP1_IN, &tempData, 1); SetEPTxValid(ENDP1); } } /***************************************************************************************/
Если флаг needToSend установлен в 1, то передаем хосту байт 0х12. Тут вроде бы все,теперь осталось подправить обработчик транзакций типа IN:
/***************************************************************************************/ void EP1_IN_Callback (void) { if (needToSend == 1) { uint8_t tempData = 0x34; USB_SIL_Write(EP1_IN, &tempData, 1); SetEPTxValid(ENDP1); needToSend = 0; } } /***************************************************************************************/
Здесь мы отправляем еще один байт и сбрасываем наш флаг, чтобы отправка не продолжалась вечно.
Пришло время проверки нашей программы. Подключаем плату, отправляем запрос (0х55) и в ответ получаем от хоста два байта 0х12, 0x34. Все работает как мы и хотели, поэтому на этом на сегодня мы заканчиваем наши опыты!