Вы находитесь на странице: 1из 10

БИЛЕТ 23

1. Программирование контроллера прерываний, назначение OCW и ICW.


Программируемый контроллер прерываний (PIC) реализуется на микросхеме 8259А
фирмы Intel (в современных ПК реализуется чипсетом), и поддерживает 8 уровней
прерываний от восьми различных устройств. Основные функции контроллера:
 - фиксация запросов на прерывания от восьми внешних источников;
 - программное маскирование поступающих запросов;
 - присвоение фиксированных или циклически изменяемых приоритетов входам
контроллера, на которые поступают запросы; инициализация вызова процедуры
обработки поступившего аппаратного прерывания.

Рисунок 1 - Архитектура контроллера прерываний

Программирование контроллера
OCW и ICW
Описание работы основных элементов PIC. Схема управления чтением/записью
(Read/Write Control Logic). Основной функцией этого блока является прием команд от
микропроцессора и передача ему информации о состоянии PIC. Обмен с микропроцессором
осуществляется через специальный 8-разрядный буфер данных (Data Bus Buffer),
являющийся интерфейсом между PIC и шиной данных.
В состав блока входят регистры управляющих слов ICW и OCW. Схема управляется
входами CS, RD, WR и АО. Вход CS (Chip select) отвечает за выбор микросхемы. Низкий
уровень сигнала на входе CS разрешает выполнение обмена с PIC. Низкий уровень сигнала на
входе WR (Write) разрешает микропроцессору выводить управляющие слова ICW и OCW для
приема их PIC. Низкий уровень сигнала на входе RD (Read) разрешает PIC передать
микропроцессору информацию о состоянии специальных регистров IRR, ISR и IMR, которые
описаны ниже.
Все управляющие слова ICW и OCW принимаются контроллером в виде 9-разрядных
значений. Разряды 0-7 передаются через 8-разрядный буфер данных. Старший разряд
(восьмой, считая с нуля) носит название АО и устанавливается в 0 или 1 в зависимости от
того, через какой из двух возможных портов ввода-вывода (четный или нечетный) было
передано управляющее слово. Если для вывода значения использовался порт с четным
адресом, АО будет равен 0, если использовался порт с нечетным адресом на единицу
большим, чем предыдущий, тогда АО будет равен 1.

2. Разработать программу, которая программирует срабатывание будильника на 43


секунде каждой минуты 8-го часа, а при срабатывании будильника играет мелодию
до(4)- си(2)-соль(3).
Текст програми:
#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

#include <dos.h>

#include <iostream.h>

typedef struct _SYSTIMER_

char hour; // часы

char min; // минуты

char sec; // секунды

unsigned year; // год

char month; // месяц

char day; // число


char daylight_savings; // флаг летнего времени

} SYSTIMER;

#define RTC_GET_TIME 2 // прочитать показания часов;

#define RTC_SET_TIME 3 // установить часы;

#define RTC_GET_DATE 4 // прочитать дату;

#define RTC_SET_DATE 5 // установить дату;

#define RTC_SET_ALARM 6 // установить будильник;

#define RTC_CLEAR_ALARM 7 // сбросить будильник.

// Выключаем проверку стека и указателей

#pragma check_stack( off )

#pragma check_pointer( off )

int bcd1bin(char *bcd);

int bcd2bin(char *bcd);

void bin1bcd(int bin, char *bcd);

void _interrupt _far alarm(void);

int timer(char fn, SYSTIMER *tm);

// Прототип программы-обработчика прерывания будильника

void interrupt far alarm(...);

// Переменная для хранения старого вектора будильника

void interrupt (far *old_4a)(...);

void tm_sound(int freq, int time);

union REGS reg;

// Массив частот для мелодии

int mary[] = { 268, 494, 392 };

// Массив длительностей

int del[] = { 4, 2, 3 };

int main(void)

