Занятие №6. Бегущие огни. Использование прерываний по таймеру

Рейтинг:  5 / 5

Звезда активнаЗвезда активнаЗвезда активнаЗвезда активнаЗвезда активна
 

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

В данном случае мы будем использовать режим работы таймера – сброс при совпадении(СТС). В этом режиме таймер сам периодически вырабатывает запросы на прерывания с заранее заданным периодом.

Все функции управления движением огней выполняет процедура обработки прерывания. При каждом вызове прерывания процедура производит сдвиг огней на 1 шаг в нужном направлении.

Алгоритм основной программы:

- Настроить порты ввода-вывода;
- Настроить таймер и систему прерываний;
- Записать в рабочий регистр начальное значение;
- Разрешить работу таймера;
- Разрешить прерывания;
- Перейти к выполнению основного цикла.

Алгоритм процедуры обработки прерываний:

- Проверяем состояние переключателя режимов;
- Если контакты разомкнуты произвести сдвиг вправо;
- Если контакты замкнуты произвести сдвиг вправо;
- Вывести содержимое рабочего регистра в порт D;
- Закончить процедуру обработки прерывания.

Пишем программу. Текст будет содержать две функции: главная функция main и функция обработки прерываний ISR. Функция main будет содержать только строки инициализации. Разберем функцию прерываний. Сначала подключим стандартную библиотеку прерываний <avr/interrupt.h>, заголовочный файл находится в директории AVR.

ISR (TIMER1_COMPA_vect)

ISR это управляющее слово указывает транслятору на то, что данная функция является процедурой обработки прерываний. Вид прерывания , которое будет вызывать данную функцию указывается в скобках, это TIMER1_COMPA_vect, означает, что данная функция является процедурой обработки прерывания по совпадению таймера Т1. Функция обработки прерываний занимается сдвигом содержимого переменной temp на один шаг вправо или влево, в зависимости от положения переключателя S1.

 //***Процедура обработки прерывания по Таймеру 1***/  
ISR (TIMER1_COMPA_vect) 
{ 
if ((PINC&(1 << PC0)) == 0)  // Проверка состояния переключателя 
{ 
temp = temp >> 1;  // Сдвиг разрядов 
if (temp==0) {temp = 0b10000000;}  // Сдвиг вправо 
}  
else 
{ 
temp = temp << 1;  // Сдвиг разрядов 
if (temp==0) {temp = 0b00000001;}  // Сдвиг влево 
}  
PORTD = temp;  // Запись в порт D 
} 

Для проверки состояния переключателя служит команда if. Эта команда проверяет значение младшего разряда порта C. Если значение разряда равно нулю, то выполняется процедура сдвига на один бит вправо, если равно единице выполняется сдвиг влево. Так же еще один оператор if проверяет не дошла ли сдвигаемая единица до конца байта. Признаком того, сто единица уже дошла до конца , является равенство переменной temp нулю. Если условие выполняется, то переменной temp будет присвоено значение 0b00000000. То есть дойдя до правого края, единица появляется слева. Таким образом реализуется эффект кругового движения единичного бита. Процедура сдвига влево построена таким же образом. После выполнения одной из процедур сдвига производится запись содержимого переменной temp в порт D.

Полный текст программы:

/***Занятие №6 "Бегущие огни" Использованием прерывания по таймеру***/

#include <avr/io.h>
#include <avr/interrupt.h>

unsigned char temp;

/***Процедура обработки прерывания по Таймеру 1***/

ISR (TIMER1_COMPA_vect)
{
if ((PINC&(1 << PC0)) == 0) // Проверка состояния переключателя
{
temp = temp >> 1; // Сдвиг вправо
if (temp == 0)
{ temp = 0b10000000;}
}
else
{
temp = temp << 1; // Сдвиг влево
if (temp == 0)
{ temp = 0b00000001;}
}
PORTD = temp; // Запись в порт PD
}

int main(void)
{

/***настраиваем порты ввода-вывода***/

DDRC = 0x00;
PORTC |= (1 << PC0); // Подключаем внутренний резистор
DDRD = 0xFF;
PORTD = 0x00;

/***Настраиваем таймер***/

TCCR1A = 0x00;
TCCR1B = (1 << CS12)|(0 << CS11)|(1 << CS10)|(1 << WGM12); //предделитель clk/1024, режим таймера СТС
TCNT1 = 0x00;
OCR1A = 780; // максимальный предел счета
TIMSK |= (1 << OCIE1A); // разрешение прерывания по совпадению

temp = 0b00000000; // Присвоение начального значения

sei(); // Разрешение прерываний

while (1) {}; // Бесконечный цикл
}

