Пятница, 29.03.2024
Интеллектуальные системы управления
Меню сайта
Категории раздела
Эксперименты [3]
Программирование микроконтроллеров PIC [7]
Программирование микроконтроллеров STM32 [0]
Программирование микроконтроллеров STM8 [0]
Программирование микроконтроллеров AVR [0]
Программирование микроконтроллеров MSP430 [0]
Программирование ПЛИС ALTERA [0]
Облако тегов
Главная » Статьи » Программирование микроконтроллеров » Программирование микроконтроллеров PIC

Изучаем таймеры и прерывания на примере микроконтроллера PIC16F877A

В прошлой статье мы написали нашу первую программу для микроконтроллера PIC16F877A. Программа была простая и увеличивала значение содержащееся в регистре PORTB, попутно выводя его наружу в двоичном коде, то есть "дергала ножками" микроконтроллера. Происходило это хаотически и было больше похоже именно на дерганье. В этой статье мы продолжим усовершеноствовать нашу программу попутно разбирая такие понятия как таймеры и прерывания. Если вдруг случилось так что у вас не оказалось под рукой такого же микроконтроллера как у меня, вы спокойно можете взять любой из семейства PIC16 однако брать нужно только те у которых в названии есть буква F, это значит что память под программу (прошивку) у него перезаписываемая и его можно перепрограммировать много раз. Буква C в названии говорит что этот контроллер программируется только один раз и больше мы с ним ничего сделать не сможем. Буквы LF означают что это контроллер в пониженным энергопотреблением, меньшей тактовой частотой (быстродействием) и температурным режимом. Поэтому можете брать любой, а я, в свою очередь, постараюсь писать программы, так чтобы они подходили к любому контроллеру без глобального изменения кода.

Итак приступим. Собственно что такое таймер понятно, в микроконтроллерах он используется для организации пауз и задержек, а вот что такое прерывания? Прерывания - это сигнал, сообщающий микроконтроллеру о наступлении какого-либо события. При этом выполнение текущей последовательности команд приостанавливается и управление передаётся обработчику прерывания, который реагирует на событие и обслуживает его, после чего возвращает управление в прерванный код. Прерывания бывают разных видов... Бла, бла, бла... В принципе я не вижу смысла заниматься плагиатом и переписывать чужие статьи, к тому же так же хорошо я врятли смогу написать, поэтому более подробно о прерываниях можно почитать здесь: http://ru.wikipedia.org/wiki/Прерывание

 В принципе паузу в программе можно организовать и без таймеров - прерываний, через пустой цикл:

Code
for(i=0;i>n;i++);

Но с помощью такой реализации мы остановим всю программу и пока этот пустой цикл не выполнится наш микроконтроллер так и будет топтаться на месте ничего не исполняя, а если нам необходимо чтобы определенные действия выполнялись независимо от основной программы? Как раз для этого нам пригодятся таймеры и прерывания.

Всего в данном микроконтроллере три таймера (Timer0, Timer1, Timer2). Все они независимы друг от друга, то есть включить, выключить и настраивать их можно по разному, а значит использовать их можно для разных процессов. Принцип работы и управление у них одинаковый, различны только регистры управления и разрядность, поэтому рассматривать все счетчики мы не будем.

По практикуемся пока на 8-ми разрядном таймере-счетчике Timer0, если как следует разобраться во всех нюансах то с остальными таймерами проблем уже не будет. Итак, регистр управления таймером Timer0 называется OPTION_REG. В Hi-tech его название немного сократили до OPTION, но это одно и тоже. Вообще узнать как обзываются регистры в компиляторе можно открыв подключаемый библиотечный файл для своего контроллера. Для этого заходим в дирректорию "c:\Program Files\HI-TECH Software\PICC\std\9.60\" или же туда куда у вас установлен Hi-tech, и ищем папку "include"  в ней разполагаются все библиотечные файлы. Далее открываем знакомый нам файл pic.h и ищем в нем такие строчки:

Code
#if defined(_16F873A) || defined(_16F874A) ||\
defined(_16F876A) || defined(_16F877A)
                  #include <pic168xa.h>
#endif

Это значит что названия регистров, модулей, а также настроек для нашего ядра PIC16F877A находятся в той же папке, в файле pic168xa.h. Запомним название, теперь он часто будет нам требоваться. Открываем MPLAB и наш старый проект. Попутно давайте изменим строку с макросом __CONFIG(0x03F72) на более понятную и читаемую:

Code
__CONFIG(HS & WDTDIS & PWRTDIS & BORDIS & LVPDIS & DUNPROT & WRTEN & DEBUGEN & UNPROTECT);

