Jump to content

    
Sign in to follow this  
Haamu

STM32F4 USB CDC-класс

Recommended Posts

Разбираюсь с работой USB (CDC-класс) на STM32F4.

У меня на плате имеется возможность связи с копмьютером как через USB (виртуальный COM-порт), так и через USART (RS-485). Для приема данных, не столь важно откуда они пришли. А вот при отправке нужно знать, куда отправлять, то есть нужно знать, подключено ли устройство по USB или нет. Как более правильно это сделать, используя драйвер USB (использую драйвер с сайта st.com)? Можно отслеживать, откуда пришли данные, можно проверить, есть ли питание от USB, наверняка можно еще кучу способов придумать, но хотелось бы через драйвер.

Share this post


Link to post
Share on other sites
...но хотелось бы через драйвер.

 

Если я правильно понял вопрос, то: можно в функции USBD_OTG_ISR_Handler() в обработке прерывания SOF фиксировать подключение, а в обработке SUSPEND - отключение (я так делаю). Работает вполне устойчиво.

Share this post


Link to post
Share on other sites
Если я правильно понял вопрос, то: можно в функции USBD_OTG_ISR_Handler() в обработке прерывания SOF фиксировать подключение, а в обработке SUSPEND - отключение (я так делаю). Работает вполне устойчиво.

Спасибо. Попробую такой способ.

 

Появился еще один вопрос. Отправляю большой объем данных (порядка 30кБайт). В файле usbd_conf.h вот такие строки:

 #define CDC_IN_FRAME_INTERVAL          5    /* Number of frames between IN transfers */
#define APP_RX_DATA_SIZE               2048 /* Total size of IN buffer: APP_RX_DATA_SIZE*8/MAX_BAUDARATE*1000 should be > CDC_IN_FRAME_INTERVAL */

Поясните пожалуйста, почему тут заданны именно такие значения и в каких пределах можно их изменять? Можно ли сделать размер буфера равным 30кБ? На что это повлияет?

Я не трогал размер буфера, а пробую передавать данные частями, не превышающими размер буфера (передаю по 1000 байт). Как в таком случае определить, что данные из буфера переданны и можно подсовывать следующую порцию данных?

Share this post


Link to post
Share on other sites

Значение CDC_IN_FRAME_INTERVAL определяет частоту попыток драйвера передать блок данных хосту и (соответственно) размер этого блока.

Значение APP_RX_DATA_SIZE определяется махимальным количеством байт, поступающих в драйвер (изнутри) за один usb-шный квант (1ms). Для 256кБод это 320 байт = CDC_IN_FRAME_INTERVAL * 64. Естественно, для исключения потерь при передаче, размер передаваемого пакета должен быть кратен величине (CDC_IN_FRAME_INTERVAL * 64). Это уже под Вашу ответственность. Я использовал этот код для своего, более простого bulk драйвера, передаю пакеты с размером, кратным 512 байт и, соответственно, использую значение CDC_IN_FRAME_INTERVAL, равное 8.

Share this post


Link to post
Share on other sites
Для 256кБод это 320 байт = CDC_IN_FRAME_INTERVAL * 64.

А откуда эти 256кБод берутся?

размер передаваемого пакета должен быть кратен величине (CDC_IN_FRAME_INTERVAL * 64).

Значит в моем случае для кратности либо CDC_IN_FRAME_INTERVAL нужно с делать равным 8, либо изменить APP_RX_DATA_SIZE на 1920 или 2240. Я правильно понял?

Подскажите пожалуйста, как лучше сделать для моего конкретного случая, когда нужно передать до 30000 Байт, какие должны быть значения?

На данный момент, когда я передаю 2000 байт, всё передается нормально. Когда передаю больше 2000, на комп приходит только (N - 2048) последних байт.

Share this post


Link to post
Share on other sites
А откуда эти 256кБод берутся?

Да, вобщем, ниоткуда... Просто это близко (с некоторым запасом) к одной из высоких (для COM порта) стандартных скорстей - 250кБод. Вы, естсственно, можете использовать другую. Касательно размера буфера - трудно сказать. Он во многом определяется тем, как прерывание IN endpoint забирает из него данные. Я использую FIFO в виде кольцевого буфера. По поводу потерь данных - попробуйте просмотреть обмен каким-нибудь сниффером (напр. Bus Hound). Многое может прясниться...

Share this post


Link to post
Share on other sites

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

Соответственно возникает вопрос. По какому признаку можно определить, что данные из буфера успешно отправленны и буфер готов к приему очередной порции данных?

Share this post


Link to post
Share on other sites
По какому признаку можно определить, что данные из буфера успешно отправленны и буфер готов к приему очередной порции данных?

