Перейти к содержанию
    

USB CDC: Как "притормозить" приём данных

Добрый день,

реализуем YModem поверх USB CDC. Тестируем под Win7 с помощью TeraTerm.

Как только начинается передача пакетов данных от ПК (TeraTerm) в STM32, пакеты данных (по 512) прилетают с такой скоростью, что забивают кольцевой буфер под завязку.
Посмотрели более детально, TeraTerm (Ymodem TX) повторяет пакеты до тех пор, пока STM32 не пришлёт ACK (тогда шлёт следующий кусок данных).

Возник вопрос - как "притормозить" приходящие пакеты? Мы рассматривали вариант отключать прерывания USB:

hpcd_USB_OTG_HS.Instance->GAHBCFG &= ~USB_OTG_GAHBCFG_GINT;

Но это как-то жестоко, хоть и работает.

Второй вариант который мы рассматривали - это Flow Control, но отбросили - не всегда его конечный клиент использует, да и потом без него тоже система должна корректно отрабатывать.

Обработчик который сейчас реализован:

static int8_t CDC_Receive_HS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 11 */
  USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceHS.pClassData;
  if (hcdc)
  {
    USBD_CDC_SetRxBuffer(&hUsbDeviceHS, Buf);
    USBD_CDC_ReceivePacket(&hUsbDeviceHS);

    if (bs_usb_rx_command(Buf, hcdc->RxLength) == 0)
    {
        return (USBD_BUSY);
    }
  }
  return (USBD_OK);
  /* USER CODE END 11 */
}

Если функция bs_usb_rx_command не может записать в кольцевой буфер (он полон) данные - возвращаем USBD_BUSY (по сути теряем данные). Но следующие тут же прилетают.

Поделитесь, пожалуйста, опытом, как вы решали подобную задачу. Спасибо!

MCU: STM32H743XIH
STM32CubeIDE Version: 1.0.2
 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 минут назад, SimpleSoft сказал:

Поделитесь, пожалуйста, опытом, как вы решали подобную задачу. Спасибо!

Написать парсер выделяющий границы кадров прям в этом обработчике приёма (CDC_Receive_HS()). Выделил кадр - записал его в буфер (послал на обработку задаче обработчика) и взвёл флаг - "занято".

Когда задача-обработчик полностью вычитает кадр из буфера - она снимет флаг. Пока флаг стоит - обработчик приёма должен кадры пропускать (но всё равно сначала выделять их границы! - и только целиком пропускать). 

Т.е. - говоря грамотным языком: Вам надо процесс приёма потока кадров разбить на уровни (канальный, прикладной, ...). Канальный выполнять в обработчике приёма, прикладной - в фоновой задаче.

 

PS: Я не читал описание протокола YModem - ACK там является признаком чего? Просто признаком приёма кадра (вне зависимости от его содержимого) или признаком что удалённая сторона обработала данные из этого кадра и готова принять ещё? Если первое - то ACK должен генерить обработчик приёма сразу по завершении обнаружения границ кадра (ну или если там есть CRC - после обнаружения границ и проверки CRC).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

52 минуты назад, SimpleSoft сказал:

Возник вопрос - как "притормозить" приходящие пакеты? Мы рассматривали вариант отключать прерывания USB:

Почему? Вот почему вы не прочитали хотя бы какую-нибудь статью вроде "USB на пальцах"? Там, ближе к началу, обязательно должно быть сказано, что если конечная точка не готова принимать данные, она должна отвечать NAK. Все остальное будет решаться на уровне контроллера.

Изменено пользователем Сергей Борщ

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

51 minutes ago, SimpleSoft said:

Возник вопрос - как "притормозить" приходящие пакеты?

Просто не читать, чтобы USB device возвращал хосту NAK.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

11 минут назад, Сергей Борщ сказал:

Почему? Вот почему вы не прочитали хотя бы какую-нибудь статью вроде "USB на пальцах"? Там, ближе к началу, обязательно должно быть сказано, что если конечная точка не готова принимать данные, она должна отвечать NAK. Все остальное будет решаться на уровне контроллера.

А если кадры YModema-а не будут совпадать с USB-кадрами? Т.е. - комп пошлёт 2 кадра YModem-а "впритык" друг к другу не дожидаясь ACK.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, jcxz said:

А если кадры YModema-а не будут совпадать с USB-кадрами? Т.е. - комп пошлёт 2 кадра YModem-а "впритык" друг к другу не дожидаясь ACK.

И каким боком это помешает управлению потоком на уровне USB?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

11 часов назад, jcxz сказал:

А если кадры YModema-а не будут совпадать с USB-кадрами?

А если YModem передается через RS232, где вообще кадров нет? А если этот RS232 с "железным" управлением потоком и приемная сторона дернет RTS?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 часов назад, aaarrr сказал:

И каким боком это помешает управлению потоком на уровне USB?

Управлению потоком - никак, но вот парсингу на кадры - запросто может помешать. Так как будут приходить обрывки кадров.

36 минут назад, Сергей Борщ сказал:

А если YModem передается через RS232, где вообще кадров нет? А если этот RS232 с "железным" управлением потоком и приемная сторона дернет RTS?