Вообще эти две записи одинаковые, но вторая более наглядная и более понятная тем кто в дальнейшем будет разбираться в вашем коде. Означают эти надписи следующее:

  • HS - высокоскоростной кварцевый генератор;
  • WDTDIS - сторожевой таймер отключен;
  • PWRTDIS - таймер отключен;
  • BORDIS - детектор пониженного напряжения отключен;
  • LVPDIS - низковольтное программирование отключено;
  • DUNPROT - отключена защита данных;
  • WRTEN - запись во флеш память программ включена; 
  • DEBUGEN - режим отладки кода включен; 
  • UNPROTECT - отключена защита программного кода.

Все эти данные можно найти в конце файла pic168xa.h, в разделе "configurations". В дальнейшем, если нам необходимо будет включить сторожевой таймер или защиту кода, то можно будет не лазить в меню конфигурации ядра переставляя переключатели и вычисляя конечную сумму, а просто изменив тот или иной пункт в макросе CONFIG.

С одним покончено, вернемся к таймеру. После строки void main(void) в разделе настроек пишем следующий код:

Code
OPTION=0b00000000;
   PS2=1;
   PS1=1;
   PS0=1;

Все остальные биты мы оставляем в "0". Наш таймер тактируется от того же источника что и ядро микроконтроллера, тоесть от высокочастотного кварцевого генератора, подключенного к выводам 13 (OSC1/CLKI) и 14 (OSC2/CLKO), поэтому T0CS мы сбрасываем в "0". Если вдруг для тактирования Timer0 вам потребуется другой источник тактирования то его уже надо будет подключать к выводу 6 (RA4/T0CKI/C1OUT) и бит T0CS устанавливать в "1". T0SE отвечает за характер увеличения счетчика нашего таймера, либо он будет увеличиваться на 1 при переходе от высокого уровня к низкому, либо от низкого к высокому (имеются ввиду колебания кварцевого генератора). В принципе особой разницы в этом нет, поэтому у меня T0SE сброшен в "0". Прескалер у нас используется подключен к таймеру Timer0, так как сторожевой таймер мы не используем и он у нас находится в выключеном состоянии. PSA тоже сбрасываем в "0". Программу мы пишем для реализации эффекта бегущий огонь, поэтому временные задержки у нас будут большие (1-10 милисек), в противном случае человеческий глаз просто не сможет ничего уловить, поэтому и размер прескалера мы берем самый большой. В принципе разобрались и здесь все понятно. Да, кстати, прескалер это делитель, он делит частоту источника тактирования на выбранный нами коэффициент. Для того чтобы запустить наш таймер нужно переменной TMR0 присвоить значение от 0 до 255 соответсвующее длительности задержки. Расчитывается оно по формуле:

TMR0 = 256 - (Delay * Freqency) / (Prescaler * 4);

где Delay - длительность задержки, которую мы хотим получить в конечном счете от таймера; Freqency - частота нашего кварцевого генератора; Prescaler - коэффициент нашего прескалера. Для других таймеров формула измениться, но не на много. Для 10 микС - 

Code
 TMR0=0x3C;

 Это в шестнадцатеричная форма записи, мне она в написании программного кода нравится больше, в нормальном человеческом TMR0 = 60.

На следующем шаге настраиваем прерывания и записываем его обработчик.

Code
INTCON=0b00000000;
    GIE=1;
    PEIE=1;
    T0IE=1;

   GIE - Разрешение всех прерываний
   PEIE - Разрешение прерываний от периферии
   T0IE - Разрешение прерываний от TMR0

Code
void interrupt main_int(void) // Функция прерывания
   // Здесь может размещаться ваш текст
   TMR0=0x3С;
   TMR0IF=0;
}

В конце функции прерывания всегда нужно сбрасывать флаг прерывания от таймера TMR0IF, чтобы не было ложного срабатывания. Если наш таймер отвечает за циклическое событие, повторяющееся во времени множество раз, то имеет смысл в теле функции снова включить таймер присвоив переменной TMR0 значение задержки.

В принципе основной костяк программы сделан, все включено, прописано и настроено. Даже если оставить все в таком же виде как есть программа уже будет работать прерываясь через каждые 10 микС на обработку прерывания от таймера. Для большей наглядности перенесем строку PORTB++ в обработчик прерывания, а цикл while(1) сделаем пустым.

Чисто внешне наш бегущий огонь так и остался хаотичным, но теперь мы можем регулировать его реальную скорость не подбирая длительность пустого цикла, а изменяя значение таймера.

Категория: Программирование микроконтроллеров PIC | Добавил: Anubis (16.01.2012)
Просмотров: 17050 | Комментарии: 2 | Теги: Таймеры | Рейтинг: 4.5/2
Всего комментариев: 1
1 Anubis  
0
Статьи конечно будут, просто последнее время очень мало времени стало...
Что же касается прерываний, я чуть позже напишу по ним статью.

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0
Форма входа
Copyright MyCorp © 2024