Доработаем программу «Бегущие огни», изменив процедуру формирования задержки. Чтобы не загружать процессор новая процедура задержки должна использовать один из внутренних таймеров/счетчиков и не использовать прерывания.

В микроконтроллере Atmega8 имеются 3 таймера: 2 восьмиразрядных и 1 шестнадцатиразрядный. Для формирования временных интервалов таймер просто подсчитывает тактовые импульсы от системного генератора.

В нашем случае частота равна 4 MHz, а период импульсов 1/4MHz=0,25мкс. Для того чтобы получить на выходе 200мс, необходимо иметь коэффициент деления равный 200мс/0,25мкс= 800000. Восьмиразрядный таймер имеет максимальный коэффициент пересчета 28=256, а шестнадцатиразрядный 216=65536. То есть даже шестнадцатиразрядного таймера нам не хватит для формирования требуемой задержки. Тогда воспользуемся предварительным делителем. Этот делитель производит предварительное деление тактового сигнала перед тем как он поступит на вход таймера.

Выберем самый большой коэффициент предделителя 1024. Тогда на его выходе получим сигнал с частотой 4MHz/1024=3906Hz . Период тактового сигнала будет равен 1/3906Hz=0,256мс. Посчитаем коэффициент деления, который наш таймер должен нам обеспечить: 200мс/0,256мс=780. Такой коэффициент пересчета нам может обеспечить только шестнадцатиразрядный таймер Т1.

Доработка программы сводится к созданию новой функции задержки.

Наша новая функция задержки получила название wait1. Описание этой функции в нашей программе расположено раньше, чем описание функции main.

В языке Си действует правило: любая функция должна быть прежде описана и лишь затем в первый раз применена.

Так как функция main использует функцию wait1 в качестве процедуры задержки, то описание wait1 должно располагаться перед описанием main.

В модуль инициализации добавляем настройки таймера

TCCR1A = 0x00;

TCCR1B = (1 << CS12)|(0 << CS11)|(1 << CS10);

Нулевое значение в регистр TCCR1A можно не записывать т. к. там и так ноль по умолчанию. Для TCCR1B из даташита на микроконтроллер Atmega8 выбираем коэффициент предделителя 1024, и устанавливаем нужные биты CS в единицу.

Добавляем функцию задержки с использованием таймера:

void wait1 (void)
{
TCNT1 = 0;
while (TCNT1 < 780){};
} 

Функция wait1 формирует задержку с использованием таймера. Где первая строка – это заголовок описания функции. Из него видно, что функция wait1 не использует параметров и не возвращает ни каких значений. Счетному таймеру TCNT1 присваивается нулевое значение. Дальше расположен цикл проверки. Это пустой цикл, в качестве условия которого выступает выражение TCNT1<780. Цикл проверки будет выполняться до тех пор, пока значение счетного регистра не будет превышать 780. Как только окажется, что это не так, цикл завершится, а с завершением цикла завершится и вся функция wait1. Полный код программы ниже:

/***Занятие 5. Бегущие огни с использованием таймера***/
#include <avr/io.h>
/***функция задержки***/
void wait1 (void)
{
TCNT1 = 0;
while (TCNT1 < 780){};
}
int main(void)
{
unsigned char temp;
DDRC = 0x00; // Порт C вход
PORTC |= (1 << PC0); // Подключаем внутренний резистор к PC0
DDRD = 0xFF; // Порт D выход
PORTD = 0x00; // Лог. 0 на выходе
// Настройка таймера
TCCR1A = 0x00;
TCCR1B = (1 << CS12)|(0 << CS11)|(1 << CS10); //выбор коэффициента предделителя clk/1024
while(1)
{
if ((PINC&(1 << PC0)) == 0) //проверяем нажатие кнопки
{//сдвиг вправо
temp = 0x80; //записываем начальное значение
while (temp != 0) // Пока temp не равно 0 
{
PORTD = temp; //запишем temp в порт D
temp = temp >> 1; //сдвигаем разряды
wait1(); // Задержка
}
}
else
{ //сдвиг влево
temp = 0x01; //записываем начальное значение
while (temp != 0) // Пока temp не равно 0
{
PORTD = temp; //запишем temp в порт D
temp = temp << 1; //сдвиг вправо
wait1(); // Задержка
}
}
}
} 

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