Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: STM32F4 USB CDC-класс
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Haamu
Разбираюсь с работой USB (CDC-класс) на STM32F4.
У меня на плате имеется возможность связи с копмьютером как через USB (виртуальный COM-порт), так и через USART (RS-485). Для приема данных, не столь важно откуда они пришли. А вот при отправке нужно знать, куда отправлять, то есть нужно знать, подключено ли устройство по USB или нет. Как более правильно это сделать, используя драйвер USB (использую драйвер с сайта st.com)? Можно отслеживать, откуда пришли данные, можно проверить, есть ли питание от USB, наверняка можно еще кучу способов придумать, но хотелось бы через драйвер.
Axel
Цитата(Haamu @ Feb 14 2014, 09:33) *
...но хотелось бы через драйвер.


Если я правильно понял вопрос, то: можно в функции USBD_OTG_ISR_Handler() в обработке прерывания SOF фиксировать подключение, а в обработке SUSPEND - отключение (я так делаю). Работает вполне устойчиво.
Haamu
Цитата(Axel @ Feb 14 2014, 11:12) *
Если я правильно понял вопрос, то: можно в функции 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 байт). Как в таком случае определить, что данные из буфера переданны и можно подсовывать следующую порцию данных?
Axel
Значение 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.
Haamu
Цитата(Axel @ Feb 18 2014, 08:36) *
Для 256кБод это 320 байт = CDC_IN_FRAME_INTERVAL * 64.

А откуда эти 256кБод берутся?
Цитата(Axel @ Feb 18 2014, 08:36) *
размер передаваемого пакета должен быть кратен величине (CDC_IN_FRAME_INTERVAL * 64).

Значит в моем случае для кратности либо CDC_IN_FRAME_INTERVAL нужно с делать равным 8, либо изменить APP_RX_DATA_SIZE на 1920 или 2240. Я правильно понял?
Подскажите пожалуйста, как лучше сделать для моего конкретного случая, когда нужно передать до 30000 Байт, какие должны быть значения?
На данный момент, когда я передаю 2000 байт, всё передается нормально. Когда передаю больше 2000, на комп приходит только (N - 2048) последних байт.
Axel
Цитата(Haamu @ Feb 18 2014, 08:40) *
А откуда эти 256кБод берутся?

Да, вобщем, ниоткуда... Просто это близко (с некоторым запасом) к одной из высоких (для COM порта) стандартных скорстей - 250кБод. Вы, естсственно, можете использовать другую. Касательно размера буфера - трудно сказать. Он во многом определяется тем, как прерывание IN endpoint забирает из него данные. Я использую FIFO в виде кольцевого буфера. По поводу потерь данных - попробуйте просмотреть обмен каким-нибудь сниффером (напр. Bus Hound). Многое может прясниться...
Haamu
Понял свою ошибку. Я накидываю кучу данных в буфер раньше, чем успевает что-то отправиться, соответственно данные затерают друг-друга и остаются только хвост, который приходит последним, он же и отправляется на комп.
Соответственно возникает вопрос. По какому признаку можно определить, что данные из буфера успешно отправленны и буфер готов к приему очередной порции данных?
Axel
Цитата(Haamu @ Feb 19 2014, 12:15) *
По какому признаку можно определить, что данные из буфера успешно отправленны и буфер готов к приему очередной порции данных?

Это может сделать сам буфер, если будет чуток "умнее", напр. очередь (я сделал именно так). Можно попытаться ловить NAKи в прерываниях. Можно дожидаться подтверждения от хоста (с потерей лишних двух ms). Как больше нравится...
Haamu
Цитата(Axel @ Feb 19 2014, 12:55) *
Это может сделать сам буфер, если будет чуток "умнее", напр. очередь (я сделал именно так).

А можно поподробнее, как и где Вы это реализовали?
Axel
Цитата(Haamu @ Feb 19 2014, 14:10) *
А можно поподробнее, как и где Вы это реализовали?

CODE
#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 - для прерываний.
Haamu
Проблемму с буфером решил, данные передаются успешно, независимо от их количества. Если в двух словах, закидываю в буфер порцию данных (512, для кратности, на всякий случай), жду завершения передачи, закидываю очередную порцию данных и т.д. Возникла новая проблема, иногда происходит потеря данных. Программа на компе потправляет запрос, контроллер выполняет некоторые измерения и отправляет данные на комп. Размер запроса и количество данных в данном случае каждый раз одинаковые, 23 и 1604 байта соответственно. Вот лог обмена:
Нажмите для просмотра прикрепленного файла
Почему-то данные приходят каждый раз разными пачками. Почему размер этих пачек каждый раз разный и никак не связан с цифрой 512?
Это ладно, вся проблема в том, что иногда приходят не все пачки данных. А самое интересное, что при следующем запросе приходят новые данные, а перед ними идут те, что потерялись при предыдущей передаче. Что интересно, число "отстваших" байт кратно 64. С чем это может быть связано, в каком месте смотреть?
Golikov A.
стоит смотреть в месте где описывается стандарт.
ну например у low speed девайсов размер буфера 8 байт, а для full speed 64 байта.

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

Данные не отдаются потому что теряется запрос, а почему теряется запрос?!... вы не первое поколение которое бьется с этой проблемой, все прошлые искатели сошлись что ошибки где то в недрах винды, что-то в драйвере не так, и не проходит запроса, следовательно не проходит обмена и теряются данные... как то так
js_slider
Цитата(Haamu @ Feb 19 2014, 21:32) *
Проблемму с буфером решил, данные передаются успешно, независимо от их количества. Если в двух словах, закидываю в буфер порцию данных (512, для кратности, на всякий случай), жду завершения передачи


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

Цитата(Haamu @ Feb 19 2014, 21:32) *
вся проблема в том, что иногда приходят не все пачки данных. А самое интересное, что при следующем запросе приходят новые данные, а перед ними идут те, что потерялись при предыдущей передаче. Что интересно, число "отстваших" байт кратно 64. С чем это может быть связано, в каком месте смотреть?


Имею аналогичную проблему... удалось ли вам ее решить?
Haamu
Цитата(js_slider @ May 20 2014, 11:37) *
Поделитесь пожалуйста кодом как вы определяете завершение передачи?
Имею аналогичную проблему... удалось ли вам ее решить?

Код
    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, в завершении передачи пакета, этот флаг сбрасывается.
Запутано немного получилось, но зато работает.
Проблему с неполной передачей пакетов не решил.
Haamu
В дескрипторе конфигурации USB-устройства есть такой параметр, как максимальный потребляемый ток от шины USB. Ткните пожалуйста пальцем, где в STMовском стеке этот параметр настраивается?

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

Появился новый вопрос. cdc-класс как-то ограничивает максимальное потребление тока устройством? Ни что не мешает мне установить потребляемый ток в 500мА?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.