Это может сделать сам буфер, если будет чуток "умнее", напр. очередь (я сделал именно так). Можно попытаться ловить NAKи в прерываниях. Можно дожидаться подтверждения от хоста (с потерей лишних двух ms). Как больше нравится...

Share this post


Link to post
Share on other sites
Это может сделать сам буфер, если будет чуток "умнее", напр. очередь (я сделал именно так).

А можно поподробнее, как и где Вы это реализовали?

Share this post


Link to post
Share on other sites
А можно поподробнее, как и где Вы это реализовали?

#pragma once

#define USB_XCH_QUEUE_SIZE	0x3000//0xD00 
#define USB_QUEUE_CELL_SIZE 64

class CUSB_Queue
{
   public:
       uint32_t m_Data[uSB_XCH_QUEUE_SIZE >> 2];
       volatile uint32_t m_Tail;
       volatile uint32_t m_Head;
   public:
       volatile uint32_t m_NumEntries;
       CUSB_Queue() : m_Tail(0), m_Head(0), m_NumEntries(0) {};

       int32_t Queue_put(uint32_t *data);

       __attribute__( ( always_inline ) )uint32_t *Queue_puti(void)
       {
           uint32_t res = 0;

           if(m_NumEntries < (USB_XCH_QUEUE_SIZE >> 6))
           {
               res = (uint32_t)m_Data + m_Head;
               m_Head = (m_Head + USB_QUEUE_CELL_SIZE) % USB_XCH_QUEUE_SIZE;
               m_NumEntries++;
           }
           return (uint32_t *)res;
       };

       volatile int32_t Queue_get(uint32_t *inf);

       __attribute__( ( always_inline ) )uint32_t *Queue_geti(void)
       {
           uint32_t res = 0;

           if(m_NumEntries)
           {
               res = (uint32_t)m_Data + m_Tail;
               m_Tail = (m_Tail + USB_QUEUE_CELL_SIZE) % USB_XCH_QUEUE_SIZE;
               m_NumEntries--;
           }
           return (uint32_t *)res;
       };

       __attribute__( ( always_inline ) )void Queue_validate(void)
       {
           m_NumEntries++;
       };

       void  Queue_Clear(void)
       {
           m_NumEntries = 0;
           s_memset(m_Data, 0x05, USB_XCH_QUEUE_SIZE);
           m_Tail = 0;
           m_Head = 0;
       };
};

extern CUSB_Queue USB_TxQueue;
extern CUSB_Queue USB_RxQueue;

//===========================

#include "usb_queue.h"


int32_t CUSB_Queue::Queue_put(uint32_t *inf)
{
   int32_t i = 100000;
   do
   {
       uint32_t *buffer = Queue_puti(); 
       if(buffer)
       {
           s_memcpy(buffer, inf, USB_QUEUE_CELL_SIZE);
           return NO_ERR;
       }
       i--;
   }
   while(i > 0);
   return -1;
}

volatile int32_t CUSB_Queue::Queue_get(uint32_t *inf)
{
   uint32_t *buffer = Queue_geti();

   if(buffer)
   {
       s_memcpy(inf, buffer, USB_QUEUE_CELL_SIZE);
       return NO_ERR;
   }
   return -1;
}


CUSB_Queue USB_TxQueue;
CUSB_Queue USB_RxQueue;

 

Методы Queue_get и Queue_put - для основчой программы, Queue_geti и Queue_puti - для прерываний.

Share this post


Link to post
Share on other sites

Проблемму с буфером решил, данные передаются успешно, независимо от их количества. Если в двух словах, закидываю в буфер порцию данных (512, для кратности, на всякий случай), жду завершения передачи, закидываю очередную порцию данных и т.д. Возникла новая проблема, иногда происходит потеря данных. Программа на компе потправляет запрос, контроллер выполняет некоторые измерения и отправляет данные на комп. Размер запроса и количество данных в данном случае каждый раз одинаковые, 23 и 1604 байта соответственно. Вот лог обмена:

post-79587-1392815779_thumb.png

Почему-то данные приходят каждый раз разными пачками. Почему размер этих пачек каждый раз разный и никак не связан с цифрой 512?

Это ладно, вся проблема в том, что иногда приходят не все пачки данных. А самое интересное, что при следующем запросе приходят новые данные, а перед ними идут те, что потерялись при предыдущей передаче. Что интересно, число "отстваших" байт кратно 64. С чем это может быть связано, в каком месте смотреть?

Edited by Haamu

Share this post


Link to post
Share on other sites

стоит смотреть в месте где описывается стандарт.

ну например у low speed девайсов размер буфера 8 байт, а для full speed 64 байта.

 

