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

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

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



§ 29. WoodmanUSB. Передача данных через порт PORTB. Теория записи данных.

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

Оставляем порт PORTA и переходим к рассмотрению нового для нас порта - PORTB. Как я уже упомянул, с ним не все так просто, и потребуется немного рассказать о том как он работает, и что нужно сделать для того что передавать по нему данные.

PORTB может работать в нескольких режимах. Существует два основных режима работы - асинхронный и синхронный. При этом синхронный режим работы порта подразделяется еще на три модификации: с внешним источником тактирования, внутренним тактированием с частотой 30 МГц и 48 МГц.

режимы работы порта PORTB модуля WoodmanUSB

Начнем разбираться именно с асинхронного режима, поскольку он проще для освоения и практической реализации. К тому же синхронный режим есть его развитие. Сейчас упомяну, что синхронный режим наряду со всем тем что присуще асинхронному режиму дополнительно требует линии тактирования, что усложняет схемотехнику, однако именно в синхронном режиме можно добиться максимальной скорости передачи данных.


Асинхронный режим: запись данных.

Рассмотрим ситуацию, когда наша программа хочет передать данные в асинхронном режиме внешнему устройству. Со стороны програмной части в первую очередь необходимо настроить порт PORTB на соответствующий режим работы. Для этого предназначена функция WUSB_SetupPortB(). Ей в качестве параметра передается специальная числовая константа, определяющая в какой режим необходимо перевести порт. Для асинхронного режима вызов этой функции должен выглядеть следющим образом:

WUSB_SetupPortB(ASYNC_MODE);

После этого можно вызывать функию записи данных в порт PORT - WUSB_WritePortB(). У этой функии 3 параметра. Первый - адрес на буфер с данными для передачи в порт, второй - размер (число байт) данных, которые необходимо передать из указанного буфера, третий - указатель на переменную, в которую будет помещено число реально записанных данных в порт. Приведу сразу пример записи данных в порт PORTB:

//устанавливаем асинхронный режим работы
WUSB_SetupPortB(ASYNC_MODE); 

//создаем массив из 2048 байт
char buf[2048];  

//заполняем весь массив еденицами (например)
memset(buf, 1, sizeof(buf)); 

unsigned int dwWrite;

//записываем данные в порт (весь буфер целиком)
int status = WUSB_WritePortB(buf, sizeof(buf), &dwWrite);
if(status == WUSB_ERROR)
{
	//ошибка записи
} 
else if(status == WUSB_TIMEOUT)
{
	//истечение времени тайм-аута
}

С программной точки зрения вроде бы все более менее нормально и понятно (кроме разве что WUSB_TIMEOUT, об этом позже). Рассмотрим теперь что же происходит в "железе". В упрощенном варианте рассказа, как только мы вызвали функцию WUSB_WritePortB() данные из буфера buf нашей программы передаются на уровень ядра ОС в драйвер WUSBdrv.sys. Он в свою очередь формирует запрос к драйверу шины USB Windows и пепредает теперь уже ему все тот же буфер с данными. Как известно, по шине USB данные передаются пакетами определенной длины в сложном формате. Системный драйвер USB берет первые 512 байт данных и отправляет их в модуль (512 байт - установленный размер пакета для модуля WoodmanUSB). WoodmanUSB принимает этот пакет (512 байт), декодирует его и помещает данные в промежуточное "хранилище", т.н. FIFO-буфер. Таких буферов в модуле две штуки. Один предназначен для хранения данных передаваемых с компьютера (IN FIFO), другой для отправки на компьютер (OUT FIFO). Размер каждого из них составляет 1024 байта.

принципы передачи данных WoodmanUSB

