Разработка программ-драйверов и тестирующих программ.
Специфика программирования аппаратуры и, в частности, устройств сопряжения для компьютера, заключается в повышенных требованиях к быстродействию программного обеспечения и в необходимости получения программы минимального размера.
При выборе языка программирования необходимо обеспечить:
• программный доступ к устройствам ввода/вывода и к памяти,
• обработку прерываний,
• битовые логические операции.
• управление системным таймером.
Для этих целей наиболее удобен язык Си.
Взаимодействие с устройством связано с подачей на него и приемом от него определенных сигналов в определенном порядке и представляет собой последовательность операций ввода/вывода, а также битовые логические операции над принимаемыми и передаваемыми данными.
Разработанное программное обеспечение для контроллера параллельного обмена опирается на следующие правила:
• устройство сопряжения выполняет определенные функции, каждая из которых реализуется в виде отдельного драйвера; на более низком уровне (управление отдельными разрядами отдельных регистров) разделения на программные модули не производится;
• устройство имеет регистр управления или состояния, в котором каждый бит или группа битов соответствуют определенным режимам работы устройства; маски этих битов (позиции в байте или слове) определяются перед запуском драйвера и используются в битовых операциях для установки или проверки;
• в качестве глобальной переменной определяется первый ("базовый") адрес устройства в адресном пространстве, а его остальные адреса вычисляются добавлением к базовому адресу величины смещения, оцениваемой номером порта.
Контроллер параллельного обмена (КПО) содержит 7 программно-доступных байтовых двунаправленных портов и регистр управляющего слова. Таким образом, на нижнем уровне программирования КПО необходимо реализовать три функции: запись управляющего слова, запись данных в любой порт и чтение данных из любого порта.
На языке Си они выглядят так:
// *** Функция записи управляющего слова
// CW_ADDR — адрес регистра управляющего слова
// cw — байт управляющего слова
outportb (CW_ADDR, cw);
// *** Функция записи данных в порт
// BASE_ADDR — базовый адрес КПО (адрес порта 0)
// port — номер порта (0...6)
// data — байт данных
outportb (BASE_ADDR + port, data):
// *** Функция чтения данных из порта
// BASE_ADDR — базовый адрес КПО (адрес порта 0)
// port — номер порта (0...6)
// data — байт данных
data = inportb (BASE_ADDR + port):
Программа тестирования записи данных КПО.
Последовательно записывается во все порты код 55Н и контролируется правильность записи. При этом все порты, кроме тестируемого, переводятся в режим чтения.
// *** Программа тестирования КПО ***
#include <STDIO.H>
#include <DOS.H>
#define NPORT 7 // Число портов КПО
// Глобальные переменные
unsigned BASE_ADDR; // Базовый адрес КПО
unsigned CW_ADDR; // Адрес регистра управляющего слова
void main (void)
{
unsigned i;
unsigned char data = 0х55;
for (i=0; i<NPORT; i++)
{
outportb (CW_ADDR, 1 « i); // Установка конфигурации:
// i—ый порт на вывод остальные на ввод
outportb (BASE_ADDR + i, data); // Запись данных
if (inportb (BASE_ADDR + i) != data)
printf ("\n\tПорт %d неисправен", i);
else
printf ("\n\tПорт %d исправен", i);
}
}
//*** Конец программы
Каждому порту КПО соответствует один бит управляющего слова. Единица в этом бите устанавливает порт в режим передачи данных, а ноль — в режим приема. Естественно, что когда порт находится в режиме передачи, ничто нe мешает прочитать его содержимое, контролируя таким образом правильность вывода. Именно это свойство КПО использовано в программе его тестирования.
В программе тестирования для последовательной установки портов в режим вывода использовалась операция сдвига единицы на число разрядов, равное номеру порта. Очевидно, что при этом данный порт устанавливается на вывод, а все остальные — на ввод.
Для организации протоколов обмена с "быстрым" устройством — модулем ОЗУ емкостью 16 КБайт с байтовой организацией разработана программа-драйвер обмена батом данных по произвольному адресу.
Модуль ОЗУ подключен к портам КПО следующим образом:
Р0.0 ... Р0.7 — младший байт шины адреса: А0 ... А7,
Р1.0 ... Р1.5 — старшие линии шины адреса: А8 ... А13,
Р1.6 — сигнал разрешения записи -WE,
Р1.7 — сигнал разрешения выхода -ОЕ,
Р2.0 ... Р2.7 — шина данных: D0 ... D7.
Перед вызовом драйверов должно быть предварительно установлено управляющее слово КПО — 03Н (порты 0 и 1 — на вывод, порт 2 — на ввод).
// *** Драйвер модуля ОЗУ ***
#define WE 0х40 // Маска бита WE (бит 6 порта 1)
#define ОЕ 0х80 //Маска бита ОЕ (бит 7 порта 1)
#define АН 0x3F // Маска линий А8...А13 шины адреса (биты 0...5 порта 1)
// Глобальные переменные
unsigned BASE_ADDR: // Базовый адрес КПО
unsigned CW_ADDR; // Адрес регистра управляющего слова
// Прототипы функций
void Write_RAM (unsigned addr, unsigned char data);
unsigned char Read_RAM (unsigned addr);
// Функция записи байта данных
void Write_RAM (unsigned addr, unsigned char data)
{
outportb (CW_ADDR, 7); // Порты 0...2 — на вывод
outportb (BASE_ADDR, addr); // Младший байт адреса
outportb (BASE_ADDR+1, (addr » 8) & AH | OE & (~WE));
// Старшие линии адреса, OE=1, WE=0
outportb (BASE_ADDR+2, data); // Установка данных
outportb (BASE_ADDR+1, (addr » 8) & AH | OE | WE);
// WE=1
outportb (CW_ADDR, 3); // Порты 0,1 — на вывод, порт 2 — на ввод
}
// Функция чтения байта данных
unsigned char Read_RAM (unsigned addr)
{
unsigned char data;
outportb (BASE_ADDR, addr); // Младший байт адреса
outportb (BASE_ADDR+1, (addr » 8) & AH & (~OE) | WE);
// Старшие линии адреса, OE=0, WE=1
data = inportb (BASE_ADDR+2); // Чтение данных
outportb (BASE_ADDR+1, (addr » 8) & AH | OE | WE);
// OE=1
return (data);
}
//*** Конец драйвера
Важно обратить внимание на управление отдельными битами порта 1. Для этого использованы логические операции с прямыми и инверсными масками соответствующих битов. Кроме того, следует отметить, что подключение к порту 2 двунаправленной шины данных требует изменения направления передачи по этому порту, то есть перепрограммирования управляющего слова (в отличие от случаев подключения однонаправленных линий, когда управляющее слово устанавливается в начале программы и больше не модифицируется).