{
int min;

clrscr();

SYSTIMER tmr;

char tempCh;

do

// Определяем текущие время

timer(RTC_GET_TIME, &tmr);

// Настройка времени срабатывания будильника

// на 43 секунде каждой минуты 8-го часа

if (bcd1bin(&(tmr.hour)) == 8) {

min = bcd1bin(&(tmr.min));

if(bcd1bin(&(tmr.sec)) >= 43)

min++;

min %= 60;

bin1bcd(8, &(tmr.hour));

bin1bcd(min, &(tmr.min));

bin1bcd(43, &(tmr.sec));

printf("\nTime alarm "

"- %02.2d:%02.2d:%02.2d"

"\n",

bcd1bin(&(tmr.hour)),

bcd1bin(&(tmr.min)),

bcd1bin(&(tmr.sec)));

// Подключаем свой обработчик прерывания

// будильника, старое значение вектора

// 0x4a сохраняем

old_4a = _dos_getvect(0x4a);

_dos_setvect(0x4a, alarm);
printf("\nPress any key...");

getch();

cout << "Set alarm again?\n1 - yes\n2 - no\n";

cin >> tempCh;

if(tempCh == '2')

break;

} while(1);

// Сбрасываем будильник и восстанавливаем

// вектор прерывания будильника

timer(RTC_CLEAR_ALARM, &tmr);

_dos_setvect(0x4a, old_4a);

return 0;

// Преобразование однобайтового

// числа из формата BCD в двоичный формат

int bcd1bin(char *bcd)

return( ((*bcd) & 0x0f) +

10 * (((*bcd) & 0xf0) >> 4) );

// Преобразование двухбайтового

// числа из формата BCD в двоичный формат

int bcd2bin(char *bcd)

return( bcd1bin(bcd) +

100 * bcd1bin(bcd + 1) );

}
// Преобразование однобайтового

// числа из двоичного формата в формат BCD

void bin1bcd(int bin, char *bcd)

int i;

i = bin / 10;

*bcd = (i << 4) + (bin - (i * 10));

// Программа получает управление при срабатывании будильника.

// Ее назначение - выдать звуковой сигнал

void interrupt far alarm(...)

int i;

for(i=0; mary[i] != 0; i++)

tm_sound(mary[i], del[i]);

// Формирование тона заданной длительности

void tm_sound(int freq, int time)

int cnt;

// Задаем режим канала 2 таймера

outp(0x43, 0xb6);

// Вычисляем задержку для загрузки в

// регистр счетчика таймера

cnt = (int)(1193180L / freq);

// Загружаем регистр счетчика таймера - сначала

// младший, затем старший байты

outp(0x42, cnt & 0x00ff);

outp(0x42, (cnt & 0xff00) >> 8);

// Включаем громкоговоритель. Сигнал от

// канала 2 таймера теперь будет проходить

// на вход громкоговорителя

outp(0x61, inp(0x61) | 3);


// Выполняем задержку.

delay(time*1000);

// Выключаем громкоговоритель.

outp(0x61, inp(0x61) & 0xfc);

// Работа с часами реального времени

int timer(char fn, SYSTIMER *tm)

reg.h.ah = fn;

switch (fn)

case RTC_SET_TIME:

reg.h.ch = tm->hour;

reg.h.cl = tm->min;

reg.h.dh = tm->sec;

reg.h.dl = tm->daylight_savings;

break;

case RTC_SET_DATE:

reg.x.cx = tm->year;

reg.h.dh = tm->month;

reg.h.dl = tm->day;

break;

case RTC_SET_ALARM:

reg.h.ch = tm->hour;

reg.h.cl = tm->min;

reg.h.dh = tm->sec;

break;
}

int86(0x1a,&reg,&reg);

if(reg.x.cflag == 1)

return(-1);

switch (fn)

case RTC_GET_TIME:

tm->hour = reg.h.ch;

tm->min = reg.h.cl;

tm->sec = reg.h.dh;

break;

case RTC_GET_DATE:

tm->year = reg.x.cx;

tm->month = reg.h.dh;

tm->day = reg.h.dl;

break;

return 0;

Вам также может понравиться