Как только хоть один байт будет помещен в IN FIFO буфер, модуль установит свой 24-ый вывод (PORTB_FNE) в логическую еденицу (исходно этот вывод - логический ноль). Это необходимо для того, чтобы сообщить внешнему устройству, что компьютер передает данные и в буфере модуля есть доступные данные для чтения. Далее системный драйвер USB пересылает еще один пакет из 512 байт в модуль. Они записываются в IN FIFO. Теперь входной буфер модуля заполнен полностью и больше принимать данные не может, пока их от туда не считает внешнее устройство. Поэтому, если размер пересылаемых данных был больше чем 1024 байта передача по шине USB врменно приостанавливается. Чтобы ее возобновить, необходимо считать данные из IN FIFO буфера модуля. Для этого внешнее устройство (например, контроллер) должно быть подсоединено своим портом к выводам порта модуля PORTB. Далее, чтобы считать данные из IN FIFO контроллеру необходимо установить на линии PB_RD ноль. Тогда, первый байт из IN FIFO устанавливается на линиях порта PORTB. При этом он стирается из IN FIFO и размер данных в нем стал 1024-1=1023 байта. Пока байт установлен на линии, контроллер может его считать и сделать с ним что ему будет угодно. Затем, нужно PB_RD поставить в логическую еденицу. На этом один цикл чтения данных закончен. Чтобы счить следующий байт опять ставим на PB_RD ноль. Второй байт из IN FIFO устанавливается на линиях порта PORTB и стирается из буфера. Контроллер считывает байт, ставит на PB_RD ноль и т.д. Так будет продолжаться пока размер данных в IN FIFO буфере не уменьшится до 512 байт. Тогда системный драйвер USB перешлет еще один пакет в модуль. Когда все данные будут переданы и из IN FIFO будет прочтен последний байт, линия PORTB_FNE вернется в нулевое состояние, сигнализируя внешнему устройству, что данных для чтения больше нет.


Если систематизировать данное описание работы, то все операции по передаче данных из программы во внешнее устройство через порт PORTB модуля можно свести к следующей последовательности действий:

1. устанавить в программе необходимый режим работы порта PORTB с помощью функции WUSB_SetupPortB()

2. с помощью функции WUSB_WritePortB() отправить в модуль необходимый объем данных

3. внешне устройство, которое должно получать данные, анализирует состояние вывода PORTB_FNE модуля WoodmanUSB. Как только уровень стал высоким, устройство может начать считывание данных (см. следующие шаги)

4. устанавить на линии PB_RD логический ноль

5. считать байт данных с выводов порта PORTB

6. устанавить на линии PB_RD логическую еденицу

7. повторять шаги 4-6 до тех пор, пока линия PORTB_FNE не перейдет в логический ноль. При наступлении этого события перейти к шагу 3



Необходимо сделать несколько замечаний по поводу записи данных. Во первых, следует сказать о размерах буферов, которые можно записывать за одно обращение к функции WUSB_WritePortB() - можно пересылать данные размером от 1 байта до 65535 байт включительно.

Так же необходимо пояснить некий код WUSB_TIMEOUT, который может вернуть функция WUSB_WritePortB(). Дело здесь вот в чем: предположим, что мы начинаем передавать данные. Заполнили целиком буфер IN FIFO модуля, но внешнее устройство данные по какой-то причине не считывает. Что в данной ситуации необходимо делать функции записи? Ждать пока внешнее устройство "оживет" и начнет принимать данные? А если этого вообще не произойдет (питание, например, на контроллер не подано)? Можно конечно и подождать, но следует учитывать, что пока функция не завершит свою работу, она будет "тормозить" программный поток в рамках которого она выполняется. А это уже не хорошо. Для решения этой проблемы в системе WoodmanUSB применяется понятие времени тайм-аута для выполнения операций чтения и записи. Это означает, что по истечение определенного временного интервала (время тайм-аута) происходит автоматическое завершение операции. В качестве результата возвращается код WUSB_TIMEOUT. По умолчанию значение тайм-аута для операции записи данных равно 1000 мс. Т.е. если за одну секунду данные не будут переданы, произойдет автоматическое завершение работы функции. Велечину тайм-аута можно изменять. Для работы с тайм-аутами предназначены функции WUSB_GetTimeOuts() и WUSB_SetTimeOuts() (см. описание функций модуля).


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


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



© KERNELCHIP 2006 - 2023