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

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

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



§ 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



© KERNELCHIP 2006 - 2023