Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых
[Назад] [Содеожание] [Дальше]

Аппаратное обеспечение IBM PC

© Александр Фролов, Григорий Фролов
Том 2, книга 1, М.: Диалог-МИФИ, 1992.

4. ЧАСЫ РЕАЛЬНОГО ВРЕМЕНИ 

  • 4.1. Прочитать показания часов реального времени
  • 4.2. Установить часы реального времени
  • 4.3. Прочитать дату из часов реального времени
  • 4.4. Установить дату в часах реального времени
  • 4.5. Установить будильник
  • 4.6. Сброс будильника
  • 4.7. Использование часов реального времени
  • Компьютеры IBM AT и PS/2 оснащены часами реального времени. Эти часы питаются от аккумулятора, поэтому их показания не пропадают при выключении компьютера.

    Доступ к часам реального времени возможен либо через ячейки КМОП-памяти, либо через специальные функции BIOS (что более предпочтительно с точки зрения независимости работы программы от особенностей аппаратуры).

    Использование регистров КМОП-памяти часами реального времени приведено в таблице:

    Регистр Назначение
    0 счетчик секунд
    1 регистр секунд будильника
    2 счетчик минут
    3 регистр минут будильника
    4 счетчик часов
    5 регистр часов будильника
    6 счетчик дней недели (1 - воскресенье)
    7 счетчик дней месяца
    8 счетчик месяцев
    9 счетчик лет (последние две цифры текущего года)
    • 0aH регистр состояния A
    7 6 5 4 3 2 1 0
    T-T-T-T-T-T-T-¬
    ¦ ¦     ¦       ¦
    LT+T+-+T+T+-+-+T-
    ¦ L=T=- L=====¦= переключатель скорости (установлен в 0110)
    ¦   L=========== 22-разрядный делитель (установлен в 010)
    L=============== Флаг обновления, 0 означает готовность
                               данных для чтения.
    
    
    
    
    
    • 0bH регистр состояния B
    7 6 5 4 3 2 1 0
    T-T-T-T-T-T-T-¬
    ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦
    LT+T+T+T+T+T+T+T-
     ¦ ¦ ¦ ¦ ¦ ¦ ¦ L= 1 - использование летнего времени
     ¦ ¦ ¦ ¦ ¦ ¦ ¦    (daylight savings enable);
     ¦ ¦ ¦ ¦ ¦ ¦ ¦    0 - стандартное время (установлен в 0)
     ¦ ¦ ¦ ¦ ¦ ¦ ¦
     ¦ ¦ ¦ ¦ ¦ ¦ L=== 12 или 24-часовой режим.  0 - 12-часовой
     ¦ ¦ ¦ ¦ ¦ ¦      режим (установлен в 1)
     ¦ ¦ ¦ ¦ ¦ ¦
     ¦ ¦ ¦ ¦ ¦ L===== режим данных BCD. 1 - двоичный, 0 - BCD.
     ¦ ¦ ¦ ¦ ¦        (установлен в 0)
     ¦ ¦ ¦ ¦ ¦
     ¦ ¦ ¦ ¦ L======= разрешение прямоугольной волны.
     ¦ ¦ ¦ ¦          1 - включение прямоугольной волны.
     ¦ ¦ ¦ ¦          (установлен в 0)
     ¦ ¦ ¦ ¦
     ¦ ¦ ¦ L========= разрешение прерывания по окончанию
     ¦ ¦ ¦            изменения данных (установлен в 0)
     ¦ ¦ ¦
     ¦ ¦ L=========== разрешение прерывания будильника
     ¦ ¦              (установлен в 0)
     ¦ ¦
     ¦ L============= разрешение периодических прерываний
     ¦                (установлен в 0)
     ¦
     L=============== флаг обновления, 0 означает готовность
                               данных для чтения КМОП-памяти.
    
    0cH     регистр состояния C.
            Биты состояния прерывания, их можно только читать.
     
    0dH     регистр состояния D.
            Если бит 7 равен 0, это означает,       что разрядился
            аккумулятор, питающий КМОП-память.
    
    
    
    
    

    Часы реального времени вырабатывают аппаратное прерывание IRQ8, которому соответствует прерывание с номером 70h. Это прерывание может вырабатываться по трем причинам:

    • Прерывание по окончанию изменения данных. Вырабатывается при установленном в 1 бите 4 регистра состояния B после каждого обновления регистров часов.
    • Прерывание будильника вырабатывается при совпадении регистров часов и регистров будильника и при установленном в 1 бите 5 регистра состояний B.
    • Периодическое прерывание вырабатывается с интервалом примерно 1 миллисекунда при установленном в 1 бите 6 регистра состояний B.

    При срабатывании будильника BIOS вырабатывает прерывание INT 4Ah. Программа может подготовить собственный обработчик для этого прерывания.

    Для работы с часами реального времени вы можете обращаться непосредственно к перечисленным выше ячейкам КМОП-памяти, используя порты 70h и 71h. Однако лучше всего воспользоваться функциями 2 - 7 прерывания 1Ah, описанными ниже.

    4.1. Прочитать показания часов реального времени

    На входе:       AH = 02h.
    
    На выходе:      CH = часы в BCD-формате (например,
                    13h означает 13 часов);
    
                    CL = минуты в BCD-формате;
    
                    DH = секунды в BCD-формате;
    
                    CF = CY = 1, если часы реального времени
                      не установлены.
    
    
    
    

    4.2. Установить часы реального времени

    На входе:       AH = 03h;
    
                    CH = часы в BCD-формате (например,
                      13h означает 13 часов);
    
                    CL = минуты в BCD-формате;
    
                    DH = секунды в BCD-формате;
    
                    DL = 1, если необходимо использовать
                      летнее время (daylight savings time
                      option).
    
    На выходе: не используются.
    
    
    
    

    4.3. Прочитать дату из часов реального времени

    На входе:       AH = 04h.
    
    На выходе:      CH = столетие в BCD-формате ;
    
                    CL = год в BCD-формате (например,
                      CX=1991h означает 1991 год);
    
                    DH = месяц в BCD-формате;
    
                    DL = число в BCD-формате;
    
                    CF = CY = 1, если часы реального времени
                     не установлены.
    
    
    
    

    4.4. Установить дату в часах реального времени

    На входе:       AH = 05h;
    
                    CH = столетие в BCD-формате ;
    
                    CL = год в BCD-формате (например,
                      CX=1991h означает 1991 год);
    
                    DH = месяц в BCD-формате;
    
                    DL = число в BCD-формате;
    
    На выходе:      не используются.
    
    
    
    

    4.5. Установить будильник

    На входе:       AH = 06h;
    
                    CH = часы в BCD-формате;
    
                    CL = минуты в BCD-формате;
    
                    DH = секунды в BCD-формате.
    
    На выходе:      CF = CY = 1, если часы реального времени
                     не установлены.
    
    
    
    

    Эта функция позволяет установить будильник на заданное время. Когда будильник "зазвенит", будет вызвано прерывание INT 4Ah (это прерывание вызывают модули BIOS после прихода аппаратного прерывания от часов реального времени IRQ8, т.е. прерывания с номером 70h). Программа, использующая функцию будильника, должна подготовить обработчик прерывания INT 4Ah, завершающий свою работу выполнением команды IRET.

    Программа может установить только один будильник.

    4.6. Сброс будильника

    На входе:       AH = 07h.
    
    На выходе: не используются.
    
    
    
    

    Эта функция позволяет сбросить будильник, например, для того чтобы установить его заново на другое время.

    4.7. Использование часов реального времени

    Вы можете использовать часы реального времени для решения двух задач. Во-первых, часы позволяют определить текущую дату и время с точностью до секунды. Во-вторых, будильник можно использовать для выполнения каких-либо действий в заданное время или периодически.

    Так как установленное время срабатывания будильника хранится в КМОП-памяти, питающейся от аккумулятора, будильник не будет сброшен при случайном выключении компьютера.

    Для работы с часами реального времени мы подготовили следующую функцию:

    /**
    *.Name         timer
    *.Title        Работа с часами реального времени
    *
    *.Descr        Эта функция предназначена для обслуживания
    *              системных часов реального времени через
    *              прерывание INT 1Ah.
    *
    *.Proto        int timer(char fn, SYSTIMER *tm)
    *
    *.Params       char     fn - выполняемая функция:
    *
    *              RTC_GET_TIME      - прочитать показания часов;
    *              RTC_SET_TIME      - установить часы;
    *              RTC_GET_DATE      - прочитать дату;
    *              RTC_SET_DATE      - установить дату;
    *              RTC_SET_ALARM     - установить будильник;
    *              RTC_CLEAR_ALARM   - сбросить будильник.
    *
    *                 Все эти константы описаны в файле sysp.h
    *
    *              SYSTIMER tm - структура, содержащая данные
    *                            для установки часов или
    *                            показания часов:
    *
    *              typedef struct _SYSTIMER_ {
    *
    *                 char hour;     // часы
    *                 char min;      // минуты
    *                 char sec;      // секунды
    *                 unsigned year; // год
    *                 char month;    // месяц
    *                 char day;      // число
    *                 char daylight_savings; // флаг
    *                                       // использование
    *                        // летнего времени
    *                        // (для включения режима
    *                        // должен быть равен 1)
    *
    *              } SYSTIMER;
    *
    *.Return       0   - успешное выполнение функции;
    *              -1  - часы реального времени отсутствуют
    *                    в компьютере;
    *
    *.Sample       setalarm.c
    **/
    
    #include <stdio.h>
    #include <dos.h>
    #include "sysp.h"
    
    union REGS reg;
    
    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);
    }
    
    
    
    

    Эта функция выполняет все виды обслуживания часов реального времени, которые поддерживаются BIOS.

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

    Перед установкой будильника программа подключает свой обработчик прерывания 4Ah. Это прерывание вызывается при срабатывании будильника. Перед завершением работы программа сбрасывает будильник и восстанавливает вектор прерывания 4Ah.

    #include <stdio.h>
    #include <stdlib.h>
    #include <dos.h>
    #include "sysp.h"
    
    // Выключаем проверку стека и указателей
    
    #pragma check_stack( off )
    #pragma check_pointer( off )
    
    // Макро для выдачи звукового сигнала
    
    #define BEEP() _asm { \
            _asm mov bx,0 \
            _asm mov ax, 0E07h \
            _asm int 10h \
    }
    
    void main(void);
    
    // Описание программы-обработчика прерывания
    // будильника
    
    void _interrupt _far alarm(void);
    
    // Переменная для хранения старого
    // вектора будильника
    
    void (_interrupt _far *old_4a)(void);
    
    
    void main(void) {
    
    char *month_to_text[] = {
    
            "январь",
            "февраль",
            "март",
            "апрель",
            "май",
            "июнь",
            "июль",
            "август",
            "сентябрь",
            "октябрь",
            "ноябрь",
            "декабрь"
    
    };
    
    SYSTIMER tmr;
    
    // Определяем текущие дату и время
    
            timer(RTC_GET_DATE, &tmr);
            timer(RTC_GET_TIME, &tmr);
    
    // Выводим дату и время на экран
    
            printf("\nСейчас %d год, %s, %d число."
                     "\n",
                     bcd2bin(&(tmr.year)),
                     month_to_text[bcd1bin(&(tmr.month)) - 1],
                     bcd1bin(&(tmr.day)));
    
            printf("\nВремя - %02.2d:%02.2d:%02.2d"
                     "\n",
                     bcd1bin(&(tmr.hour)),
                     bcd1bin(&(tmr.min)),
                     bcd1bin(&(tmr.sec)));
    
    // Для установки будильника увеличиваем
    // счетчик минут на единицу. Для упрощения
    // программы мы не проверяем счетчик на
    // переполнение, поэтому если текущее
    // значение счетчика минут равно 59,
    // будильник не сработает. Вы можете сами
    // немного усовершенствовать программу для
    // проверки переполнения.
    
            bin1bcd(bcd1bin(&(tmr.min)) + 1,
                                                     &(tmr.min));
    
    // Выводим на экран время, когда сработает
    // будильник.
    
            printf("\nВремя срабатывания будильника"
                                    "- %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);
    
    // Устанавливаем будильник
    
            timer(RTC_SET_ALARM, &tmr);
    
            printf("\nБудильник установлен. Для отмены "
                             "и завершения программы нажмите"
                             "\nлюбую клавишу...");
    
            getch();
    
    // Сбрасываем будильник и восстанавливаем
    // вектор прерывания будильника
    
            timer(RTC_CLEAR_ALARM, &tmr);
    
            _dos_setvect(0x4a, old_4a);
    
            exit(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.
    // ----------------------------------
    
    int bin1bcd(int bin, char *bcd) {
    
            int i;
    
            i = bin / 10;
    
            *bcd = (i << 4) + (bin - (i * 10));
    
    }
    
    // ----------------------------------
    // Программа получает управление
    // при срабатывании будильника.
    // Ее назначение - выдать звуковой сигнал.
    // ----------------------------------
    
    void _interrupt _far alarm(void) {
    
            BEEP();
            BEEP();
            BEEP();
            BEEP();
            BEEP();
            BEEP();
            BEEP();
    
    }
    
    
    
    [Назад] [Содеожание] [Дальше]