почему решено делать обмен кратно 512 байт не очень понятно. Ибо для low speed максимальный размер на пакет 8 байт, а для full speed 1023? high speed 1024. Но это для балк и интерапт обменов, cdc вроде бы вообще ведет обмен через контрольную точку размер которой ограничен 8/64 байтами...

 

Данные не отдаются потому что теряется запрос, а почему теряется запрос?!... вы не первое поколение которое бьется с этой проблемой, все прошлые искатели сошлись что ошибки где то в недрах винды, что-то в драйвере не так, и не проходит запроса, следовательно не проходит обмена и теряются данные... как то так

Share this post


Link to post
Share on other sites
Проблемму с буфером решил, данные передаются успешно, независимо от их количества. Если в двух словах, закидываю в буфер порцию данных (512, для кратности, на всякий случай), жду завершения передачи

 

Поделитесь пожалуйста кодом как вы определяете завершение передачи?

 

вся проблема в том, что иногда приходят не все пачки данных. А самое интересное, что при следующем запросе приходят новые данные, а перед ними идут те, что потерялись при предыдущей передаче. Что интересно, число "отстваших" байт кратно 64. С чем это может быть связано, в каком месте смотреть?

 

Имею аналогичную проблему... удалось ли вам ее решить?

Share this post


Link to post
Share on other sites
Поделитесь пожалуйста кодом как вы определяете завершение передачи?

Имею аналогичную проблему... удалось ли вам ее решить?

    while(1)
    {
        if (send_data_over_usb) {
        uint8_t i;
        for (i = 0; i < usb_tx_packets; i ++) {
            DWTreset;
            USB_DataTx(&sensors_data[usb_tx_packet_size / 2 * i], usb_tx_packet_size);
            while (tx_data_senging) {}
        }
        USB_DataTx(&sensors_data[usb_tx_packet_size / 2 * usb_tx_packets], usb_tx_tail);
        while (tx_data_senging) {}
        send_data_over_usb = 0;
        }
    }

Вся основная программа работает в прерываниях, а в основной цикл запихал такую вот конструкцию. Когда данные готовы к отправке, определяю количество пакетов (usb_tx_packets) и размер последнего неполного пакета (usb_tx_tail), устанавливаю флаг старта передачи данных (send_data_over_usb).

void USB_DataTx(uint8_t* Buf, uint32_t Len)
{
    VCP_DataTx((uint8_t*)Buf, Len);
    tx_data_senging = 1;
}

Тут устанавливается флаг начала передачи пакета (tx_data_senging).

static uint8_t  usbd_cdc_DataIn (void *pdev, uint8_t epnum)
{
  uint16_t USB_Tx_ptr;
  uint16_t USB_Tx_length;

  if (USB_Tx_State == 1)
  {
    if (APP_Rx_length == 0) 
    {
      USB_Tx_State = 0;
      tx_data_senging = 0;
    }
    else 
    {
      if (APP_Rx_length > CDC_DATA_IN_PACKET_SIZE){
        USB_Tx_ptr = APP_Rx_ptr_out;
        USB_Tx_length = CDC_DATA_IN_PACKET_SIZE;
        
        APP_Rx_ptr_out += CDC_DATA_IN_PACKET_SIZE;
        APP_Rx_length -= CDC_DATA_IN_PACKET_SIZE;    
      }
      else 
      {
        USB_Tx_ptr = APP_Rx_ptr_out;
        USB_Tx_length = APP_Rx_length;
        
        APP_Rx_ptr_out += APP_Rx_length;
        APP_Rx_length = 0;
      }
      
      /* Prepare the available data buffer to be sent on IN endpoint */
      DCD_EP_Tx (pdev,
                 CDC_IN_EP,
                 (uint8_t*)&APP_Rx_Buffer[USB_Tx_ptr],
                 USB_Tx_length);
    }
  }  
  
  return USBD_OK;
}

Затем в недрах драйвера в usbd_cdc_core.c, в завершении передачи пакета, этот флаг сбрасывается.

Запутано немного получилось, но зато работает.

Проблему с неполной передачей пакетов не решил.

Edited by Haamu

Share this post


Link to post
Share on other sites

В дескрипторе конфигурации USB-устройства есть такой параметр, как максимальный потребляемый ток от шины USB. Ткните пожалуйста пальцем, где в STMовском стеке этот параметр настраивается?

 

Сам же и отвечу. Описание дескрипторов конфигурации находится в файле usbd_cdc_core.c, примерно с 216 строки. Там в комментах всё описано.

 

Появился новый вопрос. cdc-класс как-то ограничивает максимальное потребление тока устройством? Ни что не мешает мне установить потребляемый ток в 500мА?

Edited by Haamu

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this