Независимое управление несколькими пинами микроконтроллера ATmega8515 с помощью встроенного таймера. Четыре светодиода моргают каждый со своей частотой независимо от другого.
Используемый таймер - T0. Режим таймера - прерывание по переполнению. Задействованы пины порта С (С0, С1, С2, С3).
______________________________________________________________________________________
Описание программы: при старте программы счётный регистр TCNT0 таймера T0 начинает заполняться (по импульсам от тактового генератора). Когда счётный регистр таймера переполняется, программа выходит в прерывание, где программные счётчики Svet увеличиваются на 1. Счётный регистр при этом обнуляется и начинает заполняться опять, до следующего переполнения. Цикл повторяется.
В основной программе в бесконечном цикле просматривается содержимое программных счётчиков Svet. Если содержимое программного счётчика больше требуемого, программный счётчик обнуляется, а привязанный к счётчику пин микроконтроллера инвертируется.
Чтобы замедлить скорость переполнения счётного регистра, импульсы от тактового генератора (идущие на счётный регистр) пропускаются через предделитель (1/1024), пропускающие каждый 1024-й импульс. Предделитель настраивается регистром TCCR0.
• Частота тактового генератора в нашем случае: 1000000 Гц.
• Скорость подачи импульсов на счётный регистр при делителе 1/1024: 1000000 / 1024 = 976.56 Гц
• Разрядность счётного регитста T0 - 8 бит (256 состояний вместе с нулём).
• Скорость переполнения счётного регистра (и скорость инвертирования пина C0): 976.56 / 256 = 3,81 Гц (около четырёх изменений в секунду, что хорошо заметно).
• Итого: 3.81 / 2 = 1.9 полных импульсов в секунду.
Программа на CodeVisionAVR:
#include <mega8515.h>
int Svet0, Svet1, Svet2, Svet3; // Резервируем программные счётчики для пинов С0, С1, С2, С3.
interrupt [8] void timer0_overflow(void) // Обработчик прерывания от таймера T0 по переполнению его счётного регистра.
{
Svet0++ ; // При каждом переполнении счётного регистра увеличиваем каждый программый счётчик на единицу.
Svet1++ ;
Svet2++ ;
Svet3++ ;
}
Svet0++ ; // При каждом переполнении счётного регистра увеличиваем каждый программый счётчик на единицу.
Svet1++ ;
Svet2++ ;
Svet3++ ;
}
void main(void)
{
DDRC = 0b11111111; // Настройка всех пинов порта С на вывод.
{
DDRC = 0b11111111; // Настройка всех пинов порта С на вывод.
TCCR0 |= 0b00000101; /* Включаем предделитель 1/1024. Три первых бита регистра управления таймером T0 (TCCR0 - Timer-Counter Control Register 0) устанавливают, на сколько будут делиться импульсы, идущие от тактового генератора к таймеру.
00000101 - 1/1024 ; 00000100 - 1/256 ; 00000011 - 1/64 ; 00000010 - 1/8 ;
00000001 - 1/1 (чистые импульсы) ; 00000000 - выключить таймер. */
SREG |= 0b10000000; // Записываем единичку в 7 бит регистра состояния (SREG - Status Regster) и разрешаем общие прерывания.
TIMSK |= 0b00000010; // Записываем единичку в первый бит регистра маскирования прерываний от таймеров (TIMSK - Timer Interrupt Mask Register), и разрешаем прерывания по переполнению таймера T0.
SFIOR = 0b00000001; // Записываем единичку в первый бит регистра особых условий ввода-вывода (SFIOR - Special Function Input-Output Register) и сбрасываем предделитель (не обязательно).
TCNT0 = 0; // Обнуляем счётный регистр таймера T0 (TCNT0 - Timer-Counter 0) перед началом цикла опроса программных счётчиков (на всякий случай).
while (1) // Бесконечный цикл опроса программных счетчиков, их обнулений и инвертирования пинов порта.
{
if (Svet0 >= 1) // При КАЖДОМ переполнении счётного регистра инвертируем пин С0 и обнуляем счётчик Svet0.
{
PORTC ^= 0b00000001;
Svet0=0;
{
if (Svet0 >= 1) // При КАЖДОМ переполнении счётного регистра инвертируем пин С0 и обнуляем счётчик Svet0.
{
PORTC ^= 0b00000001;
Svet0=0;
}
if (Svet1 >= 2) // При каждом ВТОРОМ переполнении счётного регистра инвертируем пин С1 и обнуляем счётчик Svet1.
{
PORTC ^= 0b00000010;
Svet1=0;
}
{
PORTC ^= 0b00000010;
Svet1=0;
}
if (Svet2 >= 4) // При каждом ЧЕТВЁРТОМ переполнении счётного регистра инвертируем пин С2 и обнуляем счётчик Svet2.
{
PORTC ^= 0b00000100;
Svet2=0;
}
Svet2=0;
}
if (Svet3 >= 8) // При каждом ВОСЬМОМ переполнении счётного регистра инвертируем пин С3 и обнуляем счётчик Svet3.
{
PORTC ^= 0b00001000;
Svet3=0;
}
{
PORTC ^= 0b00001000;
Svet3=0;
}
}
}
Каждый светодиод моргает с частотой в два раза большей соседнего, что легко наблюдается. Можете менять цифры после Svet и, соответственно, менять частоту моргания каждого светодиода.
____________________________________________________________________________
Та же задача же для микроконтроллера ATmega8.
Программа на CodeVisionAVR:
#include <mega8.h>
int Svet0, Svet1, Svet2, Svet3;
interrupt [10] void timer0_overflow(void)
{
Svet0++ ;
Svet1++ ;
Svet2++ ;
Svet3++ ;
}
Svet0++ ;
Svet1++ ;
Svet2++ ;
Svet3++ ;
}
void main(void)
{
DDRD = 0b11111111;
{
DDRD = 0b11111111;
TCCR0 |= 0b00000101; // 00000101 - 1/1024 ; 00000100 - 1/256 ; 00000011 - 1/64 ; 00000010 - 1/8 ;
// 00000001 - 1/1 (чистые импульсы) ; 00000000 - выключить таймер.
SREG |= 0b10000000;
TIMSK |= 0b00000001; // Записываем единичку в нулевой бит регистра маскирования прерываний от таймеров (TIMSK - Timer Interrupt Mask Register), и разрешаем прерывания по переполнению таймера T0.
SFIOR = 0b00000001;
TCNT0 = 0;
while (1)
{
if (Svet0 >= 1)
{
PORTD ^= 0b00000001;
Svet0=0;
{
if (Svet0 >= 1)
{
PORTD ^= 0b00000001;
Svet0=0;
}
if (Svet1 >= 2)
{
PORTD ^= 0b00000010;
Svet1=0;
}
{
PORTD ^= 0b00000010;
Svet1=0;
}
if (Svet2 >= 4)
{
PORTD ^= 0b00000100;
Svet2=0;
}
Svet2=0;
}
if (Svet3 >= 8)
{
PORTD ^= 0b00001000;
Svet3=0;
}
{
PORTD ^= 0b00001000;
Svet3=0;
}
}
}
Красным указаны отличия программы для ATmega8 от программы для ATmega8515.
1) Заголовочный файл меняем на Mega8.h
2) Вектор прерывания по переполнению T0 для ATmega8 равен 10, а не 8.
3) У ATmega8 в регистре TIMSK разрешением прерывания по переполнению T0 управляет нулевой бит, а не первый.
4) Используем порт D, пины D0 , D1 , D2 , D3 (№№2-6 по левой стороне сверху).
5) В конфигурации проекта меняем тип кристалла с Меги8515 на Мегу8 Project >> Configure >> C Compiler >> Chip >> ATmega8.