|
STM32F4 USB CDC-класс, Вопросы по работе с USB (CDC-класс) на STM32F4 |
|
|
|
Feb 14 2014, 06:33
|
Частый гость
 
Группа: Участник
Сообщений: 90
Регистрация: 12-12-13
Пользователь №: 79 587

|
Разбираюсь с работой USB (CDC-класс) на STM32F4. У меня на плате имеется возможность связи с копмьютером как через USB (виртуальный COM-порт), так и через USART (RS-485). Для приема данных, не столь важно откуда они пришли. А вот при отправке нужно знать, куда отправлять, то есть нужно знать, подключено ли устройство по USB или нет. Как более правильно это сделать, используя драйвер USB (использую драйвер с сайта st.com)? Можно отслеживать, откуда пришли данные, можно проверить, есть ли питание от USB, наверняка можно еще кучу способов придумать, но хотелось бы через драйвер.
|
|
|
|
|
 |
Ответов
|
Feb 14 2014, 07:12
|
Местный
  
Группа: Свой
Сообщений: 480
Регистрация: 21-11-04
Пользователь №: 1 188

|
Цитата(Haamu @ Feb 14 2014, 09:33)  ...но хотелось бы через драйвер. Если я правильно понял вопрос, то: можно в функции USBD_OTG_ISR_Handler() в обработке прерывания SOF фиксировать подключение, а в обработке SUSPEND - отключение (я так делаю). Работает вполне устойчиво.
|
|
|
|
|
Feb 17 2014, 13:55
|
Частый гость
 
Группа: Участник
Сообщений: 90
Регистрация: 12-12-13
Пользователь №: 79 587

|
Цитата(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 байт). Как в таком случае определить, что данные из буфера переданны и можно подсовывать следующую порцию данных?
|
|
|
|
|
Feb 18 2014, 04:36
|
Местный
  
Группа: Свой
Сообщений: 480
Регистрация: 21-11-04
Пользователь №: 1 188

|
Значение 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.
|
|
|
|
|
Feb 18 2014, 05:40
|
Частый гость
 
Группа: Участник
Сообщений: 90
Регистрация: 12-12-13
Пользователь №: 79 587

|
Цитата(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) последних байт.
|
|
|
|
|
Feb 18 2014, 06:11
|
Местный
  
Группа: Свой
Сообщений: 480
Регистрация: 21-11-04
Пользователь №: 1 188

|
Цитата(Haamu @ Feb 18 2014, 08:40)  А откуда эти 256кБод берутся? Да, вобщем, ниоткуда... Просто это близко (с некоторым запасом) к одной из высоких (для COM порта) стандартных скорстей - 250кБод. Вы, естсственно, можете использовать другую. Касательно размера буфера - трудно сказать. Он во многом определяется тем, как прерывание IN endpoint забирает из него данные. Я использую FIFO в виде кольцевого буфера. По поводу потерь данных - попробуйте просмотреть обмен каким-нибудь сниффером (напр. Bus Hound). Многое может прясниться...
|
|
|
|
|
Feb 19 2014, 08:55
|
Местный
  
Группа: Свой
Сообщений: 480
Регистрация: 21-11-04
Пользователь №: 1 188

|
Цитата(Haamu @ Feb 19 2014, 12:15)  По какому признаку можно определить, что данные из буфера успешно отправленны и буфер готов к приему очередной порции данных? Это может сделать сам буфер, если будет чуток "умнее", напр. очередь (я сделал именно так). Можно попытаться ловить NAKи в прерываниях. Можно дожидаться подтверждения от хоста (с потерей лишних двух ms). Как больше нравится...
|
|
|
|
|
Feb 19 2014, 10:10
|
Частый гость
 
Группа: Участник
Сообщений: 90
Регистрация: 12-12-13
Пользователь №: 79 587

|
Цитата(Axel @ Feb 19 2014, 12:55)  Это может сделать сам буфер, если будет чуток "умнее", напр. очередь (я сделал именно так). А можно поподробнее, как и где Вы это реализовали?
|
|
|
|
|
Feb 19 2014, 10:43
|
Местный
  
Группа: Свой
Сообщений: 480
Регистрация: 21-11-04
Пользователь №: 1 188

|
Цитата(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 - для прерываний.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|