Опубликован: 24.04.2009 | Доступ: свободный | Студентов: 1180 / 358 | Оценка: 4.39 / 4.28 | Длительность: 18:45:00
Специальности: Программист
Лекция 8:

Программирование приложений в CE

Пример программы PortIO, использующей CE Device Driver Kit

Подобно предыдущему примеру программы эта программа C++ читает данные последовательного ввода из COM2:, выводит каждый символ в консольном окне eBox, и выводит их назад в COM2:.

Вместо вызовов API В/В файлов CE, она использует функции WRITE_PORT_UCHAR и READ_PORT_UCHAR, находящиеся в библиотеке CEDDK. Требуется заголовочный файл CEDDK.h, и код должен быть скомпонован с библиотекой CEDDK.lib. Эти функции взаимодействуют прямо с портами В/В на оборудовании UART порта COM и являются типичными операциями, требуемыми на самом нижнем уровне драйвера устройства, который требуется для прямой коммуникации с оборудованием. Мы модифицируем позже этот пример, чтобы создать простой драйвер устройства потокового интерфейса ОС.

Назначение этого примера состоит в иллюстрации того, как низкоуровневый код драйвера устройства взаимодействует с оборудованием В/В. Драйвер устройства в операционной системе будет использоваться обычно для коммуникации с последовательным портом, как в предыдущем примере. Поэтому главным здесь является просто понимание некоторых первых базовых шагов, которые потребуются, чтобы в конечном счете написать свой собственный драйвер устройства для ОС.

COM2: содержит совместимый с 16550 UART аппаратный интерфейс с базовым адресом В/В 0x2F8. Спецификация 16550 UART или справочник по оборудованию ПК объяснит детали того, как все восемь портов В/В UART используются для данных, статуса и многих других характеристик последовательной связи, таких как скорость в бодах, число битов данных и т.д. Прерывания отключены и для передачи данных последовательного порта используется программируемый В/В. Таблица 8.1 перечисляет базовые функции портов В/В UART.

Два первых адреса выполняют двойную функцию. Они могут также использоваться для задания делителя скорости в бодах, когда бит задания делителя (DLB) равен единице (в регистре формата данных бит 7). В обычном операционном режиме бит задания делителя возвращается в ноль.

Этот код не предназначен для замены профессионального управляемого прерываниями драйвера последовательного устройства с буферизацией и задержками, такого как поставляется с CE, который использовался в первом примере программы с последовательным портом. Он служит только для демонстрации использования программируемого В/В для передачи данных, и использования функций чтения/записи В/В из CEDDK, которые обычно находятся на самом нижнем уровне драйверов устройств, которые непосредственно взаимодействуют с оборудованием.

Таблица 8.1. Порты В/В 16550 UART
Функция регистра Адрес порта В/В
Данные (DLB=1, делитель скорости в бодах LSB) Base + 0
Разрешение прерываний (DLB=1, делитель скорости в бодах MSB) Base + 1
ID прерывания Base + 2
Формат данных Base + 3
Управление по модему Base + 4
Статус линии Base + 5
Статус модема Base + 6
Scratch-Pad Base + 7

Отметим использование программируемого В/В в функциях write_serial_character и read_serial_character. Эти функции считывают соответствующий бит готовности устройства UART из порта статуса, маскирует этот бит, тестирует его, и делает цикл, если понадобится, прежде чем передавать данные. Вспомните, что "&" является побитовой операцией AND, а "|" является побитовой операцией OR в C/C++. Sleep(0) выходит на текущий квант времени и помещает процесс в очередь ожидания своего очередного выполнения.

Здесь также, чтобы увидеть данные последовательный null-модемный кабель соединяется из COM2: на eBox с неиспользуемым портом COM на настольном ПК системы разработки. Запустите HyperTerminal или другую программу эмулятора терминала, соединенную с соответствующим портом COMx: на скорости 9600 бод, с 8 битами данных, без контроля четности, с 1 стоп-битом, и без управления потоком. Проверьте, что не выполняется другое приложение на любом устройстве, которое использует тот же самый порт COM!

