- Защо таймер, когато имаме Delay ()?
- Таймери за микроконтролер PIC:
- Програмиране и работно обяснение:
- Електрическа схема и симулация на Proteus:
Това ще бъде петият урок от нашата серия уроци по PIC, който ще ви помогне да научите и използвате таймери в PIC16F877A. В предишните ни уроци бяхме започнали с Въведение в PIC и MPLABX IDE, след това написахме първата си програма за PIC, за да мига светодиода с помощта на PIC и след това направихме LED мигаща последователност, използвайки функцията за забавяне в PIC Microcontroller. Сега нека използваме същата последователност на мигащите светодиоди, която сме използвали в предишния хардуер на урока и с това ще научим как да използваме таймери в нашия PIC MCU. Току-що добавихме още един бутон в LED дъска за този урок. Прегледайте урока, за да научите повече.
Таймерите са един от важните работни коне за вграден програмист. Всяко приложение, което проектираме, по някакъв начин включва приложение за синхронизация, като включване или изключване на нещо след определен интервал от време. Добре, но защо ни трябват таймери, когато вече имаме макроси за забавяне (__delay_ms ()), които правят едно и също !!
Защо таймер, когато имаме Delay ()?
Макросът за забавяне се нарича „забавяне“. Тъй като по време на изпълнението на функцията за забавяне, MCU се зарежда само като създава забавяне. По време на този процес MCU не може да прослушва своите ADC стойности или да чете нищо от своите регистри. Следователно не е препоръчително да се използват функции за забавяне, с изключение на приложения като мигане на LED, когато забавянето във времето не трябва да бъде точно или дълго.
Макросите за забавяне също имат следните кратки идвания,
- Стойността на закъснението трябва да е константа за макроси за закъснение; не може да се променя по време на изпълнение на програмата. Следователно остава дефиниран програмист.
- Забавянето няма да бъде точно в сравнение с използването на таймери.
- По-големи стойности на закъснения не могат да бъдат създадени с помощта на макроси, например забавяне от половин час не може да бъде създадено от макроси със забавяне. Максималното забавяне, което може да се използва, се основава на използвания кристален осцилатор.
Таймери за микроконтролер PIC:
Физически таймерът е регистър, чиято стойност непрекъснато се увеличава до 255 и след това започва отначало: 0, 1, 2, 3, 4… 255…. 0, 1, 2, 3……и т.н.
В PIC16F877A PIC MCU има три таймера модули. Те са имена като Timer0, Timer1 и Timer2. Таймерът 0 и Таймерът 2 са 8-битови таймери, а Таймерът 1 е 16-битов таймер. В този урок ще използваме таймера 0 за нашето приложение. След като разберем таймера 0, ще бъде лесно да работим и с таймер 1 и таймер 2.
Таймерът / броячът на модула Timer0 има следните характеристики:
- 8-битов таймер / брояч
- Четене и записване
- 8-битов софтуерен програмируем предскалер
- Избор на вътрешен или външен часовник
- Прекъсване при преливане от FFh до 00h
- Избор на ръба за външен часовник
За да започнем да използваме таймер, трябва да разберем някои от изисканите термини като 8-битов / 16-битов таймер, Prescaler, прекъсвания на таймера и Focs. Сега нека видим какво всъщност означава всеки един от тях. Както беше казано по-рано, в нашия PIC MCU има както 8-битови, така и 16-битови таймери, основната разлика между тях е, че 16-битовият таймер има много по-добра резолюция от 8-битовия таймер.
Prescaler е име за частта от микроконтролера, която разделя часовника на осцилатора, преди да достигне логика, която увеличава състоянието на таймера. Обхватът на идентификатора на предскалатора е от 1 до 256 и стойността на прескалера може да бъде зададена с помощта на регистъра OPTION (Същият, който използвахме за издърпващи резистори). Например, ако стойността на предскалера е 64, тогава за всеки 64 -и импулс Таймерът ще се увеличи с 1.
С увеличаването на таймера и когато достигне максималната си стойност от 255, той ще задейства прекъсване и ще се инициализира отново до 0. Това прекъсване се нарича таймерно прекъсване. Това прекъсване информира MCU, че това конкретно време е изтекло.
В FOSC означава честота на осцилатора, е честотата на кристала се използва. Времето, необходимо за регистъра на таймера, зависи от стойността на Prescaler и стойността на Fosc.
Програмиране и работно обяснение:
В този урок ще зададем два бутона като два входа и 8 LED като 8 изхода. Първият бутон ще се използва за задаване на закъснението (500ms за всяко натискане), а вторият бутон ще се използва за стартиране на мигането на последователността на таймера. Например, ако първият бутон бъде натиснат три пъти (500 * 3 = 1500ms), закъснението ще бъде зададено за 1,5 секунди и при натискане на бутона два всеки светодиод ще се включва и изключва с предварително зададеното времезакъснение. Проверете демонстрационното видео в края на този урок.
Сега, имайки предвид тези основи, нека разгледаме нашата програма, дадена в края в раздел Код.
Добре е, ако не сте получили програмата, но ако сте !! Дайте си бисквитка и зарежете програмата, за да се насладите на резултата си. За други ще разбия програмата на смислени части и ще ви обясня какво се случва във всеки блок.
Както винаги първите няколко реда на кода са конфигурационните настройки и заглавните файлове, няма да обяснявам това, тъй като вече го направих в предишните си уроци.
След това нека пропуснем всички редове и скочим направо в основната функция void, вътре в която имаме конфигурацията PORT за Timer0.
void main () {/ ***** Конфигурация на порт за таймер ****** / OPTION_REG = 0b00000101; // Timer0 с външна честота и 64 като прескаларна // Също така позволява PULL UPs TMR0 = 100; // Зареждане на стойността на времето за 0.0019968s; delayValue може да бъде между 0-256 само TMR0IE = 1; // Активиране на бит за прекъсване на таймера в регистър PIE1 GIE = 1; // Активиране на глобално прекъсване PEIE = 1; // Активиране на периферното прекъсване / *********** ______ *********** /
За да разберем това, трябва да разгледаме регистъра OPTION в нашия лист с данни за PIC.
Както беше обсъдено в предишния урок, битът 7 се използва за активиране на слаб издърпващ резистор за PORTB. Погледнете горната фигура, битът 3 е направен 0, за да инструктира MCU, че следващият предварително зададен прескалер трябва да се използва за таймера, а не за WatchDogTimer (WDT). Режимът на таймера се избира чрез изчистване на бит 5 T0CS
(OPTION_REG <5>)
Сега bits2-0 се използва за задаване на стойността на предскалиращото устройство за таймера. Както е показано в таблицата по-горе, за да се зададе стойност на прескалиране от 64, битовете трябва да бъдат зададени като 101.
След това нека разгледаме регистрите, свързани с Timer0
Таймерът ще започне да се увеличава, след като бъде зададен и препълнен, след като достигне стойност от 256, за да позволи прекъсването на таймера по време на тази точка, регистърът TMR0IE трябва да бъде настроен високо. Тъй като самият таймер 0 е периферен, трябва да активираме периферното прекъсване, като направим PEIE = 1. Накрая трябва да активираме глобалното прекъсване, така че MCU да бъде уведомен за прекъсването по време на всяка операция, това се прави, като се направи GIE = 1.
Забавяне = ((256-REG_val) * (Prescal * 4)) / Fosc
Горната формула се използва за изчисляване на стойността на забавяне.
Където
REG_val = 100;
Прескал = 64
Fosc = 20000000
Това при изчислението дава, Забавяне = 0,0019968s
Следващият набор от редове е да зададете I / O портове.
/ ***** Конфигурация на порт за I / O ****** / TRISB0 = 1; // Инструктирайте MCU, че PORTB щифтът 0 се използва като вход за бутон 1. TRISB1 = 1; // Инструктирайте MCU, че PORTB щифт 1 се използва като вход за бутон 1. TRISD = 0x00; // Инструктирайте MCU, че всички изводи на PORT D се извеждат PORTD = 0x00; // Инициализирайте всички щифтове на 0 / *********** ______ *********** /
Това е същото като на предишния урок, тъй като използваме същия хардуер. Освен че сме добавили още един бутон като вход. Това се прави от линията TRISB1 = 1.
На следващо място, отвътре навън безкраен цикъл while имаме два блока код. Единият се използва за получаване на входа на таймера от потребителя, а другият за изпълнение на последователността на забавяне над светодиодите. Обясних ги, като използвах коментари срещу всеки ред.
докато (1) {брой = 0; // Не пускайте таймер, докато сте в основния цикъл // ******* Вземете закъснението на номера от потребителя **** ////// if (RB0 == 0 && flag == 0) // Когато вход е даден {get_scnds + = 1; // get_scnds = get_scnds + http: // Увеличи флаг на променлива = 1; } if (RB0 == 1) // За да се предотврати непрекъснато увеличаване флаг = 0; / *********** ______ *********** /
Променлива, наречена get_scnds, се увеличава всеки път, когато потребителят натисне бутона 1. Променлива с флаг (дефинирана от софтуера) се използва, за да задържи процеса на увеличаване, докато потребителят премахне пръста си от бутона.
// ******* Изпълнение на последователност със закъснение **** ////// докато (RB1 == 0) {PORTD = 0b00000001 <
Следващият блок влиза в действие, ако се натисне бутон два. Тъй като потребителят вече е определил необходимото времезакъснение с помощта на бутон един и той е бил запазен в променливата get_scnds. Използваме променлива, наречена hscnd, тази променлива се контролира от ISR (Прекъсване на услугата рутина).
В програма за прекъсване услуга е с прекъсвания, че ще се нарича всеки път, когато Timer0 е залято. Нека да видим как се управлява от ISR в следващия блок, като искаме да увеличим времето за забавяне с половин секунда (0.5s) при всяко натискане на бутон, след което трябва да увеличим променливата hscnd за всяка половин секунда. Тъй като сме програмирали нашия таймер да претоварва за всеки 0.0019968s (~ 2ms), така че за отчитане на половин секунда променливата за отчитане трябва да бъде 250, защото 250 * 2ms = 0.5 секунда. Така че, когато count получава 250 (250 * 2ms = 0,5 секунди), означава, че е минало половин секунда, така че увеличаваме hscnd с 1 и инициализираме count до нула.
void interrupt timer_isr () {if (TMR0IF == 1) // Знамето на таймера е задействано поради препълване на таймера {TMR0 = 100; // Зареждане на стойността на таймера TMR0IF = 0; // Изчистване на броя на флаговете за прекъсване на таймера ++; } if (брой == 250) {hscnd + = 1; // hscnd ще се увеличи за всяка половин секунда count = 0; }}
Затова използваме тази стойност и я сравняваме с нашия hscnd и преместваме нашия светодиод въз основа на определеното от потребителя време. Също така е много подобен на последния урок.
Това е всичко, което нашата програма разбира и работи.
Електрическа схема и симулация на Proteus:
Както обикновено позволява първо да проверим изхода, като използваме Proteus, тук съм свързал схематичните файлове на Proteus.
Добавете бутон към предишната ни LED платка и нашият хардуер е готов за работа. Тя трябва да изглежда по следния начин:
След като свързването свърши, качете кода и проверете изхода. Ако имате някакъв проблем, моля, използвайте раздела за коментари. Също така проверете видеото по-долу, за да разберете целия процес.