Здравствуй, уважаемый хабрапользователь.
В предыдущей статье мы начали рассматривать программирование под MSP430.
Материал, описанный в данной статье, позволит в общих чертах ознакомиться с прерываниями и понять некоторые тонкости MSP430.
Введение
Прерывание (англ. interrupt) — сигнал, сообщающий процессору о наступлении какого-либо события. При этом выполнение текущей последовательности команд приостанавливается и управление передаётся обработчику прерывания, который реагирует на событие и обслуживает его, после чего возвращает управление в прерванный код.
Механизм прерываний создан для обеспечения максимально оперативной реакции программы на определенные события. Это очень важная часть знакомства с любым микроконтроллером.
Прерывания
Начнём с небольшого примера.
- #include "msp430f2274.h"
- void main()
- {
- WDTCTL = WDTPW + WDTHOLD;
- P1DIR &= ~BIT2;
- P1REN |= BIT2;
- P1IE |= BIT2; // Разрешение прерываний на P1.2
- P1IES |= BIT2; // Прерывание происходит по 1/0 (отпусканию/нажатию)
- P1IFG &= ~BIT2; // Очистка флага прерываний для P1.2
- P1DIR |= BIT0 + BIT1;
- P1OUT |= BIT0;
- P1OUT &= ~BIT1;
- __bis_SR_register(GIE); // Установка флага глобального разрешения прерываний
- while(true);
- }
- #pragma vector=PORT1_VECTOR
- __interrupt void P1INT() // Обработчик прерывания
- {
- P1IE &= ~BIT2; // Запрет прерываний на P1.2
- P1OUT ^= BIT0; // P1.0 меняет своё состояние
- P1OUT ^= BIT1; // P1.1 меняет своё состояние
- for(volatile unsigned int i = 30000; i != 0; i--); // Задержка
- P1IE |= BIT2; // Разрешение прерываний на P1.2
- P1IFG &= ~BIT2; // Очистка флага прерываний для P1.2
- }
В данном примере по нажатию на кнопку (P1.2) происходит прерывание, которое меняет состояние двух светодиодов (P1.0 и P1.1). Давайте разберёмся как же это происходит.
PxIE разрешает прерывания для пинов порта Px. Значение «1», записанное в определенный разряд позволяет получать прерывания от определенного пина.
PxIES определяет по какому уровню сигнала будет происходить прерывание. В нашем случае это означает, что единица, помещенная в конкретный разряд данного регистра, позволит получать событие нажатия кнопки. И наоборот, ноль позволит получать событие отпускания кнопки.
Напомню, что кнопка в данном примере «подтянута» до единицы, это означает что нажатая кнопка имеет значение ноль.
На схеме подключения резистор R1 нарисован показательно, на самом деле он находится внутри микроконтроллера и включается регистром P1REN.
PxIFG и есть флаг прерывания. В случае возникновения события нажатия кнопки он будет установлен в единицу, что и вызовет обработчик прерывания (__interrupt void P1INT()). Соответственно, сразу после обработки события флаг необходимо сбросить.
Попробуйте убрать из примера строчку 32 в конце обработчика, и тогда, сразу после первого нажатия кнопки, лампочки будут переключаться так, как будто вы постоянно нажимаете на кнопку.
__bis_SR_register(GIE) устанавливает флаг глобального разрешения прерываний (Global Interrupt Enable) в status register. Фактически это эквивалентно записи SR |= GIE, но мы можем обращаться к регистру SR только по средствам функций __bis_SR_register и __get_SR_register.
Ниже приведена схема битов в SR регистре.
0, 1, 2 и 8 биты — это биты математических операций.
4, 5, 6, 7 — биты управления уровнями энергопотребления, за счёт уменьшения частоты или отключения АЛУ.
Ну и собственно бит 3 — это глобальное разрешение прерываний. Пока этот бит не установлен в единицу, никакие прерывания обрабатываться не будут.
#pragma vector — директива, которая определяет что нижеследующая функция является обработчиком указанного прерывания.
Список всех доступных векторов прерываний можно посмотреть в заголовочном файле для Вашего контроллера. В данном случае это msp430f2274.h.
Дребезг контактов
Про это явление можно подробно прочитать в википедии. Дребезг контактов возникает при нажатии кнопки, благодаря чему обработчик прерываний будет вызван многократно. В данном примере этого удалось избежать следующим образом:
- Сразу после вызова обработчика устанавливается запрет прерываний (строка 25);
- после переключения состояний светодиодов делается задержка (строка 30);
- снова устанавливается разрешение прерываний (строка 31).
Директива volatile в 30 строке кода означает, что переменная i не будет оптимизироваться при компиляции. Если её убрать, то задержки не будет.
Заключение
После прочтения предыдущей статьи, коллеги и друзья упрекнули меня в том, что я не уделяю достаточно внимания мелочам. В этот раз я постарался исправить этот недостаток и сделать материал статьи ещё более доступным. Однако это значительно увеличило объем информации.
В следующий раз я постараюсь рассмотреть передачу данных с контроллера на персональный компьютер по средствам программатора и, в зависимости от полученного объема, watchdog.
Я надеюсь, что эта статья оказалась полезна тебе, читатель.