Top.Mail.Ru

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

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

Подписаться
Уведомить о
guest

17 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
kirill
kirill
9 лет назад

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

niza93
niza93
9 лет назад

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

niza93
niza93
9 лет назад

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

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

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

Embedder
Embedder
Ответ на комментарий  Aveal
9 лет назад

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

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

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

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

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

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

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

Salavat
Salavat
9 лет назад

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

Salavat
Salavat
9 лет назад

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

Salavat
Salavat
9 лет назад

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

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

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

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

Разбирался с отправкой данных. Нужна была максимальная скорость. Делюсь опытом. Первый вариант как в статье все работает. Дальше если увеличивать количество отправленных байт начинается белиберда.
Работает нормально так :
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).
Если кому-то удалось выжать на передачу больше, поделитесь пожалуйста

17
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x