Когда выполняется программа, она печатает заголовок, а затем каждый символ, вводимый на клавиатуре настольного ПК, посылается в устройство eBox, оно считывает символы, выводит их в консольном окне, и посылает символы назад. Так как символы посылаются назад, они выводятся также в эмуляторе терминала. Ввод Ctrl-C в эмуляторе терминала заставляет eBox выйти из программы.

// PortIO.cpp : Определяет точку входа для консольного приложения.
// Учебный пример предназначен для иллюстрации того, как работает 
// программируемый В/В на целевой системе, используя оборудование 
// последовательного порта В/В, и показывающий использование   
// функций READ_PORT_UCHAR и WRITE_PORT_UCHAR
// из CE Device Driver Kit (CEDDK)
//
// Настройка для X86 PC  (CEPC)
// используя совместимое с 16550 UART оборудование последовательного порта 
// 
// Не предназначено для замены хорошего драйвера последовательного порта!
// Не использует прерывания, не имеет никаких задержек, и не предоставляет 
// поддержку для всех свойств последовательного порта 
// Обычно для этого используют вызовы API ОС!
//
// Для примера: Соедините Ebox COM2: с ПК с помощью null-модемного кабеля  
// Запустите HyperTerminal на скорости 9600 бод, с 8 битами данных, 
// 1 стоп битом, без контроля четности, и без управления потоком 

#include "stdafx.h"

// Для функций WRITE_PORT_UCHAR & READ_PORT_UCHAR 
#include "..\WINCE600\CEPC_x86\cesysgen\ddk\inc\ceddk.h"
// Также необходимо включить при компоновке CEDDK.lib 
// (см. файл sources)
// добавить $(_SYSGENOAKROOT)\lib\$(_CPUINDPATH)\ceddk.lib
// в записи TARGETLIBS 

void Setup_UART (PUCHAR Data_Port_Address);
void Write_Serial_Character (PUCHAR Data_Port_Address, UCHAR Serial_Data);
UCHAR Read_Serial_Character (PUCHAR Data_Port_Address);

int _tmain(int argc, TCHAR *argv[], TCHAR *envp[])
{
	PUCHAR Data_Port_Address;
	UCHAR  Serial_Input_Data = 0;
	char  Title[]= "\f\n    Hello Serial World!\n\rType something and watch it echo back\n\rCtrl C to exit\n\r";
	int i;
// Принудительный вывод консоли 
	printf("I/O Port READ/WRITE Demo Program - Echos data on COM2:\n\r  Type Ctrl C on other device to exit\n\r");
// Адрес порта данных для COM2:
	Data_Port_Address = (PUCHAR)0x2F8;
// Настройка UART для скорости 9600 бод & без прерываний с 8D NP 1S
	Setup_UART(Data_Port_Address);
// Печать заголовка на последовательный порт 
	for (i=0; i<strlen(Title); i++)
		Write_Serial_Character(Data_Port_Address, (UCHAR)Title[i]);
// Запуск цикла Echo - Цикл пока не будет нажато Ctrl C 
	while (Serial_Input_Data != 0x03){
			// Считывание данных 
Serial_Input_Data = Read_Serial_Character (Data_Port_Address);
			// Копирование данных на консоль 
		printf("%c", Serial_Input_Data);
			// Возврат данных назад (Echo)
Write_Serial_Character(Data_Port_Address, Serial_Input_Data);
	}
	return 0;
}

void Write_Serial_Character (PUCHAR Data_Port_Address, UCHAR Serial_Data)
// Запись символа в последовательный порт 
{
	UCHAR Status;
		// Ожидание TX бита готовности вывода =1
		// Статус адреса порта В/В будет адрес порта В/В данных + 5
	do{
		Status = READ_PORT_UCHAR(Data_Port_Address + 5);
	} while ((Status & 0x40) == 0);
		// Запись (Echo) новых данных назад на COM2:
    	WRITE_PORT_UCHAR(Data_Port_Address, Serial_Data);
	return;
}

UCHAR Read_Serial_Character (PUCHAR Data_Port_Address)
{
// Считывание символа из последовательного порта 
	UCHAR Serial_Data, Status;
		// Ожидание RX бит готовности ввода =1
		// Статус адреса порта В/В будет адрес порта В/В данных + 5
	do{
Status = READ_PORT_UCHAR(Data_Port_Address + 5); 
// Если не готов, отправить напоминание о кванте времени
		if ((Status & 0x01) == 0) Sleep(0);
	} while ((Status & 0x01) == 0);
		// Считать новые последовательные данные 
	Serial_Data = READ_PORT_UCHAR(Data_Port_Address);
	return Serial_Data;
}
void Setup_UART (PUCHAR Data_Port_Address)
{
	UCHAR Temp;
// Чтобы полностью понять это потребуется хороший справочник по 
// оборудованию ПК и/или спецификация 16550 UART!
// Отключение COMx: Прерывания (используйте программируемый В/В)
	WRITE_PORT_UCHAR(Data_Port_Address + 1, 0);
// Задаем скорость в бодах как 9600 с настройками делителя частоты 
// Вставляем задание режима делителя 
	Temp = READ_PORT_UCHAR(Data_Port_Address + 3);			
	WRITE_PORT_UCHAR(Data_Port_Address + 3, Temp | 0x83);
// Задаем LSB делителя  (примечание: 12 = 115200/9600)
	WRITE_PORT_UCHAR(Data_Port_Address , 12);
// Задаем MSB делителя
	WRITE_PORT_UCHAR(Data_Port_Address + 1, 0);
// Возврат в обычный операционный режим (и настройка 8D NP 1S)
	Temp = READ_PORT_UCHAR(Data_Port_Address + 3);			
	WRITE_PORT_UCHAR(Data_Port_Address + 3, Temp & 0x03);
	return;
}
8.3.

Компоновка с другими модулями с помощью файла sources проекта

При написании более сложных приложений должен использоваться файл sources подпроекта для задания путей поиска включаемых файлов и определения других модулей для компоновки с подпроектом. Процесс сборки (Build) читает файл sources каждого подпроекта, чтобы определить, как собрать проект. По умолчанию все подпроекты компонуются с coredll.lib в файле sources. Библиотека coredll.lib определяет много базовых вызовов API, но многие вызовы API определяются в других библиотеках.

В оперативной справочной системе каждая запись вызова API перечисляет заголовочные и библиотечные файлы, необходимые для этого API в конце своей страницы в разделе Требования. Если необходимо скомпоновать приложение с созданным пользователем файлом *.DLL, который имеет соответствующий доступный файл *.lib, то процесс будет таким же.

Вызовы API, которые определяются в других библиотеках, должны будут иметь путь доступа к этой библиотеке в файле sources подпроекта. Они добавляются в раздел "TARGETLIBS=" в файле sources, или могут добавляться с помощью окна свойств подпроекта (сделайте щелчок правой кнопкой мыши на имени проекта, выберите properties и вкладку link). Если файл ссылки отсутствует, то будет сгенерирована ошибка компоновки, такая как "неопределенный символ" или "отсутствует внешняя ссылка".

Для длинных сложных путей доступа легче отредактировать непосредственно файл sources. Если сделать двойной щелчок на записи имени основного подпроекта, в редакторе откроется файл sources. Символ "\" используется для продолжения строки. Будьте внимательны, символ слеш ("\") должен быть последним символом в каждой строке, имена файлов должны разделяться пробелом, и после последнего "\" фактически перед следующим разделом требуется пустая строка или строка, содержащая последнюю текстовую строку данного раздела.

