Merhabalar, geçen yazımızda kesmeler üzerine oldukça marjinal bazı çalışmalar yapmıştık. Şu ana kadar PIC mikrokontrolörün giriş çıkış ünitelerini, kesmeleri de içerecek şekilde evire çevire kullanabilecek bilgi birikimini edindik. Tabi bu bahsettiğimiz konu, işin bahanesiydi. Asıl amacımız, tüm mikrokontrolörlerde kullanabileceğimiz bir modelleme ve geliştirme altyapısını tahsis etmekti. Buna istinaden yaptığımız en orijinal iş, Microchip firmasının sunduğu kütüphanelerden performans, estetik, yazılım kalitesi, taşınabilirlik ve geliştirilebilirlik bakımından çok daha iyi bir kütüphane oluşturmak oldu. Mikrokontrolör’ün veri kağıdını nasıl modelleyeceğimizi gördüğümüzden aynı işlemleri herhangi bir başka platform için yapmak sorun olmayacaktır.
Dış dünyaya mikrokontrolörün I/O pinleri üzerinden müdahale edebildiğimizden bir sonraki adıma geçmenin sırası geldi. Bir sonraki adım şüphesiz ki periyodik işlerle nasıl başa çıkabileceğimizi bulmak olacak. Güncel durumda yazdığımız kodlar ömürleri boyunca aynı işi aralıksız olarak yaptılar. Oysa gerçek hayattaki pek çok uygulamada gerçekleşmesi gereken zamanda ve periyodik gerçekleştirilmesi gereken olaylar görürüz. Periyodik yapılan işlerden ilk akla gelen örneklemedir. Nyquist amcamızın da yıllar önce ortaya koyduğu gibi, bir işareti doğru zamanlama ile örneklemezsek o işaretin taşıdığı bilgiyi kaybetmiş olabiliyoruz. Biraz daha net şekilde açıklamak gerekirse:
Sonuç olarak, mikrokontrolörde zamanı ölçmenin bir yolunun olması gerekiyor. İşte bu noktada devreye Timer (Zamanlayıcı) modülleri giriyor. Ancak zamanlayıcılara geçmeden önce, zamanlama sorununa sunduğumuz en basit çözümü hatırlayalım. BURADAKİ yazımızda, DummyDelayMs fonksiyonun kurnazca tanımlayarak milisaniye mertebesinde bir zamanlama sağlamıştık. Hemen implementasyonu hatırlayalım.
1 2 3 4 5 6 |
static void DummyDelayMs(unsigned int delay_ms) { unsigned long cnt=0; unsigned long delay_limit_ms= delay_ms*(F_OSC/120000); for(cnt=0;cnt<delay_limit_ms;cnt++); } |
Gördüğünüz gibi bu kodda, mikrokontrolöre boş beleş sayı saydırıp, geçen zamandan faydalandık. Tersten giderek, 1ms beklemek için gerekli olan sayma sayısını hesaplayıp, mikrokontrolörün o kadar boş beleş (dummy) işlem yapmasını sağladık. Bu da adeta bir araçta vitesi boşa atıp gaza sonuna kadar basmaya benziyor. Bir yere gitmiyoruz ama cayır cayır benzin yakıyoruz 🙂 İşte bunu yapmamak için Timer modülü var.
Bizim bu zamana kadar çalıştığımız PIC16F84A’da ne yazık ki tek bir zamanlayıcı modülü var. Ancak diğer modellerde daha fazla sayıda zamanlayıcı modülü mevcut. Unutmamak gerekir ki zamanlayıcılar gömülü sistemlerde “elzem” olduğundan, hemen her mikrokontrolörde zamanlayıcı bulunur. Şimdi hemen bir tablo ile hangi PIC’te hangi zamanlayıcı var görelim.
Zamanlayıcı | PIC16F8X | PIC16F62X | PIC16F877 |
Timer0 | + | + | + |
Timer1 | – | + | + |
Timer2 | – | + | + |
Bu arada hemen araya sıkıştırayım; zamanlayıcıları da bitirince artık daha gelişmiş PIC modelleri üzerinden devam etmemizde bir sakınca kalmayacak. O nedenle yavaştan diğerlerine de geçebiliriz. Şimdi Timer0’ın hakkından gelelim 😉
TIMER 0
Timer0 kardeşimiz, 8-bit’lik bir zamanlayıcı/sayıcı olarak tasarlanmış olup, doğal olarak 0’dan 255’e kadar sayabilmektedir. Yine bu efendi kardeşimizin sayacını okumak da sayacına yazmak da mümkündür; bu da bize zamanlayı istediğimiz değerden başlatma ve güncel değeri okuma imkanları sunar.
Zamanlayıcı, esasında; herhangi bir saat işaretini (clock pulse) alıp o işareti çeşitli ön işlemlerle örnekleyerek sayan bir devreciktir. Buna istinaden söz konusu saat işareti dışarıdaki bir osilatörden (Ring osilatör) ya da iç osilatörden (RC osilatör) alınabilmektedir.
Bu zamanlayıcılar 255’e kadar sayabildiğinden 255’ten sonraki sayımda taşma olur yani değer tekrar 0’a döner. Bu anda kesme kurmak mümkündür. Bu da zamanlayıcı kesmesi olarak adlandırılmaktadır.
Timer0 ile alakası olan kütükler, mikrodoentleyicinin veri kağıdında açıkça belirtilmiştir. Buna göre OPTION_REG, INTCON_REG, TMR0_REG, TMR0_REG ve inanmazsınız TRISA/PORTA kütükleri Timer0 ile ilintili imiş. Bunların her birini didik didik ederek zamanlayıcıların suyunu çıkaracağız. Tabi ki yapacağımız ilk iş, henüz modellemediğimiz kütükleri modelleyerek kütüphanemize eklemek olacak.
Tüm bunlara ek olarak, kütüphanemizi yine adım adım optimize etmeye devam edeceğiz. Burada yine tasarım trade-off”larını tartışacağız ve yeni eksikleri hep birlikte tespit edeceğiz 🙂 Öyleyse işe koyulalım ve TIMER0 ile ilk periyodik fonksiyonumuza hayat verelim!
Malumunuz her yazıda yeni kesme değerlendirme rutinleri, yeni veri yapıları ekliyoruz. Bunları her uygulamada kullanmayacağımızdan, bu uygulamalarda biraz bellek israfı yapmış oluyorduk. Şimdi onları bir config dosyası üzerinden seçimli hale getirmenin tam sırası. Yeni config dosyamız “pic16lib_conf.h” aşağıdaki gibi.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
/* * File: pic16flib_conf.h * Author: ozenozkaya * * Created on 25 Eylül 2015 Cuma, 00:46 */ #ifndef PIC16FLIB_CONF_H #define PIC16FLIB_CONF_H #ifdef __cplusplus extern "C" { #endif #define MAX_INT_CALLBACK_NUM (1) //#define PIC16LIB_USE_RB0_INT //#define PIC16LIB_USE_PORTB_INT #define PIC16LIB_USE_TIMER0_INT #ifdef __cplusplus } #endif #endif /* PIC16FLIB_CONF_H */ |
Görmüş olduğunuz PIC16FLIB_USE* etiketlerinden ikisi commentlenmiş. Eğer uygulamanızda RB0/PORTB kesmesini kullanacaksanız bunları da açabilirsiniz. Biz bu uygulamamızda yalnızca TIMER0 kesmelerini kullanacağımızdan seçimi o şekilde yaptık.
Hemen güncellediğimiz kütüphane dosyalarımızı görelim.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
/* * File: pic16f84a_lib.h * Author: ozenozkaya * * Created on 20 Temmuz 2015 Pazartesi, 00:05 */ #ifndef PIC16F84A_LIB_H #define PIC16F84A_LIB_H #ifdef __cplusplus extern "C" { #endif #include <stdint.h> #include "pic16flib_conf.h" #define PIC16LIB_ERROR_NONE (0) #define PIC16LIB_ERROR_CB_NULL (-1) #define PIC16LIB_ERROR_CB_ALREADY_REGISTERED (-2) #define F_OSC (1000000UL) #define REG_TIMER0_ADDR (0x01) #define REG_TIMER0 (*( volatile unsigned char*)REG_TIMER0_ADDR) #define REG_TRISB_ADDR (0x86) #define REG_TRISB (*( volatile unsigned char*)REG_TRISB_ADDR) #define REG_TRISA_ADDR (0x85) #define REG_TRISA (*( volatile unsigned char*)REG_TRISA_ADDR) #define TRIS_PIN_OUTPUT (0) #define TRIS_PIN_INPUT (1) #define TRIS_PORT_OUTPUT (0) #define TRIS_PORT_INPUT (0xFF) #define REG_PORTB_ADDR (0x06) #define REG_PORTB (*( volatile unsigned char*)REG_PORTB_ADDR) #define REG_PORTA_ADDR (0x05) #define REG_PORTA (*( volatile unsigned char*)REG_PORTA_ADDR) #define PORT_PIN_LOW (0) #define PORT_PIN_HIGH (1) #define PORT_ALL_LOW (0) #define PORT_ALL_HIGH (0xFF) #define REG_INTCON_ADDR (0x0B) #define REG_INTCON (*( volatile unsigned char*)REG_INTCON_ADDR) #define INT_ENABLE (1) #define INT_ENABLE_ALL (0xFF) #define INT_DISABLE (0) #define INT_DISABLE_ALL (0) #define INT_FLAG_CLEAR (0) #define REG_OPTION_ADDR (0x81) #define REG_OPTION (*( volatile unsigned char*)REG_OPTION_ADDR) #define INT_EDGE_FALLING (0) #define INT_EDGE_RISING (1) typedef struct { //one bit is stored in one BYTE uint8_t pin0:1; uint8_t pin1:1; uint8_t pin2:1; uint8_t pin3:1; uint8_t pin4:1; uint8_t pin5:1; uint8_t pin6:1; uint8_t pin7:1; }_tris_port_pin_t, *_tris_port_pin_ptr_t; typedef struct { uint8_t RBIF:1; uint8_t INTF:1; uint8_t T0IF:1; uint8_t RBIE:1; uint8_t INTE:1; uint8_t T0IE:1; uint8_t EEIE:1; uint8_t GIE:1; }_intcon_reg_t,*_intcon_reg_ptr_t; typedef struct { uint8_t PS:3; uint8_t PSA:1; uint8_t T0SE:1; uint8_t T0CS:1; uint8_t INTEDG:1; uint8_t RBPU:1; }_option_reg_t,*_option_reg_ptr_t; typedef union { _tris_port_pin_t pins; uint8_t port; }tris_port_t,*tris_port_ptr_t; #define REG_TRISA_UNION (*(tris_port_ptr_t)REG_TRISA_ADDR) #define REG_TRISB_UNION (*(tris_port_ptr_t)REG_TRISB_ADDR) #define REG_PORTA_UNION (*(tris_port_ptr_t)REG_PORTA_ADDR) #define REG_PORTB_UNION (*(tris_port_ptr_t)REG_PORTB_ADDR) typedef union { _option_reg_t bits; uint8_t value; }option_reg_t, *option_reg_ptr_t; #define T0CS_SOURCE_RA4_T0CKI_PIN (0x01) #define T0CS_SOURCE_INTERNAL_CLOCK (0x00) #define PSA_ASSIGN_TO_WATCHDOG (0x01) #define PSA_ASSIGN_TO_TIMER0 (0x00) #define PS_TMR0_RATE_DIVIDE_2 (0x00) #define PS_TMR0_RATE_DIVIDE_4 (0x01) #define PS_TMR0_RATE_DIVIDE_8 (0x02) #define PS_TMR0_RATE_DIVIDE_16 (0x03) #define PS_TMR0_RATE_DIVIDE_32 (0x04) #define PS_TMR0_RATE_DIVIDE_64 (0x05) #define PS_TMR0_RATE_DIVIDE_128 (0x06) #define PS_TMR0_RATE_DIVIDE_256 (0x07) #define REG_OPTION_UNION (*(option_reg_ptr_t)REG_OPTION_ADDR) typedef union { _intcon_reg_t bits; uint8_t value; }intcon_reg_t, *intcon_reg_ptr_t; #define REG_INTCON_UNION (*(intcon_reg_ptr_t)REG_INTCON_ADDR) typedef union { uint8_t value; uint8_t count; }timer0_reg_t, *timer0_reg_ptr_t; #define TIMER0_MAX_CNT (0xFF) #define TIMER0_ASSIGN_CNT(NEW_TMR0_CNT) (TIMER0_MAX_CNT-NEW_TMR0_CNT) #define REG_TIMER0_UNION (*(timer0_reg_ptr_t)REG_TIMER0_ADDR) #define WAIT_LOOP_FOREVER() while(1) #ifdef PIC16LIB_USE_RB0_INT typedef void (*rb0_int_callback_t)(); int8_t register_rb0_int_callback(rb0_int_callback_t new_cb); int8_t unregister_rb0_int_callback(rb0_int_callback_t cb_to_unreg); #endif #ifdef PIC16LIB_USE_PORTB_INT typedef void (*portb_int_callback_t)(); int8_t register_portb_int_callback(portb_int_callback_t new_cb); int8_t unregister_portb_int_callback(portb_int_callback_t cb_to_unreg); #endif #ifdef PIC16LIB_USE_TIMER0_INT typedef void (*timer0_int_callback_t)(); int8_t register_timer0_int_callback(timer0_int_callback_t new_cb); int8_t unregister_timer0_int_callback(timer0_int_callback_t cb_to_unreg); #endif #ifdef __cplusplus } #endif #endif /* PIC16F84A_LIB_H */ |
Timer0 adres ve kütük tanımlamalarının yapılmış olduğuna dikkatinizi çekerim 🙂 Aynı zamanda, config dosyasında kullandığımız etiketler de, kullanılmayan kesmelerin fonksiyon prototiplerini derleme zamanında kapatmak için yerli yerinde. Öyleyse hemen .c dosyamızı görelim.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
#include "pic16f84a_lib.h" #define BIT_TOGGLE(x) (x^=1) #ifndef NULL #define NULL ((void*)0) #endif #ifdef PIC16LIB_USE_RB0_INT rb0_int_callback_t rb0_int_callback_list[MAX_INT_CALLBACK_NUM]; #endif #ifdef PIC16LIB_USE_PORTB_INT portb_int_callback_t portb_int_callback_list[MAX_INT_CALLBACK_NUM]; #endif #ifdef PIC16LIB_USE_TIMER0_INT timer0_int_callback_t timer0_int_callback_list[MAX_INT_CALLBACK_NUM]; #endif #if defined(PIC16LIB_USE_RB0_INT) || defined(PIC16LIB_USE_PORTB_INT) || defined(PIC16LIB_USE_TIMER0_INT) static void interrupt global_isr_handler() { #ifdef PIC16LIB_USE_RB0_INT if(REG_INTCON_UNION.bits.INTF) { uint8_t cb_index=0; for(cb_index=0;cb_index<MAX_INT_CALLBACK_NUM; cb_index++) { if(NULL != rb0_int_callback_list[cb_index]) { rb0_int_callback_list[cb_index](); } } BIT_TOGGLE(REG_OPTION_UNION.bits.INTEDG); REG_INTCON_UNION.bits.INTF=INT_FLAG_CLEAR; } #endif #ifdef PIC16LIB_USE_PORTB_INT if(REG_INTCON_UNION.bits.RBIF) { uint8_t cb_index=0; for(cb_index=0;cb_index<MAX_INT_CALLBACK_NUM; cb_index++) { if(NULL != portb_int_callback_list[cb_index]) { portb_int_callback_list[cb_index](); } } BIT_TOGGLE(REG_OPTION_UNION.bits.INTEDG); REG_INTCON_UNION.bits.RBIF=INT_FLAG_CLEAR; } #endif #ifdef PIC16LIB_USE_TIMER0_INT if(REG_INTCON_UNION.bits.T0IF) { REG_INTCON_UNION.bits.T0IF=INT_FLAG_CLEAR; uint8_t cb_index=0; for(cb_index=0;cb_index<MAX_INT_CALLBACK_NUM; cb_index++) { if(NULL != timer0_int_callback_list[cb_index]) { timer0_int_callback_list[cb_index](); } } } #endif } #endif #ifdef PIC16LIB_USE_RB0_INT int8_t register_rb0_int_callback(rb0_int_callback_t new_cb) { if(NULL != new_cb) { uint8_t cb_index=0; for(cb_index=0;cb_index<MAX_INT_CALLBACK_NUM; cb_index++) { if(NULL == rb0_int_callback_list[cb_index]) { rb0_int_callback_list[cb_index]=new_cb; } } if(MAX_INT_CALLBACK_NUM == cb_index) { return PIC16LIB_ERROR_CB_ALREADY_REGISTERED; //All callbacks are already registered } } else { return PIC16LIB_ERROR_CB_NULL; // Callback is NULL } return 0; } int8_t unregister_rb0_int_callback(rb0_int_callback_t cb_to_unreg) { if(NULL != cb_to_unreg) { uint8_t cb_index=0; for(cb_index=0;cb_index<MAX_INT_CALLBACK_NUM; cb_index++) { if(cb_to_unreg == rb0_int_callback_list[cb_index]) { rb0_int_callback_list[cb_index]=NULL; } } } else { return PIC16LIB_ERROR_CB_NULL; // Callback is NULL } return PIC16LIB_ERROR_NONE; } #endif #ifdef PIC16LIB_USE_PORTB_INT int8_t register_portb_int_callback(portb_int_callback_t new_cb) { if(NULL != new_cb) { uint8_t cb_index=0; for(cb_index=0;cb_index<MAX_INT_CALLBACK_NUM; cb_index++) { if(NULL == portb_int_callback_list[cb_index]) { portb_int_callback_list[cb_index]=new_cb; } } if(MAX_INT_CALLBACK_NUM == cb_index) { return PIC16LIB_ERROR_CB_ALREADY_REGISTERED; //All callbacks are already registered } } else { return PIC16LIB_ERROR_CB_NULL; // Callback is NULL } return PIC16LIB_ERROR_NONE; } int8_t unregister_portb_int_callback(portb_int_callback_t cb_to_unreg) { if(NULL != cb_to_unreg) { uint8_t cb_index=0; for(cb_index=0;cb_index<MAX_INT_CALLBACK_NUM; cb_index++) { if(cb_to_unreg == portb_int_callback_list[cb_index]) { portb_int_callback_list[cb_index]=NULL; } } } else { return PIC16LIB_ERROR_CB_NULL; // Callback is NULL } return PIC16LIB_ERROR_NONE; } #endif #ifdef PIC16LIB_USE_TIMER0_INT int8_t register_timer0_int_callback(timer0_int_callback_t new_cb) { if(NULL != new_cb) { uint8_t cb_index=0; for(cb_index=0;cb_index<MAX_INT_CALLBACK_NUM; cb_index++) { if(NULL == timer0_int_callback_list[cb_index]) { timer0_int_callback_list[cb_index]=new_cb; } } if(MAX_INT_CALLBACK_NUM == cb_index) { return PIC16LIB_ERROR_CB_ALREADY_REGISTERED; //All callbacks are already registered } } else { return PIC16LIB_ERROR_CB_NULL; // Callback is NULL } return PIC16LIB_ERROR_NONE; } int8_t unregister_timer0_int_callback(timer0_int_callback_t cb_to_unreg) { if(NULL != cb_to_unreg) { uint8_t cb_index=0; for(cb_index=0;cb_index<MAX_INT_CALLBACK_NUM; cb_index++) { if(cb_to_unreg == timer0_int_callback_list[cb_index]) { timer0_int_callback_list[cb_index]=NULL; } } } else { return PIC16LIB_ERROR_CB_NULL; // Callback is NULL } return PIC16LIB_ERROR_NONE; } #endif |
Görüldüğü üzere, alışılageldik şekilde timer0 kesme rutinlerini de ekledik. Ek olarak yine config etiketlerimiz de fonksiyonları çevrelemiş durumda 🙂 Artık kütüphanemizi kullanan kimselere çok daha geniş optimizasyon imkanı sunuyoruz! Her durumda yine Microchip liblerinden çok daha yüksek performanslı bir konumda kütüphanemiz 🙂 0 byte kullanarak tüm register erişimlerini yapmak mümkün. Konfor arayan yazılımcılar için de, kütük erişimlerini çok kolaylaştıran veri yapılarını implement ettik. İsteyen istediği yönde optimizasyon yapabiliyor. Bu da kütüphanemizin esnekliğini ciddi oranda artırmış durumda. Şimdi uygulama koduna geçelim.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
/* * File: main.c * Author: ozenozkaya * * Created on 24 September 2015 */ #include "pic16f84a_lib.h" /*FOSC is 4Mhz. Prescaler input it 1Mhz. Because prescaler is 8, prescaler output f=125kHz. So,the prescaler output period is 8uS. Hence, we need 125 timer pulses to reach 1mS. 8uS*125 = 1mS*/ #define TIMER0_VAL_FOR_1MS (125) static void milisecond_event_callback() { REG_PORTB_UNION.port^=0xFF; REG_TIMER0_UNION.count = TIMER0_ASSIGN_CNT(TIMER0_VAL_FOR_1MS); } void main() { REG_TRISB_UNION.port = TRIS_PORT_OUTPUT; REG_PORTB_UNION.port = PORT_ALL_LOW; REG_OPTION_UNION.bits.T0CS = T0CS_SOURCE_INTERNAL_CLOCK; REG_OPTION_UNION.bits.PSA = PSA_ASSIGN_TO_TIMER0; REG_OPTION_UNION.bits.PS = PS_TMR0_RATE_DIVIDE_8; //8uS /*! 125*8uS = 1000uS = 1mS*/ REG_TIMER0_UNION.count = TIMER0_ASSIGN_CNT(TIMER0_VAL_FOR_1MS); REG_INTCON_UNION.value = INT_DISABLE_ALL; REG_INTCON_UNION.bits.GIE = INT_ENABLE; REG_INTCON_UNION.bits.T0IE = INT_ENABLE; register_timer0_int_callback(milisecond_event_callback); WAIT_LOOP_FOREVER(); } |
Bu yazılımda, B portlarını 1 milisaniyede bir eviren bir yazılım oluşturduk. Peki ama bunu nasıl yaptık? Hemen hesap kitaba değinelim. PIC mikrokontrolörümüze dışarıdan bir kristal osilatör bağlı ve frekansı 4MHz.
PIC16F84A’da timer modülüne gelen saat işareti 4’e bölünerek gelir (datasheet’te yazmakta). Dolayısıyla modülün girişi 1MHz yani saniyede 1 milyon saat darbesi geliyor. Timer modülünün giriş kısmında bir frekans ölçeklendirici (prescaler) bulunmakta. Biz ölçeklendirme oranını 8’e bölecek şekilde seçtik. Bunu, aşağıdaki satır ile yaptık.
1 |
REG_OPTION_UNION.bits.PS = PS_TMR0_RATE_DIVIDE_8; //8uS |
Buna göre, 1MHz’lik işaret frekansını 8’e böldük ve prescaler çıkışındaki frekans 125Khz oldu. Bu da 8 micro saniyede bir saat darbesi demek. Eğer bu saat darbelerinden 125 tane sayılınca kesme olacak şekilde bir konfigurasyon yaparsak, 8*125 us = 1ms’lik bir kesme rutini elde etmiş olacağız. Bu sebeple aşağıdaki tanımla 125 değeri tanımlandı.
1 2 3 4 5 6 |
/*FOSC is 4Mhz. Prescaler input it 1Mhz. Because prescaler is 8, prescaler output f=125kHz. So,the prescaler output period is 8uS. Hence, we need 125 timer pulses to reach 1mS. 8uS*125 = 1mS*/ #define TIMER0_VAL_FOR_1MS (125) |
Bu değer ise timer0 kütüğüne aşağıdaki şekilde atandı.
1 2 |
/*! 125*8uS = 1000uS = 1mS*/ REG_TIMER0_UNION.count = TIMER0_ASSIGN_CNT(TIMER0_VAL_FOR_1MS); |
Burada ibretlik bir macromuz var. Bu macro TIMER0_ASSIGN_CNT. Peki neden buna ihtiyaç duyduk? Malumunuz bizim timer0 modülü yukarı doğru sayıyor ve 255(0xFF)’de taşıyor. Taşmadan önce 125 tur sayabilmesi için aslında 255-125 =130 değerinden başlamalı Timer0. Bu durumda timer 130’dan başlatılacak ve tam 125 tur sonra (1ms yapıyor) kesme gelmiş olacak. İşte bu çıkarma hesabını yapan macromuz TIMER0_ASSIGN_CNT. Kütüphanemizdeki tanımı da aşağıdaki gibi idi.
1 2 |
#define TIMER0_MAX_CNT (0xFF) #define TIMER0_ASSIGN_CNT(NEW_TMR0_CNT) (TIMER0_MAX_CNT-NEW_TMR0_CNT) |
Bu sayede 1ms’lik zamanlama sağlamış oluyor. Ancak sürekli olarak 1ms’lik zamanlamayı sağlamak için TIMER0 taştığında, yani kesme oluştuğunda Timer0’ı tekrar 130 değerinden başlatmak gerekiyor. Bunun için, milisaniye callback fonskiyonumuzda aşağıdaki gibi bir tekar yükleme yaptık.
1 2 3 4 5 |
static void milisecond_event_callback() { REG_PORTB_UNION.port^=0xFF; REG_TIMER0_UNION.count = TIMER0_ASSIGN_CNT(TIMER0_VAL_FOR_1MS); } |
Bu sayede her şey çalışır durumda ve enerji efektif şekilde periyodik işlerimizi yapabilir hale geldik 🙂 Hemen proteus isis üzerindeki simülasyon çıktımızı da ekleyelim.
Osiloskoptaki zaman ölçümünün 1.06 mS oluşuna dikkat ediniz. 1ms güzel de, bu 0.06 mS nereden geldi? Buradan da çok güzel ibretlere değineceğiz dostlar, ancak bu sorunun yanıtı ve dahasını bir sonraki yazıya bırakıyoruz 🙂
Zamanlayıcılar konusunda ne kadar çok tekrar yaparsanız, farklı uygulamalar yazarsanız o kadar iyi olur. Örneğin karaşimşek uygulamasını zamanlayıcı kullanarak yazmakta faydalar var.
Şimdi devam!
Yazıları beğendiyseniz, faydalanabilecek tanıdıklarınızla paylaşmayı unutmayınız.
Önceki Sayfa Sonraki Sayfa