STM32. Передача данных по USB.

Всем доброго дня!

Возвращаемся к экспериментам с микроконтроллерами STM32 и сегодня продолжим обсуждать связь контроллера и компьютера при помощи интерфейса USB. Раньше мы разобрались со стандартом USB в целом, а также с приемом данных от хоста (ПК), вот ссылки на эти статьи: раз и два. Так вот, теперь пришло время разобраться с передачей данных в обратном направлении, то есть от USB-устройства (в нашем случае это отладочная плата STM32F3Discovery) к хосту.

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

Мы собираемся передавать данные хосту, поэтому нас сегодня интересуют в первую очередь транзакции типа 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х1234. Все работает как мы и хотели, поэтому на этом на сегодня мы заканчиваем наши опыты!

Поделиться!

Подписаться
Уведомление о
guest
17 Комментарий
старее
новее большинство голосов
Inline Feedbacks
View all comments
kirill
kirill
6 лет назад

Спасибо, самому было бы не разобраться. А теперь все работает и есть желание изучить работу USB подробнее.

niza93
niza93
6 лет назад

Скажите, а как можно передать массив длиннее, чем 64 байта??

niza93
niza93
6 лет назад

Все разобрался, надо было отправлять 200. отправил 4 раза по 50)

Марина
Марина
5 лет назад

поясните , пожалуйста, такой момент. Насколько понимаю, получается, что та инфа, которая передается в комп состоит из двух кусков: один выдается по обработке SOF -прерывания, в функции void Handle_USBAsynchXfer (void) ,
А другая часть – в EP1_IN_Callback.
Вот если бы у Вас было не два, а десять байтов на отправку, откуда их нужно выпихивать:: из SOF или из EP1_IN?

Embedder
Embedder
Reply to  Aveal
5 лет назад

Мне тоже не совсем понятно, зачем второй байт данных отправлять в EP1_IN_Callback, если их сразу можно отправить в SOF_Callback ?

Илья
Илья
5 лет назад

Здравствуйте! А можно ли этот проект приспособить под USB-HID?

Илья
Илья
5 лет назад

Пробовал по их примерам делать, но всё летит в ошибку(даже не компилируется). На буржуйских сайтах писали, мол проекты сделаны под старый кейл, и в новом плохо работают. Так ничего завести не удалось 🙁 .
А за статьи огромное Спасибо!! По ним и начал осваивать STM32 🙂

Андрей
Андрей
5 лет назад

Функция void Handle_USBAsynchXfer (void) из проекта “USB” из предыдущей статьи https://microtechnics.ru/mikrokontroller-stm32-i-usb/ содержала код регулирующий параметры COM порта. В этом проекте его в функции нет. Так и задумано и будет ли оно без него работать?

Salavat
Salavat
5 лет назад

Я так понял, что обработчик SOF_Callback() не надо постоянно вызывать для отправки серий данных (например, для опроса датчика на плате stm32f3discovery по прерыванию таймера) хватит и одного раза. Или это не так?

Salavat
Salavat
5 лет назад

И ещё один момент: при попытке считать значение с датчика гироскопа на плате stm32f3discovery ничего не выводится на терминал, хотя обычный ваш пример работает. Проверял осциллографом сигналы есть с датчика, а вот данные почему то не выводятся. Обычный пример с опросом датчика без библиотеки Usb работает нормально, но вот приспособить пример под ваш проект не получается.

Salavat
Salavat
5 лет назад

Разобрался. Оказывается не обязательно использовать обработчик SOF_Callback() (в нём задаётся интервал для отправки данных) и ,соответственно, Handle_USBAsynchXfer (void); я просто в обработчике по прерыванию по таймеру отправлял данные через usb_sil_write() seteptxvalid(endp1). Теперь всё работает !

Илья
Илья
4 лет назад

Здравствуйте! Спасибо за Ваши статьи, они очень помогли мне с STM32 🙂 . Имеется вопрос по USB. Почему две передачи разнесены по разным функциям? Читаю по форумам, но так и не могу понять откуда лучше отправлять данные….

Олег
Олег
3 лет назад

Разбирался с отправкой данных. Нужна была максимальная скорость. Делюсь опытом. Первый вариант как в статье все работает. Дальше если увеличивать количество отправленных байт начинается белиберда.
Работает нормально так :
1. Все отправляем через функцию Handle_USBAsynchXfer Flag_TX =1;.
можно прямо , но я в ней оставил только установщик флага
Flag_TX =1; Все остальное убрал.
2. размер отправки в функции USB_SIL_Write(EP1_IN, &Tx, 63);
максимум 63 байта, если больше то вообще ничего не шлет.
3.В Мэйна через вайл постоянно в цикле проверяю Flag_TX =1
если =1 отправляю данные и сразу сбрасываю флаг.
4. по умолчанию частота отправки = 6 мс. Что бы было =1мс меняем #define VCOMPORT_IN_FRAME_INTERVAL 0 на ноль,
5 . Получаем скорость 1мс * 63 байта = 63 кб.с или 63 *8=0.504 мегабита в секунду.
Если пытаться всунуть в один пакет два куска, например 50 и 2 байта то начинается белиберда
если пытаться отправить байт из void EP1_IN_Callback (void),не отравив перед этим из Handle_USBAsynchXfer , ничего не уйдет. Вывод- шлем все по пакетам не более 63 байта через SOF(Handle_USBAsynchXfer).
Если кому-то удалось выжать на передачу больше, поделитесь пожалуйста

Присоединяйтесь!

Profile Profile Profile Profile Profile
Vkontakte
Twitter

Язык сайта

Август 2020
Пн Вт Ср Чт Пт Сб Вс
 12
3456789
10111213141516
17181920212223
24252627282930
31  

© 2013-2020 MicroTechnics.ru