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;
 
	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;
 
	tempData = 0x34;
 
	USB_SIL_Write(EP1_IN, &tempData, 1);
	SetEPTxValid(ENDP1); 
        needToSend = 0;
    }
}
 
 
 
/*******************************************************************************/

Здесь мы отправляем еще один байт и сбрасываем наш флаг, чтобы отправка не продолжалась вечно )

Пришло время проверки нашей программы. Подключаем плату, отправляем запрос (0х55) и в ответ получаем от хоста два байта 0х1234! Все работает как мы и хотели, поэтому на этом на сегодня мы заканчиваем наши опыты =)

Понравилась статья? Поделись с друзьями!

STM32. Передача данных по USB.: 17 комментариев
  1. Спасибо, самому было бы не разобраться. А теперь все работает и есть желание изучить работу USB подробнее.

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

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

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

    • Я уже честно говоря не помню, что там было в проектах, но оба рабочие, абсолютно точно проверял

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

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

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

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

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

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *