makc 192 22 февраля, 2017 Опубликовано 22 февраля, 2017 · Жалоба Имеется кит на базе STM32F072 (на STM32F042 те же проблемы), на котором отлаживается прошивка USB-device с двумя bulk endpoint'ами (in/out). В режиме single-buffer все работает штатно: передача идет только при установке RX_VALID/TX_VALID в соответствующих битах регистров USB_EPnR. Переключение управляющих битов в этих регистрах происходит в соответствии с их описанием в RM0091 (toggling). После успешной отладки в этом режиме перешли к использованию режима double-buffer и с ним начались неожиданные проблемы: при первой передаче по Bulk-in endpoint происходит неконтролируемая отправка второго пакета. Суть эксперимента в следующем: 1. устройство подключается к хосту, успешно проходит энумерацию, чтение дескрипторов и т.п. (lsusb -vvv показывает все правильно); 2. в устройстве активируется bulk-in endpoint в режиме double buffer и SW_BUF/DTOG_TX биты ставятся в положение 1/0, настраиваются адреса буферов для передачи в памяти пакетов (два буфера), данные для передачи записываются в область памяти первого буфера и его счетчик передачи устанавливается равным 60. При этом второй буфер в памяти пакетов заполняется характерной сигнатурой и его размер устанавливается равным 11 (это несущественно). Статус endpoint устанавливается равным TX_VALID; 3. на хосте программа в цикле с помощью libusb пытается выполнить передачи блоков данных размером 64 байта (чтение из устройства, bulk in); 4. запускаем программу и получаем два буфера данных, вместо одного (60 и 11 байт). При этом второй буфер передается несмотря на то, что после передачи первого буфера SW_BUF/DTOG_TX == 11, т.е. согласно описанию передача должна приостановиться до момента, когда SW_BUF/DTOG_TX станут равны 01 соответственно. Получается, что контроллер отправляет второй буфер "авансом", несмотря на состояние специальных разрядов управления потоком передачи. На больших блоках данных, например, при работе с mass storage это незаметно, т.к. второй буфер используется сразу "по делу" и эта паразитная передача не мешает. Однако если, например, выполнять поллинг состояния устройства с помощью bulk-in endpoint, то второй пакет будет содержать неожиданные данные, которые не были еще сформированы программой в микроконтроллере. Интересно то, что после еще одной отправки, т.е. когда состояние SW_BUF/DTOG в вышеописанном примере перейдет из 01 в 00, передача честно остановится, контроллер будет отправлять NAK при попытке передачи данных через bulk-in и описанный в документации механизм flow control начинает работать согласно описанию. Но это не дает ответа, что делать со вторым блоком данных, который отправляется вопреки всему. В errata на этот счет никаких данных нет. В STM32CubeMX драйвер HAL PCD реализован из рук вон плохо в области работы с double-buffered endpoint и поэтому как источник информации о правильном методе работы в этом режиме служить не может. В других открытых библиотеках этого режима нет. С другой стороны опыт показывает, что разница в скорости передачи между single-buffered bulk-in endpoint и double-buffered не превышает 10% по нашим замерам (800 кБайт/с против 870 кБайт/с в тесте передачи блоков внутреннего флеша без использования DMA), поэтому применение этого режима трудно назвать критичным. Но все же интересно, у кого-нибудь он работает правильно и согласно описанию производителя? :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Timmy 1 27 февраля, 2017 Опубликовано 27 февраля, 2017 (изменено) · Жалоба Я использовал DBL_BUF на F103, и всё работает нормально. В мануале, к сожалению, слишком куцое описание этого режима, а в HAL реализация двойных буферов, мне кажется, забагована. Баг в том, что обработчик прерывания должен уметь при необходимости обслуживать минимум два буфера за один вход в прерывание, если контроллер того требует. Отличие моего от вашего алгоритма в том, что я при инициализации заполнял SW_BUF/DTOG_TX нулями, и только потом, когда уже установлен бит DBL_BUF, переключал SW_BUF, чтобы начать передачу одного буфера, это различие может оказаться важно. UPD: Могу предположить, что у вас случилось: вы инициализировали SW_BUF/DTOG_TX в 1/0, потом заполнили буфер данными и переключили SW_BUF(в нуль). В таком случае контроллер совершенно справедливо отправит два буфера, просто из куцого мануала этого не понять. Изменено 27 февраля, 2017 пользователем Timmy Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
makc 192 27 февраля, 2017 Опубликовано 27 февраля, 2017 · Жалоба Я использовал DBL_BUF на F103, и всё работает нормально. В мануале, к сожалению, слишком куцое описание этого режима, а в HAL реализация двойных буферов, мне кажется, забагована. Баг в том, что обработчик прерывания должен уметь при необходимости обслуживать минимум два буфера за один вход в прерывание, если контроллер того требует. Да, там с этим проблема и по сути вместо подсовывания следующего буфера они там еще раз кладут тот же, что только что успешно передали. В этом нет смысла, только дополнительная задержка. Отличие моего от вашего алгоритма в том, что я при инициализации заполнял SW_BUF/DTOG_TX нулями, и только потом, когда уже установлен бит DBL_BUF, переключал SW_BUF, чтобы начать передачу одного буфера, это различие может оказаться важно. Только что попробовал изначально установить SW_BUF/DTOG_TX в 0/0, после чего DBL_BUF устанавливается в 1, записывается буфер размером 64 байта и переключается SW_BUF (из 0 в 1) для начала передачи. Со стороны хоста даётся запрос на 128 байт для Bulk-in endpoint и вместо ожидания приёма данных с вылетом по таймауту (нет короткого блока данных, завершающего передачу) я получаю 64 + содержимое из второго буфера, которого не ждал. Т.е. складывается впечатление, что начальное состояние этих бит не играет роли. UPD: Могу предположить, что у вас случилось: вы инициализировали SW_BUF/DTOG_TX в 1/0, потом заполнили буфер данными и переключили SW_BUF(в нуль). В таком случае контроллер совершенно справедливо отправит два буфера, просто из куцого мануала этого не понять. В документации у них нет информации, что контроллер может запоминать переключения. У них написано, что анализируется только комбинация управляющих разрядов регистра и при сочетании 1/1 или 0/0 в хост отдаётся NAK до момента переключения в 0/1 или 1/0 соответственно. А у меня выходит, что эта логика начинает работать только спустя одну "лишнюю" транзакцию, которая по номеру идёт второй и передаёт неподтверждённый буфер. После первого круга по буферам все начинает работать правильно. Но вот как решить проблему первого круга? :crying: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Timmy 1 28 февраля, 2017 Опубликовано 28 февраля, 2017 · Жалоба Только что попробовал изначально установить SW_BUF/DTOG_TX в 0/0, после чего DBL_BUF устанавливается в 1, записывается буфер размером 64 байта и переключается SW_BUF (из 0 в 1) для начала передачи. Со стороны хоста даётся запрос на 128 байт для Bulk-in endpoint и вместо ожидания приёма данных с вылетом по таймауту (нет короткого блока данных, завершающего передачу) я получаю 64 + содержимое из второго буфера, которого не ждал. Т.е. складывается впечатление, что начальное состояние этих бит не играет роли.Было бы любопытно посмотреть на лог, описывающий последовательнось и времена переходов между состояниями регистра USB_EPxR. То есть программируете старт передачи, все прерывания должны быть всё время теста запрещены, и опрашиваете где-нибудь полсекунды USB_EPxR, при любых изменениях записывая в лог-вектор новое состояние и время изменения из системного таймера. А также сбрасывая CTR_TX, если он появится. Потом лог-вектор срисовать в отладчике или выплюнуть через консоль. В документации у них нет информации, что контроллер может запоминать переключения. У них написано, что анализируется только комбинация управляющих разрядов регистра и при сочетании 1/1 или 0/0 в хост отдаётся NAK до момента переключения в 0/1 или 1/0 соответственно. А у меня выходит, что эта логика начинает работать только спустя одну "лишнюю" транзакцию, которая по номеру идёт второй и передаёт неподтверждённый буфер. После первого круга по буферам все начинает работать правильно. Но вот как решить проблему первого круга? :crying: В результате экспериментов я установил, что в USB контроллере есть скрытый бит, который определяет, как интерпретируются сочетания SW_BUF/DTOG 0/0 и 1/1 - либо как два пустых буфера, либо как два занятых. Этот бит приходится восстанавливать программно - после переключения SW_BUF - два занятых, после чтения единичного CTR_TX или начальной инициализации - два пустых. То есть в исходном состоянии 1/0 || 0/1 можно набить данными один свободный буфер, переключить SW_BUF, и новое состояние окажется 0/0 || 1/1, однако оно будет означать два занятых и готовых к передаче буфера, а в мануале про это ничего не написано. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
makc 192 28 февраля, 2017 Опубликовано 28 февраля, 2017 · Жалоба Было бы любопытно посмотреть на лог, описывающий последовательнось и времена переходов между состояниями регистра USB_EPxR. То есть программируете старт передачи, все прерывания должны быть всё время теста запрещены, и опрашиваете где-нибудь полсекунды USB_EPxR, при любых изменениях записывая в лог-вектор новое состояние и время изменения из системного таймера. А также сбрасывая CTR_TX, если он появится. Потом лог-вектор срисовать в отладчике или выплюнуть через консоль. Отладка идёт через последовательный порт и даже на скорости 430 кБит/с сильно тормозит процесс передачи через bulk, по эксперимент попробую устроить. В результате экспериментов я установил, что в USB контроллере есть скрытый бит, который определяет, как интерпретируются сочетания SW_BUF/DTOG 0/0 и 1/1 - либо как два пустых буфера, либо как два занятых. Этот бит приходится восстанавливать программно - после переключения SW_BUF - два занятых, после чтения единичного CTR_TX или начальной инициализации - два пустых. То есть в исходном состоянии 1/0 || 0/1 можно набить данными один свободный буфер, переключить SW_BUF, и новое состояние окажется 0/0 || 1/1, однако оно будет означать два занятых и готовых к передаче буфера, а в мануале про это ничего не написано. Честно говоря я не понял, как можно инициализировать этот теневой бит, о котором Вы говорите. Как контроллер отличает переключения SW_BUF (DTOG_RX) в режиме инициализации от рабочего режима? По значению бита DBL_BUF? Как у Вас выглядит последовательность настройки конечной точки? Какие биты устанавливаются/сбрасываются и в каком порядке? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Timmy 1 28 февраля, 2017 Опубликовано 28 февраля, 2017 · Жалоба Отладка идёт через последовательный порт и даже на скорости 430 кБит/с сильно тормозит процесс передачи через bulk, по эксперимент попробую устроить.Последовательный порт не помешает, промежуточный результат можно в сыром виде в памяти сложить, там же не больше десяти записей должно получиться. Честно говоря я не понял, как можно инициализировать этот теневой бит, о котором Вы говорите. Как контроллер отличает переключения SW_BUF (DTOG_RX) в режиме инициализации от рабочего режима? По значению бита DBL_BUF? Как у Вас выглядит последовательность настройки конечной точки? Какие биты устанавливаются/сбрасываются и в каком порядке? Я не пытался инициализировать теневой бит, достаточно знать, что при включении двойной буферизации он находится в состоянии "пусто при равных битах". При простейшем запуске конечной точки 1 после сброса делаем так: usb->EPR[1] = 1|USB_EPxR_EP_TYPE_BULK|USB_EPxR_STAT_TX_VALID|USB_EPxR_EP_KIND; тут исходное состояние было нулевое, поэтому можно опустить специальное управление t битами USB_EPxR_STAT_TX После этого точка будет возвращать NAK. Далее можно заполнить буфер данными, записать в COUNT_TX длину пакета и записать usb->EPR[1] = 1|USB_EPxR_EP_TYPE_BULK|USB_EPxR_EP_KIND|USB_EPxR_CTR_TX|USB_EPxR_DTOG_RX, чтобы переключить SW_BUF в единицу, и больше ничего не делать. По идее должен уйти один пакет, когда попросят, после чего установятся биты USB_EPxR_CTR_TX и USB_EPxR_DTOG_TX. Я слишком дотошно не тестировал протокол на стороне PC, и может быть что-то упустил и на самом деле оно не может работать правильно. Но начинал тоже с попытки начальной инициализации SW_BUF/DTOG в 1/0, получилась ерунда. Кстати, в EPR пропадает зря как минимум CTR_RX, могли бы в него теневой бит отображать:) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться