§ 35. WoodmanUSB. PORTB. Практика по PKTEND.
|
Дмитрий Иванов, 10 Декабря 2013
|
В этой статье мы познакомимся с выводом PKTEND модуля WoodmanUSB, грамотное использование которого позволяет передавать из внешнего устройства в модуль (а далее соответственно в ПК) любой объем данных, не обязательно кратный 512 байтам. Причины по которым данный вывод нужен и должен использоваться подробно рассмотрены в статье по основам передачи данных из внешнего устройства в ПК. Для демонстрации его работы я хочу предложить Вам следующую тестовую конструкцию: микроконтроллер PIC16F877 соединен с WoodmanUSB (как обычно), дополнительно по отношению к прошлой статье контроллер может подавать сигнал управления на вывод PKTEND. Данные для отправки на компьютер контроллер сам генерировать не будет. Мы поступим интереснее - контроллер будет передавать по USB на ПК то, что мы ему передадим по COM порту. Т.е. мы через терминальную программу будем посылать данные в контроллер, а он их будет передавать в WoodmanUSB. Это позволит нам во-первых, еще раз убедиться что при передаче WoodmanUSB данных не теряет и не искажает, а также поработать с PKTEND. Сделаем так, что при приеме определенного символа из COM порта (давайте возьмем символ решетка "#") PIC16F877 сгенерирует управляющий сигнал для PKTEND.
Рассмотрим сначала схему. Я позволил себе немного изменить используемые выводы контроллера для управления модулем по сравнению с прошлой статьей. Также появилась возможность управлять выводом PKTEND. Особняком расположения схема TTL<->RS232 преобразователя. Также здесь я убрал ключ К1 - дело в том, что контроллер начнет передавать данные только тогда когда мы их сами ему передадим по COM порту. Представленная распиновка COM порта соответствует разъему "мама".


А вот и код для контроллера PIC16F877. Что в нем делается? Настраиваем USART (последовательный порт), затем инициализируем линии контроля и управления модулем WoodmanUSB. Затем стартуем "бесконечный цикл". Если из COM порта пришел байт данных и в OUT_FIFO буфере модуля есть свободное место - отправляем этот байт в WoodmanUSB. Дополнительно проверяем, что если этот байт соответствует символу # - подаем управляющий сигнал на вывод PKTEND модуля WoodmanUSB. В конце каждого прохода цикла показываем на светодиоде, подключенном к выводу RC1 контроллера, текущий статус OUT_FIFO буфера модуля - если там есть свободное место, светодиод горит, иначе он потушен (решил немного изменить по сравнению с прошлой статьей).
#include <pic.h>
__CONFIG(0x03F72);
unsigned char temp;
void UsartInit(void)
{
SPBRG = 31; // baud rate 9600
TXEN=1;
CREN=1;
SPEN=1;
SYNC=0;
RCIF=0;
}
void main(void)
{
UsartInit();
//**************** Шины данных ********************
TRISB = 0; // настраиваем все линии порта B на выход
PORTB = 0;
TRISD = 0; // настраиваем все линии порта D на выход
PORTD = 0;
//******* Линии контроля и управления *************
//
// вывод С0 будем использовать как сигнал записи
// (вывод PB_WR модуля);
// настраиваем его на выход
//
TRISC0 = 0; // PB_WR
//
// вывод C1 будем использовать для визуального отображения
// состояния OUT_FIFO буфера на светодиоде. Если буфер запонен
// светодиод погашен, иначе горит
//
TRISC1 = 0; // LED indicator for PORTB_FF
//
// вывод C2 будем использовать для управления
// выводом модуля PKT_END
//
TRISC2 = 0; // PKTEND
//
// вывод С4 будем использовать для слежения за состоянием
// OUT_FIFO буфера (вывод PORTB_FF модуля);
// настраиваем его на вход
//
TRISC4 = 1; // PORTB_FF
RC0 = 1; // PB_WR, 1 - т.к. активный уровень низкий
RC1 = 0; // LED
RC2 = 1; // PKTEND, 1 - т.к. активный уровень низкий
RC4 = 0; // PORTB_FF
//**********************************************/
while(1 == 1)
{
if(RCIF)
{
// пришли данные с COM порта для записи в порт
temp = RCREG;
PORTD = temp;
if(RC4 == 1) // have free memory in FIFO
{
PORTB = temp;
RC0 = 0; // set active level
RC0 = 1; // set unactive level
if(temp == 0x23) //'#'
{
//send PAKET_END Command
RC2 = 0; //active level
RC2 = 1; //unactive
}
}
RCIF = 0; // free COM interput
}
if(RC4 == 1) // have free memory in FIFO
{
RC1 = 1; // LED indicator
}
else // FIFO full
{
RC1 = 0; // LED indicator
}
}
}
Пора тестировать систему. Программу для PC используем из прошлой статьи. Итак, включаем USB кабель. Запускаем программу, открываем доступ к устройству. При этом светодиод, сигнализирующий о состоянии буфера модуля должен гореть, тем самым показывая, что буфер свободен.

Давайте, нажмем кнопку Get FIFOs Status. При этом будет вызвана функция WUSB_GetFIFOStatus(), которая позволяет узнать текущие состояния обоих FIFO буферов модуля. Возможные состояния буфера: 0 - буфер пустой, 1 - буфер заполнен до конца, 2 - буфер не пуст и не полон (т.е. в нем содержится > 0 но < 1024 байт). Код нажатия на кнопку предствлен ниже:
//****************************************************************************
void CTestWinDlg::OnGetFifoStatus()
{
// TODO: Add your control notification handler code here
unsigned char INstatus = 0;
unsigned char OUTstatus = 0;
int status = WUSB_GetFIFOStatus(&INstatus, &OUTstatus);
if(status == WUSB_ERROR)
{
MessageBox("ERROR get Status!", "Info", MB_ICONERROR);
return ;
}
CString s1;
s1.Format(" INFIFO: %d\nOUTFIFO: %d", INstatus, OUTstatus);
MessageBox(s1, "Info", MB_ICONINFORMATION);
}
Итак, если сейчас вызвать эту функцию, то состояния обоих буферов будут нулевыми, что логично. Давайте "исправим" это, записав в них какие-нибудь данные. Запускаем терминальную программу HyperTerminal и проводим ее настройку абсолютно аналогично тому, как мы уже рассматривали в одной из статей. В добавок к этому нужно сделать еще пару настроек, т.к. если оставить все как есть USART контроллера будет очень странно себя вести при отправке одного и того же символа несколько раз подряд. На окне программы нажмите кнопку "Свойства" и перейдите на закладку "Параметры", в списке "Эмуляция терминала" выберите вариант Minitel.

Далее нажмите кнопку "Параметры ASCII". Установите флажки так как на рис. ниже.

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

Теперь давайте прочтем данные из модуля. Для этого в программе в соответствующем окошке ввода указываем размер читаемого пакета 512 байт (самый минимальный). Нажимаем кнопку Read Single Packet. При этом произойдет следующее: мы увидим сообщение о успешно прочтенных 512 байтах, загорится светодиод RC1, показывая что в буфере освободилось место. Если посмотреть на код обработчика нажатия этой кнопки, можно заметить, что прочтенные данные скидываются в файл. Можно его открыть и увидеть в нем именно те данные которые мы только что отправили через HyperTerminal. Читаем еще раз пакет из 512 байт - буфер теперь пустой, что нам может подтвердить функция WUSB_GetFIFOStatus().
А теперь переходим собственно непосредственно к главной теме этой статьи. Ну вот хорошо, если нам нужно отправлять много данных. А теперь представим что нам нужно передать на ПК всего один байт. Как мы знаем, функция WUSB_ReadPortB() может читать только те пакеты размер которых кратен 512. Давайте протестируем это. Через терминал отправим в контроллер 1 символ. Теперь в буфере модуля 1 байт. Пытаемся прочесть еденичный пакет. Примерно через 1 сек. видим неприятное сообщение о тайм-ауте. Что произошло? - функция WUSB_ReadPortB() обращается к модулю с запросом на 512 байт, а у модуля есть только 1. Функция пытается подождать некоторое время (1 секунду по умолчанию), вдруг за это время данные наберуться. Поскольку мы новых данных через терминал не отправляли, WUSB_ReadPortB() возвращает код TIMEOUT.
Но вернемся к нашей проблеме. Нам опять же нужно прочесть всего один байт. Дополнять пакет до 512 байт тоже не хочется. Как быть? Вот тут то и нужен вывод модуля PKTEND. Чтобы его "запустить" нужно передать символ "решетка" (так мы в нешей тестовой системе договорились). Ок, так и сделаем отправляем # через терминал. Теперь читаем еденичный пакет размером 512 байт. И что получилось - функция чтения тут же завершилась с упехом. Она прочла 2 байта. Если мы посмотрим в файл, куда сбрасываются прочтенные байты мы увидим там символ, который был отправлен в контроллер до решетки и собственно саму #.

Что же при этом произошло? Как тлько мы подали низкий уровень напряжения на вывод модуля PKTEND а затем вернули высокий, модуль при первом обращении к нему системного драйвера USB обрывает его транзакцию, передает ему только те данные которые были в OUT_FIFO буфере до момента передергивания вывода PKTEND. Драйвер при этом свой запрос завершает, даже если в буфере было 0 байт. Мы можем запросить прочесть хоть 65024 а в буфере всего например 5 байт. Если мы применим PKTEND - функция успешно прочтет эти 5 байт.
На этом рассмотрение основ программирования и использования модуля пожалуй можно закончить (ну только кроме рассмотрения синхронного режима). Теперь мы знаем почти все приемы работы с модулем, которые отработали на простеньком контроллре. Основная стихия WoodmanUSB - это скорость. Поэтому, вооружившись базовыми знаниями можно смело приступать к более серьзным вещам: получение максимальных скоростей передачи, сопряжению WoodmanUSB с ПЛИС и микропроцессорами, созданию высокочастотных USB осциллографов и т.д.
© Дмитрий Иванов
10 Декабря 2013 года
http://www.kernelchip.ru