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


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

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

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

Какими микроконтроллерами вы чаще всего пользуетесь


PIC10
PIC12
PIC16
PIC18
PIC24
PIC32
Другими


Последние материалы
  • Тестовая плата для отладки программ на микроконтроллере PIC18F4550
  • Кнопка On/OFF на PIC12F629.
  • Часы с синхронизацией от китайского будильника
  • ШИМ регулятор на PIC16F628A.
  • Счетчики прямого и обратного счета на PIC16F628A.
  • Таймер отключения питания для мультиметра и не только.
  • Измеритель напряжения и тока
  • Маршрутный компьютер для электровелосипеда
  • Простой двухканальный термометр на PIC16F690 и датчиках DS18B20
  • Электронная "Незабудка" для забывчивых
  • Популярные материалы
    Случайная книга
    Программирование устройств на PIC микроконтроллерах » Справочник » Микроконтроллеры PIC. Архитектура и программирование. Часть 5. Программирование прерываний
    Микроконтроллеры PIC. Архитектура и программирование. Часть 5. Программирование прерываний
    Автор публикации: alex Просмотров: 13134 Добавлен: 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 имеют развитую систему прерываний, позволяющую использовать это устройство в приложениях реального времени различной степени сложности. Модуль прерываний микроконтроллеров PIC24F обладает следующими характеристиками:
    • обеспечивает обработку 8 немаскируемых системных прерываний;
    • позволяет задавать до семи уровней приоритетов пользовательских прерываний;
    • позволяет задавать до 118 векторов прерываний в таблице векторов прерываний IVT. При этом каждому прерыванию назначается уникальный вектор;
    • позволяет настроить дополнительную таблицу векторов прерываний (AIVT), которую можно использовать при отладке программ.
         Таблица векторов прерываний IVT размещается в памяти программ, начиная с адреса 0x000004, и содержит 126 векторов прерываний, из которых 8 используются системными прерываниями, а остальные 118 могут использоваться периферийными модулями. Системные прерывания позволяют выполнить обработку серьезных аппаратно-программных сбоев в системе, поэтому их нельзя запретить или замаскировать. Векторы системных прерываний описаны в Табл. 5.1.

    Таблица 5.1. Векторы немаскируемых ловушек
    Номер вектора прерывания Адрес в таблице IVT Источник ошибки
    0 0x000004 Зарезервирован
    1 0x000006 Сбой генератора тактовой частоты
    2 0x000008 Ошибка адресации
    3 0x00000A Ошибка обращения к стеку
    4 0x00000C Ошибка математических вычислений(как правило, деление на 0)
    5 0x00000E Зарезервирован
    6 0x000010 Зарезервирован
    7 0x000012 Зарезервирован

         Каждый вектор прерывания содержит 24-битный адрес, по которому располагается соответствующая функция-обработчик прерывания (Interrupt Service Routine, ISR).
         Любое пользовательское прерывание может быть разрешено или запрещено посредством установки или сброса соответствующего бита в одном из регистров разрешения прерываний IECn. Установка бита соответствующего прерывания в 1 разрешает прерывание, а сброс этого бита — запрещает. В микроконтроллерах PIC24F предусмотрен ряд механизмов, позволяющих разрешить или запретить все прерывания одновременно (кроме, естественно, немаскируемых прерываний).
         При сбросе микроконтроллера все биты разрешения прерываний сбрасываются в 0, запрещая генерацию прерываний, поэтому программа пользователя должна сама устанавливать необходимые разрешения.
         При разработке приложений было бы очень неудобно изменять биты разрешения прерываний, явно указывая регистры IECn, поскольку каждый раз пришлось бы просматривать все эти регистры в поисках соответствующего бита.
         К счастью, в компиляторе MPLAB С предусмотрен очень удобный механизм работы с битами разрешения прерываний посредством макросов, определенных в заголовочных файлах. Например, для разрешения/запрета прерывания Таймера 1 используется макрос _T1IE, для INT3 используется _INT3IE и т. д. Так, разрешить прерывание Таймера 1 в программе на Си можно с помощью оператора
    _T1IE = 1;

         Каждому пользовательскому прерыванию может быть присвоен тот или иной уровень приоритета. Биты управления приоритетом располагаются на трех младших позициях каждой тетрады регистров IPCn. Биты 3 тетрад не используются и читаются как 0. Соответственно уровень приоритета каждого прерывания может изменяться от 1 (самый низкий приоритет) до 7 (наивысший приоритет). Если все биты приоритета прерывания сброшены в 0, то прерывание запрещено. Как и в случае с битами разрешения прерываний, в заголовочных файлах описаны макросы, облегчающие программные манипуляции при установке приоритетов. Например, для установки приоритета 4 прерывания Таймера 1 можно использовать оператор
    _T1IP = 4;

         Для установки приоритета 2 прерывания INT4 можно использовать оператор
    _INT4IP = 2;

         По умолчанию (после сброса микроконтроллера) приоритеты пользовательских прерываний устанавливаются равными 4. Для более подробной информации читатели могут обратиться к соответствующим заголовочным файлам.
         Рассмотрим несколько проектов, в которых демонстрируются различные варианты реализации прерываний в системах с микроконтроллерами PIC24F.
         Вначале вкратце рассмотрим, каким образом организуется обработка прерываний в компиляторе MPLAB С.
         Каждое прерывание, используемое в программе, должно иметь соответствующую функцию-обработчик, прототип которой в MPLAB C для PIC24 можно представить следующим образом:
    void __attribute__ ((__interrupt__)) isr0(void);

         Из объявления прототипа функции-обработчика прерывания isr0 видно, что она не принимает никаких параметров и не возвращает никаких значений. При вызове функции-обработчика компилятор автоматически сохраняет в стеке все рабочие регистры, а также регистр состояния процессора SR.
         Остальные переменные можно сохранить, перечислив их в поле атрибутов.
         Например, для того чтобы компилятор автоматически сохранял и восстанавливал переменные var1 и var2, можно использовать такой прототип функции-обработчика:
    void __attribute__((__interrupt__(__save__(var1, var2)))) isr0(void);

         Обратите внимание, что в объявлении функции-обработчика используется двойное подчеркивание. Компилятор MPLAB С «понимает» и слегка упрощенный вариант объявления:
    void __attribute__ ((interrupt)) isr0(void);

         Если функция-обработчик не требует указания дополнительных параметров, для ее объявления можно применить упрощенный синтаксис. Это делается с помощью макросов, которые объявлены в заголовочных файлах устройств.
    Вот пример такого объявления:
    #define ISR __attribute__((interrupt))

         Тогда функция-обработчик прерывания Таймера 1 может быть объявлена в программе следующим образом:
    #include <p24xxxx.h>
    void _ISR _INT1Interrupt(void);

         Дополнительные аспекты обработки прерываний мы рассмотрим в контексте последующих практических примеров.
         В первом примере демонстрируется техника обработки прерываний с различным уровнем приоритетов. Аппаратная часть проекта показана на Рис. 5.1.
         В этой схеме сигнал прерывания (перепад 0—1 на выходе инвертора) подается одновременно на два входа внешних прерываний INT3 и INT4, имеющих разные приоритеты. Функция-обработчик прерывания INT3 инвертирует выход 0 порта А, а обработчик прерывания INT3 то же самое делает с выходом 7 того же порта. В каждый из обработчиков включена небольшая задержка, реализованная на Таймере 1. Присваивая разные приоритеты прерываниям INT3 и INT4, можно наблюдать за изменениями последовательности переключения светодиодов на выходах 0 и 7. Если приоритет прерывания INT3 выше приоритета INT4, то светодиод, подключенный к выходу 0 порта А, переключится раньше, чем светодиод на выходе 7. Если же установить приоритет прерывания INT3 ниже приоритета INT4, то можно будет наблюдать обратную картину: светодиод на выходе 7 при подаче сигнала прерывания будет переключаться раньше, чем светодиод на выходе 0.
         Программная часть проекта разработана в среде MPLAB IDE на языке Си и включает файл со следующим исходным текстом:
    Микроконтроллеры PIC. Архитектура и программирование. Часть 5. Программирование прерываний
    Рис. 5.1. Аппаратная часть проекта

    #include <p24fj128ga010.h>
    _CONFIG2(FCKSM_CSDCMD&OSCIOFNC_ON&POSCMOD_HS&FNOSC_PRI)
    void Delay(void)
    {
       TMR1 = 0;
       T1CON = 0x8030;
       while (TMR1 < 30000);
       T1CON = 0x0;
    }
    void __attribute__ ((interrupt)) _INT3Interrupt(void)
    {
       _INT3IF = 0;
       _RA0 = ~_RA0;
       Delay();
    }
    void __attribute__ ((interrupt)) _INT4Interrupt(void)
    {
       _INT4IF = 0;
       _RA7 = ~ _RA7;
       Delay();
    }
    void main(void)
    {
       TRISA = 0;
       TRISAbits.TRISA14 = 1;
       TRISAbits.TRISA15 = 1;
       _INT3IF = 0;
       _INT3IP = 7;
       _INT3IE = 1;
       _INT4IF = 0;
       _INT4IP = 3;
       _INT4IE = 1;
       while(1);
    }

         В программе определены два обработчика прерываний: _INT3Interrupt (прерывание INT3) и _INT4Interrupt (прерывание INT4). В обработчиках прерываний биты 0 (прерывание INT3) и 7 (прерывание INT4) порта A инвертируются каждый раз при генерации прерываний. В каждом из обработчиков присутствует функция Delay, реализующая посредством Таймера 1 микроконтроллера небольшую задержку. Первый оператор обоих обработчиков прерываний сбрасывает флаги прерывания (_INT3IF и _INT4IF соответственно).
         Инициализация прерывания INT3 выполняется в основной программе с помощью операторов

    _INT3IF = 0;
    _INT3IP = 7;
    _INT3IE = 1;

         Вначале сбрасывается флаг прерывания (первый оператор), затем прерыванию присваивается уровень приоритета, равный 7, после чего устанавливается флаг разрешения прерывания. Точно та же последовательность действий выполняется и для инициализации прерывания INT4, только уровень приоритета для этого прерывания устанавливается равным 3, т. е. ниже, чем для INT3.
         Таким образом, при возникновении ситуации, когда сигналы прерывания поступают на оба входа одновременно, обработчик прерывания INT3 выполнится первым. Эта ситуация как раз и моделируется схемой, показанной на Рис. 5.1.
         Выводы прерываний должны быть сконфигурированы как входы, поэтому соответствующие биты регистра TRIS порта А устанавливаются в 1 с помощью операторов

    TRISAbits.TRISA14 = 1;
    TRISAbits.TRISA15 = 1;

         Задавая разные уровни приоритетов прерываниям INT3 и INT4, можно наблюдать различную очередность переключения светодиодов, подключенных к выходам порта А.
         Микроконтроллеры PIC24F позволяют генерировать прерывания программно, устанавливая флаг соответствующего прерывания в регистре IFSn. Например, можно разработать приложение, в котором функция-обработчик прерывания Таймера 1 вызывала бы прерывание INT3. В этом случае прерывание INT3 будет вложенным по отношению к прерыванию Таймера 1. Микроконтроллеры PIC24F позволяют использовать вложенные прерывания, при этом вложенные прерывания должны иметь более высокий приоритет по сравнению с текущим прерыванием. Это означает, что прерывание INT3, вызванное из обработчика прерывания Таймера 1, будет обработано раньше только в том случае, если его приоритет будет выше, чем у текущего прерывания. Если приоритет прерывания INT3 будет ниже, чем у прерывания Таймера 1, то обработчик INT3 будет вызван по завершении обработчика прерывания Таймера 1.
         В нашем следующем проекте будет продемонстрирована последовательность обработки вложенных прерываний.
         Аппаратная часть проекта показана на Рис. 5.2.
         В этой схеме светодиод, подключенный к выводу RA0 порта А, будет переключаться каждый раз при вызове прерывания INT3. Светодиод, подключенный к выводу RA7, будет переключаться при вызове прерывания Таймера 1. Визуально очередность переключения светодиодов будет зависеть от приоритета вложенного прерывания INT3.
         Программная часть демонстрационного проекта разработана в MPLAB IDE и включает файл с программой, исходный текст которой приводится далее:
    Микроконтроллеры PIC. Архитектура и программирование. Часть 5. Программирование прерываний
    Рис. 5.2. Аппаратная часть проекта

    #include <p24fj128ga010.h>
    _CONFIG2(FCKSM_CSDCMD&OSCIOFNC_ON&POSCMOD_HS&FNOSC_PRI)
    #define SYSCLK 8000000
    #define t1 1
    #define PREG SYSCLK/2/256*t1
    void Delay(void)
    {
       long cnt = 100000;
       while (cnt != 0)cnt—;
    }
    void __attribute__ ((interrupt)) _T1Interrupt(void)
    {
       _T1IF = 0;
       _INT3IF = 1;
       _RA7 = ~_RA7;
       Delay();
    }
    void __attribute__ ((interrupt)) _INT3Interrupt(void)
    {
       _INT3IF = 0;
       _RA0 = ~_RA0;
       Delay();
    }
    void main(void)
    {
       TRISA = 0;
       TRISAbits.TRISA14 = 1;
       PR1 = PREG;
       TMR1 = 0;
       IFS0bits.T1IF = 0;
       _T1IP = 5;
       _T1IE = 1;
       T1CON = 0x8030;
       _INT3IF = 0;
       _INT3IP = 3;
       _INT3IE = 1;
       while(1);
    }

         В этой программе определены две функции-обработчики прерываний. Функция _T1Interrupt обрабатывает прерывание таймера, которое при тактовой частоте микроконтроллера 8 МГц, указанных настройках регистра периода PR1 и предделителя будет вызываться с интервалом в 1 с. Функция _INT3Interrupt вызывается из обработчика _T1Interrupt путем установки флага прерывания _INT3IF. Функция Delay() реализует временную задержку и используется для того, чтобы пользователь мог визуально отследить очередность переключения светодиодов при различных настройках параметров прерываний. Исходный уровень приоритета для прерывания Таймера 1 устанавливается равным 5, а уровень приоритета для прерывания INT3 — равным 3. Поскольку приоритет INT3 ниже, чем приоритет Таймера 1, то, несмотря на установку флага прерывания _INT3IF в функции-обработчике _T1Interrupt, обработчик INT3 не сможет выполниться, пока не завершится обработка прерывания Таймера 1.
         Визуально светодиод на выводе 7 порта А будет переключаться первым, и только затем, с определенной задержкой, переключится светодиод на выводе 0. Если теперь изменить приоритеты прерываний так, чтобы INT3 имело высший приоритет, например 6, то картина изменится. Первым будет переключаться светодиод на выводе 0 порта А, поскольку вначале будет выполняться обработчик вложенного прерывания INT3, и только затем продолжит выполнение обработчик прерывания Таймера 1.
         Еще несколько слов о программе. Параметры Таймера 1 настраиваются с помощью операторов

    PR1 = PREG;
    TMR1 = 0;
    T1CON = 0x8030;

         В регистр периода PR1 Таймера 1 записывается значение, соответствующее времени срабатывания Таймера 1 и равное 1 с. Здесь учитывается то, что тактовая частота микроконтроллера, равная 8 МГц, делится на 2 и на коэффициент деления предделителя, равный 256. В результате получаем необходимое значение для регистра периода (оно равно константе PREG). Таймер 1 запускается при установке бита 15 (TON) регистра управления T1CON и битов 5 и 4 предделителя (отношение 1:256). Настройки прерывания Таймера 1 выполняются по стандартной схеме с помощью операторов

    IFS0bits.T1IF = 0;
    _T1IP = 5;
    _T1IE = 1;

         Здесь уровень приоритета прерывания Таймера 1 устанавливается равным 5.
         Обработку вложенных прерываний можно запретить, если установить бит15 (NSTDIS) регистра управления INTCON1. В этом случае пользовательские установки приоритетов будут игнорироваться. Если, например, в исходный текст нашей программы включить оператор
    INTCON1bits.NSTDIS = 1;

         то все прерывания будут работать с одинаковым приоритетом, а вложенные прерывания работать не будут. В нашем случае установка флага прерывания _INT3IF в обработчике Таймера 1 не вызовет прерывания его выполнения, т. е. обработчик прерывания INT3 всегда будет выполняться после обработчика Таймера 1.
         В ряде случаев возникает необходимость запретить на некоторое время пользовательские прерывания, например при выполнении критических участков программного кода. Можно применить один из двух способов. Простейший вариант — использовать инструкцию disi микроконтроллера, которая позволяет на определенный интервал времени запретить прерывания с приоритетами 1…6. Прерывания с приоритетом 7 и выше с помощью этой инструкции запретить нельзя.
         Второй способ, более эффективный, позволяет запретить все прерывания без каких-либо ограничений по времени. Для этого можно использовать следующую последовательность операций:
    • сохранить регистр состояния микроконтроллера в стеке с помощью инструкции push SR;
    • записать в биты приоритета IPL2…IPL0 процессора (биты 7…5 регистра состояния процессора SR) значение , что позволит установить уровень 7 приоритета процессора и запретить все пользовательские прерывания. Для установки этих битов можно выполнить, например, инструкцию ior (логическое ИЛИ) над содержимым младшего байта (SRL) регистра состояния SR и значения 0xE0.
         Если критический участок программного кода пройден, можно восстановить состояние процессора и разрешить прерывания инструкцией pop SR.
         Рассмотрим практический пример, иллюстрирующий запрещение/разрешение прерываний. Аппаратная часть проекта показана на Рис. 5.3.
    Микроконтроллеры PIC. Архитектура и программирование. Часть 5. Программирование прерываний
    Рис. 5.3. Аппаратная часть проекта

         Работа схемы во многом напоминает функционирование аппаратной части из предыдущего проекта, но имеется и существенное отличие: при нажатии на кнопку разблокируется работа прерываний Таймера 1 и INT3. Прерывание INT3 вызывается из обработчика _T1Interrupt. При запуске программы все прерывания блокируются, поэтому никакого изменения состояния светодиодов наблюдаться не будет. При нажатии на кнопку все пользовательские прерывания разблокируются, и светодиоды начнут мигать.
         Исходный текст программы, написанной на языке Си, показан далее:

    #include <p24fj128ga010.h>
    _CONFIG2(FCKSM_CSDCMD&OSCIOFNC_ON&POSCMOD_HS&FNOSC_PRI)
    #define SYSCLK 8000000
    #define t1 1
    #define PREG SYSCLK/2/256*t1
    void Delay(void)
    {
       long cnt = 100000;
       while (cnt != 0)cnt—;
    }
    void __attribute__ ((interrupt)) _T1Interrupt(void)
    {
       _T1IF = 0;
       _INT3IF = 1;
       _RA7 = ~_RA7;
       Delay();
    }
    void __attribute__ ((interrupt)) _INT3Interrupt(void)
    {
       _INT3IF = 0;
       _RA0 = ~_RA0;
       Delay();
    }
    void main(void)
    {
       AD1PCFG = 0xffff;
       TRISB = 0xffff;
       TRISA = 0;
       TRISAbits.TRISA14 = 1;
       PR1 = PREG;
       TMR1 = 0;
       _T1IF = 0;
       _T1IP = 5;
       _T1IE = 1;
       T1CON = 0x8030;
       _INT3IF = 0;
       _INT3IP = 6;
       _INT3IE = 1;
       asm ("push SR"
       "\n mov #0xE0, w0"
       "\n ior SR");
    // Сюда можно поместить критический участок
    // программного кода
       while (_RB0 != 1);
       asm("pop SR");
       while(1);
    }

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

    push SR
    mov #0xE0, w0
    ior SR

         выполняет сохранение в стеке содержимого регистра состояния (push SR), устанавливает уровень приоритета 7 для процессора, запрещая тем самым пользовательские прерывания (mov #0xE0, w0 и ior SR). Таким образом, при запуске программы никакие прерывания обрабатываться не будут. Для разрешения прерываний нужно просто извлечь из стека старое содержимое регистра состояния SR (pop SR). Это произойдет при нажатии на кнопку (см. Рис. 5.3), инициирующую подачу сигнала ВЫСОКОГО уровня на вывод 0 порта В. После этого можно будет наблюдать выполнение обработчиков прерываний по смене состояния светодиодов.
         Вместо ассемблерной вставки можно воспользоваться макросами, специально предусмотренными для запрещения/разрешения всех прерываний. Тогда ту часть программного кода, в которой используются команды ассемблера, можно переписать следующим образом:

    int current_cpu_ipl;
    . . .
    SET_AND_SAVE_CPU_IPL(current_cpu_ipl, 7);
    // Сюда можно поместить критический участок
    // программного кода
    while (_RB0 != 1);
    RESTORE_CPU_IPL(current_cpu_ipl);

         Переменная current_cpu_ipl будет содержать текущее на момент обращения значение приоритета процессора. Установка уровня приоритета равным 7 выполняется макросом SET_AND_SAVE_CPU_IPL, а восстановление предыдущего уровня приоритета и разблокирование пользовательских прерываний осуществляется макросом RESTORE_CPU_IPL.

    Комментарии