В данной статье я открою цикл статей, посвящённых работе с прерываниями. Прерывание – это сигнал процессору микроконтроллера о том, что произошло какое-либо событие и ему необходимо приостановить выполнение основной программы, выполнить подпрограмму и снова вернуться к выполнению основной программы. Прерывания бывают внешние (аппаратные) и внутренние (програмные).
Если открыть datasheet любого микроконтроллера AVR, то в разделе «Interrupts» можно увидеть все доступные векторы прерываний. Для Attiny13 их 10:
1й вектор расписывать особо не буду – это сброс микроконтроллера. В простейшем случае это внешний вывод, при подаче на который лог. «0» микроконтроллер перезагружается:
2й вектор, это вход внешнего прерывания INT0. На его примере я расскажу, как работать с внешними прерываниями INTx, т.к в других микроконтроллерах AVR этих прерываний может быть несколько, например в ATmega48/88//168 их 2, INT0 и INT1, но программируются они так же, ка описано в данной статье:
Но снова вернемся к микроконтроллеру attiny13:
Для работы с прерыванием INTx необходимо:
1) Подключить стандартную библиотеку для работы с прерываниями:
#include <avr/interrupt.h>
2) Разрешаем прерывания в программе
sei()
3) В регистре – General Interrupt Mask Register (GIMSK) записью логической «1» в бит в 6й бит разрешаем использование в программе прерывания INT0
GIMSK|= (1<<INT0)
4) В регистре MCU Control (MCUCR) устанавливаем условие, при каком изменении на входе микроконтроллера будет выполнено прерывание. Согласно даташита микроконтроллера за это отвечают биты ISC01 и ISC00. В зависимости от их настройки зависит условие генерации прерываний: По низкому уровню сигнала (00), при любом логическом изменении (01) и по заднему (10) или нарастающему фронту сигнала (11).
MCUCR|= (1<<ISC01); // Срабатывания прерывания по заднему фронту
5) Добавляем обработчик прерываний с подпрограммой, которая должна быть выполнена при наступлении события, указанном в прерывании в формате: ISR(«имя прерывания»_vect) {выполняемая подпрограмма}. В нашем случае это:
ISR(INT0_vect) { // Код подпрораммы выполняемый при наступлении события, // указанного в прерывании }
На этом с теорией все.
Покажу, как это работает, изменив немного программу мигания светодиодом:
/* * led_blink_new3.c * * Created: 26.10.2023 15:19:41 * ============================================ * H A R D W A R E I N F O R M A T I O N S * ============================================ * Attiny13a ___________ * 1 /____ |8 * o--|RESET Vcc|--o +5V * ADC o--|PB3/ADC3 PB2|--o Key2 * Led2 o--|PB4/ADC2 PB1|--o Key1 * GND o--|GND PB0|--o Led1 ----LED--->+5V * |_____________| * * Author : Vyacheslav */ #define F_CPU 1000000L // Частота для delay.h #include <avr/io.h> // Порты ввода-вывода для AVR #include <util/delay.h> // Задержка в ms #define ZADERZHKA 1000 // Задержка в ms для _delay_ms int main(void) { DDRB = (1 << PB0); // Устанавливаем порт PB0 на выход while (1) // Повторяем все что ниже { _delay_ms(ZADERZHKA); // Ждем 1с PORTB |= (1 << PB0); // Выключаем PB0 светодиод _delay_ms(ZADERZHKA); // Ждем 1с PORTB &= ~ (1 << PB0); // Выключаем PB0 светодиод } }
Добавим в нее строки для работы с прерываниями, которые я описал выше:
#include <avr/interrupt.h>
ISR(INT0_vect) { PORTB ^= (1<< PB4); //инвертируем состояние светодиода на BP4 }
sei(); MCUCR |= (1<<ISC01) | (1<<ISC00); // Прерывание будет по нарастающему фронту сигнала (по отпусканию кнопки) GIMSK |= (1<<INT0); PORTB |= (1 << PB1); // Включаем подтяжку к 5В на кнопку
Получилось так:
/* * interrupt INT.c * Мигание светодиодом и кнопка через прерывание * Created: 23.10.2023 14:23:53 * ============================================ * H A R D W A R E I N F O R M A T I O N S * ============================================ * Attiny13a ___________ * 1 /____ |8 * o--|RESET Vcc|--o +5V * ADC o--|PB3/ADC3 PB2|--o Key2 * Led2 o--|PB4/ADC2 PB1|--o Key1 * GND o--|GND PB0|--o Led1 ----LED--->+5V * |_____________| * * Author : Vyacheslav */ #define F_CPU 1000000L // Частота для delay.h #include <avr/io.h> // Порты ввода-вывода для AVR #include <util/delay.h> // Задержка в ms #include <avr/interrupt.h> #define ZADERZHKA 1000 // Задержка в ms для _delay_ms (1 сет) ISR(INT0_vect) { PORTB ^= (1<< PB4); //инвертируем состояние светодиода на BP4 } int main(void) { DDRB = (1 << PB0) | (1 << PB4); // Устанавливаем порт PB0 и PB4 на выход, остальные на вход sei(); MCUCR |= (1<<ISC01) | (1<<ISC00); // Прерывание будет по нарастающему фронту сигнала (по отпусканию кнопки) GIMSK |= (1<<INT0); PORTB |= (1 << PB1); // Включаем подтяжку к 5В на кнопку while (1) // Повторяем все что ниже { _delay_ms(ZADERZHKA); // Ждем 1с PORTB |= (1 << PB0); // Выключаем PB0 светодиод _delay_ms(ZADERZHKA); // Ждем 1с PORTB &= ~ (1 << PB0); // Включаем PB0 светодиод } }
Теперь алгоритм работы программы следующий:
Программа мигает светодиодом, подключенным к выводу PB0. При нажатии на кнопку, подключенную к порту PB1, в момент ее отпускания происходит прерывание INT0, по которому другой светодиод, подключенный к порту PB4 меняет свое состояние на противоположное. После этого программа возвращается снова к миганию светодиодом PB0.
Визуально это выглядит так:
Комментариев нет:
Отправить комментарий