Микроконтроллеры PIC. Архитектура и программирование. Часть 6. Программирование таймеров » Программирование устройств на PIC микроконтроллерах


Логин:
Пароль:
О сайте:

Pic.Rkniga.ru - Сайт как для начинающих, так и для опытных радиолюбителей, разрабатывающих свои устройства на популярных PIC микроконтроллерах.
Здесь можно обмениваться сообщениями на форуме, а также добавлять на сайт статьи и схемы своих устройств.

Меню сайта
Главная Форум по PIC микроконтроллерам Форум Статьи по PIC микроконтроллерам Статьи Справочная информаци по PIC микроконтроллерам Справочник Литература по PIC микроконтроллерам Литература Схемотехника Схемотехника устройств на PIC микроконтроллерах Микроконтроллеры Программаторы Все по программированию PIC микроконтроллеров Программы, Софт Программы Ссылки
Опрос

Сколько лет вы занимаетесь программированием PIC микроконтроллеров?


от 0-1 года
1-3 года
3-5 лет
5-10 лет
более 10


Последние материалы
  • Тестовая плата для отладки программ на микроконтроллере PIC18F4550
  • Кнопка On/OFF на PIC12F629.
  • Часы с синхронизацией от китайского будильника
  • ШИМ регулятор на PIC16F628A.
  • Счетчики прямого и обратного счета на PIC16F628A.
  • Таймер отключения питания для мультиметра и не только.
  • Измеритель напряжения и тока
  • Маршрутный компьютер для электровелосипеда
  • Простой двухканальный термометр на PIC16F690 и датчиках DS18B20
  • Электронная "Незабудка" для забывчивых
  • Популярные материалы
    Случайная книга
    Программирование устройств на PIC микроконтроллерах » Справочник » Микроконтроллеры PIC. Архитектура и программирование. Часть 6. Программирование таймеров
    Микроконтроллеры PIC. Архитектура и программирование. Часть 6. Программирование таймеров
    Автор публикации: alex Просмотров: 13766 Добавлен: 27-09-2012, 14:07 Комментарии: 0

    Содержание
    1. Обзор 16-битных PIC-микроконтроллеров
    2. Архитектура микроконтроллеров PIC24F
    3. Система команд и основы программирования микроконтроллеров PIC24F
       3.1 Программная модель микроконтроллеров PIC24F
       3.2 Режимы адресации и система команд
    4. Программирование портов ввода-вывода
       4.1 Аппаратно-программная архитектура портов ввода/вывода
       4.2 Программирование портов ввода/вывода
       4.3 Модуль регистрации событий
    5. Программирование прерываний
    6. Программирование таймеров
       6.1 Практическое использование 16-битных таймеров
       6.2 Работа таймеров в 32-битном режиме
    7. Интерфейс SPI микроконтроллеров PIC24F
       7.1 Аппаратно-программная реализация SPI в микроконтроллерах PIC24F
       7.2 Практическое программирование обмена данными по SPI
    8. Интерфейс I2C микроконтроллеров PIC24F
       8.1 Принципы функционирования интерфейса I2C
       8.2 Модуль интерфейса I2C микроконтроллеров PIC24F
       8.3 Практическое использование интерфейса I2C
    9.Программирование интерфейса PMP
       9.1 Режимы работы PMP
       9.2 Практические примеры программирования интерфейса PMP
    10. Последовательный интерфейс микроконтроллеров PIC24F
       10.1 Аппаратно$программная архитектура UART
       10.2 Практическое использование последовательного порта
    11. Обработка аналоговых сигналов в микроконтроллере PIC24F
       11.1 Программная модель интегрированного АЦП
       11.2 Практическое использование модуля АЦП
       11.3 Использование внешнего АЦП
    12. Генерация аналоговых и цифровых сигналов
       12.1 Модуль генерации цифровых сигналов
       12.2 Аналоговые компараторы в микроконтроллерах PIC24F

         Микроконтроллеры семейства PIC24F, в зависимости от исполнения, могут иметь несколько 16-битных таймеров, которые принято обозначать как Таймер 1, Таймер 2, Таймер 3 и т. д. Аппаратно-программная архитектура каждого 16-битного таймера включает несколько регистров, доступных для чтения/записи.
         К ним относятся:
    • TMRx — 16-битный регистр-счетчик (х = 1, 2, 3, …);
    • PRx — 16-битный регистр периода;
    • TxCON — 16-битный регистр управления Таймера х.
         Каждый модуль таймера может генерировать прерывание, поэтому ему назначены следующие программные ресурсы:
    • бит TxIE — флаг разрешения прерывания Таймера х;
    • бит TxIF — флаг состояния прерывания Таймера х;
    • биты TxIP — установка приоритета Таймера х.
         Для 16-битных таймеров предусмотрено три основных режима (хотя не каждый таймер может работать во всех режимах):
    • режим «таймера», когда таймер синхронизируется внутренним или внешним тактовым сигналом и инкрементируется до значения, установленного в регистре периода PRx;
    • режим запуска/остановки внешним сигналом (Gated Time Accumulation), при котором запуск таймера осуществляется перепадом 0—1 внешнего сигнала, а остановка осуществляется по перепаду 1—0 этого же источника;
    • асинхронный режим работы от внешнего источника тактового сигнала.
         В зависимости от режима работы 16-битного таймера прерывание генерируется или по превышению значения регистра периода PRx (если таймер не работает в режиме внешнего запуска/останова), или по спадающему фронту внешнего сигнала запуска/останова. Вне зависимости от того, разрешено прерывание таймера или нет, при возникновении любой из этих ситуаций будет установлен флаг прерывания TxIF, который должен сбрасываться программой.
         Если необходимо работать с прерываниями таймера, то соответствующий флаг разрешения прерывания TxIF должен быть установлен. Биты приоритетов TxIP устанавливаются в соответствии с требованиями к приложению (по умолчанию это значение равно 4).
         Кроме указанных режимов работы, существуют и определенные отличия функциональных возможностей 16-битных таймеров, поэтому все таймеры разделены на три базовых типа: A, B и C. Таймер базового типа A, который присутствует во всех микроконтроллерах PIC24F, может работать в режиме «таймера» и внешнего запуска/останова, а к особенностям таймеров типа B и C следует отнести возможность их конфигурирования для работы в 32-битном режиме. К таймеру типа А относится Таймер 1, функциональная схема которого показана на Рис. 6.1.

    Рис. 6.1. Функциональная схема 16-битного таймера (базовый тип А)

         Из Рис. 6.1 довольно легко понять работу таймера в зависимости от установок отдельных битов регистра управления TxCON. Рассмотрим более подробно, какие биты регистра управления используются при различных режимах работы таймера типа A. Сразу оговорюсь: функциональная схема с элементами цифровой логики, показанная на Рис. 6.1, далеко не полностью соответствует принципиальной схеме модуля Таймера 1, скорее это достаточно хорошее приближение к реальной схеме. Тем не менее, такая функциональная схема в точности отображает поведение таймера, поэтому мы рассмотрим ее более подробно — это значительно облегчит понимание принципов программирования таймера.
         Предположим, что Таймер 1 типа А работает в режиме «таймера» с коэффициентом деления тактовой частоты 1:8. В этом случае бит TGATE = 0, TCS = 0 и мультиплексор MUX 1 пропускает на свой выход тактовый сигнал TCY, частота которого равна половине частоты тактового генератора микроконтроллера. Для дальнейшего прохождения сигнала бит TON регистра управления должен быть установлен в 1. В этом случае сигнал попадает на предделитель, коэффициент деления которого определяется значениями пары битов TCKPS1:TCKPS0. Если, например, требуется поделить частоту сигнала TCY на 8, то TCKPS1:TCKPS0 = 01.
         Поскольку бит TCS = 0, то значение бита синхронизации от внешнего источника TSYNC игнорируется, и сигнал через мультиплексор приходит на схему инкремента регистра таймера TMR1. Содержимое регистра таймера TMR1 постоянно сравнивается с содержимым регистра периода PR1, и если значения равны, то регистр таймера вместо инкрементирования сбрасывается в 0, а на выходе компаратора формируется сигнал, который, проходя через мультиплексор MUX 2, вызывает установку флага прерывания T1IF.
         Второй режим, который мы рассмотрим, — режим запуска/остановки Таймера 1 внешним сигналом с вывода T1CK. При работе в этом режиме таймер, как и в только что рассмотренном режиме «таймера», синхронизируется импульсами тактового сигнала TCY, но запуск и остановка таймера выполняются внешним сигналом: запуск таймера осуществляется по фронту (перепад 0—1) сигнала, а остановка — по спаду (перепад 1—0). Для работы в этом режиме бит TGATE должен быть установлен в 1, а бит TCS должен быть равен 0 (работа от внутреннего источника тактового сигнала).
         При этой комбинации сигнал TCY проходит логику совпадения и через мультиплексор MUX 1 поступает на предделитель и далее с коэффициентом деления 1:8 — на логику управления регистром таймера TMR1. В данном режиме флаг прерывания устанавливается по спаду внешнего сигнала, который переключает D-триггер T1, работа которого разрешается ВЫСОКИМ уровнем вследствие установки бита TGATE. Сигнал с выхода Q триггера T1 инициирует установку флага прерывания T1IF. В этом режиме установка флага прерывания вызывается не переполнением Таймера 2, а только перепадом 1—0 внешнего сигнала.
         Третий режим — работа от внешнего источника тактового сигнала. В качестве источника может быть использован генератор (выделен пунктирной линией), к выводам SOSC0 и SOSC1 которого подключается кварцевый резонатор.
         Как альтернатива, к выводу SOSC0 можно подключить внешний тактовый генератор. В любом случае биты конфигурации микроконтроллера должны быть установлены соответствующим образом. В этом режиме бит TCS должен быть установлен в 1, тогда внешний тактовый сигнал проходит через мультиплексор MUX 1 и предделитель на логику управления регистром таймера. Если дополнительно установлен бит синхронизации TSYNC, то внешний сигнал будет синхронизирован внутренним тактовым сигналом. Логика формирования сигнала прерывания будет работать так же, как и в режиме «таймера».
    Назначение битов регистра управления T1CON Таймера 1 (тип А) приводится в Табл. 6.1.

    Таблица 6.1. Назначение битов регистра T1CON
    Позиция бита Обозначение Описание
    15 TON Запуск/остановка таймера:
    1 – запуск таймера;
    0 – остановка таймера
    14   Читается как 0
    13 TSIDL Режим остановки в режиме «холостого хода» микроконтроллера:
    1 – работа таймера прекращается в режиме «холостого хода»;
    0 – таймер продолжает работать
    12-7   Читается как 0
    6 TGATE Разрешение запуска/остановки таймера внешним сигналом. При установленном бите TCS значение этого бита игнорируется. Если TCS = 0, то установка бита в 1 разрешает запуск/остановку таймера, а сброс в 0 запрещает работу в этом режиме
    5-4 TCKPS1–TCKPS0 Биты установки предделителя:
    11 – соотношение 1:256
    10 – соотношение 1:64
    01 – соотношение 1:8
    00 – соотношение 1:1
    3   Читается как 0
    2 TSYNC Если TCS = 1, то:
    1 – выполняется синхронизация внешнего сигнала;
    0 – не выполняется синхронизация внешнего сигнала.
    Если TCS = 0 (используется внутренний источник тактовой частоты), то бит игнорируется и читается как 0
    1 TCS Выбор источника тактовой частоты:
    1 – используется сигнал на выводе T1CK;
    0 – используется внутренний генератор тактовой частоты (FOSC/2)
    0   Читается как 0

         Кроме Таймера 1, который относится к типу А, в большинстве микроконтроллеров PIC24F есть Таймер 2 и Таймер 4, которые относятся к типу В.
         Таймер типа B позволяет (вместе с таймером типа С) организовать 32-битный таймер. Для этого в таймерах типа В предусмотрен бит Т32 (бит 3 регистра TxCON), который, будучи установлен, разрешает функционирование в 32-битном режиме.
         В большинстве микроконтроллеров PIC24F имеются также и таймеры типа С (Таймер 3 и Таймер 5). Таймер типа С имеет следующие специальные возможности:
    • вместе с таймером типа В может образовывать 32-битный таймер;
    • как минимум один таймер типа C может управлять процессом преобразования АЦП.
         Формирование 32-битного таймера выполняется с помощью либо Таймера 2 типа B и Таймера 3 типа C, либо Таймера 4 типа B и Таймера 5 типа C. За исключением бита T32 в регистре управления таймера типа B, все остальные биты таймеров типов В и С идентичны по назначению битам регистра управления таймера типа A.
         Остановимся на некоторых практических аспектах программирования таймеров микроконтроллеров PIC24F.

    6.1. Практическое использование 16-битных таймеров

         Рассмотрим несколько практических примеров программирования таймеров микроконтроллеров PIC24F. В нашем первом проекте мы будем переключать светодиод, подключенный к выводу 0 порта B, каждые 10 секунд, используя метод опроса Таймера 1. В этом проекте тактовая частота микроконтроллера выбрана равной 8 МГц. Аппаратная часть проекта показана на Рис. 6.2.

    Рис. 6.2. Аппаратная часть проекта

         Программную часть нашего проекта создадим с помощью среды MPLAB IDE. В созданный проект включим исходный текст программы на языке Си, приведенный далее:

    #include <p24fj128ga010.h>
    _CONFIG2(FCKSM_CSDCMD&OSCIOFNC_ON&POSCMOD_HS&FNOSC_PRI)
    #define SYSCLK 8000000
    #define t1 0.5
    #define PREG SYSCLK/2*t1/256
    #define DELAY 20
    #define PORTB_0 PORTBbits.RB0
    void main(void)
    {
       int cnt = 0;
       AD1PCFG = 0xffff;
       TRISB = 0xfffe;
       PR1 = PREG;
       TMR1 = 0;
       T1CON = 0x8030;
       while (1)
       {
         if (_T1IF == 1)
         {
           _T1IF = 0;
           if (cnt == DELAY)
           {
             cnt = 0;
             PORTB_0 = ~PORTB_0;
           }
           cnt++;
         }
       }
    }

         Для установки временных интервалов в программе нам нужно просчитать некоторые константы. Мы приняли, что светодиод должен переключаться каждые 10 секунд. Если выбрать интервал срабатывания таймера t1 равным 0.5 с, то программе понадобится 20 циклов опроса, прежде чем светодиод переключится. Таким образом, мы определяем в программе две константы:
    #define t1 0.5
    #define DELAY 20

         Здесь t1 определяет время срабатывания Таймера 1, а DELAY определяет общий интервал задержки, равный 20 * 0.5 = 10 с. Далее нам нужно определить число, которое следует записать в регистр периода Таймера 1, чтобы получить интервал перезагрузки, равный 0.1 с (константа t1). Значение регистра периода (обозначим его PREG) по известному интервалу времени t1 можно вычислить из формулы:
    PREG = (t1 x Fosc/2)/Prescaler,

    где PREG — значение регистра периода; t1 — заданный интервал времени; Fosc — тактовая частота (во всех наших примерах Fosc = 8 МГц); Prescaler — значение предделителя (выберем значение, равное 256).
         После того, как значение регистра периода определено, мы можем запустить таймер:
    PR1 = PREG;
    TMR1 = 0;
    T1CON = 0x8030;

         Собственно для запуска таймера нужно установить бит 15 (TON) регистра управления T1CON и значение коэффициента деления в битах 5:4 (TCKPS1:TCKPS0 = 11 для значения 256). Помимо этого мы обнуляем регистр таймера TMR1.
         При указанных настройках Таймер 1 будет перегружаться каждые 0.1 с, при этом будет устанавливаться флаг прерывания _T1IF (независимо от того, разрешено или нет прерывание таймера). Именно по состоянию флага и можно будет определить, перегрузился ли таймер, т. е. прошло ли 0.1 с. Когда значение счетчика cnt в цикле while достигнет 20, то бит 0 порта B инвертируется и светодиод переключится.
    Команда
    AD1PCFG = 0xffff;

    переводит порт B в режим ввода/вывода цифровых сигналов, а команда
    TRISB = 0xfffe;

    переключает вывод 0 на выход.
         Данная программа позволяет работать с относительно большими интервалами времени. Если требуются интервалы времени порядка единиц секунд, то предыдущий листинг исходного текста можно значительно упростить. Например, если требуется временная задержка 2 с, то можно использовать такую программу:

    #include <p24fj128ga010.h>
    _CONFIG2(FCKSM_CSDCMD&OSCIOFNC_ON&POSCMOD_HS&FNOSC_PRI)
    #define SYSCLK 8000000
    #define t1 2
    #define PREG SYSCLK/2*t1/256
    #define DELAY 31250
    #define PORTB_0 PORTBbits.RB0
    void main(void)
    {
       AD1PCFG = 0xffff;
       TRISB = 0xfffe;
       PR1 = PREG;
       TMR1 = 0;
       T1CON = 0x8030;
       while (1)
       {
         while (TMR1 < DELAY);
         TMR1 = 0;
         PORTB_0 = ~PORTB_0;
       }
    }

         Здесь мы указываем значение интервала времени прямо в переменной t1 (2), а для константы DELAY устанавливаем значение 31250, что соответствует значению регистра периода PR1 для 2-секундного интервала. В этом случае ожидание реализуется в цикле
    while (TMR1 < DELAY)

    что исключает необходимость использования флага прерывания и счетчика cnt.
         Для приложений реального времени цикл ожидания в основной программе — непозволительная роскошь, поэтому в таких случаях можно использовать прерывание Таймера 1. Модифицируем текст только что рассмотренной программы так, чтобы инверсия бита 0 порта B осуществлялась в обработчике прерывания. Новая программа будет выглядеть следующим образом:

    #include <p24fj128ga010.h>
    _CONFIG2(FCKSM_CSDCMD&OSCIOFNC_ON&POSCMOD_HS&FNOSC_PRI)
    #define SYSCLK 8000000
    #define t1 2
    #define PREG SYSCLK/2*t1/256
    #define PORTB_0 PORTBbits.RB0
    void __attribute__ ((interrupt)) _T1Interrupt(void)
    {
       _T1IF = 0;
       PORTB_0 = ~PORTB_0;
    }
    void main(void)
    {
       AD1PCFG = 0xffff;
       TRISB = 0xfffe;
       _T1IF = 0;
       _T1IP = 3;
       _T1IE = 1;
       PR1 = PREG;
       TMR1 = 0;
       T1CON = 0x8030;
       while (1)
       {
       . . .
       // другие операторы программы
       . . .
       }
    }

         Для переключения светодиода в этой программе используется обработчик прерывания _T1Interrupt, первая команда которого сбрасывает флаг прерывания, а вторая, собственно, и выполняет переключение бита 0 порта B. В основной программе необходимо установить разрешение прерывания Таймера 1, что выполняет оператор
    _T1IE = 1;

         Перед этим нужно сбросить флаг прерывания и, если необходимо (хоть это и не обязательно), установить приоритет прерывания (по умолчанию он равен 4), что и выполняется операторами
    _T1IF = 0;
    _T1IP = 3;

         В данном случае приоритет прерывания Таймера 1 установлен равным 3.
         Команды инициализации и запуска Таймера 1 те же, что и в предыдущих листингах, поэтому останавливаться на них я не буду.
         Кроме внутренней синхронизации, в Таймере 1 можно применить внешнюю синхронизацию, подавая сигнал на вывод T1CK микроконтроллера (Рис. 6.3).

    Рис. 6.3. Подключение внешнего источника синхронизации Таймера 1

         В этом проекте используется внешний источник сигнала с частотой 1000 Гц.
    Исходный текст программы нашего модифицированного проекта представлен далее:

    #include <p24fj128ga010.h>
    _CONFIG2(FCKSM_CSDCMD&OSCIOFNC_ON&POSCMOD_HS&FNOSC_PRI)
    #define EXTCLK 1000
    #define t1 16
    #define PREG EXTCLK*t1
    #define PORTB_0 PORTBbits.RB0
    void __attribute__ ((interrupt)) _T1Interrupt(void)
    {
       _T1IF = 0;
       PORTB_0 = ~PORTB_0;
    }
    void main(void)
    {
       AD1PCFG = 0xffff;
       TRISB = 0xfffe;
       TRISC = 0xffff;
       _T1IF = 0;
       _T1IP = 3;
       _T1IE = 1;
       PR1 = PREG;
       TMR1 = 0;
       T1CON = 0x8006;
       while (1)
    {
    }
    }

         Здесь значение частоты 1000 Гц определяется через константу EXTCLK. Выберем время задержки, равное 16 с (константа t1), а коэффициент деления частоты равным 1. В этом случае значение регистра периода PR1 будет вычисляться как EXTCLK * t1.
         Переключение светодиода будет выполняться, как и в предыдущем проекте, в обработчике прерывания Таймера 1, но сами настройки таймера изменятся.
         Поскольку мы решили обойтись без предварительного деления частоты и должны установить биты TCS и TSYNC регистра управления T1CON, то команда для настройки и запуска выглядит так:
    T1CON = 0x8006;

         То же самое можно выполнить при помощи операторов
    T1CONbits.TSYNC = 1;
    T1CONbits.TCS = 1;
    T1CONbits.TON = 1;

         Кроме того, поскольку внешние синхроимпульсы приходят через порт C, то переключим выводы порта на вход:
    TRISC = 0xffff;

         Во всем остальном исходный текст нам уже знаком и дополнительный анализ кода не нужен. Все наши проекты касались режима работы «таймер» и анализировались на примере работы Таймера 1.
         Перейдем к обсуждению следующего режима работы, в котором запуск и остановка таймера осуществляются внешним сигналом на входе TxCK. В этом режиме таймер запускается по нарастающему фронту сигнала (перепад 0—1) и останавливается по спадающему фронту (перепад 1—0). При обнаружении на входе TxCK перепада 1—0 устанавливается соответствующий флаг прерывания _TxIF. Если задан обработчик соответствующего прерывания, то можно выполнить определенные действия по обработке этого события. Этот режим работы мы рассмотрим в следующих примерах. Аппаратная часть проектов, с которыми мы будем работать, показана на Рис. 6.4.

    Рис. 6.4. Аппаратная часть проекта

         В этом проекте используется внутренний тактовый генератор с частотой 8МГц. Схема работает следующим образом: при нажатии кнопки на входе T1CK формируется перепад напряжения из НИЗКОГО уровня в ВЫСОКИЙ.
         При этом запускается Таймер 1, который выполняет инвертирование бита 0 порта B, в результате чего светодиод периодически включается и выключается. Интервал переключения задается программным способом и для нашего проекта устанавливается равным 2 с. Если отпустить кнопку, то на входе T1CK появится НИЗКИЙ уровень и таймер остановится. В результате бит 0 порта B перестанет переключаться и светодиод останется в последнем перед отключением таймера состоянии.
         С помощью мастера проектов среды MPLAB IDE создадим проект для микроконтроллера PIC24FJ128GA010 и добавим в него файл со следующим исходным текстом:

    #include <p24fj128ga010.h>
    _CONFIG2(FCKSM_CSDCMD&OSCIOFNC_ON&POSCMOD_HS&FNOSC_PRI)
    #define SYSCLK 8000000
    #define t1 2
    #define PREG SYSCLK/2*t1/256
    #define DELAY 31250
    #define PORTB_0 PORTBbits.RB0
    void main(void)
    {
       AD1PCFG = 0xffff;
       TRISB = 0xfffe;
       TRISC = 0xffff;
       PR1 = PREG;
       TMR1 = 0;
       T1CON = 0x8070;
       while (1)
       {
         while (TMR1 < DELAY);
         TMR1 = 0;
         PORTB_0 = ~PORTB_0;
       }
    }

         Для нашего проекта выбираем интервал переключения (константа t1), равный 2 с. Значение регистра периода (константа PREG) в этом случае будет равно 31250. Это же значение будет иметь и константа DELAY, с которой будет сравниваться содержимое регистра Таймера 1.
         Как и в предыдущих примерах, записываем значение PREG в регистр периода Таймера 1 (PR1) и значение 0 — в регистр Таймера 1 (TMR1). В регистре управления и состояния T1CON устанавливаем биты TON, биты предделителя для коэффициента деления 256 и бит синхронизации таймера внешним сигналом (бит 6, TGATE):
    T1CON = 0x8070;

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

       while (TMR1 < DELAY);
       TMR1 = 0;
       PORTB_0 = ~PORTB_0;

         Как обычно, в начале программы следует установить соответствующие режимы работы портов B и C.
         Режим работы таймера с запуском/остановкой от внешнего сигнала можно использовать во многих весьма полезных практических приложениях. Например, можно измерить длительность сигнала, подаваемого на вход TxCK. Принцип измерения длительности сигнала показан на Рис. 6.5.

    Рис. 6.5. Принцип измерения длительности сигнала

         Для измерения длительности сигнала можно использовать следующий принцип: фронт (или спад) сигнала запускает таймер (счетчик), который будет ра ботать до следующего спада (фронта) сигнала. Таким образом, в интервале между изменениями уровней сигнала пройдет определенное число периодов таймера. Если период следования импульсов таймера заранее известен (его можно задать программно) и равен Ттаймера, а количество таких периодов равно N, то длительность сигнала Тсигнала можно определить по формуле:
    Тсигнала = Ттаймера x N.

         Разработаем демонстрационный проект, в котором будем измерять длительность импульсного сигнала, подаваемого на вход T1CK микроконтроллера. Аппаратная часть проекта реализуется достаточно просто: к выводу T1CK подсоединяется источник импульсного сигнала. Принципиально несложно измерить длительность любого, не только чисто цифрового сигнала, но для этого на выходе источника сигнала следует включить формирователь сигнала прямоугольной формы. Для этого можно использовать, например, триггер Шмита 74HC14 или аналоговый компаратор с высокой скоростью нарастания выходного напряжения. В качестве источника тактовой частоты нашей схемы используется внутренний генератор 8 МГц.
         Программную часть, как обычно, создаем в среде MPLAB IDE. Исходный текст программы представлен далее:

    #include <p24fj128ga010.h>
    _CONFIG2(FCKSM_CSDCMD&OSCIOFNC_ON&POSCMOD_HS&FNOSC_PRI)
    #define SYSCLK 8000000
    #define t1 0.01
    #define PREG SYSCLK/2*t1/256
    #define DELAY 156
    unsigned int cnt = 0;
    unsigned int time1;
    void __attribute__ ((interrupt)) _T1Interrupt(void)
    {
       time1 = cnt;
       cnt = 0;
       _T1IF = 0;
    }
    void main(void)
    {
       TRISC = 0xffff;
       _T1IF = 0;
       _T1IP = 3;
       _T1IE = 1;
       PR1 = PREG;
       TMR1 = 0;
       T1CON = 0x8070;
       while (1)
       {
         if (_T1IF != 1)
         {
           while (TMR1 < DELAY);
           cnt++;
           TMR1 = 0;
         }
       }
    }

         В этой программе мы воспользуемся прерыванием Таймера 1. Если запуском/остановкой таймера управляет внешний сигнал, то прерывание возникает только в том случае, если на входе T1CK происходит смена ВЫСОКОГО уровня сигнала на НИЗКИЙ. Прерывание не вызывается переполнением таймера, как это имеет место в обычном режиме. Таким образом, в обработчике прерывания Таймера 1 можно организовать обработку значения счетчика, в котором на момент возникновения прерывания будет храниться количество периодов (число N, см. Рис. 6.5).
         В программе объявлено несколько констант и переменных. Константа t1 равна интервалу срабатывания Таймера 1 (0.01 с). Переменная cnt хранит текущее значение количества периодов, прошедших с момента запуска Таймера 1 перепадом напряжения 0—1 на входе T1CK. В переменную time1 помещается значение счетчика cnt в момент возникновения прерывания (перепад 1—0 на входе T1CK). Обработчик прерывания _T1Interrupt сохраняет количество периодов в переменной time1, обнуляет счетчик cnt и сбрасывает флаг прерывания _T1IF.
         Длительность импульсного сигнала на входе T1CK (обозначим ее Т) после завершения работы обработчика прерывания можно вычислить по формуле:
    Т = t1 x time1.

         Если в обработчике прерывания в переменную time1 записывается значение 178, то длительность импульсного сигнала будет равна 0.01 x 178 = 1.78 секунды. Поскольку это демонстрационная программа, то мы не заботимся о высокой точности отсчета временных интервалов. В приложениях реального времени, особенно при измерении небольших интервалов времени, нужно учитывать и другие факторы, влияющие на точность: время выполнения команд микроконтроллера, время инициализации прерывания и т. д.
         До сих пор мы рассматривали функционирование 16-битного таймера.
         Напомню, что в микроконтроллерах PIC24F допускается работа таймеров и в 32-битном режиме.

    6.2. Работа таймеров в 32-битном режиме

         Для работы в 32-битном режиме могут использоваться таймеры типа B и C.
         В нашем примере 32-битный таймер будет реализован на Таймере 2 и Таймере 3. Аппаратная часть нашего проекта показана на Рис. 6.2. Здесь в качестве источника тактового сигнала используется внутренний тактовый генератор с частотой 8 МГц. Программная часть разработана с помощью мастера проектов среды MPLAB IDE. Текст программы будет следующим:
    #include <p24fj128ga010.h>
    _CONFIG2(FCKSM_CSDCMD&OSCIOFNC_ON&POSCMOD_HS&FNOSC_PRI)
    #define SYSCLK 8000000
    #define t1 8
    #define PREGCONST SYSCLK/2*t1
    #define PORTB_0 PORTBbits.RB0
    void __attribute__ ((interrupt)) _T3Interrupt(void)
    {
       PORTB_0 = ~PORTB_0;
       _T3IF = 0;
    }
    void main(void)
    {
       long PREG = PREGCONST;
       int *pPREG = &PREG;
       AD1PCFG = 0xffff;
       TRISB = 0xfffe;
       T2CON = 0x00;
       T3CON = 0x00;
       TMR3 = 0x00;
       TMR2 = 0x00;
       PR2 = *pPREG++;
       PR3 = *pPREG;
       _T3IP = 0x01;
       _T3IF = 0;
       _T3IE = 1;
       T2CONbits.T32 = 1;
       T2CONbits.TON = 1;
       while (1);
    }

         В данной программе мы используем Таймер 2 как базовый таймер типа B, а Таймер 3 — как базовый таймер типа C. При 32-битных операциях с таймерами бит T32 устанавливается в регистре T2CON таймера типа B. Если Таймер 2 и Таймер 3 используются совместно в качестве 32-битного таймера, то регистр T3CON вообще не используется. В качестве регистра управления и состояния используется только регистр T2CON. Тем не менее, если в такой 32-битной конфигурации используется прерывание, то его настройка выполняется для Таймера 3. В регистре Таймера 2 TMR2 будет содержаться младшее 16-битное слово, а в регистре Таймера 3 TMR3 — старшее.
         При каждом переполнении регистра Таймера 2 TMR2 будет увеличиваться значение регистра TMR3. Прерывание инициируется при переполнении обоих регистров.
         В нашей программе мы переключаем бит 0 порта В каждые 8 секунд. Наш 32-битный таймер синхронизируется тактовой частотой процессора, равной 8 МГц, а интервал времени срабатывания таймера выбран равным 8 секундам.
         Константа PREGCONST определяет значение, которое должно быть записано в регистры периода PR2 (младшее слово) и PR3 (старшее слово). В данном случае значение PREGCONST равно 28000000.
         Для хранения значения PREGCONST используется 32-битная переменная PREG типа long. Младшее слово заносится в регистр периода PR2, а старшее — в PR3, для чего используется 16-битный указатель pPREG:

    PR2 = *pPREG++;
       PR3 = *pPREG;

         Как уже упоминалось, для управления 32-битным таймером используется только регистр T2CON. Здесь устанавливаются значение предделителя 1:1, бит T32 и бит запуска таймера TON:

    T2CONbits.T32 = 1;
       T2CONbits.TON = 1;

         Обработка прерывания для Таймера 3 настраивается обычным способом.
         В функции-обработчике прерывания _T3Interrupt всего два оператора: инверсии бита 0 порта А и сброса флага прерывания _T3IF. Это, собственно, все, что касается программной части этого проекта.
         Как и 16-битный, 32-битный таймер можно синхронизировать внешним источником тактовых импульсов, например, как показано на Рис. 6.6.

    Рис. 6.6. Синхронизация внешним сигналом 32-битного таймера

         Здесь внешний сигнал синхронизации подается на вход T2CK (напомню, что 32-битный таймер мы образовали из 16-битных таймеров 2 и 3). Для данной схемы (Рис. 6.6) несложно написать программное обеспечение. Если, например, частота внешнего тактового сигнала составляет 1 кГц, а светодиод должен переключаться каждые 13 с, то исходный текст программы будет выглядеть так:

    #include <p24fj128ga010.h>
    _CONFIG2(FCKSM_CSDCMD&OSCIOFNC_ON&POSCMOD_HS&FNOSC_PRI)
    #define EXTCLK 1000
    #define t1 13
    #define PREGCONST EXTCLK*t1
    #define PORTB_0 PORTBbits.RB0
    void __attribute__ ((interrupt)) _T3Interrupt(void)
    {
       PORTB_0 = ~PORTB_0;
       _T3IF = 0;
    }
    void main(void)
    {
       long PREG = PREGCONST;
       int *pPREG = &PREG;
       AD1PCFG = 0xffff;
       TRISB = 0xfffe;
       TRISC = 0xffff;
       T2CON = 0x00;
       T3CON = 0x00;
       TMR3 = 0x00;
       TMR2 = 0x00;
       PR2 = *pPREG++;
       PR3 = *pPREG;
       _T3IP = 0x03;
       _T3IF = 0;
       _T3IE = 1;
       T2CON = 0x800A;
       while (1);
    }

         В этой программе константа EXTCLK определяет частоту внешнего сигнала (1000 Гц), константа t1 — интервал времени переключения (13 с), а PREGCONST — содержимое регистров периода PR2 и PR3. Значение константы PREGCONST сохраняется в 32-битной переменной PREG, а для загрузки регистров периода используются операторы

    PR2 = *pPREG++;
    PR3 = *pPREG;

         Как и в предыдущем примере, переключение бита 0 порта В осуществляется в обработчике _T3Interrupt, а само прерывание конфигурируется операторами

    _T3IP = 0x03;
    _T3IF = 0;
    _T3IE = 1;

         Поскольку в данной 32-битной конфигурации для управления используется регистр T2CON, то с помощью оператора
    T2CON = 0x800A;

         устанавливаются бит T32, бит разрешения внешней синхронизации (TCS) и бит запуска (TON). Значение предделителя в этой конфигурации выбирается равным 1:1.

    6.3. Часы реального времени

         В рассмотренных проектах мы изучили, как можно получать различные временные интервалы для приложений реального времени. С помощью 32-битных таймеров можно получить достаточно длительные интервалы времени – от единиц до десятков и сотен секунд. Но для получения очень длинных интервалов времени, не зависящих от состояния микроконтроллера, в микроконтроллеры семейства PIC24F включен модуль часов/календаря реального времени (RealTime Clock and Calendar, RTCC), который характеризуется такими параметрами:
    • позволяет работать с часами, минутами и секундами;
    • позволяет работать в формате календаря: неделя/день/месяц/год;
    • имеет функцию «будильника»;
    • диапазон задаваемого времени — 2000…2099 год;
    • коррекция високосных лет;
    • позволяет работать со значениями в формате BCD, что удобно для портативных систем;
    • допускает подстройку временных интервалов пользователем;
    • погрешность ±2.64 с в месяц.
         Для работы модуля RTCC требуется внешний источник тактового сигнала с частотой 32768 Гц. При установленной опции «будильника» на выводе RTCC будет генерироваться сигнал. Особенностью этого модуля является то, что он может работать продолжительное время без «вмешательства» процессора, к тому же он оптимизирован по энергопотреблению, что позволяет длительное время обходиться одной батарейкой. Дискретность отсчета времени составляет 1 с.
         Программирование режима реального времени на уровне регистров — занятие довольно утомительное, но здесь можно пойти по другому пути. Компилятор MPLAB C для PIC24 включает целый ряд библиотечных функций, предназначенных для работы с часами/календарем реального времени. Чтобы воспользоваться этой возможностью, следует включить в программный проект библиотечный файл libpPIC24Fxxx-elf.a или libpPIC24Fxxx-coff.a (в зависимости от того, какой тип объектного файла генерирует компилятор). Кроме того, в текст программы следует включить файл rtcc.h.
         Приведу исходный текст простейшей программы для работы с часами реального времени:

    #include <p24fj128ga010.h>
    #include <rtcc.h>
    _CONFIG2(FCKSM_CSDCMD&OSCIOFNC_ON&POSCMOD_EC&FNOSC_SOSC)
    #define PORTB_0 PORTBbits.RB0
    void main(void)
    {
       rtccTime wTime;
       rtccTime rTime;
       AD1PCFG = 0xffff;
       TRISB = 0xfffe;
       wTime.f.rsvd = 0;
       wTime.f.sec = 0x10;
       wTime.f.min = 0x07;
       wTime.f.hour = 0x10;
       RtccInitClock();
       RtccWrOn();
       RtccWriteTime(&wTime, TRUE);
       do {
       RtccReadTime(&rTime);
       } while (rTime.f.sec != 0x15);
       PORTB_0 = ~PORTB_0;
       while (1);
    }

         Для работы с интервалами времени в программе следует определить одну или несколько специальных переменных типа rtccTime. Такая переменная представляет собой структуру, содержащую элементы sec, min и hour, в которые можно записывать или из которых можно считывать время. Наша программа переключит бит 0 порта В в противоположное значение по прошествии интервала в 5 с. Для этого в переменную wTime с помощью функции RtccWriteTime записывается время 10 часов 7 минут и 10 секунд, после чего программа считывает текущее значение секунд в другую переменную rTime и сравнивает ее со значением 0x15. Обратите внимание, что интервалы времени (часы, минуты, секунды) имеют формат BCD.
         При запуске этой программы необходимо инициализировать часы реального времени с помощью функций RtccInitClock и RtccWrOn. Затем в цикле do…while с помощью функции RtccReadTime непрерывно считывается время, и по достижении нужного значения выполняется переключение бита 0 порта В. С помощью этих библиотечных функций можно реализовать и другие полезные функции модуля часов реального времени, например установить будильник, который будет вызывать функцию-обработчик в определенный момент времени.

    Комментарии