Интернет-магазин

Просмотр корзины
В корзине:

товаров - 0 шт.



§ 33. WoodmanUSB. PORTB. Теория чтения данных.

Дмитрий Иванов, 09 Декабря 2013

Асинхронный режим: чтение данных.

Рассмотрим ситуацию, когда внешнее устройство (например, микроконтроллер), хочет передать данные через WoodmanUSB в компьютер. Для этого необходимо сделать следующее: контроллер должен установить на линии PB_WR лигический ноль, затем установить на линиях порта PORTB модуля WoodmanUSB байт данных, затем установить на линии PB_WR лигическую еденицу. При этом этот байт помещается в выходной FIFO буфр модуля OUT_FIFO. Затем, указанная последовательно действий повторяется, в результате в OUT_FIFO модуля будет помещен еще один байт и т.д. до тех пор пока в буфере есть свободное место (напомню, размер OUT_FIFO буфера равен 1024 байт). Как только весь буфер будет заполнен WoodmanUSB установит свой вывод PORTB_FF в логический ноль, сигнализируя внешнему устройству, что буфер заполнен целиком и модуль не может принимать новые данные, пока их не заберет ПК. Формально можно продолжать пытаться помещать данные в OUT_FIFO буфер - криминального в этом ни чего нет, просто эти данные будут "пропадать", пока не освободится место в OUT_FIFO.

Как видим дело встало за программной частью ПК. Драйвер WUSBdrv.sys автоматически данных из модуля не забирает. Он будет проводить такую операцию по запросу пользовательского приложения. Для чтения данных предназначена функция WUSB_ReadPortB(). По внешнему виду она очень похожа на функцию записи данных. Сразу простенький пример использования:

char buf[1024];
unsigned int dwRead = 0;
int status = WUSB_ReadPortB(buf, sizeof(buf), &dwRead);
if(status == WUSB_ERROR)
{
	//ошибка чтения
}
else if(status == WUSB_TIMEOUT)
{
	//истечение времени тайм-аута
}

Что произойдет если вызовем эту функцию? Драйвер WUSBdrv.sys сформирует запрос на чтение 1024 байт данных и отправит его системному драйверу USB (SDUSB). Тот в свою очередь сформирует запрос на чтение пакета размером 512 байт (512 байт - фиксированный размер контрольной точки WoodmanUSB для чтения данных) и отправит его в модуль. Первые 512 байт из OUT_FIFO буфера модуля будут отправлены на ПК и сохранены в памяти SDUSB. При этом PORTB_FF вернется в свое исходное состояние - логическую еденицу. Внешнее устройство может продолжить писать данные в OUT_FIFO буфер. Поскольку мы запросили 1024 байт, системный драйвер формирует еще один запрос на чтение следующих 512 байт из выходного буфера модуля. Данные отправляются на ПК. Теперь весь запрос пользовательского приложения выполнен полностью, буфер с данными перекочевывает из системного драйвера USB в драйвер WUSBdrv, а тот в свою очередь заполняет этими данными пользовательский буфер, адрес на который мы передали первым параметром функции WUSB_ReadPortB().

Это было, скажем так, общее описание. Теперь тонкости. Функция чтения данных может прочитать из модуля только такой объем данных, который кратен 512 байтам. Это очень важное замечание. Например, функция записи данных могла записать любой объем данных (всмысле, не обязательно кратный 512 байтам).

Итого, при чтении данных из WoodmanUSB, размер читаемых данных всегда должен быть кратен 512 но при этом не превышать величины 65024 (ограничение драйвера), т.е. может принимать значения 512, 1024, 2048, ..., 65024 байт (нужно сказать что есть возможность прочесть и любой другой объем данных, но это требует применения дополнительных аппаратных ресурсов, а именно вывода модуля PKTEND; рассмотрим эту возможность чуть позже).

Рассмотрим небольшой пример. Допустим, мы заказали прочесть 1024 байт, а в модуле есть только 612 байт. Сначала будет считан первый пакет из 512 байт. Однако следующий пакет не может быть считан (если не использовать PKTEND), т.к. он не равен 512 (а равен 100 байтам в нашем примере). Что будет происходить? Функция чтения будет ждать в течение 1000 мс (время тайм-аута по умолчанию для операции чтения) заполнения OUT_FIFO буфера до 512 байт. Если этого не произойдет, она вернет код WUSB_TIMEOUT, буфер чтения будет пуст а переменная dwRead, определяющая сколько было успешно прочтено данных будет содержать значение 0.