В следующем примере вместо переключателя использована кнопка, при однократном нажатии на которую огонек меняет свое направление:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

unsigned char temp, direction;

ISR (TIMER1_COMPA_vect)
{
if(direction) // Проверка состояния
{
temp = temp >> 1; // Сдвиг вправо
if (temp == 0) temp = 0x80;
}
else
{
temp = temp << 1; // Сдвиг влево
if (temp == 0) temp = 0x01;
}
PORTD = temp; // Запись в порт D
}

int main(void)
{
DDRC = 0x00;
PORTC |= (1 << PC0); // Подключаем внутренний подтягивающий резистор
DDRD = 0xFF;
PORTD = 0x00;

TCCR1B = (1 << CS12)|(1 << CS10)|(1 << WGM12); //предделитель clk/1024, режим таймера СТС
OCR1A = 780; // Выбор коэффициента деления
TIMSK |= (1 << OCIE1A); // Разрешение прерывания по совпадению

temp = 0; // Присвоение начального значения

sei(); // Разрешение прерываний

while(1)
{
if((PINC&(1 << PC0)) == 0) // Если кнопка нажата
{
_delay_ms(20);
direction ^= 1; // Переключаем состояние
while((PINC&(1 << PC0)) == 0){} // Ждем отпускания кнопки
}
}
}

В статье были использованы материалы из книги Белова А.В. "Самоучитель разработчика устройств на AVR"

Печать E-mail

Комментарии  

0 #41 uuu000 09.02.2020 13:29
Спасибо,ребята.
Сообщить модератору
0 #42 uuu000 29.07.2021 13:49
Не могу послать сообщение на форуме.
Возможно ли использовать два вида прерываний для одного счетчика T0
Attiny13.
Не хочет работать такая программа

Код:#define F_CPU 1200000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

ISR (TIM0_OVF_vect)
{


PORTB |=(1<<PB0);
_delay_ms (1000);
PORTB &= ~(1<<PB0);
_delay_ms (1000);
}



ISR(TIM0_COMPA_vect)
{
unsigned int u;
ADCSRA |= (1 << ADSC);
while ((ADCSRA&(1 << ADIF))== 0);

u = (ADCL|ADCH << 8);
if (u > 128) //0.625V
PORTB = 0b00000010;

if (u > 730) //12.8V
PORTB = 0b00000100;
_delay_ms(50);
}

int main(void)
{
DDRB = 0b00000111;
PORTB= 0b11111000;
TCCR0A = 0x02; // режим подсчета импульсов (сброс при совпадении)
TCCR0B = (1 << CS02)|(0 << CS01)|(1 << CS00); //clk/1024
TCNT0 = 0x00;
OCR0A = 111;
TIMSK0 |= (1 << OCIE0A) |(1 << TOIE0);
sei();
/*** Настройка АЦП ***/
ADCSRA |= (1 << ADEN)
|(1 << ADPS1)|(1 << ADPS0);
ADMUX &= ~(1<< REFS0) ;// внешний ИОН
ADMUX|=(1<<MUX0)|(1<<MUX1);// вход PB3 ADC3



while (1) {}


}
Сообщить модератору
0 #43 AntonChip 29.07.2021 17:01
У вас в прерывании секундная задержка, такого быть не должно, попробуйте так
Код:
ISR (TIM0_OVF_vect)
{
PORTB ^=(1<<PB0); // Меняем состояние бита на противоположный
}
Сообщить модератору
0 #44 uuu000 29.07.2021 19:50
Мне нужно 1.получить на ножке PB0 импульсы 2-3 Гц.
2. АЦП для контроля заряда аккумулятора.
Любой из этих процессов нормально работает в main,
но добавление другого( с помощью прерывания) как-то
отключает основную функцию. Например, если в прерывании работает АЦП
то импульсы с PB0(из main)перестают поступать .И если поменять местами процессы ,т.е. АЦП в main ,а пульсатор в прерывания - АЦП не работает ,а пульсатор выдает импульсы .Прерывания в обоих случаях по совпадению. Попытка использовать два прерывания тоже не дает результат. Может быть
проблема в специфике Attiny 13?
Сообщить модератору
0 #45 AntonChip 29.07.2021 22:10
Значит ваши два прерывания от таймера не могут одновременно работать
Сообщить модератору
0 #46 uuu000 29.07.2021 22:17
Антон, спасибо за помощь и внимание.
Надеюсь с другими МК дело обстоит лучше.
Сообщить модератору
0 #47 AntonChip 31.07.2021 17:00
Посмотрите ATtiny25/45/85, там уже есть два таймера
Сообщить модератору

Авторизация