суббота, 28 октября 2023 г.

Внешние прерывания INTx при программировнии Attiny13 в среде Microchip Studio на языке С


        В данной статье я открою цикл статей, посвящённых работе с прерываниями. Прерывание – это сигнал процессору микроконтроллера о том, что произошло какое-либо событие и ему необходимо приостановить выполнение основной программы, выполнить подпрограмму и снова вернуться к выполнению основной программы. Прерывания бывают внешние (аппаратные) и внутренние (програмные).

        Если открыть 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.


        Визуально это выглядит так:




Комментариев нет:

Отправить комментарий