реклама на сайте
подробности

 
 
> STM32F0x2 и USB double-buffer, ошибки flow-control на double-buffered bulk-in endpoint
makc
сообщение Feb 22 2017, 19:36
Сообщение #1


Гуру
******

Группа: Админы
Сообщений: 3 621
Регистрация: 18-10-04
Из: Москва
Пользователь №: 904



Имеется кит на базе 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), поэтому применение этого режима трудно назвать критичным. Но все же интересно, у кого-нибудь он работает правильно и согласно описанию производителя? sm.gif


--------------------
BR, Makc
В недуге рождены, вскормлены тленом, подлежим распаду. (с) У.Фолкнер.
Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов (1 - 5)
Timmy
сообщение Feb 27 2017, 07:15
Сообщение #2


Знающий
****

Группа: Участник
Сообщений: 835
Регистрация: 9-08-08
Из: Санкт-Петербург
Пользователь №: 39 515



Я использовал DBL_BUF на F103, и всё работает нормально. В мануале, к сожалению, слишком куцое описание этого режима, а в HAL реализация двойных буферов, мне кажется, забагована. Баг в том, что обработчик прерывания должен уметь при необходимости обслуживать минимум два буфера за один вход в прерывание, если контроллер того требует. Отличие моего от вашего алгоритма в том, что я при инициализации заполнял SW_BUF/DTOG_TX нулями, и только потом, когда уже установлен бит DBL_BUF, переключал SW_BUF, чтобы начать передачу одного буфера, это различие может оказаться важно.
UPD: Могу предположить, что у вас случилось: вы инициализировали SW_BUF/DTOG_TX в 1/0, потом заполнили буфер данными и переключили SW_BUF(в нуль). В таком случае контроллер совершенно справедливо отправит два буфера, просто из куцого мануала этого не понять.

Сообщение отредактировал Timmy - Feb 27 2017, 07:46
Go to the top of the page
 
+Quote Post
makc
сообщение Feb 27 2017, 10:32
Сообщение #3


Гуру
******

Группа: Админы
Сообщений: 3 621
Регистрация: 18-10-04
Из: Москва
Пользователь №: 904



Цитата(Timmy @ Feb 27 2017, 10:15) *
Я использовал 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.gif


--------------------
BR, Makc
В недуге рождены, вскормлены тленом, подлежим распаду. (с) У.Фолкнер.
Go to the top of the page
 
+Quote Post
Timmy
сообщение Feb 28 2017, 04:49
Сообщение #4


Знающий
****

Группа: Участник
Сообщений: 835
Регистрация: 9-08-08
Из: Санкт-Петербург
Пользователь №: 39 515



Цитата(makc @ Feb 27 2017, 13:32) *
Только что попробовал изначально установить 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, если он появится. Потом лог-вектор срисовать в отладчике или выплюнуть через консоль.

Цитата(makc @ Feb 27 2017, 13:32) *
В документации у них нет информации, что контроллер может запоминать переключения. У них написано, что анализируется только комбинация управляющих разрядов регистра и при сочетании 1/1 или 0/0 в хост отдаётся NAK до момента переключения в 0/1 или 1/0 соответственно. А у меня выходит, что эта логика начинает работать только спустя одну "лишнюю" транзакцию, которая по номеру идёт второй и передаёт неподтверждённый буфер. После первого круга по буферам все начинает работать правильно. Но вот как решить проблему первого круга? crying.gif

В результате экспериментов я установил, что в USB контроллере есть скрытый бит, который определяет, как интерпретируются сочетания SW_BUF/DTOG 0/0 и 1/1 - либо как два пустых буфера, либо как два занятых. Этот бит приходится восстанавливать программно - после переключения SW_BUF - два занятых, после чтения единичного CTR_TX или начальной инициализации - два пустых.
То есть в исходном состоянии 1/0 || 0/1 можно набить данными один свободный буфер, переключить SW_BUF, и новое состояние окажется 0/0 || 1/1, однако оно будет означать два занятых и готовых к передаче буфера, а в мануале про это ничего не написано.
Go to the top of the page
 
+Quote Post
makc
сообщение Feb 28 2017, 07:48
Сообщение #5


Гуру
******

Группа: Админы
Сообщений: 3 621
Регистрация: 18-10-04
Из: Москва
Пользователь №: 904



Цитата(Timmy @ Feb 28 2017, 07:49) *
Было бы любопытно посмотреть на лог, описывающий последовательнось и времена переходов между состояниями регистра 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?
Как у Вас выглядит последовательность настройки конечной точки? Какие биты устанавливаются/сбрасываются и в каком порядке?


--------------------
BR, Makc
В недуге рождены, вскормлены тленом, подлежим распаду. (с) У.Фолкнер.
Go to the top of the page
 
+Quote Post
Timmy
сообщение Feb 28 2017, 10:08
Сообщение #6


Знающий
****

Группа: Участник
Сообщений: 835
Регистрация: 9-08-08
Из: Санкт-Петербург
Пользователь №: 39 515



Цитата(makc @ Feb 28 2017, 10:48) *
Отладка идёт через последовательный порт и даже на скорости 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, могли бы в него теневой бит отображатьsm.gif
Go to the top of the page
 
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 13th August 2025 - 23:01
Рейтинг@Mail.ru


Страница сгенерированна за 0.04846 секунд с 7
ELECTRONIX ©2004-2016