Haamu 0 Posted February 14, 2014 · Report post Разбираюсь с работой USB (CDC-класс) на STM32F4. У меня на плате имеется возможность связи с копмьютером как через USB (виртуальный COM-порт), так и через USART (RS-485). Для приема данных, не столь важно откуда они пришли. А вот при отправке нужно знать, куда отправлять, то есть нужно знать, подключено ли устройство по USB или нет. Как более правильно это сделать, используя драйвер USB (использую драйвер с сайта st.com)? Можно отслеживать, откуда пришли данные, можно проверить, есть ли питание от USB, наверняка можно еще кучу способов придумать, но хотелось бы через драйвер. Quote Ответить с цитированием Share this post Link to post Share on other sites
Axel 0 Posted February 14, 2014 · Report post ...но хотелось бы через драйвер. Если я правильно понял вопрос, то: можно в функции USBD_OTG_ISR_Handler() в обработке прерывания SOF фиксировать подключение, а в обработке SUSPEND - отключение (я так делаю). Работает вполне устойчиво. Quote Ответить с цитированием Share this post Link to post Share on other sites
Haamu 0 Posted February 17, 2014 · Report post Если я правильно понял вопрос, то: можно в функции 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 байт). Как в таком случае определить, что данные из буфера переданны и можно подсовывать следующую порцию данных? Quote Ответить с цитированием Share this post Link to post Share on other sites
Axel 0 Posted February 18, 2014 · Report post Значение 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. Quote Ответить с цитированием Share this post Link to post Share on other sites
Haamu 0 Posted February 18, 2014 · Report post Для 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) последних байт. Quote Ответить с цитированием Share this post Link to post Share on other sites
Axel 0 Posted February 18, 2014 · Report post А откуда эти 256кБод берутся? Да, вобщем, ниоткуда... Просто это близко (с некоторым запасом) к одной из высоких (для COM порта) стандартных скорстей - 250кБод. Вы, естсственно, можете использовать другую. Касательно размера буфера - трудно сказать. Он во многом определяется тем, как прерывание IN endpoint забирает из него данные. Я использую FIFO в виде кольцевого буфера. По поводу потерь данных - попробуйте просмотреть обмен каким-нибудь сниффером (напр. Bus Hound). Многое может прясниться... Quote Ответить с цитированием Share this post Link to post Share on other sites
Haamu 0 Posted February 19, 2014 · Report post Понял свою ошибку. Я накидываю кучу данных в буфер раньше, чем успевает что-то отправиться, соответственно данные затерают друг-друга и остаются только хвост, который приходит последним, он же и отправляется на комп. Соответственно возникает вопрос. По какому признаку можно определить, что данные из буфера успешно отправленны и буфер готов к приему очередной порции данных? Quote Ответить с цитированием Share this post Link to post Share on other sites
Axel 0 Posted February 19, 2014 · Report post По какому признаку можно определить, что данные из буфера успешно отправленны и буфер готов к приему очередной порции данных? Это может сделать сам буфер, если будет чуток "умнее", напр. очередь (я сделал именно так). Можно попытаться ловить NAKи в прерываниях. Можно дожидаться подтверждения от хоста (с потерей лишних двух ms). Как больше нравится... Quote Ответить с цитированием Share this post Link to post Share on other sites
Haamu 0 Posted February 19, 2014 · Report post Это может сделать сам буфер, если будет чуток "умнее", напр. очередь (я сделал именно так). А можно поподробнее, как и где Вы это реализовали? Quote Ответить с цитированием Share this post Link to post Share on other sites
Axel 0 Posted February 19, 2014 · Report post А можно поподробнее, как и где Вы это реализовали? #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 - для прерываний. Quote Ответить с цитированием Share this post Link to post Share on other sites
Haamu 0 Posted February 19, 2014 (edited) · Report post Проблемму с буфером решил, данные передаются успешно, независимо от их количества. Если в двух словах, закидываю в буфер порцию данных (512, для кратности, на всякий случай), жду завершения передачи, закидываю очередную порцию данных и т.д. Возникла новая проблема, иногда происходит потеря данных. Программа на компе потправляет запрос, контроллер выполняет некоторые измерения и отправляет данные на комп. Размер запроса и количество данных в данном случае каждый раз одинаковые, 23 и 1604 байта соответственно. Вот лог обмена: Почему-то данные приходят каждый раз разными пачками. Почему размер этих пачек каждый раз разный и никак не связан с цифрой 512? Это ладно, вся проблема в том, что иногда приходят не все пачки данных. А самое интересное, что при следующем запросе приходят новые данные, а перед ними идут те, что потерялись при предыдущей передаче. Что интересно, число "отстваших" байт кратно 64. С чем это может быть связано, в каком месте смотреть? Edited February 19, 2014 by Haamu Quote Ответить с цитированием Share this post Link to post Share on other sites
Golikov 0 Posted February 19, 2014 · Report post стоит смотреть в месте где описывается стандарт. ну например у low speed девайсов размер буфера 8 байт, а для full speed 64 байта. почему решено делать обмен кратно 512 байт не очень понятно. Ибо для low speed максимальный размер на пакет 8 байт, а для full speed 1023? high speed 1024. Но это для балк и интерапт обменов, cdc вроде бы вообще ведет обмен через контрольную точку размер которой ограничен 8/64 байтами... Данные не отдаются потому что теряется запрос, а почему теряется запрос?!... вы не первое поколение которое бьется с этой проблемой, все прошлые искатели сошлись что ошибки где то в недрах винды, что-то в драйвере не так, и не проходит запроса, следовательно не проходит обмена и теряются данные... как то так Quote Ответить с цитированием Share this post Link to post Share on other sites
js_slider 0 Posted May 20, 2014 · Report post Проблемму с буфером решил, данные передаются успешно, независимо от их количества. Если в двух словах, закидываю в буфер порцию данных (512, для кратности, на всякий случай), жду завершения передачи Поделитесь пожалуйста кодом как вы определяете завершение передачи? вся проблема в том, что иногда приходят не все пачки данных. А самое интересное, что при следующем запросе приходят новые данные, а перед ними идут те, что потерялись при предыдущей передаче. Что интересно, число "отстваших" байт кратно 64. С чем это может быть связано, в каком месте смотреть? Имею аналогичную проблему... удалось ли вам ее решить? Quote Ответить с цитированием Share this post Link to post Share on other sites
Haamu 0 Posted May 22, 2014 (edited) · Report post Поделитесь пожалуйста кодом как вы определяете завершение передачи? Имею аналогичную проблему... удалось ли вам ее решить? 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 May 22, 2014 by Haamu Quote Ответить с цитированием Share this post Link to post Share on other sites
Haamu 0 Posted August 18, 2014 (edited) · Report post В дескрипторе конфигурации USB-устройства есть такой параметр, как максимальный потребляемый ток от шины USB. Ткните пожалуйста пальцем, где в STMовском стеке этот параметр настраивается? Сам же и отвечу. Описание дескрипторов конфигурации находится в файле usbd_cdc_core.c, примерно с 216 строки. Там в комментах всё описано. Появился новый вопрос. cdc-класс как-то ограничивает максимальное потребление тока устройством? Ни что не мешает мне установить потребляемый ток в 500мА? Edited August 19, 2014 by Haamu Quote Ответить с цитированием Share this post Link to post Share on other sites