1. Если внешнее устройство явно тихоходное и данные быстро передавать не собирается можно увеличить время тайм-аута используя функцию WUSB_SetTimeOuts().

//устанавливаем значение тайм-аута для обоих операций в 10 сек.
unsigned int ReadTimeOut  = 10000;
unsigned int WriteTimeOut = 10000;
int status = WUSB_SetTimeOuts(ReadTimeOut, WriteTimeOut);
if(status == WUSB_ERROR)
{
	//ошибка установки
}

2. Перед чтением данных вызвать функцию WUSB_GetFIFOStatus(). Ей передаются адреса на две переменные. В них она положит числовые константы, характерезующие текущее состояние FIFO буферов модуля. Если переменная для OUT_FIFO буфера имеет значение FIFO_FULL - это значит что весь буфер заполнен целиком и мы можем без проблем прочесть 1024 байт (два раза по 512).

unsigned char Instatus, OUTstatus;
int status = WUSB_GetFIFOStatus(&INstatus, &OUTstatus);
if(status == WUSB_ERROR)
{
	//ошибка
}

if(OUTstatus == FIFO_EMPTY)
{
	//буфер пуст, в нем содержится 0 байт данных
}
if(OUTstatus == FIFO_FULL)
{
	//буфер полный, нем содержится 1024 байт данных	
}
if(OUTstatus == FIFO_NOTEMPTY)
{
	//в буфере содержится > 0 но < 1024 байт данных
}

Однако "злоупотреблять" функцией WUSB_GetFIFOStatus() не следует. Дело в том что она очень "медленная". Если пытаться ее вызывать в цикле перед обращением к функции чтения - скорость системы получится очень небольшой.

3. Самый интересный вариант о котором я уже немного упомянул - использование вывода модуля PKTEND. Поясну сразу на примере. Например, мы хотим передать из модуля в ПК 513 байт. На ПК запускаем функцию чтения с запросом прочесть 1024 байта. По уже описанной технологии системный драйвер USB прочтет первые 512 байт, в OUT_FIFO останется 1 байт. А дальше будем ждать пока не истечет время тайм-аута. Но нам ну никак не нужно пересылать еще каких-то 511 байт - нам нужно переслать последний байт и все. Вот тут-то и нужно использовать вывод модуля PKTEND. Если его поставть в логический ноль, а затем обратно в исходное высокое состояние функция чтения не будет дожидаться заполнения буфера - она просто считает этот последний байт (или любое другое колличество, смотря сколько их в буфере осталось) и закончит свою работу с кодом возврата WUSB_OK. Буфер котрорый мы передали функции будет содержать 513 байт, а переменная dwRead будет содержать число 513. Мало того, можно сделать так: заполнить OUT_FIFO буфер неким объемом данных, например, десятью байтами. Затем "передернуть" вывод PKTEND (сначала в 0, затем в 1 состояние). Теперь при ближайшем вызове функции чтения с произвольным запросом на объем читаемых данных (например, мы попросили прочесть 2048 байт), она прочтет только те данные, которые находились в OUT_FIFO на момент обращения к PKTEND, т.е. те 10 байт. На последующие вызовы функции чтения это уже распространяется, и WUSB_ReadPortB() будет ждать пока в OUT_FIFO не будет объем данных кратный 512 байтам (если только мы снова не воспользуемся PKTEND).


При передаче данных от внешнего устройства через WoodmanUSB на PC есть еще одна тонкость котрую нужно учитывать. Начинать передачу данных можно только тогда, когда в программе на PC будет вызвана функция WUSB_Open() (а для синхронного режима еще и WUSB_SetupPortB()) и не раньше! Дело в том, что при открытии устройства проводятся целая группа подготовительных операций в модуле. Пока они не сделаны, передача данных работать не будет. Верене, формально будет, только за исключением корректной работы вывода PORTB_FF.



Вот, пожалуй, и все, что я хотел рассказать пока по теории записи данных в порт PORTB модуля WoodmanUSB в асинхронном режиме. Давайте перейдем к практике. В следующей статье мы будем передавать данные из контроллера PIC16F877 в нашу программу на компьютере по шине USB через модуль WoodmanUSB.


© Дмитрий Иванов
09 Декабря 2013 года
http://www.kernelchip.ru



© KERNELCHIP 2006 - 2017