Т.е. Вы хотите сказать, что по NAK-у USB-хост будет просто повторять данные позже?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 minutes ago, jcxz said:

Т.е. Вы хотите сказать, что по NAK-у USB-хост будет просто повторять данные позже?

Разумеется.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 минут назад, jcxz сказал:

Т.е. Вы хотите сказать, что по NAK-у USB-хост будет просто повторять данные позже?

Если мне не изменяет память, вы хвастались тут самописным стеком USB. Неужели с момента его написания все забылось напрочь?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

54 минуты назад, Сергей Борщ сказал:

Если мне не изменяет память, вы хвастались тут самописным стеком USB. Неужели с момента его написания все забылось напрочь?

Причём тут? Я его не использовал для CDC.

Да и не хвастался я самописным. Брал его из примеров IAR. Память точно изменяет... :smile:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

14 minutes ago, jcxz said:

Причём тут? Я его не использовал для CDC.

Так это общий механизм USB, причем тут CDC?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо всем за ответы.

18 hours ago, Сергей Борщ said:

Почему? Вот почему вы не прочитали хотя бы какую-нибудь статью вроде "USB на пальцах"?

Мы хорошо разбираемся в USB, мой вопрос был про опыт решения на STM32. 

По поводу не читать USB RX - мы не пробовали, но возник вопрос - а не будет ли STM32 дергать постоянно прерывание о приёме данных? (в Tech Ref не копались) Может кто-то знает как OTG в STM32H7 обрабатывает RX data interrupt?

Изменено пользователем SimpleSoft

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 8/5/2019 at 6:59 PM, SimpleSoft said:

Мы хорошо разбираемся в USB

Не похоже, извините.

On 8/5/2019 at 6:59 PM, SimpleSoft said:

мой вопрос был про опыт решения на STM32

Контроллер позволяет отправлять и ACK, и NAK. То, что это не позволяет сделать кубохал (кажется. Детально не исследовал) - не проблема контроллера.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 8/4/2019 at 11:55 PM, SimpleSoft said:

Поделитесь, пожалуйста, опытом, как вы решали подобную задачу. Спасибо!

Это делается созданием дополнительной задачи. Она только читает из USB и отправляет указатели на полученные пакеты в очередь.

Просто запомните на будущее - все зависимые друг от друга процессы обрабатывающие данные с разной скоростью используют для обмена между собой очереди.
Если у вас нет вытесняющей RTOS, то такую очередь делать довольно хлопотно. 

А если есть RTOS то все проще.  Вместе с задачей  Ymodem надо создать параллельную задачу чтения из USB. 
Вот как такая задача выглядит в RTOS ThreadX:

Spoiler

static void Mb_usbfs_rcv_task(ULONG ptr)
{
  UINT              res;
  ULONG             actual_length;
  ULONG             actual_flags;
  T_usbfs_drv_cbl * p = (T_usbfs_drv_cbl *)ptr;
  uint32_t          n;

  do
  {
    if (p->active)
    {
      n = p->head_n;
      res = ux_device_class_cdc_acm_read(p->cdc, p->rd_pack[n].buff,USBDRV_BUFFER_MAX_LENGTH,  &actual_length); // Чтение пакета из USB
      p->rd_pack[n].len = actual_length;
      if (res == UX_SUCCESS)
      {
        // Перемещаем указатель головы очереди
        n++; 
        if (n >= IN_BUF_QUANTITY) n = 0;
        p->head_n = n;

        // Выставляем флаг выполненного чтения
        if (tx_event_flags_set(&(p->evt), MB_USBFS_READ_DONE, TX_OR) != TX_SUCCESS)
        {
          tx_thread_sleep(2); // Задержка после ошибки 
        }

        // Если все буферы на прием заполнены, то значит системе не требуются данные
        if (p->tail_n == n)
        {
          // Перестаем принимать данные из USB и ждем когда система обработает уже принятые данные и подаст сигнал к началу приема по USB
          p->no_space = 1;
          if (tx_event_flags_get(&(p->evt), MB_USBFS_READ_REQUEST, TX_AND_CLEAR,&actual_flags, TX_WAIT_FOREVER) != TX_SUCCESS)
          {
            tx_thread_sleep(2); // Задержка после ошибки 
          }
        }
      }
      else
      {
        tx_thread_sleep(2); // Задержка после ошибки
      }
    }
    else
    {
      tx_thread_sleep(2); // Задержки после ошибки нужны для того чтобы задача не захватила все ресурсы в случает постоянного появления ошибки
    }

  } while (1);
}

 

Т.е. здесь задача чтения из USB при заполненной очереди перестает вызывать функцию получения пакетов, а зависает на ожидании флага от задачи  Ymodem-а
Заполненность очереди определяется просто по равенству указателей на хвост (p->tail_n) и на голову очереди (p->head_n).

И очередь здесь не очередь байт, а очередь буферов.
В простейшем случае  очередь состоит из двух буферов.  

Контроллеры USB не реагируют прерываниями на запросы хоста к пустым точкам доступа, тут можно быть спокойным. 

Изменено пользователем AlexandrY

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...