Если требуется дополнительный заголовочный файл, необходимо добавить раздел "INCLUDES=" в конце файла sources. Пути доступа для включаемых файлов разделяются точкой с запятой (не пробелами) и снова для продолжения используется "\". Это можно задать также с помощью окна свойств подпроекта на вкладке C/C++ или редактированием файла sources.

При вводе путей доступа в файл sources доступно несколько предопределенных переменных окружения путей доступ CE в форме $(……..). Когда возможно, старайтесь использовать предопределенные переменные путей, чтобы файлы проекта было легче перемещать в случае необходимости в другие каталоги или на другие диски. Переменная окружения содержит такую информацию, как диск, путь доступа, или конфигурационная строка. Они отделены от переменных Sysgen, которые контролируют включенные в дизайн ОС функции. Таблица 8.2 содержит различные переменные окружения, которые можно использовать для определения путей доступа каталогов. Многие из них задаются в Wince.bat.

Таблица 8.2. Переменные окружения, используемые для путей доступа
Переменная окружения Описание
_WINCEROOT Определяет корневой каталог. Если у вас достаточно пространства в одном разделе, создайте корневой каталог в этом разделе, например, C:\WINCE600, и установите в нем ОС. Для этого задайте для %_WINCEROOT% полный путь доступа к корневому каталогу.
_FLATRELEASEDIR Определяет каталог, где будут размешаться выпущенный исходный код и двоичные файлы. По умолчанию используется %_WINCEROOT%\ PBWorkspaces\%_TGTPROJ%\RelDir\%_TGTPLAT%\%_TGTCPUFAMILY%_Release.
PBWORKSPACEROOT Определяет расположение текущего рабочего пространства Platform Builder. По умолчанию используется %_WINCEROOT%\PBWorkspaces \%_TGTPROJ%. Однако, если вы поместили свой корневой каталог в место, отличное от используемого по умолчанию, то это размещение используется как определение для %PBWORKSPACEROOT%.
_PLATFORMROOT Определяет расположение каталога Platform. По умолчанию используется %_WINCEROOT%\Platform..
_PRIVATEROOT Определяет расположение альтернативного корневого каталога, например, каталога для хранения другого инструмента задания переменных окружения, Setenv.bat. Можно задать эту переменную для указания на каталог по своему выбору. По умолчанию используется %_WINCEROOT%\Private.
_PROJECTOAKROOT Определяет расположение каталога Oak для проекта. Во время процесса сборки Build.exe помещает собираемые файлы в этот каталог. По умолчанию используется %_PROJECTROOT%\Oak.
_PROJECTROOT Определяет расположение собираемого проекта; например, %_PBWORKSPACEROOT%\WINCE500 \%PBCONFIG%.
_PUBLICROOT Определяет расположение публичных проектов. По умолчанию используется %_WINCEROOT%\Public.
_SDKROOT Определяет расположение инструментов, которые используются для сборки проектов, таких как компилятор командной строки, редактор внешних связей, отладчик, и библиотеки времени выполнения. По умолчанию используется %_WINCEROOT%\SDK.
_TGTPROJ Определяет имя активного в данный момент рабочего пространства.

При создании дизайна ОС с помощью Platform Builder, интегрированная среда разработки задает специальные переменные окружения на основе настроек, выбранных для дизайна и образа времени выполнения. Можно модифицировать эти настройки переменных окружения во время процесса разработки.

Можно задавать или удалять переменные окружения используя:

  • Приглашение команды окна сборки с помощью команды set. Используйте меню Build верхнего уровня и откройте окно команды сборки.
  • Пакетного файла
  • IDE. Сделайте щелчок правой кнопкой мыши на проекте и выберите свойства, а затем раскройте объект configuration.

Откройте файл sources для подпроекта PORTIO и исследуйте разделы TARGETLIBS и INCLUDE. Отметим, что была добавлена строка для соединения этого модуля с ceddk.lib. Эта библиотека требуется для функций READ_PORT_UCHAR и WRITE_PORT_UCHAR.