Very useful machine learning book with a lot of practical applications. The best part of the book is that it both covers theoretical and practical aspects. You can start from a conventional approach on ML/AI problems via using classical algorithms. And then book covers a lot of state-of-the art topics like CNN, RNN, LSTM, etc.
Author: ozenozkaya
PIC Programlama – 11 – USART
Merhabalar,
Önceki yazı ile birlikte, bir mikrokontrolöre nasıl SDK geliştirebileceğimizi ve PIC16F84A’nın tüm modüllerini detaylıca incelemiştik. Her iki başlık için de gerekli mesajların verildiği, gerekli bilgilerin aktarıldığı düşüncesi ile PIC yazılarının seyrini biraz daha değiştiriyorum.
PIC programlama yazı dizisinin bu bölümünde, şu zamana kadar incelemediğimiz, ancak gömülü sistemler dünyasının adeta kolonları/kirişleri olan temel modülleri inceleyeceğiz. Bunların başında da USART modülü geliyor. PIC16F84A’da ne yazık ki USART modülü bulunmuyor. Bu sebeple PIC16F877A üzerinden devam edeceğiz 🙂 Bu defa, PIC16F877A için de ayrıca bir kütüphane yazmak yerine, mecvut kütüphaneleri kullanarak uygulama yapacağız. Kütüphane geliştirme ile ilgili bilgileri önceki yazılarda yeterince işlemiş olduğumuzdan, bu yola girmekte artık bir sakınca görmüyorum. Dileyen azimli arkadaşlar, PIC16F877A için de kütüphane yazabilirler.
Şimdi gelelim her zamanki gibi USART’a bir girizgah yapmaya. Ancak öncesinde irdelememiz gereken bir başka önemli kavram var…
Haberleşme
Bir bilgiyi, bir kaynaktan başka bir hedefe aktarmak o bilgili veriye dönüştürüp; veriyi kaynaktan hedefe aktaracak bir haberleşme hattı kurmakla mümkündür. Çok derinden girdiğimiz bu hususu bir örnekle irdeleyelim. Ali adlı arkadaşımız, o anki şaşkınlığını ifade eden ‘A’ sesini, Veli’ye duyurmak istediğinde haberleşme süreci nasıl oluşur?
Şaşkınlık bilgisi bu şemada ‘A’ sesi ile kodlanmıştır. Bu bilgi, havayı belli frekansta titreştiren ve Ali’nin ağzından gelen ‘A’ sesi vasıtası ile havada ilerler ve Veli’nin kulağında oluşan titreşimlerin algılanması ile Veli’ye bu veri aktarılır. Ardından Veli’nin beyni bu veriyi tekrardan bilgiye dönüştürerek ‘A’ sesini algılar ve Veli; Ali’nin şaşırdığını algılamış olur. Bu şemada kaynak Ali, hedef Veli, haberleşme kanalı hava, bilgi şaşkınlık, veri ise ‘A’ sesi olmaktadır. Haberleşme ses vasıtasyla da taşınsa, elektriksel olarak da taşınsa bu şema değişmez. Bilgi, veri ve haberleşme kaynaklarını açıkladığımız bu örnek çok saf ve naif görünse de, kompleksleşen haberleşme sistemlerinde temelleri hatırlamak ve bu gerçekliklerin farkında olmak hayat kurtarmaktadır.
Bilginin veriye dönüşümü, bu yazının odak noktası olmadığından; biz verinin aktarımı üzerine yoğunlaşacağız.Peki tek bir veriyi değil de, birden fazla veriyi göndermek istediğimizde ne yapacağız?
Paralel ve Seri Haberleşme
Yukarıdaki örnekte Ali’nin biraz daha konuşkan biri olduğunu düşünelim. Ali, Veli’ye “Selam Veli” dediğinde sesler ardışıl olarak; yani peşi sıra olarak Veli’ye gider. Yani Ali sırasıyla önce ‘S’,’e’,’l’,’a’,’m’ der biraz bekler, ardından sırasıyla ‘V’,’e’,’l’,’i’ harflerini peşisıra söyler. Veli de bu sesleri -kısa bir gecikmeden sonra- aynı sıra ile duyar. İşte bu şekilde ardı-sıra veri aktarımı olan haberleşme şemaları, seri haberleşme şemaları olarak adlandırılır. Özellikle, tek haberleşme kanalının bulunduğu durumlarda, bu haberleşme yöntemi kullanılır. Seri haberleşmede yalnızca tek kanal kullanılır, ancak tüm veri sırasıyla gönderildiğinden tüm verinin aktarımı zaman alır.
Paralel haberleşmede ise veri, -adı üzerinde- paralel olarak gönderilir. 8 hoparlörü olan bir cihaz, 8 mikrofonu olan bir cihaza ‘Selamlar’ mesajını paralel olarak iletmek isterse (her bir hoparlörü sadece 1 mikrofonun duyabildiğini varsayarsak) 1. hoparlör ‘S’ sesini, 2. hoparlör ‘e’ sesini, … , 8. hoparlör ‘r’ sesini aynı anda çıkartır. Karşı tarafta da bu sesler mikrofonlardan aynı karşılıkla aynı anda algılanırsa; tüm sesler paralel olarak gönderildiğinden bu haberleşme adı üzerinde “paralel haberleşme” olur. Paralel haberleşme, birden fazla haberleşme kanalı gerektirir ancak bilgi daha kısa sürede aktarılır.
Aynı konseptler elektriksel olarak da geçerlidir. Yani ses yerine bitleri inceleyecek olursak, aşağıdaki gibi bir gösterim yapmamız yanlış olmaz. [Resim Wikipedia sayfasından alınmıştır.]
Güzel.. Şimdi dönelim konumuza. USART dediğimiz nane, bir seri haberleşme algoritması olup; sıkça kullanılmaktadır. Özellikle “tracing”,”serial logging” gibi işler için; sıklıkla USART kullanılır. Ayrıca USART ile haberleşen çok sayıda sensör vb. elektronik ekipman bulunmaktadır.
PIC16F877A ve USART
Şimdi gelelim PIC16F877A ile USART haberleşmesi yapacak kodu yazmaya… Bunun için oldukça basit bir örnek yapacağız. İlk hedefimiz, USART üzerinden “Merhaba dünya!” yazısını gönderecek bir kod yazmak. Bunu, Microchip’in en yeni kütüphanesi olan; xc kütüphanesini kullanarak yapacağız. Unutmayınız ki, xc kütüphanesini kullanabilmek için XC8 derleyicisini yüklemeniz gerekmekte.
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 |
/* * File: main.c * Author: ozenozkaya * * Created on 26 Haziran 2016 Pazar, 18:10 */ #include <stdio.h> #include <stdlib.h> #include <xc.h> #define _XTAL_FREQ 4000000 void UART_Initialize() { TRISC = 0b10000000; TXSTA=0b00100100; RCSTA=0b10010000; SPBRG=25; TXREG=0x00; } void UART_Write(char data) { while(!TRMT); TXREG = data; } void UART_Write_Text(char *text) { int i; for(i=0;text[i]!='\0';i++) UART_Write(text[i]); } int main() { UART_Initialize(); while(1) { UART_Write_Text("Merhaba dunya!\r\n"); __delay_ms(1000); } return (EXIT_SUCCESS); } |
USART haberleşmesi seri bir haberleşme olduğundan, verilerin ardı sıra gönderilimindeki zamanlama çok önemlidir. Bu sebeple, kodumuza PIC16F877A’nın kristal osilatör fraksnsını aşağıdaki gibi giriyoruz.
1 |
#define _XTAL_FREQ 4000000 |
Öncelikli olarak UART_Intialize fonksiyonunu incelemeye başlayalım. PIC16F877A’nın C portunun 6. pini USART_TX (transmit, veri gönderme), 7. pini ise USART_RX (receive, veri alma) pini olduğundan, 6. pini çıkış, 7. pini giriş olarak konfigure ediyoruz. Buna ilişkin kod aşağıda yer alıyor:
1 |
TRISC = 0b10000000; |
Ardından modülümüzü konfigüre edeceğiz. Buna göre datasheet’e baktığımızda konfigüre etmemiz gereken kütüklerin TXSTA, RCSTA, SPBRG kütükleri olduğunu görüyoruz. Veri göndermek için ise TXREG kütüğüne ihtiyaç duyacağız. Veri kağıdından sırası ile bu kütüklerle ilgli bilgilere bakıp, konfigürasyonumuzu yapalım 🙂
1 |
TXSTA=0b00100100; |
Buna göre SYNC=0 (Asenkron mod), BRGH = 1 (Yüksek hızlı mod), TXEN=1 (Veri gönderimi aktive edildi, izin verildi), TX9=0 (8 bit veri aktarımı modu) şeklinde konfigürasyon yaptığımız gözüküyor 🙂
Şimdi hemen diğer bir önemli kütüğümüz olan RCSTA’ya bakalım.
1 |
RCSTA=0b10010000; |
Buna göre RX9=0 (8 bit mod), ADDEN= 0 (9. bit parity biti olarak kullanılsın), CREN=1 (Sürekli veri alımı aktive edildi, izin verildi), SPEN=1 (Seri port aktive edildi) şeklinde konfigürasyon yaptığımız gözüküyor 🙂
Seri haberleşmemizi 9600 baudrate’te yapmak istiyoruz. Buna göre SPBRG kütüğüne yazmamız gereken değeri, PIC16F877A’nın datasheet dökümanından buluyoruz. Buna göre, veri kağıdının aşağıdaki kısmından da görülebildiği üzere BRGH bitinin durumuna göre hesaplamalar değişiyor. Biz BRGH=1 olacak şekilde konfigüre etmiştik.
Bir diğer önemli tablo da, BRGH seçim tablosu. Veri kağıdından aldığım tablolar ise aşağıdaki gibi:
Yukarıdaki tablonun alt kısmını kullancağız, çünkü BRGH=1 olarak konfigüre etmiştik. Kullanacağımız osilatör frekansı 4MHz olduğunda ve istediğimiz Baudrate (veri aktarım hızı) 9600 baud = 9.6K baud olduğundan, SPBRG değerinizin 25 olması gerektiği tablodan görülebiliyor. Biz de bu sebeple aşağıdaki konfigürasyonu yaptık:
1 |
SPBRG=25; |
Artık USART modülümüzü konfigüre ettiğimizden, veri gönderme kısmına geçelim. Veri göndermek için yapmamız gereken şey, göndereceğimiz 8 bitlik değeri TXREG kütüğüne yazmak. Ancak hali hazırda gönderilmekte olan bir veri var ise öncelikle onun gönderilmesini beklememiz gerekiyor. TRMT biti 1 oldğunda veri gönderimi yok demektir. Buna göre TRMT kütüğü sıfır olduğu sürece bekliyoruz.
1 2 3 4 5 |
void UART_Write(char data) { while(!TRMT); TXREG = data; } |
Yukarıdaki fonksiyon ile her seferinde 8 bitlik bir veri gönderebiliriz. Bu, her seferinde en fazla 1 ASCII karakteri gönderebileceğimiz anlamına geliyor. String, yani karakter dizisi göndermek için ise aşağıdaki fonksiyonu yazdık.
1 2 3 4 5 6 |
void UART_Write_Text(char *text) { int i; for(i=0;text[i]!='\0';i++) UART_Write(text[i]); } |
Buna göre uygulamamızın Proteus ISIS simülasyonundaki çıktısı aşağıdaki gibi oluyor [Benzetimden önce PIC16F877A’ya sağ tıklayarak Processor Clock Frequency parametresini 4MHz yapmanız gerekmekte] :
Şimdilik bu kadar 🙂 Bir sonraki yazımızda USART RX kesmesini ve USART aracılığı ile veri okumayı inceleyeceğiz.
Yazıları beğendiyseniz, faydalanabilecek tanıdıklarınızla paylaşmayı unutmayınız.
Önceki Sayfa Sonraki SayfaPIC Programlama – 10 – EEPROM
Merhabalar, doktora yeterlik sınavı dolayısıyla ara verdiğim blog yazılarıma, yeterlik aşamasını geçtiğimden dolayı devam ediyorum 😀 PIC16F84A ile ilgili, geriye kalan son bir iki konuyu da irdeleyeceğimiz bu yazıda, ana gündemimiz EEPROM olacak.
“EEPROM nedir?” sorusuna yanıt arayanlar için WIKIPEDIA sayfasında yeterli bilgi olduğunu belirtmek isterim. Kısaca özetlemek gerekirse EEPROM, elektriksel olarak yazılıp silinebilen, kalıcı bir bellek tipi; yani elektrik gitse de içerisinde kayıtlı olan veri kendiliğinden silinmiyor. Flash belleğe göre yazım ömrünün çok fazla olması, bu devre elemanını bu gün dahi popüler tutmaya yetiyor.
PIC16F84A içinde de 64 byte’lık bir EEPROM bulunuyor. 64 byte kadar küçük bir EEPROM olsa da, özellikle kalıcı konfigürasyonları saklama konusunda, bu modül hayat kurtarıyor. Bu durumda bize düşen de, PIC16FLIB adıyla geliştirdiğimiz alternatif PIC kütüphanesine bu modülü de eklemek olacak. Öyleyse hemen başlayalım!
İlk adım olarak, her zamanki gibi datasheet’i okuyup anlayarak, yazılımsal olarak modelleyeceğiz. Datasheet’te EEPROM modülü, sistemin blok diyagramında şöyle yer alıyor:
Buradan da görüldüğü gibi EEPROM modülümüz 64*8 bitlik veriyi saklayabiliyor. Şimdi de, EEPROM ile ilgili kütüklerin (register), hangi adreslerde olduğuna bakalım.
İlgili kütükleri fosforlu sarı ile işaretlediğimden adreslerini görebilirsiniz. Artık adreslerini ve yapılarını da bildiğimizden, bu kütükleri yazdığımız PIC kütüphanesine ekleyebiliriz. EEPROM modülünü de derlemeye dahil edip etmemeyi seçimli yapmak için işe pic16flib_conf.h dosyasına aşağıdaki işaretli satırı ekleyerek başlıyoruz.
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 |
/* * 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 #define PIC16LIB_USE_EEPROM #ifdef __cplusplus } #endif #endif /* PIC16FLIB_CONF_H */ |
Şimdi pic16f84a_lib.h içerisindeki kütük tanımlamalarına geçelim. Aşağıdaki işaretli satırları pic16f84a_lib.h dosyasına ekliyoruz.
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 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
/* * 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 PIC16LIB_ERROR_WRONG_EEPROM_ADDRESS (-3) #define F_OSC (1000000UL) #define _REG_TIMER0_ADDR (0x01) #define REG_TIMER0 (*( volatile uint8_t*)_REG_TIMER0_ADDR) #define _REG_PROGRAM_COUNTER_ADDR (0x02) #define REG_PROGRAM_COUNTER (*( volatile uint8_t*)_REG_PROGRAM_COUNTER_ADDR) #define _REG_STATUS_ADDR (0x03) #define REG_STATUS (*( volatile uint8_t*)_REG_STATUS_ADDR) #define _REG_PORTA_ADDR (0x05) #define REG_PORTA (*( volatile uint8_t*)_REG_PORTA_ADDR) #define _REG_PORTB_ADDR (0x06) #define REG_PORTB (*( volatile uint8_t*)_REG_PORTB_ADDR) #define PORT_PIN_LOW (0) #define PORT_PIN_HIGH (1) #define PORT_ALL_LOW (0) #define PORT_ALL_HIGH (0xFF) #define _REG_EEDATA_ADDR (0x08) #define REG_EEDATA (*( volatile uint8_t*)_REG_EEDATA_ADDR) #define _REG_EEADR_ADDR (0x09) #define REG_EEADR (*( volatile uint8_t*)_REG_EEADR_ADDR) #define _REG_TRISB_ADDR (0x86) #define REG_TRISB (*( volatile uint8_t*)_REG_TRISB_ADDR) #define _REG_TRISA_ADDR (0x85) #define REG_TRISA (*( volatile uint8_t*)_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_EECON1_ADDR (0x88) #define REG_EECON1 (*( volatile uint8_t*)_REG_EECON1_ADDR) #define _REG_EECON2_ADDR (0x89) #define REG_EECON2 (*( volatile uint8_t*)_REG_EECON2_ADDR) #define EECON2_PROTECTION_SEQ_1 (0x55) #define EECON2_PROTECTION_SEQ_2 (0xAA) #define _REG_INTCON_ADDR (0x0B) #define REG_INTCON (*( volatile uint8_t*)_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 uint8_t*)_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 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 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; #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) typedef struct { uint8_t RD:1; uint8_t WR:1; uint8_t WREN:1; uint8_t WRERR:1; uint8_t EEIF:1; uint8_t _RESERVED:3; }_eecon1_reg_t,*_eecon1_reg_ptr_t; #define EEPROM_MAX_ADDRESS (0x3F) #define RD_EEPROM_INIT_READ (0x01) #define RD_EEPROM_DEINIT_READ (0x00) #define WR_EEPROM_INIT_WRITE (0x01) #define WR_EEPROM_DEINIT_WRITE (0x00) #define EEPROM_SELECT_ADDRESS(EE_ADDR) do{REG_EEADR=EE_ADDR;}while(0) #define EEPROM_GET_DATA() (uint8_t)(REG_EEDATA) typedef union { _eecon1_reg_t bits; uint8_t value; }eecon1_reg_t, *eecon1_reg_ptr_t; #define REG_EECON1_UNION (*(eecon1_reg_ptr_t)_REG_EECON1_ADDR) #define WAIT_LOOP_FOREVER() while(1) #define DO_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 PIC16LIB_USE_EEPROM int8_t eeprom_read_data(uint8_t ee_address, uint8_t *ee_read_data_ptr); int8_t eeprom_write_data(uint8_t ee_address, uint8_t ee_write_data_val); #endif #ifdef __cplusplus } #endif #endif /* PIC16F84A_LIB_H */ |
Register tanımlarını nasıl yaptığımızı daha önceki yazılarda çokça açıkladığımdan bu defa tekrara düşmemek adına açıklamayacağım. Her zamanki gibi, veri kağıdındaki bilgileri modelleyerek kütük tanımlarını yaptık.
Şimdi gelelim eeprom’dan veri okuma ve eeprom’a veri yazma fonksiyonlarına. Onları da aşağıda görebilirsiniz:
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 |
int8_t eeprom_read_data(uint8_t ee_address, uint8_t *ee_read_data_ptr) { if(ee_address>EEPROM_MAX_ADDRESS) { return PIC16LIB_ERROR_WRONG_EEPROM_ADDRESS; } EEPROM_SELECT_ADDRESS(ee_address); REG_EECON1_UNION.bits.RD=1; *ee_read_data_ptr=REG_EEDATA; return PIC16LIB_ERROR_NONE; } int8_t eeprom_write_data(uint8_t ee_address, uint8_t ee_write_data_val) { if(ee_address>EEPROM_MAX_ADDRESS) { return PIC16LIB_ERROR_WRONG_EEPROM_ADDRESS; } EEPROM_SELECT_ADDRESS(ee_address); REG_EEDATA=ee_write_data_val; REG_EECON1_UNION.bits.WREN=1; REG_EECON2=EECON2_PROTECTION_SEQ_1; REG_EECON2=EECON2_PROTECTION_SEQ_2; REG_EECON1_UNION.bits.WR=1; REG_INTCON_UNION.bits.EEIE=1; while(REG_EECON1_UNION.bits.EEIF == 0) { asm ("nop"); } REG_INTCON_UNION.bits.EEIE=0; REG_EECON1_UNION.bits.EEIF=0; REG_EECON1_UNION.bits.WREN=0; } |
Her iki fonksiyon girişinde de, girilen adresin 63 değerinden büyük olup olmadığını kontrol ettik. Toplamda 64 byte’lık bir eeprom’umuz olduğundan, en yüksek adres 63 oluyor. Ardından okuma yapmak için, okuma yapacağımız adresi seçtik. Sonrasında RD bitini 1 yaparak okumayı başlattık ve okunan değeri REG_EEDATA kütüğünden okuduk.
Yazma kısmında ise işler biraz daha karışık şekilde çözülüyor. Yine adres seçimini yaptıktan sonra ilk iş olarak yazmak istediğimiz veriyi REG_EEDATA kütüğüne yazıyoruz. Ardından WREN bitini 1 yaparak yazma izni vermiş oluyoruz. Sonrasında, veri kağıdından okuduğumuza göre EECON2 kütüğüne sırasıyla 0x55 ve 0xAA yazmak gerekiyor. Bu, koruma amaçlı konmuş ve veri kağıdına üretici tarafından yazılmış bir ek sekans 🙂 Kodu yazan ne yaptığını biliyorsa, ancak o zaman yazabilsin demişler. Saygımız sonsuz 😉 Ardından WR bitini 1 yaparak yazım sürecini başlatıyoruz. Yazım hemen bitmiyor; bu durumda yazımın bittiğini anlayabilmek için tek çaremiz EEI (eeproma veri yazıldı) kesmesini kurarak, kesme bayrağı 1 olana kadar beklemek. Burada bekleme işini asm(“nop”); satırı ile yaptık. asm(KOMUT) fonksiyonu, XC8 derleycilerinde C kodunun içerisnde assembly komutları çağırmaya yarar. Biz de “nop” yani “bir şey yapmadan 1 saat darbesi bekle” komutunu çalıştırarak bekleme sağladık. Ardından kesme bayrağı 1 olup while döngüsünden çıktığımızda kesmeyi kapatıp, kesme bayrağını temizleyip, WREN kütüğünü sıfara çekerek çıkıp gidiyoruz 🙂 Verimiz yazıldı.
Yukarıdaki fonksiyonların eklendiği durumda pic16f84a_lib.c dosyamız şöyle oluyor:
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 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
#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 (int8_t)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 #ifdef PIC16LIB_USE_EEPROM int8_t eeprom_read_data(uint8_t ee_address, uint8_t *ee_read_data_ptr) { if(ee_address>EEPROM_MAX_ADDRESS) { return PIC16LIB_ERROR_WRONG_EEPROM_ADDRESS; } EEPROM_SELECT_ADDRESS(ee_address); REG_EECON1_UNION.bits.RD=1; *ee_read_data_ptr=REG_EEDATA; return PIC16LIB_ERROR_NONE; } int8_t eeprom_write_data(uint8_t ee_address, uint8_t ee_write_data_val) { if(ee_address>EEPROM_MAX_ADDRESS) { return PIC16LIB_ERROR_WRONG_EEPROM_ADDRESS; } EEPROM_SELECT_ADDRESS(ee_address); REG_EEDATA=ee_write_data_val; REG_EECON1_UNION.bits.WREN=1; REG_EECON2=EECON2_PROTECTION_SEQ_1; REG_EECON2=EECON2_PROTECTION_SEQ_2; REG_EECON1_UNION.bits.WR=1; REG_INTCON_UNION.bits.EEIE=1; while(REG_EECON1_UNION.bits.EEIF == 0) { asm ("nop"); } REG_INTCON_UNION.bits.EEIE=0; REG_EECON1_UNION.bits.EEIF=0; REG_EECON1_UNION.bits.WREN=0; } #endif |
Artık kütüphanemiz hazır olduğuna göre, basit bir test fonksiyonu yazacağız. 0x01 adresli EEPROM bellek alanına 1 byte’lık ‘X’ karakterini yazan ve doğru yazıp yazmadığını kontrol eden bir kod, aşağıda yer almakta.
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 |
/* * File: main.c * Author: ozenozkaya * */ #include "pic16f84a_lib.h" // CONFIG #pragma config FOSC = XT // Oscillator Selection bits (XT oscillator) #pragma config WDTE = ON // Watchdog Timer (WDT enabled) #pragma config PWRTE = ON // Power-up Timer Enable bit (Power-up Timer is enabled) #pragma config CP = OFF // Code Protection bit (Code protection disabled) #define EEPROM_TEST_ADDR (0x01) #define EEPROM_TEST_DATA ('X') void main() { uint8_t rd_data=0; REG_TRISB_UNION.port = TRIS_PORT_OUTPUT; REG_PORTB_UNION.port = PORT_ALL_LOW; eeprom_write_data(EEPROM_TEST_ADDR,EEPROM_TEST_DATA); eeprom_read_data(EEPROM_TEST_ADDR,&rd_data); if(EEPROM_TEST_DATA==rd_data) { REG_PORTB_UNION.port=0xAA; } while(1); } |
Bu kodumuz, yazım ve okuma işlemleri başarılı ise B portuna 0xAA yazıyor. Eğer hata olursa B portu ilk değeri olan 0x00 değerinde kalıyor. B portuna 0xAA=10101010 değerinin yazılması, sırasıyla pinlerin yüksekte ve alçakta olmasına sebep oluyor.
Kodda bir diğer önemli kısım ise #pragma ile başlayan konfigürasyon satırları. Buna göre osilatör olarak kristal osilatör seçmiş olduk. Ayrıca, mikrokontrolörün donup kaldığı durumlarda ona reset atacak olan watchdog timer modülünü aktive ettik. Yine güç dalgalanmalarında reset atmaya yarayan power on reset’i aktive ettik ve kod korumayı kapattık. Kod koruma açık olarak mikrokontrolöre yazılım atarsanız, o kod bir daha okunamayacak ve kolaylıkla değiştirilemeyecektir. Prototip aşamasında CP (code protection, kod koruma) seçeneğini OFF yapınız (varsayılan değer de budur).
Bu kodun çalıştığı duruma ilişkin Proteus ISIS simülasyon çıktısı ise aşağıda yer alıyor.
Gördüğünüz gibi, EEPROM sürücümüz de başarıyla çalışıyor 🙂
Projenin kaynak kodlarını BURADAN indirebilirsiniz.
Bu günlük de bu kadar. İleride yeni yazılarla devam edeceğiz.
Yazıları beğendiyseniz, faydalanabilecek tanıdıklarınızla paylaşmayı unutmayınız.
Önceki Sayfa Sonraki SayfaPIC Programlama – 9 – Zamanlayıcılar (Timers) 2
Merhabalar, görece uzunca bir aradan sonra yazılarımıza kaldığımız yerden devam ediyoruz. Önceki yazıda tam da bir takım soru işaretleri bırakarak ayrılmıştık. Gün, bunların olabildiğince yanıtlanacağı gündür.
Öncelikle şu 1.06ms meselesinin çözümünü ve cevabını verelim. Evet efendim, kodumuzda yaptığımız tek satırlık bir modifikasyon ile özlenen tabloyu öncelikle kamu huzuruna sunalım.
Gördüğünüz üzere tam 1.00 ms’lik çıktıyı yakaladık. Tabi burada tam’dan kasıt, mikrosaniye mertebesindeki ölçüme ithafen bir tamlık. Neyse, şimdi hemen ilk aşamada bu uygulamanın kodlarını paylaşalım.
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 (118) static void milisecond_event_callback() { REG_TIMER0_UNION.count = TIMER0_ASSIGN_CNT(TIMER0_VAL_FOR_1MS); REG_PORTB_UNION.port^=0xFF; } void main() { REG_TRISB_UNION.port = TRIS_PORT_OUTPUT; REG_PORTB_UNION.port = PORT_ALL_LOW; /*! 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; 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 register_timer0_int_callback(milisecond_event_callback); WAIT_LOOP_FOREVER(); } |
Kütüphanemiz yine aynı, orada bir değişikliğe ihtiyaç yok. Şimdi burada neyin değiştiğini ve neden değiştiğini açıklayalım. Bir önceki yazıdaki koddan farklı olan tek satır şu:
1 |
#define TIMER0_VAL_FOR_1MS (118) |
Bu değerin normal şartlar altında 125 olması gerektiğini açıklamıştık. Ancak gelgelelim kesme fonksiyonu içinde yazdığımız kodlar, ister istemez bir miktar gecikmeye neden oluyor. Bu sebeple bu gecikmeyi tolere edemiyorsak, timer0’a yüklenecek değeri kalibre etmemiz elzem oluyor. Denemeler ile görüyoruz ki kesme fonksiyondaki kodlarımız 7 timer0 cycle kadar bir gecikmeyi doğuruyor. Bunu kompanze etmek için 125 cycle yerine 118 cycle saydığımızda tam 1.o0 ms’lik bir zamanlama elde ediyoruz. Tabi ki bu süreç yalnızca PIC gibi çok kısıtlı kaynaklı ve görece yavaş ortamlarda yaşanıyor. Örneğin ARM Cortex M3-M4 gibi platformlarda bu süreler ihmal edilecek kadar kısalır ve böyle bir istisnai kodlama yapmanız gerekmez. Yine uygulamanız zaman kritik değilse, böyle bir istisnai kodlamaya ihtiyaç duymazsınız. Ama yine de bu önemli konuyu akılda tutmakta fayda var 🙂
Şimdi, önceki yazıdaki defteri kapattığımıza göre bu yazının esas konusuna gelebiliriz. Bu yazımızda zamanlayıcıların bir başka önemli kullanım alanına değineceğiz: PWM (Pulse Width Modulation). Nedir efendim PWM? Çevirisini yapacak olursak dalga genişliği modülasyonudur. PWM’in ne olduğu detaylıca ŞURADA anlatıldığından daha fazla açıklama yapmaya gerek duymuyorum.
Çoğu mikrokontrolörde, zamanlayıcılar üzerinde çalışan PWM modülleri donanımsal olarak bulunur. PIC’in bazı modellerinde de bu imkan sağlanmış. Onların kullanımına da elbet değineceğiz ama daha önemlisi donanımsal PWM modülü olmasa da bu modülasyonu nasıl gerçekleyebileceğimiz. Bu yazıda zamanlayıcı kullanarak PWM gerçekleyeceğiz.
Zamanlayıcılar ve GPIO ile PWM
PWM dediğimiz nane sayısız işe yarıyor. Örneğin bir motorun dönüş hızını, bir LED’in ışık şiddetini ve daha bir çok benzer şeyi PWM ile yapabiliyoruz. PWM, işaretin etkin değerini değiştirmeye yönelik bir modülasyon olduğundan, fiziksel çıktı farketmeksizin “şiddet ayarlama” işini yapar. Ne demek istiyorum?
- Bir motoru sabit 5V ile beslediğinizde o motor 5 birim hızla dönüyorsa, o motoru sabit 2.5V ile beslediğinizde motor 2.5 birim hızla döner.
- Bir LED’i sabit 5V ile beslediğinizde o LED 5 birim ışık şiddeti ile yanıyorsa, o LED’i sabit 2.5V ile beslediğinizde LED 2.5 birim ışık şiddeti ile yanar.
- Bir buzzer’ı sabit 5V ile beslediğinizde o buzzer 5 birim şiddetle ses çıkarıyorsa, o buzzer’ı sabit 2.5V ile beslediğinizde buzzer 2.5 birim şiddetle ses çıkarır.
Yani çıktının fiziksel şekli farketmeksizin, genelde bu iş böyle yürür. Gelgelelim biz dijital dünyada öyle kolay kolay 2.5V veremiyoruz. Bunun yerine işareti zamanda modüle ederek işimizi görmek istiyoruz. Nasıl yani?
Yani elinizde olan yalnızca 5V ise, bundan 2.5V etkin gerilim değeri elde etmenin başka bir yolu var. 1ms süreyle 5V, 1ms süreyle 0V verip bunu sürekli tekrarlarsanız bu işaretin etkin gerilim değeri (5V*1ms +0V*1ms)/2ms = 2.5V olur. 5V lojik 1, 0V lojik 0 olduğundan ve işaret zamanın %50sinde lojik 1 değerini aldığından bu işarete %50 PWM denir.
Benzer şekilde 8ms süreyle 5V, 2ms süreyle 0V verip bunu sürekli tekrarlarsanız bu işaretin etkin gerilim değeri (5V*8ms + 0V*2ms) /10ms= 4V olur. Hesap açık efendim. Zamanın %80’inde işaret lojik 1 değerini aldığından bu işarete %80 PWM denir. PWM’de işaretin 1 olduğu zamana duty cycle denir. Tekrar eden toplam zaman, periyottur ve burada işaretin periyodu 10ms’dir. Frekans = (1 / Periyot[sn]) olduğundan burada frekans 1/10[ms] = 1/0.01[sn] = 100 Hz olur. Periyot ve duty cycle kontrol edilerek dalga genişliği modülasyonu (PWM) yapılabilir.
PWM’i anlatmaya gerek yok dedim ama anlatmadan da duramadım. Şimdi lafı bırakıp icraate geçelim. Peki ne yapacağız, PWM’in %1’lik çözünürlükle kontrol edilebildiği bir uygulama yazalım.
Kütüphanemizde bir değişiklik yapmaksızın main.c dosyamızı aşağıdaki gibi yazarsak olay tamam oluyor.
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 |
/* * File: main.c * Author: ozenozkaya * * Created on 09 January 2016 */ #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 (118) #define PWM_DUTY_CYCLE_DEFAULT (50) #define PWM_OUTPUT_SET() do{REG_PORTB_UNION.port=0xFF;}while(0) #define PWM_OUTPUT_CLR() do{REG_PORTB_UNION.port=0;}while(0) static uint8_t pwm_duty_cycle=PWM_DUTY_CYCLE_DEFAULT; static void pwm_event_callback() { static uint8_t pwm_tick=0; REG_TIMER0_UNION.count = TIMER0_ASSIGN_CNT(TIMER0_VAL_FOR_1MS); if(pwm_tick<pwm_duty_cycle) { PWM_OUTPUT_SET(); } else { PWM_OUTPUT_CLR(); } if(pwm_tick>=100) { pwm_tick=0; } else { pwm_tick++; } } void pwm_set_duty_cycle(uint8_t new_pwm_duty_cycle) { if(new_pwm_duty_cycle <= 100) { pwm_duty_cycle = new_pwm_duty_cycle; } } void main() { REG_TRISB_UNION.port = TRIS_PORT_OUTPUT; REG_PORTB_UNION.port = PORT_ALL_LOW; /*! 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; 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 pwm_set_duty_cycle(50); register_timer0_int_callback(pwm_event_callback); WAIT_LOOP_FOREVER(); } |
Ve bu şirin mi şirin ama bir o kadar da acımasız kodun çıktısı aşağıdaki gibi oluyor.
Gördüğünüz üzere periyodu ~100ms olan %50’lik bir PWM işareti elde etmiş olduk. Gelin şimdi %25’lik PWM elde etmek için ne yapmamız gerektiğini görelim.
Yalnızca şu satırı değiştirmek yeterli:
1 |
pwm_set_duty_cycle(25); |
Bu durumda simülasyon çıktısı da aşağıdaki gibi oluyor.
Her iki çıktıda da periyodun 100ms olduğuna yani frekansın 10Hz olduğuna dikkat ediniz. PWM frekansını örneğin 20HZ yapmak için aşağıdaki değişikliği yapmanız yeterli.
1 |
#define TIMER0_VAL_FOR_1MS (59) |
Bu değişiklik ile zamanlayıcının birim zamanını 1ms’den 0.5ms’ye düşürdük. Bu da frekansı iki katına çıkardı.
Bu uygulama ile artık bir motorun hızını kontrol etmek mümkün. Robotçu arkadaşlar varsa bu kısım faydalı olacaktır diye düşünüyorum.
Projenin kaynak kodlarını BURADAN indirebilirsiniz.
Bu günlük de bu kadar. İleride yeni ibretlerle ve yeni yazılarla devam edeceğiz.
Yazıları beğendiyseniz, faydalanabilecek tanıdıklarınızla paylaşmayı unutmayınız.
Önceki Sayfa Sonraki SayfaPIC Programlama – 8 – Zamanlayıcılar (Timers)
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 SayfaPIC Programlama – 7 – Kesmeler (Interrupts) 2
Merhabalar, geçen yazıda RB0/INT kesmesini bahane ederek kesmeler hakkında bir takım feyizli işler yapmıştık. Bu yazımızda da kesmelere olan sevgimizi ve ilgimizi pekiştirerek, ilginç bir başka örnek yapacağız ve yeni ibretlerle karşılaşacağız. PIC mikrodenetleyicilerde de bir taş üstüne bir taş daha koyalım diyerekten bu günkü örneğimizi PORTB değişme kesmesi üzerinden gerçekleyeceğiz.
Bundan seneler önce, gömülü sistemlerdeki ilk dönemlerimde; keypad (tuş takımı) sürmekten nefret ederdim çünkü mikrodenetleyicilerde kullanımı hem zordu hem de inanılmaz derecede verimsiz idi. Tuş takımının pinlerini matris gibi düşünürsek, sütünları ikili düzende her seferinde bir pin 1’de diğerleri 0’da olacak şekilde, 1 olan pini bir kaydırarak sürerdik ve o esnada da satır pinlerini input olarak ayarlayıp değerini okuyup hangi pine basıldığını anlamaya çalışırdık. Bu olay son derece can sıkıcı bir olaydı ama neyse ki bu can sıkıcı durumu uzunca bir süredir unutmuştum. Ta ki bu yazıyı kafamda planlayana kadar. Bu yazı, bu çirkin yönteme bir tepki olabilirdi, olacak da.
Bir donanımı sürmek için yazılan kodun kesmeler yerine polling (sürekli değer okuma/yazma yani bir nevi deneme yanılma) ile sürülmesi gerek performans açısından gerek güç tüketimi açısından kötü bir durum. Öte yandan tuş takımı sürülmesini anlatan resmi dökümanlar bile en iyi çözüm olarak kesme/polling hibrit kodlar veriyorlar. Bu da oldukça verimsiz bir duruma tekabül ediyor. Daha önce tuş takımı süren arkadaşlar eminim anlayacaklardır. Tuştakımı sürme işinin sadece kesme ile yapılabilmesi güzel olurdu. Öyleyse sadece kesmeyle yapalım 🙂 Arada PORTB değişme kesmesini de anlatmış olacağız.
Gördüğünüz üzere b portunun 4-7 arası pinleri pull down yaparak satır girişlerine bağladık. Yine 0-3 arası pinlerle keypadin sütun pinlerini süreceğiz. Pull down amaçlı koyduğumuz dirençler bize çok yardımcı olacak 😉 Şimdi gelelim kodlara. Öncelikle güncellediğimiz kütüphanelerimiz 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 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 |
/* * 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> #define F_OSC (1000000UL) #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_DISABLE (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) #define MAX_INT_CALLBACK_NUM (2) 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; typedef union { _option_reg_t bits; uint8_t value; }option_reg_t, *option_reg_ptr_t; typedef union { _intcon_reg_t bits; uint8_t value; }intcon_reg_t, *intcon_reg_ptr_t; typedef void (*rb0_int_callback_t)(); typedef void (*portb_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); 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); #ifdef __cplusplus } #endif #endif /* PIC16F84A_LIB_H */ |
Ve yukarıdaki header dosyasına ilişkin kaynak kodu aşağıda:
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 |
#include "pic16f84a_lib.h" #define BIT_TOGGLE(x) (x^=1) #ifndef NULL #define NULL ((void*)0) #endif rb0_int_callback_t rb0_int_callback_list[MAX_INT_CALLBACK_NUM]; portb_int_callback_t portb_int_callback_list[MAX_INT_CALLBACK_NUM]; static void interrupt global_isr_handler() { intcon_reg_t *intcon_reg_ptr= (intcon_reg_ptr_t)REG_INTCON_ADDR; option_reg_t *option_reg_ptr = (option_reg_ptr_t)REG_OPTION_ADDR; uint32_t cb_index=0; if(intcon_reg_ptr->bits.INTF) { 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(option_reg_ptr->bits.INTEDG); intcon_reg_ptr->bits.INTF=INT_FLAG_CLEAR; } if(intcon_reg_ptr->bits.RBIF) { 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(option_reg_ptr->bits.INTEDG); intcon_reg_ptr->bits.RBIF=INT_FLAG_CLEAR; } } int8_t register_rb0_int_callback(rb0_int_callback_t new_cb) { uint32_t cb_index=0; if(NULL != new_cb) { 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 -2; //All callbacks are already registered } } else { return -1; // Callback is NULL } return 0; } int8_t unregister_rb0_int_callback(rb0_int_callback_t cb_to_unreg) { uint32_t cb_index=0; if(NULL != cb_to_unreg) { 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 -1; // Callback is NULL } return 0; } int8_t register_portb_int_callback(portb_int_callback_t new_cb) { uint32_t cb_index=0; if(NULL != new_cb) { 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 -2; //All callbacks are already registered } } else { return -1; // Callback is NULL } return 0; } int8_t unregister_portb_int_callback(portb_int_callback_t cb_to_unreg) { uint32_t cb_index=0; if(NULL != cb_to_unreg) { 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 -1; // Callback is NULL } return 0; } |
Gördüğünüz gibi TRISA ve PORTA kütüklerini de modelledik ve ayrıca kütüphaneyi PORTB kesmesini de destekleyecek hale getirdik. Şimdi gelelim main fonksiyonunun bulunduğu main.c’ye 🙂
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 |
/* * File: main.c * Author: ozenozkaya * * Created on 20 Temmuz 2015 Sali, 23:47 */ #include "pic16f84a_lib.h" #define KEYPAD_DRIVE0_PIN pins.pin0 #define KEYPAD_DRIVE1_PIN pins.pin1 #define KEYPAD_DRIVE2_PIN pins.pin2 #define KEYPAD_SCAN0_PIN pins.pin4 #define KEYPAD_SCAN1_PIN pins.pin5 #define KEYPAD_SCAN2_PIN pins.pin6 #define KEYPAD_SCAN3_PIN pins.pin7 static void keypad_event_manager() { tris_port_t *portb_ptr=(tris_port_ptr_t)REG_PORTB_ADDR; tris_port_t *porta_ptr=(tris_port_ptr_t)REG_PORTA_ADDR; uint8_t cnt=0; portb_ptr->port |=0x01; for(cnt=0;cnt<3;cnt++) { portb_ptr->port &= 0xF8; portb_ptr->port |= (1 << cnt); switch((portb_ptr->port&0xF0)>>4) { case 1: porta_ptr->port=0x01+cnt; break; case 2: porta_ptr->port=0x04+cnt; break; case 4: porta_ptr->port=0x07+cnt; break; case 8: switch(cnt) { case 0: porta_ptr->port=0x0A; break; case 1: porta_ptr->port=0x00; break; case 2: porta_ptr->port=0x0B; break; } break; default: break; } } portb_ptr->KEYPAD_DRIVE0_PIN = PORT_PIN_HIGH; portb_ptr->KEYPAD_DRIVE1_PIN = PORT_PIN_HIGH; portb_ptr->KEYPAD_DRIVE2_PIN = PORT_PIN_HIGH; } static void initialize_keypad_and_leds() { tris_port_t *portb_ptr=(tris_port_ptr_t)REG_PORTB_ADDR; tris_port_t *trisb_ptr=(tris_port_ptr_t)REG_TRISB_ADDR; tris_port_t *porta_ptr=(tris_port_ptr_t)REG_PORTA_ADDR; tris_port_t *trisa_ptr=(tris_port_ptr_t)REG_TRISA_ADDR; trisb_ptr->port = TRIS_PORT_INPUT; trisb_ptr->KEYPAD_SCAN0_PIN = TRIS_PIN_INPUT; trisb_ptr->KEYPAD_SCAN1_PIN = TRIS_PIN_INPUT; trisb_ptr->KEYPAD_SCAN2_PIN = TRIS_PIN_INPUT; trisb_ptr->KEYPAD_SCAN3_PIN = TRIS_PIN_INPUT; trisb_ptr->KEYPAD_DRIVE0_PIN = TRIS_PIN_OUTPUT; trisb_ptr->KEYPAD_DRIVE1_PIN = TRIS_PIN_OUTPUT; trisb_ptr->KEYPAD_DRIVE2_PIN = TRIS_PIN_OUTPUT; portb_ptr->port = PORT_ALL_LOW; portb_ptr->KEYPAD_DRIVE0_PIN = PORT_PIN_HIGH; portb_ptr->KEYPAD_DRIVE1_PIN = PORT_PIN_HIGH; portb_ptr->KEYPAD_DRIVE2_PIN = PORT_PIN_HIGH; trisa_ptr->port = TRIS_PORT_OUTPUT; porta_ptr->port = PORT_ALL_LOW; } static void initialize_keypad_event() { intcon_reg_t *intcon_reg_ptr= (intcon_reg_ptr_t)REG_INTCON_ADDR; option_reg_t *option_reg_ptr = (option_reg_ptr_t)REG_OPTION_ADDR; register_portb_int_callback(keypad_event_manager); option_reg_ptr->bits.INTEDG = INT_EDGE_RISING; intcon_reg_ptr->bits.RBIF= INT_FLAG_CLEAR; intcon_reg_ptr->bits.RBIE = INT_ENABLE; intcon_reg_ptr->bits.GIE = INT_ENABLE; } void main() { initialize_keypad_and_leds(); initialize_keypad_event(); while(1); } |
Gördüğünüz gibi gerekli pin ayarlamalarını yaptıktan sonra, kesmeyi aşağıdaki gibi aktive ettik:
1 |
intcon_reg_ptr->bits.RBIE = INT_ENABLE; |
Ardından, yazdığımız kütüphaneye eklediğimiz callback registration (fonksiyon bildirimi kaydı) mekanizmasını kullanarak kesme geldiğinde, haberdar olmak için initialize_keypad_event fonksiyonunu implement ettik. Buna göre PORTB’nin 4-7 arası pinlerinde gerilimsel bir değişme olduğunda haberdar olacağız. Yükselen kenarı ilk konfigurasyon olarak almamızın sebebi ise, keypad satır pinlerimizi donanımsal olarak pull-down’a yani lojik sıfıra çekmiş olmamız. Pull-down ne demek bilmiyorsanız buradan öğrenebilirsiniz.
Bu durumda kodumuz tamamen kesme ile çalışacak şekilde tamamlanmış oldu ve başarıyla da çalışıyor. Ancak yine içimize sinmeyen bazı noktalar var. Bunlardan birincisi şu aşağıdaki satır.
1 |
portb_ptr->port &= 0xF8; |
Bu satırın amacı, B portunun ilk 3 pinini 0 değerine çekmek ama bunu yaparken diğerlerini değiştirmemek. Aslında bu satır daha anlaşılır şekilde aşağıdaki gibi de implement edilebilirdi.
1 2 3 |
portb_ptr->KEYPAD_DRIVE0_PIN = PORT_PIN_LOW; portb_ptr->KEYPAD_DRIVE1_PIN = PORT_PIN_LOW; portb_ptr->KEYPAD_DRIVE2_PIN = PORT_PIN_LOW; |
Peki neden yukarıdaki gibi yazmadık? Çok daha anlaşılır olurdu oysa ki. İşte burada cevap “performans”. Tek satır olan kod eğer diğerinden daha kısa sürede işletiliyorsa, o zaman zaman kaybetmemek için anlaşılırlıktan feda edilebilir, ki burada yapılan da o 🙂 Ancak bu durumda bile anlaşılırlığı kaybetmemek mümkün. Nasıl mı? Comment yani yorum satırları ekleyerek. Bu vesileyle bir kez daha yorum satırlarının ne denli önemli olduğuna değinmekte fayda var. Kodu iyileştirmek için aklımda bazı hinlikler var ama önce kodun bellek kullanımına bakalım.
Yukarıdaki kodun bellek kullanımı aşağıdaki gibi:
1 2 3 4 5 6 7 |
Memory Summary: Program space used 264h ( 612) of 400h words ( 59.8%) Data space used 25h ( 37) of 44h bytes ( 54.4%) EEPROM space used 0h ( 0) of 40h bytes ( 0.0%) Data stack space used 0h ( 0) of 1Fh bytes ( 0.0%) Configuration bits used 0h ( 0) of 1h word ( 0.0%) ID Location space used 0h ( 0) of 4h bytes ( 0.0%) |
Şimdi iyileştirme yaptığımız main.c dosyamıza bakalım.
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 |
/** * @file main.c * @author Özen Özkaya * @date 31 July 2015 * @brief File containing example of keypad interfacing by using PORTB * interrupt of PIC16F84A. Keypad is attached to B port pins of PIC and a * 7-SEG display is attached to A port pins of PIC. Application displays the * last pressed number of keypad on 7-SEG led display. * * This file is freely published and distributed in my blog. * @see http://ozenozkaya.com/blog/ */ #include "pic16f84a_lib.h" #define KEYPAD_DRIVE0_PIN pins.pin0 #define KEYPAD_DRIVE1_PIN pins.pin1 #define KEYPAD_DRIVE2_PIN pins.pin2 #define KEYPAD_SCAN0_PIN pins.pin4 #define KEYPAD_SCAN1_PIN pins.pin5 #define KEYPAD_SCAN2_PIN pins.pin6 #define KEYPAD_SCAN3_PIN pins.pin7 #define KEYPAD_DIRECTION_REG ((tris_port_ptr_t)REG_TRISB_ADDR) #define KEYPAD_PORT_REG ((tris_port_ptr_t)REG_PORTB_ADDR) #define SEVEN_SEG_DIRECTION_REG ((tris_port_ptr_t)REG_TRISA_ADDR) #define SEVEN_SEG_PORT_REG ((tris_port_ptr_t)REG_PORTA_ADDR) /** * @brief keypad_event_manager function is an interrupt callback function. * * We register this function for PORTB change interrupt event. Normally, * keypad drive & scan is done here. When user is pressed to any key in * keypad, interrupt occurs and this callback is called by our library. * Then here we scan & drive keypad pins to detect the pressed button. * @see http://ozenozkaya.com/blog * @note Please share if you like :) * @warning Do not forget to register this function with: . * @code * register_portb_int_callback(keypad_event_manager); * @endcode */ static void keypad_event_manager() { uint8_t cnt=0; KEYPAD_PORT_REG->KEYPAD_SCAN0_PIN =PORT_PIN_HIGH; for(cnt=0;cnt<3;cnt++) { KEYPAD_PORT_REG->port &= 0xF8; /*Here we are shifting the drive pin. Possible values: 1 0 0 0 1 0 0 0 1 By the way we scan each row*/ KEYPAD_PORT_REG->port |= (1 << cnt); switch((KEYPAD_PORT_REG->port&0xF0)>>4) { case 1: SEVEN_SEG_PORT_REG->port=0x01+cnt; break; case 2: SEVEN_SEG_PORT_REG->port=0x04+cnt; break; case 4: SEVEN_SEG_PORT_REG->port=0x07+cnt; break; case 8: switch(cnt) { case 0: SEVEN_SEG_PORT_REG->port=0x0A; break; case 1: SEVEN_SEG_PORT_REG->port=0x00; break; case 2: SEVEN_SEG_PORT_REG->port=0x0B; break; } break; default: break; } } KEYPAD_PORT_REG->KEYPAD_DRIVE0_PIN = PORT_PIN_HIGH; KEYPAD_PORT_REG->KEYPAD_DRIVE1_PIN = PORT_PIN_HIGH; KEYPAD_PORT_REG->KEYPAD_DRIVE2_PIN = PORT_PIN_HIGH; } static void initialize_keypad_and_leds() { KEYPAD_DIRECTION_REG->port = TRIS_PORT_INPUT; KEYPAD_DIRECTION_REG->KEYPAD_SCAN0_PIN = TRIS_PIN_INPUT; KEYPAD_DIRECTION_REG->KEYPAD_SCAN1_PIN = TRIS_PIN_INPUT; KEYPAD_DIRECTION_REG->KEYPAD_SCAN2_PIN = TRIS_PIN_INPUT; KEYPAD_DIRECTION_REG->KEYPAD_SCAN3_PIN = TRIS_PIN_INPUT; KEYPAD_DIRECTION_REG->KEYPAD_DRIVE0_PIN = TRIS_PIN_OUTPUT; KEYPAD_DIRECTION_REG->KEYPAD_DRIVE1_PIN = TRIS_PIN_OUTPUT; KEYPAD_DIRECTION_REG->KEYPAD_DRIVE2_PIN = TRIS_PIN_OUTPUT; KEYPAD_PORT_REG->port = PORT_ALL_LOW; KEYPAD_PORT_REG->KEYPAD_DRIVE0_PIN = PORT_PIN_HIGH; KEYPAD_PORT_REG->KEYPAD_DRIVE1_PIN = PORT_PIN_HIGH; KEYPAD_PORT_REG->KEYPAD_DRIVE2_PIN = PORT_PIN_HIGH; SEVEN_SEG_DIRECTION_REG->port = TRIS_PORT_OUTPUT; SEVEN_SEG_PORT_REG->port = PORT_ALL_LOW; } static void initialize_keypad_event() { register_portb_int_callback(keypad_event_manager); ((option_reg_ptr_t)REG_OPTION_ADDR)->bits.INTEDG = INT_EDGE_RISING; ((intcon_reg_ptr_t)REG_INTCON_ADDR)->bits.RBIF= INT_FLAG_CLEAR; ((intcon_reg_ptr_t)REG_INTCON_ADDR)->bits.RBIE = INT_ENABLE; ((intcon_reg_ptr_t)REG_INTCON_ADDR)->bits.GIE = INT_ENABLE; } void main() { initialize_keypad_and_leds(); initialize_keypad_event(); while(1); } |
Yapılan değişiklikleri kısaca sıralarsak:
- Register erişimi için değişken kullanımını ortadan kaldırdık. Ve bu iş için hiç değişken kullanmadık!
- Bellek bayram etti (mi acaba, göreceğiz 🙂 ama etti)
- Kodun anlaşılabilirliğini çeşitli define ve yorum satırları ile boost ettik.
- Doxygen stili dökümantasyon yaparak (hepsini yapmasak da 😉 ) o işi de kodun üzerinde aradan çıkardık.
Ve güncellenmiş main kodumuzun olduğu uygulamanın bellek kullanımı:
1 2 3 4 5 6 7 |
Memory Summary: Program space used 1B8h ( 440) of 400h words ( 43.0%) Data space used 1Dh ( 29) of 44h bytes ( 42.6%) EEPROM space used 0h ( 0) of 40h bytes ( 0.0%) Data stack space used 0h ( 0) of 27h bytes ( 0.0%) Configuration bits used 0h ( 0) of 1h word ( 0.0%) ID Location space used 0h ( 0) of 4h bytes ( 0.0%) |
Gördüğünüz üzere program alanı kullanımını 612’den 440 worde (1 word=2 byte) düşürdük. Ayrıca veri alanı kullanımını da 37 byte’dan 29 byte’a düşürdük 🙂 İşte bu elle tutulur bir optimizasyon.
Keypad’i de tertemiz, kesme kullanarak sürmüş olduk. Arada PORTB değişme kesmesi nasıl kullanılır onu da hallettik. Sayısız ibretlere de değindiğimizden bu günlük bu kadar diyoruz 🙂
Yazıları beğendiyseniz, faydalanabilecek tanıdıklarınızla paylaşmayı unutmayınız.
Önceki Sayfa Sonraki SayfaPIC Programlama – 6 – Kesmeler (Interrupts)
Önceki yazımızda PIC16F84A’nın pinlerini kontrol etmeyi işlemiştik. Elbette ki buraya kadarki bilgilerle çok sayıda uygulama yapmak mümkün ancak bu yazı dizisi altyapı oluşturma amacı taşıdığından şimdilik farklı uygulamaları geleceğe bırakarak altyapı çalışmalarına devam diyoruz. Mikrokontrolör’ün giriş çıkış arayüzlerini detaylıca inceledikten sonra sıra geldi kesmelere. Önce genel manada kesmelerin ne olduğuna değinip, ardından da port ve pin kesmeleri ile devam edeceğiz. Mikrokontrolör’ün çevresellerinden bazıları farklı tipte kesmeler destekler. Örneğin zamanlayıcı kesmelerine, zamanlayıcı modülünde değineceğiz. Hazır port/pin kontrolünü görmüşken bu yazıda port/pin kesmelerine değineceğiz.
Kesme nedir demeden önce, mikrokontrolör ve CPU arasındaki farkları hatırlayalım. CPU (işlemci, merkezi işlem birimi) mikrokontrolör içerisindeki bir modüldür. Mikrokontrolör içinde, CPU’ya ek olarak bellek ve çevreseller (perihperal) bulunur. CPU içerisinde genelde aritmetik/lojik işlemleri yapan bir modül (ALU) bulunur. Hemen PIC16F84A’nın yapısını inceleyelim. Bu blog diyagram PIC16F84A’nın veri kağıdından alındı.
Diyagramda ALU’ya dikkat ediniz. Onu CPU olarak düşünebilirsiniz. ALU’dan W reg’e giden yolda sağa doğru bir kanal olduğunu göreceksiniz. O kanal I/O portlarına, TMR (timer, zamanlayıcı) modüllerine ve daha nice yerlere gidiyor. Bu kaçak kanal, olası bir kesme sinyalinin ALU’ya müdahale edebildiğinin bir kanıtı. Bu yazının sonunda da resmin sağ alt kısmında yer alan RB0/INT’in ne olduğu anlatılacak.
Kesme Nedir?
Kesmeleri tanımlamak uygulamada göstermekten zor bir iştir ama şöyle bir tanım yapmak mümkün: Kesme (interrupt), CPU’nun dışındaki bir dış etken tarafından oluşturulan ve oluştuğunda CPU’nun yaptığı işi durdurup, ayrı bir özel kod parçacığının işletilmesini (yürütülmesini) sağlayan elektriksel bir işarettir. Kesme oluştuğunda işletilen bu koda genelde kesme servis rutini (interrupt service routine, ISR) denir. Kesme oluştuğu anda, ISR’ye gitmeden önce, CPU program işletiminde kaldığı yeri kaydeder. ISR’nin işletimi bittiği anda da CPU bu kayıtlı noktadan yani kaldığı yerden programı işletmeye devam eder.
Kesme asenkron gerçekleşen bir olaydır, yani ne zaman gerçekleşeceğini CPU önceden bilmez. Gündelik hayattan buna benzer bir süreç örneği verecek olursak; kapı zilinin ya da telefonun çalması durumlarını örnek verebiliriz. Diyelim yemek yapıyorsunuz, zil çaldı; yemek yapmayı bırakıp kapıyı açıp yemek yapmaya devam ederiz. Zil çalınca, yahu dur sırası mıydı demeyiz. Ya da sürekli kapıyı kontrol etmeden zilin ne zaman çalacağını bilemeyiz. Bununla birlikte, kapının çalışını duymazdan gelmeniz bir seçenektir tabi ki. Aynı şeyler kesmeler için de geçerli 🙂
Kesmeler, gömülü sistemlerde çok çok önemli bir yere sahiptir. Kesmeler’in düzgün kullanımı ile tasarlanmış bir sistem daha verimli, daha duyarlı (responsive) ve daha anlaşılır şekilde tasarlanabilir. Kesmeler düzgün kullanılmazsa da “yaa böyle olmaması lazımdı ama, çok saçma, böyle çalışmaması lazımdı ama” sözleri havada uçuşur, işin içinden çıkılmaz. Halbuki dostlar, her şey deterministik 🙂 Çok güçlü ama biraz da tehlikeli olduklarından bazı yazılımcılar kesmelerden korkar ama gömülü sistem programcısı kesmeden korkmamalı, kesmelerle samimi olmalı, onları son derece işeyarar ve güçlü bir arkadaş olarak görmelidir. Zira, etrafınızda gördüğünüz gömülü sistemlerden en az bir kesme kullanmayanı bulmak sıradışı bir olaydır. Sözün özü, kesme candır.
Farklı mikrokontrolör mimarilerinde kesmeler farklı şekillerde ele alınır. Şimdi PIC’ten başlayalım. Yine ibretlerle dolu bir uygulama yapacağız.
Switch ve Led Uygulaması
Bir önceki yazımıza yaptığımız uygulamada, while döngüsünün içerisinde switch’in durumunu sürekli kontrol etmiştik. Buna polling deniyor ve ekstrem derecede verimsiz bir yöntemdir bu. Bunun yerine kesme kullansaydık, sadece anahtarın durumu değiştiğinde ledlerin durumuna müdahale edebileceğimiz bir senaryo oluşacaktı. Bu da ileride özellikle güç tüketimi anlamında da bize avantajlar sağlayacaktı. Haydi sağlasın madem. Uygulamayı bu defa kesme kullanarak yapacağız. Yine switch bir konumda iken kırmızı yanar sarı söner durumda olacak, switch diğer konumdayken sarı yanar kırmızı söner durumda olacak. Yine bu uygulamada da kod yazımımızı bir kaç adım öteye taşıyacağız ancak önce kaldığımız yerden devam edelim 🙂
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 |
/* * File: main.c * Author: ozenozkaya * * Created on 02 Haziran 2015 Sali, 12:47 */ #define F_OSC (1000000UL) #define REG_TRISB_ADDR (0x86) #define REG_TRISB (*( volatile unsigned char*)REG_TRISB_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 PORT_PIN_LOW (0) #define PORT_PIN_HIGH (1) #define PORT_ALL_LOW (0) #define PORT_ALL_HIGH (0xFF) #define SWITCH_PIN pins.pin0 #define RED_LED_PIN pins.pin1 #define YELLOW_LED_PIN pins.pin2 #define LED_ON (PORT_PIN_HIGH) #define LED_OFF (PORT_PIN_LOW) #define REG_INTCON_ADDR (0x0B) #define REG_INTCON (*( volatile unsigned char*)REG_INTCON_ADDR) #define INT_ENABLE (1) #define INT_DISABLE (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) #define BIT_TOGGLE(x) (x^=1) typedef struct { unsigned char pin0:1; unsigned char pin1:1; unsigned char pin2:1; unsigned char pin3:1; unsigned char pin4:1; unsigned char pin5:1; unsigned char pin6:1; unsigned char pin7:1; }tris_port_pin_t; typedef struct { unsigned char RBIF:1; unsigned char INTF:1; unsigned char T0IF:1; unsigned char RBIE:1; unsigned char INTE:1; unsigned char T0IE:1; unsigned char EEIE:1; unsigned char GIE:1; }intcon_reg_t,*intcon_reg_ptr_t; typedef struct { unsigned char PS:3; unsigned char PSA:1; unsigned char T0SE:1; unsigned char T0CS:1; unsigned char INTEDG:1; unsigned char RBPU:1; }option_reg_t,*option_reg_ptr_t; typedef union { tris_port_pin_t pins; volatile unsigned char port; }tris_port_t,*tris_port_ptr_t; tris_port_t *portb_ptr=(tris_port_ptr_t)REG_PORTB_ADDR; tris_port_t *trisb_ptr=(tris_port_ptr_t)REG_TRISB_ADDR; intcon_reg_t *intcon_reg_ptr= (intcon_reg_ptr_t)REG_INTCON_ADDR; option_reg_t *option_reg_ptr = (option_reg_ptr_t)REG_OPTION_ADDR; static void interrupt global_isr_handler() { if(intcon_reg_ptr->INTF) { switch(portb_ptr->SWITCH_PIN) { case PORT_PIN_LOW: portb_ptr->RED_LED_PIN = LED_ON; portb_ptr->YELLOW_LED_PIN = LED_OFF; break; case PORT_PIN_HIGH: portb_ptr->RED_LED_PIN = LED_OFF; portb_ptr->YELLOW_LED_PIN = LED_ON; break; default: break; } BIT_TOGGLE(option_reg_ptr->INTEDG); intcon_reg_ptr->INTF=INT_FLAG_CLEAR; } } void main() { trisb_ptr->port = TRIS_PORT_INPUT; trisb_ptr->SWITCH_PIN = TRIS_PIN_INPUT; trisb_ptr->RED_LED_PIN = TRIS_PIN_OUTPUT; trisb_ptr->YELLOW_LED_PIN = TRIS_PIN_OUTPUT; portb_ptr->port = PORT_ALL_LOW; intcon_reg_ptr->INTF = INT_FLAG_CLEAR; intcon_reg_ptr->INTE = INT_ENABLE; intcon_reg_ptr->GIE = INT_ENABLE; option_reg_ptr->INTEDG = INT_EDGE_RISING; while(1); } |
Kodun çalıştırılacağı devre bir önceki yazıdakilerle aynı. Yani çıktı aşağıdaki gibi olacak.
Şimdi gelelim fasülyenin faydalarına. Kesmeyi nasıl kullanacağımızı nereden bildik? PIC16F84A’nın veri kağıdının 6.8 Interrupts başlıklı kısmında hangi registerları nasıl konfigüre etmemiz gerektiği açıkça yazıyor. Bildiğiniz gibi bu yazı dizisinde kendi yazdıklarımızdan başka bir kütüphane kullanmıyoruz. Bunun amacı her şeyi temelinden anlamaktı. Doğal olarak kullanacağımız yeni registerlar için de tanımlamaları yapıyoruz. Aşağıda OPTION ve INTCON kütüklerinin (registerlarının) adres tanımlarını ve yardımcı etiketleri görebilirsiniz. Buna ek olarak bir de BIT_TOGGLE makrosu tanımladık. Bu makronun amacı bir biti birse sıfır, sıfırsa bir yapmak. Bunu ileride kullanacağız.
1 2 3 4 5 6 7 8 9 10 11 12 |
#define REG_INTCON_ADDR (0x0B) #define REG_INTCON (*( volatile unsigned char*)REG_INTCON_ADDR) #define INT_ENABLE (1) #define INT_DISABLE (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) #define BIT_TOGGLE(x) (x^=1) |
Registerların modellenmesini ise aşağıdaki gibi yaptık.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
typedef struct { unsigned char RBIF:1; unsigned char INTF:1; unsigned char T0IF:1; unsigned char RBIE:1; unsigned char INTE:1; unsigned char T0IE:1; unsigned char EEIE:1; unsigned char GIE:1; }intcon_reg_t,*intcon_reg_ptr_t; typedef struct { unsigned char PS:3; unsigned char PSA:1; unsigned char T0SE:1; unsigned char T0CS:1; unsigned char INTEDG:1; unsigned char RBPU:1; }option_reg_t,*option_reg_ptr_t; |
Bu tanımlamaları datasheetten bakıp yaptıktan sonra INTCON ve OPTION kütüklerini gösterecek işaretçileri aşağıdaki gibi tanımladık.
1 2 |
intcon_reg_t *intcon_reg_ptr= (intcon_reg_ptr_t)REG_INTCON_ADDR; option_reg_t *option_reg_ptr = (option_reg_ptr_t)REG_OPTION_ADDR; |
Ardından gelelim ana fonksiyon(main) içinde yaptığımız ek konfigürasyonlara.
1 2 3 4 5 |
option_reg_ptr->INTEDG = INT_EDGE_RISING; intcon_reg_ptr->INTF = INT_FLAG_CLEAR; intcon_reg_ptr->INTE = INT_ENABLE; intcon_reg_ptr->GIE = INT_ENABLE; |
Öncelikle kesme bayrağının temiz olduğundan emin olmak için onu temizledik. Ardından INTE ile RB0/INT kesmesine izin verdik. Ardından da global kesme aktivasyonu için GIE bitini etkinleştirdik. GIE ve INTE bitlerinin ikisi de 1 değerini almadan kesme gerçekleşemez. Bunu da verikağıdındaki aşağıdaki şemadan anlayabiliyoruz.
Konfigürasyonda kesmeyi de yükselen kenarda olacak şekilde ayarladık. Buna göre anahtar sıfırdan bire çekildiğinde yükselen kenar kesmesi gelecek. Ama burada bir sorun var, bize hem yükselen kenar hem düşen kenarda kesme gerekiyor. Çünkü anahtarın konumu sıfırdan bire değiştiğinde de birden sıfıra değiştiğinde de kesme almak ve LED’lerin durumunu değiştirmek istiyoruz. PIC’te böyle doğrudan böyle bir seçenek yok; ya yükselen kenar kesmesi seçilebiliyor ya da düşen kenar. Burada beyin bedava diyoruz ve kafayı çalıştırarak soruna çözüm sunuyoruz. Önce yükselen kenar kesmesini aktive ettiğimizi düşünelim, bu durumda ilk gelen yükselen kenar kesmesinin hemen ardından düşen kenar kesmesini aktive edersek, switchin durumu tekrar değiştiğinde kesme alabiliriz. Yani yükselen/düşen kenar konfigürasyonunu, kesme oluştuğunda toggle etmemiz gerekiyor. Kesme tamamlandığında kesme bayrağını yani INTF bitini de sıfıra çekip temizlemek gerekiyor. Kodda da bunları yaptık. PIC’te kesme fonksiyonu fonksiyon adının başına gelen “interrupt” kelimesi ile derleyiciye anlatılır. Buna göre kesme fonksiyonumuz aşağıdaki gibi oluyor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
static void interrupt global_isr_handler() { if(intcon_reg_ptr->INTF) { switch(portb_ptr->SWITCH_PIN) { case PORT_PIN_LOW: portb_ptr->RED_LED_PIN = LED_ON; portb_ptr->YELLOW_LED_PIN = LED_OFF; break; case PORT_PIN_HIGH: portb_ptr->RED_LED_PIN = LED_OFF; portb_ptr->YELLOW_LED_PIN = LED_ON; break; default: break; } BIT_TOGGLE(option_reg_ptr->INTEDG); intcon_reg_ptr->INTF=INT_FLAG_CLEAR; } } |
PIC’te tüm kesmeler tek bir fonksiyon içine düşer, bu da interrupt ön ekli fonksiyondur. Bu sebeple bu fonksiyonun adını global_isr_handler koyduk. Daha sonra gelen kesmenin RB0/INT olup olmadığını kontrol ettik ve eğer o ise switch değerlendirmesi işlemini yapıp yükselen/düşen kenar seçimini güncelleştirip bayrağı temizledik. INTEDG’nin toggle edilmesi ile hem düşen hem yükselen kenardaki değişimleri güzel bir oyun ile algılayabilir duruma geldik. Çiçek oldu.
Şimdi gelelim bu kodun sorunlarına. Birincisi register tanımları giderek kabarıyor ve onların ayrı bir *.h dosyası içinde yer alması gerekiyor artık. İkincisi, ilk durumda switch’in durumuna bakmaksızın kesme konfigurasyonu yaptığımız için ilk açılışta ledlerin ikisi de yanmıyor. Üçüncüsü de şu, madem her kesme aynı fonksiyona düşecek, ileride kod daha da büyüdüğünde kesme fonksiyonu sürekli değiştirilecek ve eğer birisi orada yanlış bayrakları temizlerse ya da kurarsa, önceden yazılmış kısımlar da bundan etkilenecek ve kodun her yeri bozulacak. Bu çok büyük bir risk. Burada SDK tasarımı devreye giriyor. Akıllıca bir seçenek, kesme fonksiyonunu kullanıcı programından ayırmak olacaktır. Burada kesme bayraklarının yönetiminin de otomatik yapılması da isabetli olacaktır. Bu olay gerçekten bir ileri tasarım öğesidir ancak adım adım oraya gideceğiz. Kodun bir diğer önemli sorunu da kütüphane kullanmayacağız inadımızla kullanmadığımız stdint kütüphanesidir. “unsigned char” gibi boyutu “hayırlısı” olan bir veri tipi kullanmak yerine “uint8_t” ile modelleme yapmamız daha temiz olurdu. Nitekim amacımız PIC kütüphanelerini kullanmadan, daha iyi şekilde temelden olayı yazarak olayın dinamiklerini anlamaktı. Bu da stdint’e geçişimizi engellemiyor. Bir diğer eksik nokta da register tanımlarıyla ilgili. Önceki yazıda, sebeplerini sıraladığımız üzere hem bit hem de byte erişimi sağlayabilmek için kütük modellemelerinde union kullanmıştık. Yeni modellediğimiz kütüklerde de bunu uygulamakta fayda var. Bunlara göre projemizi yine düzenleyelim 🙂
Switch ve LED Uygulaması v2
Bu projede 3 adet dosyamız olacak. Birincisi pic16f84a kütüphanemizin header dosyası. Bu dosya (pic16f84a_lib.h) aşağıdaki gibidir.
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 |
/* * 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> #define F_OSC (1000000UL) #define REG_TRISB_ADDR (0x86) #define REG_TRISB (*( volatile unsigned char*)REG_TRISB_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 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_DISABLE (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) #define MAX_INT_CALLBACK_NUM (5) typedef struct { 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; typedef union { _option_reg_t bits; uint8_t value; }option_reg_t, *option_reg_ptr_t; typedef union { _intcon_reg_t bits; uint8_t value; }intcon_reg_t, *intcon_reg_ptr_t; 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); #ifdef __cplusplus } #endif #endif /* PIC16F84A_LIB_H */ |
Yine pic16f84a kütüphanemizin kaynak dosyası (pic16f84a_lib.c) aşağıdaki gibidir.
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 |
#include "pic16f84a_lib.h" #define BIT_TOGGLE(x) (x^=1) #ifndef NULL #define NULL ((void*)0) #endif rb0_int_callback_t rb0_int_callback_list[MAX_INT_CALLBACK_NUM]; static void interrupt global_isr_handler() { intcon_reg_t *intcon_reg_ptr= (intcon_reg_ptr_t)REG_INTCON_ADDR; option_reg_t *option_reg_ptr = (option_reg_ptr_t)REG_OPTION_ADDR; uint32_t cb_index=0; if(intcon_reg_ptr->bits.INTF) { 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(option_reg_ptr->bits.INTEDG); intcon_reg_ptr->bits.INTF=INT_FLAG_CLEAR; } } int8_t register_rb0_int_callback(rb0_int_callback_t new_cb) { uint32_t cb_index=0; if(NULL != new_cb) { 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 -2; //All callbacks are already registered } } else { return -1; // Callback is NULL } return 0; } int8_t unregister_rb0_int_callback(rb0_int_callback_t cb_to_unreg) { uint32_t cb_index=0; if(NULL != cb_to_unreg) { 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 -1; // Callback is NULL } return 0; } |
Ve nihayetinde ana programımız (main.c) aşağıdaki gibidir:
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 |
/* * File: main.c * Author: ozenozkaya * * Created on 20 Temmuz 2015 Sali, 23:47 */ #include "pic16f84a_lib.h" #define SWITCH_PIN pins.pin0 #define RED_LED_PIN pins.pin1 #define YELLOW_LED_PIN pins.pin2 #define LED_ON (PORT_PIN_HIGH) #define LED_OFF (PORT_PIN_LOW) static tris_port_t *portb_ptr=(tris_port_ptr_t)REG_PORTB_ADDR; static tris_port_t *trisb_ptr=(tris_port_ptr_t)REG_TRISB_ADDR; static void switch_event_manager() { switch(portb_ptr->SWITCH_PIN) { case PORT_PIN_LOW: portb_ptr->RED_LED_PIN = LED_ON; portb_ptr->YELLOW_LED_PIN = LED_OFF; break; case PORT_PIN_HIGH: portb_ptr->RED_LED_PIN = LED_OFF; portb_ptr->YELLOW_LED_PIN = LED_ON; break; default: break; } } static void initialize_switch_and_leds() { trisb_ptr->port = TRIS_PORT_INPUT; trisb_ptr->SWITCH_PIN = TRIS_PIN_INPUT; trisb_ptr->RED_LED_PIN = TRIS_PIN_OUTPUT; trisb_ptr->YELLOW_LED_PIN = TRIS_PIN_OUTPUT; portb_ptr->port = PORT_ALL_LOW; switch_event_manager(); } static void initialize_switch_interrupt() { intcon_reg_t *intcon_reg_ptr= (intcon_reg_ptr_t)REG_INTCON_ADDR; option_reg_t *option_reg_ptr = (option_reg_ptr_t)REG_OPTION_ADDR; register_rb0_int_callback(switch_event_manager); option_reg_ptr->bits.INTEDG = INT_EDGE_RISING; intcon_reg_ptr->bits.INTF = INT_FLAG_CLEAR; intcon_reg_ptr->bits.INTE = INT_ENABLE; intcon_reg_ptr->bits.GIE = INT_ENABLE; } void main() { initialize_switch_and_leds(); initialize_switch_interrupt(); while(1); } |
Kodda, vadettiğimiz tüm değişiklikleri yapmış olduk. En en en en en önemli yer, kullanıcının yani main dosyasının kesme fonksiyonunu hiç bir şekilde görmeden kesme işlemlerini haber alması. Bunun için SDK geliştiricisi olarak çıkardığımız fonksiyonu kullancı aşağıdaki şekilde çağırmış oldu:
1 |
register_rb0_int_callback(switch_event_manager); |
Bu fonksiyonun implementasyonu da tam anlamıyla ibretlik. Maksimum 5 kaynak RB0/INT kesmesine abone olabiliyor. Bu genişletilebilirlik açısından çok çok önemli. Biz switch kodunu yarın öbürgün “switch geldiğinde bir de düdük çalsın” isteriyle genişletmek istediğimizde düdüğü çalacak fonksiyonu da register edecersek o düdük çalınacaktır ve bizim önceki işlerimiz asla bundan etkilenmeyecektir. Bu olaya lütfen çok dikkat ediniz. Listeye kaydolmuş fonksiyonların yani callback’lerin çağırıldığı satır ise aşağıdaki satır.
1 |
rb0_int_callback_list[cb_index](); |
Şimdilik bu kadar 🙂 Bu gün epey fantastik mevzulara değindik. PIC deyip geçmemek gerek, bu basit alet için bile ileri düzey tasarım yapmak mümkün. Nitekim daha da ilerleyeceğiz. Yazılar devam edecek.
Yazıları beğendiyseniz, faydalanabilecek tanıdıklarınızla paylaşmayı unutmayınız.
Önceki Sayfa Sonraki Sayfa
PIC Programlama – 5 – Giriş Çıkış (I/O, Input/Output) İşlemleri -2
Önceki yazımızın başlığını giriş çıkış işlemleri olarak koymuştuk ama açıkça görülebildiği üzere, yalnızca çıkış işlemleri üzerinde durduk 🙂 Bu yazıda, vadedileni tamamlayarak PIC mikrokontrolörlerinde giriş işlemlerini nasıl yaparız ona değineceğiz.
PIC programlama yazı dizisinin asıl amaç seti, gömülü sistemlerin programlanması hususunda temel oluşturup, sürecin iç dinamiklerini de anlatmak olduğundan yine bir kütüphane kullanmadan devam edeceğiz. Giriş işlemleri üzerinden gidebilmemiz için, mikrokontrolörün pinlerine bağlayacağımız elemanların, pinde oluşturacağı gerilimsel seviyeyi okumamız gerekir. Buna göre buton ve switch uygulamaları, mevzuyu anlatmakta kullanılabilecek iki güzel ve basit eleman oluyor. Butona daha sonra da değineceğiz. Şimdi mevzuyu anlatmak için switch üzerinden gidelim. Butonun basit kullanımı switch’den çok farklı değil ama bouncing gibi problemlere de önlem alacağımız için butonu timer’dan sonra anlatacağız. Neyse 🙂
Öncelikle daha kolay olandan başlayalım. Switch yani anahtar elemanının kullanımına değinelim. Anahtar’ın adı üzerinde, elektriksel akımı açıp kapatabilen devre elemanı. Buna göre, iki durumlu bir anahtar için; anahtar bir konumdayken açık devre, diğer konumdayken kısa devre davranışı gösterecektir.
Wikipedia sayfasında daha fazla bilgi bulabilir, çeşitli switch’lerin fotoğraflarını da görebilirsiniz.
Şimdi gelelim bu anahtarın PIC16 ile kullanılmasına. Çok basit bir uygulama olacak olsa da, anahtar ile PIC’i aracı yaparak LED sürelim. LED’i normal şartlar altında PIC’siz de sürebiliriz ama mevzunun basit yoldan anlaşılması için böyle anlamsız bir uygulama üzerinden gitmek mantıklı olacak diye düşünüyorum. Bir sarı ve bir kırmızı olmak üzere iki LED, bir anahtar ve bir de PIC olan bir devre düşünelim (Direnc, kondansatör gibi elemanları saymıyorum). Birazcık da olsa farklılık yaratmak için anahtar bir konumda iken sarı LED’i yakıp kırmızı LED’i söndüreceğiz. Diğer durumda ise kırmızı ledi yakıp sarı ledi söndüreceğiz. Tabi bu basit işi yaparken bile, kodu ibretlik şekilde yazacağız. Haydi işe koyulalım 🙂
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 |
/* * File: main.c * Author: ozenozkaya * * Created on 02 Haziran 2015 Sali, 12:47 */ #define F_OSC (1000000UL) #define REG_TRISB_ADDR (0x86) #define REG_TRISB (*( volatile unsigned char*)REG_TRISB_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 PORT_PIN_LOW (0) #define PORT_PIN_HIGH (1) #define PORT_ALL_LOW (0) #define PORT_ALL_HIGH (0xFF) typedef struct { //one bit is stored in one BYTE unsigned char pin0:1; unsigned char pin1:1; unsigned char pin2:1; unsigned char pin3:1; unsigned char pin4:1; unsigned char pin5:1; unsigned char pin6:1; unsigned char pin7:1; }tris_port_pin_t; typedef union { tris_port_pin_t pins; volatile unsigned char port; }tris_port_t,*tris_port_ptr_t; void main() { tris_port_t *portb_ptr=(tris_port_ptr_t)REG_PORTB_ADDR; tris_port_t *trisb_ptr=(tris_port_ptr_t)REG_TRISB_ADDR; trisb_ptr->port = TRIS_PORT_INPUT; trisb_ptr->pins.pin1 = TRIS_PIN_OUTPUT; trisb_ptr->pins.pin2 = TRIS_PIN_OUTPUT; portb_ptr->port = PORT_ALL_LOW; while(1) { switch(portb_ptr->pins.pin0) { case PORT_PIN_LOW: portb_ptr->pins.pin1 = PORT_PIN_HIGH; portb_ptr->pins.pin2 = PORT_PIN_LOW; break; case PORT_PIN_HIGH: portb_ptr->pins.pin1 = PORT_PIN_LOW; portb_ptr->pins.pin2 = PORT_PIN_HIGH; break; default: break; } } } |
Kodu açıklamadan önce, bir define hinliği ile, daha şeker hale getirelim diyorum. Misal aşağıdaki gibi yapsak, kodun yaptığı iş daha iyi anlaşılacak sanki.
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 |
/* * File: main.c * Author: ozenozkaya * * Created on 02 Haziran 2015 Sali, 12:47 */ #define F_OSC (1000000UL) #define REG_TRISB_ADDR (0x86) #define REG_TRISB (*( volatile unsigned char*)REG_TRISB_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 PORT_PIN_LOW (0) #define PORT_PIN_HIGH (1) #define PORT_ALL_LOW (0) #define PORT_ALL_HIGH (0xFF) #define SWITCH_PIN pins.pin0 #define RED_LED_PIN pins.pin1 #define YELLOW_LED_PIN pins.pin2 #define LED_ON (PORT_PIN_HIGH) #define LED_OFF (PORT_PIN_LOW) typedef struct { //one bit is stored in one BYTE unsigned char pin0:1; unsigned char pin1:1; unsigned char pin2:1; unsigned char pin3:1; unsigned char pin4:1; unsigned char pin5:1; unsigned char pin6:1; unsigned char pin7:1; }tris_port_pin_t; typedef union { tris_port_pin_t pins; volatile unsigned char port; }tris_port_t,*tris_port_ptr_t; void main() { tris_port_t *portb_ptr=(tris_port_ptr_t)REG_PORTB_ADDR; tris_port_t *trisb_ptr=(tris_port_ptr_t)REG_TRISB_ADDR; trisb_ptr->port = TRIS_PORT_INPUT; trisb_ptr->SWITCH_PIN = TRIS_PIN_INPUT; trisb_ptr->RED_LED_PIN = TRIS_PIN_OUTPUT; trisb_ptr->YELLOW_LED_PIN = TRIS_PIN_OUTPUT; portb_ptr->port = PORT_ALL_LOW; while(1) { switch(portb_ptr->SWITCH_PIN) { case PORT_PIN_LOW: portb_ptr->RED_LED_PIN = LED_ON; portb_ptr->YELLOW_LED_PIN = LED_OFF; break; case PORT_PIN_HIGH: portb_ptr->RED_LED_PIN = LED_OFF; portb_ptr->YELLOW_LED_PIN = LED_ON; break; default: break; } } } |
Tabi kodu yazdık öyle ama kısa bir açıklama da yapacağız. Ayrıca birinci kod ile ikincisi arasındaki farkları da ele almakta fayda var. Ancak bundan önce kodun Proteus ISIS ortamındaki çıktılarını bir paylaşalım.
Gördüğünüz gibi kodumuz çiçek gibi çalışıyor. Şimdi gelelim kod hakkında konuşma faslına. Malumunuz bu kod çok farklı şekillerde yazılabilirdi ama yine özgün bir iş yapalım istedik. Bu sebeple gittik her bir pini struct bitfield olarak tanımladık. Bu sayede port üzerindeki her pine tek tek, bir değişken üzerinden erişmek mümkün oldu. Tanımladığımız union ile, registerlara hem pin bazlı (tek tek) hem de port bazlı (topluca) erişim imkanı sağladık. Bu işin ekmeğini de kodu yazarken yedik; kodumuz hem çok anlaşılır hem de çok genişletilebilir oldu.
İkinci kodda yaptığımız trick ise, kodun okunurluğunu çok artırdı. Switch ve ledlerin hangi pinlere bağlı olduğunu define ile tanımlayarak, kodun okunurluğunu epeyce artırmış olduk. Bu sayede ikinci kodu okuyan birisi artık kodun fiziksel olarak ne iş yaptığını kolayca anlayabilir durumu geldi. Her kod bir hikaye anlatır malum. Birinci kod hikayeyi mikrokontrolörün perspektifinden anlatırken, ikinci kod hikayeyi insan gözü perspektifinden anlatmış oldu.
Bu yazı bu kadar olsun 🙂 Bir başka yazıda yine devam edeceğiz. Yazıları beğendiyseniz, faydalanabilecek tanıdıklarınızla paylaşmayı unutmayınız.
Önceki Sayfa Sonraki SayfaPIC Programlama – 4 – Giriş Çıkış (I/O, Input/Output) İşlemleri
Merhabalar, bir önceki yazımızda PIC için yazdığımız bir merhaba dünya kodunun içini dışını hallaç pamuğuna çevirmiş ve sayısız ibretlerle keyiflenmiştik. Bu yazımızda da PIC16’nın bir pinini nasıl giriş yaparız, nasıl çıkış yaparız, bir giriş pininden nasıl değer okuruz, bir çıkış pinine nasıl değer yazarız gibi işlemlere değineceğiz 🙂 Sözün özü, hayatı bir dizi gerilimsel işaretle mıncıklayacağız 🙂
Şimdi, yine bir kütüphane kullanmadan (ya da sadece kendi yazdığımız kütüphaneleri kullanarak) bir karaşimşek kodu yazalım =) Karaşimşek kodu meşhurdur dostlar =) es geçmeyelim!
Tıpkı PIC mikrodentleyicilerinin bizler için “araç” olması gibi, karaşimşek kodu da anlatmak istediklerimizi anlatmak üzere kullanacağımız bir araç olacak. Amacımız ibretleri birer birer kapmak. Sözü sakız edip uzatmadan hemen kodumuzu yazalım.
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 |
/* * File: main.c * Author: ozenozkaya * * Created on 02 Haziran 2015 Sali, 12:47 */ #define F_OSC (1000000UL) #define REG_TRISB (*( volatile unsigned char*)0x086) #define REG_PORTB (*( volatile unsigned char*)0x006) static void InitializeLedArray(void); static void DummyDelayMs(unsigned int delay_ms); void main() { unsigned char led_index=0; InitializeLedArray(); while(1) { for(led_index=0; led_index<7; led_index++) { REG_PORTB=REG_PORTB<<1; DummyDelayMs(100); } for(led_index=0; led_index<7; led_index++) { REG_PORTB=REG_PORTB>>1; DummyDelayMs(100); } } } static void InitializeLedArray(void) { REG_TRISB = 0; REG_PORTB = 1; DummyDelayMs(100); } 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++); } |
Evet efendim kodumuz yukarıda yer aldığı gibi. Çıktısını göstermeden önce, dilerseniz kodu bir açıklayayım. İlk aşamada bekletme (delay) işlemlerini yapabilmek için çalışma frekansını giriyoruz. Buna göre çalışma frekansı 1.000.000 Hz yani 1MHZ olarak aşağıdaki gibi belirleniyor.
1 |
#define F_OSC (1000000UL) |
Ardından, bir önceki yazımızda anlattığımız üzere kendi çabalarımızla register tanımını yapıyoruz. Malum bu yazı dizisinde bir hedef de PIC’i bahane edip, bir mikrokontrolöre kütüphane nasıl yazılır onu da öğrenmek. Önceki yazıda myTRISB diye yaptığımız register tanımı Ağaoğlu MyTRISB projesi ile isim benzerliği göstermesin diye isimlendirmeyi birazcık değiştirdik 😛
1 2 |
#define REG_TRISB (*( volatile unsigned char*)0x086) #define REG_PORTB (*( volatile unsigned char*)0x006) |
Bunlar malumunuz; efektif şekilde tanımlanmış kütükler. Ben afedersiniz kütük diyorum ama onun İngilizcesi “register”. Neyse bir sonraki aşamada main’e geçecek olursak önce pin’lere ilk değer veriliyor ki o kısım zaten önceki, merhaba dünya örneğinin aynısı. Ardından el yordamı ile yazdığımız DummyDelayMs fonksiyonuna değineyim.
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++); } |
Bu fonksiyonun işi, cnt değişkeninin değerini insafsızca ve ahlaksızca artırarak CPU’nun diğer işleri yapmasını engellemek. Bu da hödükçe bir beklemeye sebep oluyor. Çalışıyor mu? Çalışıyor! Oradaki 120000 sayısını empirik olarak buluyoruz. Proteusta bir OSC kullanıp zamanı ölçerek bulmak mümkün. Hesapla kitapla da yaklaşık olarak bulmak mümkündür. Neyse biz işimize devam edelim. Aşağıdaki kod ise karaşimşek işini yapmaktadır.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
while(1) { for(led_index=0; led_index<7; led_index++) { REG_PORTB=REG_PORTB<<1; DummyDelayMs(100); } for(led_index=0; led_index<7; led_index++) { REG_PORTB=REG_PORTB>>1; DummyDelayMs(100); } } |
100ms’de bir led bir kaydırılıyor. Bu kodun çıktısı ise aşağıdaki gibi oluyor 🙂
Bu yazı bu kadarcık olsun 🙂 Başka bir yazıda devam edeceğiz.
Önceki Sayfa Sonraki Sayfa
PIC Programlama – 3 – Merhaba Dünya Analizi
Uzunca bir dönemdir, çok büyük bir yoğunluk içindeyim. Bu sebepledir ki, bir türlü fırsat bulup yeni bir yazıya başlayamadım. Herhalde gün, bu gündür 🙂
Geleneksel olarak yine merhaba dünya yazımızın suyunu sıkma niyetindeyiz. Her ne kadar girizgah seviyesinde de olsa yazdığımız merhaba dünya yazısında, netleştirilmesi gereken bazı şeylerin olduğu aşikar. Bu yazı dizisinden önce Gömülü C yazı dizisinin okunmuş olmasını şiddetle tavsiye ediyorum.
Şimdi lafı uzatmadan, PIC’de yazdığımız ilk kodu hatırlayalım.
1 2 3 4 5 6 7 8 |
#include <xc.h> void main(void) { TRISB=0; PORTB=1; while(1); } |
Bir kere şu xc.h’ı bir inceleyelim ne varmış içinde?
1 2 3 4 5 6 7 8 |
#ifndef _XC_H_ #define _XC_H_ #ifdef __XC8 #include <htc.h> #endif #endif //_XC_H |
Gördüğünüz üzere, XC aslında MplabX Compiler anlamında yer alıyor ve kullandığımız platform 8 bitlik olduğundan, __XC8 etiketi sistemde tanımlı olduğundan (MplabX tarafından, biz projeyi oluşturduğumuzda otomatik tanımlanır) ilgili compiler header’ı çağırılmış. Bunun da “htc.h” olduğunu görüyoruz. Burada jeton düşüyor, hmm demek ki bu xc serisi, eski hi-tech C compiler’ın üzerine kurulmuş. Burnumuz bunun kokusunu aldı. Şimdi daha derine inelim; htc.h’ı açalım.
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 |
#ifndef _HTC_H_ #define _HTC_H_ #if defined(__CCI__) && !defined(_XC_H_) #warning "CCI projects should use the top-level support file xc.hnIncluding xc.h instead" #include <xc.h> #endif #if !defined(__CCI__) && !defined(__IAR__) /* allow CCI definitions even when CCI option is not turned on */ #include <cci.h> #endif /* Definitions for _HTC_EDITION_ values */ #define __LITE__ 0 #define __STD__ 1 #define __PRO__ 2 /* common definitions */ #define ___mkstr1(x) #x #define ___mkstr(x) ___mkstr1(x) #define _OMNITARGET ((void *)0xFFFFFFFF) extern const char __xc8_OPTIM_SPEED; #include <xc8debug.h> /* HI-TECH PICC / PICC-Lite compiler */ #if defined(__PICC__) || defined(__PICCLITE__) #include <pic.h> #endif /* HI-TECH PICC-18 compiler */ #if defined(__PICC18__) #include <pic18.h> #endif /* MPLAB C18 Compatibility Header */ #ifdef __18CXX #include <pic18.h> #endif /* HI-TECH dsPICC compiler */ #if defined(__DSPICC__) #include <dspic.h> #endif /* HI-TECH C for PIC32 */ #if defined(__PICC32__) #include <pic32.h> #endif #endif |
Bu header dosyası sayesinde, projenin özelliklerine bakılarak jenerik olarak doğru header’ların seçilmesi sağlanıyor. Muhakkak ki bunların hepsi alt alta dallanıyor ancak şöyle kısaca bir özet geçelim. CCI denilen bir muhabbet var. The Common C Interface (CCI) kavramı, taşınabilir kod yazılabilmesi için ortaya atılmış güzel bir kavram. Ancak Microchip bence bu yarışta biraz geride kalmış. Yine de CCI’ya ileride değineceğim.
xc8debug.h ise, MplabX ide üzerinden yapılan debug işlerine yardımcı olan, çok çok basit bir header. Dolayısıyla üzerinde çok konuşmaya değmeyecek.
pic.h ise önemli. Çünkü derleyicinin bizim işlemcinin register tanımlarını aldığı kütüphane bu kütüphane. Bu iş nasıl oluyor? Biz projeyi yaratır iken, IDE bizim seçtiğimiz işlemciye göre gidip -D ile, compiler define yapıyor. Daha sonra pic.h gibi kütüphaneler bu bilgileri kullanarak, hangi mikrodenetleyicinin header’ının seçileceğine karar veriyor. Örneğin bizim seçimimizde IDE, bizim seçtiğimiz işlemciye göre -D_16F84A tanımını yaptı. Bu bilgiyi cebe atıp devam edelim. pic.h’ın içine bir bakalım.
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 |
#ifndef _PIC_H_ #define _PIC_H_ #ifndef _HTC_H_ #include <htc.h> #endif #ifdef _HAS_OSCVAL_ extern unsigned char __osccal_val(void); #endif #include <pic_chip_select.h> /* MPLAB REAL-ICE related macros & includes (currently enhanced PICs only) */ #if defined(__DISABLE_REALICE_IT) || !defined(__MPLAB_REALICE__) || !defined(_PIC14E) #define __TRACE(id) /* TRACE disabled */ #define __LOG(id,value) /* LOG disabled */ #else #if defined(__MPLAB_REALICE__) #include <trace16.h> #endif #endif #define CLRWDT() asm("clrwdt") #define SLEEP() asm("sleep") // function version of nop #pragma intrinsic(__nop) extern void __nop(void); #define NOP() __nop() /* _nop() has been deprecated; use __nop() */ #define _nop() __nop() #define ASMOPT_ON() asm("opt asmopt_on") #define ASMOPT_OFF() asm("opt asmopt_off") // // Legacy Programming Macro Functions // #define __CONFIG(x) __config(___mkstr(__CONFIG), ___mkstr(pic), ___mkstr(x)) // Programs the lower 4 bits per ID location #define __IDLOC(w) __config(___mkstr(__IDLOC), ___mkstr(pic), ___mkstr(w)) // Variant of IDLOC for those devices that permit programming of the lower 7 bits per ID location #define __IDLOC7(a,b,c,d) __config(___mkstr(__IDLOC7), ___mkstr(pic), a, b, c, d) #define __PROG_CONFIG(a, x) __config(___mkstr(__PROG_CONFIG), ___mkstr(pic), a, x) #if !defined(_PIC14E) && !defined(_EEADRL) #define _EEADRL EEADR #else #define _EEADRL EEADRL #endif #if EEPROM_SIZE > 0 #define __EEPROM_DATA(a, b, c, d, e, f, g, h) asm("tpsect eeprom_data,class=EEDATA,delta=2,space=3,noexec"); asm("tdbt" ___mkstr(a) "," ___mkstr(b) "," ___mkstr(c) "," ___mkstr(d) "," ___mkstr(e) "," ___mkstr(f) "," ___mkstr(g) "," ___mkstr(h)) #endif /*********************************************************************** **** FLASH memory read/write/erase macros and function definitions **** *********************************************************************** * Notes: * __FLASHTYPE == 0 defined in devices that can only read flash memory - cannot write eg. 16F777 * __FLASHTYPE == 1 defined in traditional devices that can write 1 word at a time eg. 16F877 * __FLASHTYPE == 2 defined in devices that can only write in 4 word blocks eg. 16F877A * __FLASHTYPE == 3 defined in devices requiring 32-word block erasure before writing eg. 16F87 * __FLASHTYPE == undefined if device can neither read nor write program memory */ // macro FLASH_READ returns a word stored at a flash address #if defined(__FLASHTYPE) extern unsigned int flash_read(unsigned short addr); #if EEPROM_SIZE > 0 #define FLASH_READ(addr) (_EEADRL=(addr)&0xff, EEADRH=(addr)>>8, WREN=0, EECON1 |= 0x80, RD=1, NOP(), NOP(), (EEDATH << 8) | EEDATA) #else // FLASH_READ without EEPROM #define FLASH_READ(addr) (_EEADRL=(addr)&0xff, EEADRH=(addr)>>8, RD=1, NOP(), NOP(), (EEDATH << 8) | EEDATA) #endif #endif // end FLASH_READ // macro FLASH_WRITE used when writing only one word of data #if __FLASHTYPE==2 || __FLASHTYPE==3 /* * This is not available in this version. Contact HI-TECH support for more information. #define FLASH_WRITE(addr,data) do{ unsigned short x=data; flash_copy((const unsigned short *)&x,1,addr); }while(0) extern void flash_copy(const unsigned short * source_addr,unsigned char length,unsigned short dest_addr); */ #elif __FLASHTYPE==1 #define FLASH_WRITE(addr, value) _EEADRL=((addr)&0xff); EEADRH=((addr)>>8); EEDATH=((value)>>8); EEDATA=((value)&0xff); EECON1 |= 0x80; WREN=1; EECON2 = 0x55; EECON2 = 0xaa; WR=1; NOP(); NOP(); WREN=0 //extern void flash_copy(const unsigned short * source_addr,unsigned char length,unsigned short dest_addr); #endif // end FLASH_WRITE // macro FLASH_ERASE used to clear a 32-Byte sector of flash #if __FLASHTYPE==3 #define FLASH_ERASE(addr) while(WR)continue; _EEADRL=((addr)&0xFF); EEADRH=((addr>>8)&0xFF); EECON1=0x94; CARRY=0;if(GIE)CARRY=1;GIE=0; EECON2=0x55;EECON2=0xAA;WR=1; NOP(); if(CARRY)GIE=1 // library function version extern void flash_erase(unsigned short addr); #endif // end FLASH_ERASE #include <eeprom_routines.h> #ifdef __PICCPRO__ /****************************************************************/ /* Built-in delay routine */ /****************************************************************/ #pragma intrinsic(_delay) extern __nonreentrant void _delay(unsigned long); // NOTE: To use the macros below, YOU must have previously defined _XTAL_FREQ #define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0))) #define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0))) #endif /****************************************************************/ /****** Global interrupt enable/disable macro definitions *******/ /****************************************************************/ #if defined(_PIC14) || defined(_PIC14E) #ifndef ei #define ei() (GIE = 1) // interrupt enable bit #endif #if defined(_14000) || defined(_16C61) || defined(_16C62) || defined(_16C63) || defined(_16C63A) || defined(_16C64) || defined(_16C65) || defined(_16C65B) || defined(_16C71) || defined(_16C73) || defined(_16C73B) || defined(_16C74) || defined(_16C74B) || defined(_16C84) || defined(_16C745) || defined(_16C765) || defined(_16LC74B) #ifndef di #define di() { do { GIE = 0; } while ( GIE == 1 ); } // disable interrupt bit #endif #else #ifndef di #define di() (GIE = 0) // interrupt enable bit #endif #endif #endif /* The below reflect the state of TO and PD, respectively, which would otherwise be trashed by startup code. */ extern unsigned char __resetbits; extern __bit __powerdown; extern __bit __timeout; #endif /* _PIC_H */ |
Arkadaşlar sağolsunlar pic.h içinde bize yardımcı olabilecek çok sayıda şey tanımlamışlar. pic_chip_select.h tam olarak mikrokontrolör header’ı seçimini yapıyor ve onu mutlaka konuşacağız. Ancak sırayla buradaki bazı ibretlere bir değinelim. PIC16F84A; 68 byte RAM’i ve 1024 word’lük inanılmaz(!) FLASH belleği ile, hakikaten kısıtlı kaynaklı bir donanım olduğundan, dayılar haklı olarak LOG ve TRACE fonksiyonelitelerini kapatmışlar 🙂 Zira bu fonksiyoneliteleri kullanmaya kalksaydık, kaynakların çoğunu kullanmış olurduk.
1 2 |
#define CLRWDT() asm("clrwdt") #define SLEEP() asm("sleep") |
Yukarıdaki etiketler, assembler komutlarını çağırarak sırasıyla watchdog timer’ı resetlemenin ve uyku moduna girmenin verimli implementasyonları olarak göze çarpıyor. Watchdog timer’ı resetlemek bazı arkadaşlara bir anlam ifade etmemiş olabilir. Mikrodenetleyici bir sebepten donarsa filan, reset atabilecek bir donanımın olması gerekiyor. Eğer mikrodenetleyicideki kod, belli aralıklarla watchdog timer’ın sayacını sıfırlamazsa, sayaç artıp taşar ve taştığı gibi mikrodenetleyiciye reset atar. Yerli yerinde kullanıldığında WDT (watchdog timer) candır 🙂 Sleep ise, mikrodenetleyiciyi düşük güç tüketimi moduna sokmanın güzel bir yoludur. Bu kısmı da ileride detaylıca ele alacağız.
ei(); ve di(); fonksiyonları ile sırasyla interrupt enable ve interrupt disable işleri kolaylaştırılmış. İleride detaylıca değineceğimiz kesme fonksiyonelitesinin aktive edilmesi ya da devre dışı bırakılması için bu makroları kullanmak mümkün 🙂 Diğer kısımlar zaten benzer. Öyleyse şimdi pic_chip_select.h kütüphanesinin bir kısmını inceleyelim. Gerisi de zaten aynı 😀
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
... #ifdef _16F84 #ifdef _LEGACY_HEADERS #include <pic1684_legacy.h> #undef _HEADER_NOT_FOUND #else #include <pic16f84.h> #undef _HEADER_NOT_FOUND #endif #endif #ifdef _16F84A #ifdef _LEGACY_HEADERS #include <pic1684_legacy.h> #undef _HEADER_NOT_FOUND #else #include <pic16f84a.h> #undef _HEADER_NOT_FOUND #endif #endif ... |
Bu dosyada gördüğünüz gibi, IDE tarafından biz projeyi oluştururken tanımlanan etiketler kullanılarak doğru başlık dosyaları (header files) projeye dahil ediliyor yani çağırılıyor. Bu saydede biz xc.h dediğimizde, mikrokontrolör modeline göre, başlık dosyaları otomatik çağırılmış oluyor. Hmm, güzel 🙂 Öyleyse bir adım daha dalalım.
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 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 |
// Version 1.34 // Generated 16/02/2015 GMT /* * Copyright © 2015, Microchip Technology Inc. and its subsidiaries ("Microchip") * All rights reserved. * * This software is developed by Microchip Technology Inc. and its subsidiaries ("Microchip"). * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. Microchip's name may not be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY MICROCHIP "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MICROCHIP BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING BUT NOT LIMITED TO * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS * INTERRUPTION) HOWSOEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef _PIC16F84A_H_ #define _PIC16F84A_H_ /* * C Header file for the Microchip PIC Microcontroller * PIC16F84A * */ #ifndef __XC8 #warning Header file pic16f84a.h included directly. Use #include <xc.h> instead. #endif /* * Register Definitions * */ // Register: INDF extern volatile unsigned char INDF @ 0x000; #ifndef _LIB_BUILD asm("INDF equ 00h"); #endif // Register: TMR0 extern volatile unsigned char TMR0 @ 0x001; #ifndef _LIB_BUILD asm("TMR0 equ 01h"); #endif // Register: PCL extern volatile unsigned char PCL @ 0x002; #ifndef _LIB_BUILD asm("PCL equ 02h"); #endif // Register: STATUS extern volatile unsigned char STATUS @ 0x003; #ifndef _LIB_BUILD asm("STATUS equ 03h"); #endif // bitfield definitions typedef union { struct { unsigned C :1; unsigned DC :1; unsigned Z :1; unsigned nPD :1; unsigned nTO :1; unsigned RP :2; unsigned IRP :1; }; struct { unsigned :5; unsigned RP0 :1; unsigned RP1 :1; }; struct { unsigned CARRY :1; }; struct { unsigned :2; unsigned ZERO :1; }; } STATUSbits_t; extern volatile STATUSbits_t STATUSbits @ 0x003; // bitfield macros #define _STATUS_C_POSN 0x0 #define _STATUS_C_POSITION 0x0 #define _STATUS_C_SIZE 0x1 #define _STATUS_C_LENGTH 0x1 #define _STATUS_C_MASK 0x1 #define _STATUS_DC_POSN 0x1 #define _STATUS_DC_POSITION 0x1 #define _STATUS_DC_SIZE 0x1 #define _STATUS_DC_LENGTH 0x1 #define _STATUS_DC_MASK 0x2 #define _STATUS_Z_POSN 0x2 #define _STATUS_Z_POSITION 0x2 #define _STATUS_Z_SIZE 0x1 #define _STATUS_Z_LENGTH 0x1 #define _STATUS_Z_MASK 0x4 #define _STATUS_nPD_POSN 0x3 #define _STATUS_nPD_POSITION 0x3 #define _STATUS_nPD_SIZE 0x1 #define _STATUS_nPD_LENGTH 0x1 #define _STATUS_nPD_MASK 0x8 #define _STATUS_nTO_POSN 0x4 #define _STATUS_nTO_POSITION 0x4 #define _STATUS_nTO_SIZE 0x1 #define _STATUS_nTO_LENGTH 0x1 #define _STATUS_nTO_MASK 0x10 #define _STATUS_RP_POSN 0x5 #define _STATUS_RP_POSITION 0x5 #define _STATUS_RP_SIZE 0x2 #define _STATUS_RP_LENGTH 0x2 #define _STATUS_RP_MASK 0x60 #define _STATUS_IRP_POSN 0x7 #define _STATUS_IRP_POSITION 0x7 #define _STATUS_IRP_SIZE 0x1 #define _STATUS_IRP_LENGTH 0x1 #define _STATUS_IRP_MASK 0x80 #define _STATUS_RP0_POSN 0x5 #define _STATUS_RP0_POSITION 0x5 #define _STATUS_RP0_SIZE 0x1 #define _STATUS_RP0_LENGTH 0x1 #define _STATUS_RP0_MASK 0x20 #define _STATUS_RP1_POSN 0x6 #define _STATUS_RP1_POSITION 0x6 #define _STATUS_RP1_SIZE 0x1 #define _STATUS_RP1_LENGTH 0x1 #define _STATUS_RP1_MASK 0x40 #define _STATUS_CARRY_POSN 0x0 #define _STATUS_CARRY_POSITION 0x0 #define _STATUS_CARRY_SIZE 0x1 #define _STATUS_CARRY_LENGTH 0x1 #define _STATUS_CARRY_MASK 0x1 #define _STATUS_ZERO_POSN 0x2 #define _STATUS_ZERO_POSITION 0x2 #define _STATUS_ZERO_SIZE 0x1 #define _STATUS_ZERO_LENGTH 0x1 #define _STATUS_ZERO_MASK 0x4 // Register: FSR extern volatile unsigned char FSR @ 0x004; #ifndef _LIB_BUILD asm("FSR equ 04h"); #endif // Register: PORTA extern volatile unsigned char PORTA @ 0x005; #ifndef _LIB_BUILD asm("PORTA equ 05h"); #endif // bitfield definitions typedef union { struct { unsigned RA0 :1; unsigned RA1 :1; unsigned RA2 :1; unsigned RA3 :1; unsigned RA4 :1; }; } PORTAbits_t; extern volatile PORTAbits_t PORTAbits @ 0x005; // bitfield macros #define _PORTA_RA0_POSN 0x0 #define _PORTA_RA0_POSITION 0x0 #define _PORTA_RA0_SIZE 0x1 #define _PORTA_RA0_LENGTH 0x1 #define _PORTA_RA0_MASK 0x1 #define _PORTA_RA1_POSN 0x1 #define _PORTA_RA1_POSITION 0x1 #define _PORTA_RA1_SIZE 0x1 #define _PORTA_RA1_LENGTH 0x1 #define _PORTA_RA1_MASK 0x2 #define _PORTA_RA2_POSN 0x2 #define _PORTA_RA2_POSITION 0x2 #define _PORTA_RA2_SIZE 0x1 #define _PORTA_RA2_LENGTH 0x1 #define _PORTA_RA2_MASK 0x4 #define _PORTA_RA3_POSN 0x3 #define _PORTA_RA3_POSITION 0x3 #define _PORTA_RA3_SIZE 0x1 #define _PORTA_RA3_LENGTH 0x1 #define _PORTA_RA3_MASK 0x8 #define _PORTA_RA4_POSN 0x4 #define _PORTA_RA4_POSITION 0x4 #define _PORTA_RA4_SIZE 0x1 #define _PORTA_RA4_LENGTH 0x1 #define _PORTA_RA4_MASK 0x10 // Register: PORTB extern volatile unsigned char PORTB @ 0x006; #ifndef _LIB_BUILD asm("PORTB equ 06h"); #endif // bitfield definitions typedef union { struct { unsigned RB0 :1; unsigned RB1 :1; unsigned RB2 :1; unsigned RB3 :1; unsigned RB4 :1; unsigned RB5 :1; unsigned RB6 :1; unsigned RB7 :1; }; } PORTBbits_t; extern volatile PORTBbits_t PORTBbits @ 0x006; // bitfield macros #define _PORTB_RB0_POSN 0x0 #define _PORTB_RB0_POSITION 0x0 #define _PORTB_RB0_SIZE 0x1 #define _PORTB_RB0_LENGTH 0x1 #define _PORTB_RB0_MASK 0x1 #define _PORTB_RB1_POSN 0x1 #define _PORTB_RB1_POSITION 0x1 #define _PORTB_RB1_SIZE 0x1 #define _PORTB_RB1_LENGTH 0x1 #define _PORTB_RB1_MASK 0x2 #define _PORTB_RB2_POSN 0x2 #define _PORTB_RB2_POSITION 0x2 #define _PORTB_RB2_SIZE 0x1 #define _PORTB_RB2_LENGTH 0x1 #define _PORTB_RB2_MASK 0x4 #define _PORTB_RB3_POSN 0x3 #define _PORTB_RB3_POSITION 0x3 #define _PORTB_RB3_SIZE 0x1 #define _PORTB_RB3_LENGTH 0x1 #define _PORTB_RB3_MASK 0x8 #define _PORTB_RB4_POSN 0x4 #define _PORTB_RB4_POSITION 0x4 #define _PORTB_RB4_SIZE 0x1 #define _PORTB_RB4_LENGTH 0x1 #define _PORTB_RB4_MASK 0x10 #define _PORTB_RB5_POSN 0x5 #define _PORTB_RB5_POSITION 0x5 #define _PORTB_RB5_SIZE 0x1 #define _PORTB_RB5_LENGTH 0x1 #define _PORTB_RB5_MASK 0x20 #define _PORTB_RB6_POSN 0x6 #define _PORTB_RB6_POSITION 0x6 #define _PORTB_RB6_SIZE 0x1 #define _PORTB_RB6_LENGTH 0x1 #define _PORTB_RB6_MASK 0x40 #define _PORTB_RB7_POSN 0x7 #define _PORTB_RB7_POSITION 0x7 #define _PORTB_RB7_SIZE 0x1 #define _PORTB_RB7_LENGTH 0x1 #define _PORTB_RB7_MASK 0x80 // Register: EEDATA extern volatile unsigned char EEDATA @ 0x008; #ifndef _LIB_BUILD asm("EEDATA equ 08h"); #endif // Register: EEADR extern volatile unsigned char EEADR @ 0x009; #ifndef _LIB_BUILD asm("EEADR equ 09h"); #endif // Register: PCLATH extern volatile unsigned char PCLATH @ 0x00A; #ifndef _LIB_BUILD asm("PCLATH equ 0Ah"); #endif // bitfield definitions typedef union { struct { unsigned PCLATH :5; }; } PCLATHbits_t; extern volatile PCLATHbits_t PCLATHbits @ 0x00A; // bitfield macros #define _PCLATH_PCLATH_POSN 0x0 #define _PCLATH_PCLATH_POSITION 0x0 #define _PCLATH_PCLATH_SIZE 0x5 #define _PCLATH_PCLATH_LENGTH 0x5 #define _PCLATH_PCLATH_MASK 0x1F // Register: INTCON extern volatile unsigned char INTCON @ 0x00B; #ifndef _LIB_BUILD asm("INTCON equ 0Bh"); #endif // bitfield definitions typedef union { struct { unsigned RBIF :1; unsigned INTF :1; unsigned T0IF :1; unsigned RBIE :1; unsigned INTE :1; unsigned T0IE :1; unsigned EEIE :1; unsigned GIE :1; }; struct { unsigned :2; unsigned TMR0IF :1; unsigned :2; unsigned TMR0IE :1; }; } INTCONbits_t; extern volatile INTCONbits_t INTCONbits @ 0x00B; // bitfield macros #define _INTCON_RBIF_POSN 0x0 #define _INTCON_RBIF_POSITION 0x0 #define _INTCON_RBIF_SIZE 0x1 #define _INTCON_RBIF_LENGTH 0x1 #define _INTCON_RBIF_MASK 0x1 #define _INTCON_INTF_POSN 0x1 #define _INTCON_INTF_POSITION 0x1 #define _INTCON_INTF_SIZE 0x1 #define _INTCON_INTF_LENGTH 0x1 #define _INTCON_INTF_MASK 0x2 #define _INTCON_T0IF_POSN 0x2 #define _INTCON_T0IF_POSITION 0x2 #define _INTCON_T0IF_SIZE 0x1 #define _INTCON_T0IF_LENGTH 0x1 #define _INTCON_T0IF_MASK 0x4 #define _INTCON_RBIE_POSN 0x3 #define _INTCON_RBIE_POSITION 0x3 #define _INTCON_RBIE_SIZE 0x1 #define _INTCON_RBIE_LENGTH 0x1 #define _INTCON_RBIE_MASK 0x8 #define _INTCON_INTE_POSN 0x4 #define _INTCON_INTE_POSITION 0x4 #define _INTCON_INTE_SIZE 0x1 #define _INTCON_INTE_LENGTH 0x1 #define _INTCON_INTE_MASK 0x10 #define _INTCON_T0IE_POSN 0x5 #define _INTCON_T0IE_POSITION 0x5 #define _INTCON_T0IE_SIZE 0x1 #define _INTCON_T0IE_LENGTH 0x1 #define _INTCON_T0IE_MASK 0x20 #define _INTCON_EEIE_POSN 0x6 #define _INTCON_EEIE_POSITION 0x6 #define _INTCON_EEIE_SIZE 0x1 #define _INTCON_EEIE_LENGTH 0x1 #define _INTCON_EEIE_MASK 0x40 #define _INTCON_GIE_POSN 0x7 #define _INTCON_GIE_POSITION 0x7 #define _INTCON_GIE_SIZE 0x1 #define _INTCON_GIE_LENGTH 0x1 #define _INTCON_GIE_MASK 0x80 #define _INTCON_TMR0IF_POSN 0x2 #define _INTCON_TMR0IF_POSITION 0x2 #define _INTCON_TMR0IF_SIZE 0x1 #define _INTCON_TMR0IF_LENGTH 0x1 #define _INTCON_TMR0IF_MASK 0x4 #define _INTCON_TMR0IE_POSN 0x5 #define _INTCON_TMR0IE_POSITION 0x5 #define _INTCON_TMR0IE_SIZE 0x1 #define _INTCON_TMR0IE_LENGTH 0x1 #define _INTCON_TMR0IE_MASK 0x20 // Register: OPTION_REG extern volatile unsigned char OPTION_REG @ 0x081; #ifndef _LIB_BUILD asm("OPTION_REG equ 081h"); #endif // bitfield definitions typedef union { struct { unsigned PS :3; unsigned PSA :1; unsigned T0SE :1; unsigned T0CS :1; unsigned INTEDG :1; unsigned nRBPU :1; }; struct { unsigned PS0 :1; unsigned PS1 :1; unsigned PS2 :1; }; } OPTION_REGbits_t; extern volatile OPTION_REGbits_t OPTION_REGbits @ 0x081; // bitfield macros #define _OPTION_REG_PS_POSN 0x0 #define _OPTION_REG_PS_POSITION 0x0 #define _OPTION_REG_PS_SIZE 0x3 #define _OPTION_REG_PS_LENGTH 0x3 #define _OPTION_REG_PS_MASK 0x7 #define _OPTION_REG_PSA_POSN 0x3 #define _OPTION_REG_PSA_POSITION 0x3 #define _OPTION_REG_PSA_SIZE 0x1 #define _OPTION_REG_PSA_LENGTH 0x1 #define _OPTION_REG_PSA_MASK 0x8 #define _OPTION_REG_T0SE_POSN 0x4 #define _OPTION_REG_T0SE_POSITION 0x4 #define _OPTION_REG_T0SE_SIZE 0x1 #define _OPTION_REG_T0SE_LENGTH 0x1 #define _OPTION_REG_T0SE_MASK 0x10 #define _OPTION_REG_T0CS_POSN 0x5 #define _OPTION_REG_T0CS_POSITION 0x5 #define _OPTION_REG_T0CS_SIZE 0x1 #define _OPTION_REG_T0CS_LENGTH 0x1 #define _OPTION_REG_T0CS_MASK 0x20 #define _OPTION_REG_INTEDG_POSN 0x6 #define _OPTION_REG_INTEDG_POSITION 0x6 #define _OPTION_REG_INTEDG_SIZE 0x1 #define _OPTION_REG_INTEDG_LENGTH 0x1 #define _OPTION_REG_INTEDG_MASK 0x40 #define _OPTION_REG_nRBPU_POSN 0x7 #define _OPTION_REG_nRBPU_POSITION 0x7 #define _OPTION_REG_nRBPU_SIZE 0x1 #define _OPTION_REG_nRBPU_LENGTH 0x1 #define _OPTION_REG_nRBPU_MASK 0x80 #define _OPTION_REG_PS0_POSN 0x0 #define _OPTION_REG_PS0_POSITION 0x0 #define _OPTION_REG_PS0_SIZE 0x1 #define _OPTION_REG_PS0_LENGTH 0x1 #define _OPTION_REG_PS0_MASK 0x1 #define _OPTION_REG_PS1_POSN 0x1 #define _OPTION_REG_PS1_POSITION 0x1 #define _OPTION_REG_PS1_SIZE 0x1 #define _OPTION_REG_PS1_LENGTH 0x1 #define _OPTION_REG_PS1_MASK 0x2 #define _OPTION_REG_PS2_POSN 0x2 #define _OPTION_REG_PS2_POSITION 0x2 #define _OPTION_REG_PS2_SIZE 0x1 #define _OPTION_REG_PS2_LENGTH 0x1 #define _OPTION_REG_PS2_MASK 0x4 // Register: TRISA extern volatile unsigned char TRISA @ 0x085; #ifndef _LIB_BUILD asm("TRISA equ 085h"); #endif // bitfield definitions typedef union { struct { unsigned TRISA0 :1; unsigned TRISA1 :1; unsigned TRISA2 :1; unsigned TRISA3 :1; unsigned TRISA4 :1; }; } TRISAbits_t; extern volatile TRISAbits_t TRISAbits @ 0x085; // bitfield macros #define _TRISA_TRISA0_POSN 0x0 #define _TRISA_TRISA0_POSITION 0x0 #define _TRISA_TRISA0_SIZE 0x1 #define _TRISA_TRISA0_LENGTH 0x1 #define _TRISA_TRISA0_MASK 0x1 #define _TRISA_TRISA1_POSN 0x1 #define _TRISA_TRISA1_POSITION 0x1 #define _TRISA_TRISA1_SIZE 0x1 #define _TRISA_TRISA1_LENGTH 0x1 #define _TRISA_TRISA1_MASK 0x2 #define _TRISA_TRISA2_POSN 0x2 #define _TRISA_TRISA2_POSITION 0x2 #define _TRISA_TRISA2_SIZE 0x1 #define _TRISA_TRISA2_LENGTH 0x1 #define _TRISA_TRISA2_MASK 0x4 #define _TRISA_TRISA3_POSN 0x3 #define _TRISA_TRISA3_POSITION 0x3 #define _TRISA_TRISA3_SIZE 0x1 #define _TRISA_TRISA3_LENGTH 0x1 #define _TRISA_TRISA3_MASK 0x8 #define _TRISA_TRISA4_POSN 0x4 #define _TRISA_TRISA4_POSITION 0x4 #define _TRISA_TRISA4_SIZE 0x1 #define _TRISA_TRISA4_LENGTH 0x1 #define _TRISA_TRISA4_MASK 0x10 // Register: TRISB extern volatile unsigned char TRISB @ 0x086; #ifndef _LIB_BUILD asm("TRISB equ 086h"); #endif // bitfield definitions typedef union { struct { unsigned TRISB0 :1; unsigned TRISB1 :1; unsigned TRISB2 :1; unsigned TRISB3 :1; unsigned TRISB4 :1; unsigned TRISB5 :1; unsigned TRISB6 :1; unsigned TRISB7 :1; }; } TRISBbits_t; extern volatile TRISBbits_t TRISBbits @ 0x086; // bitfield macros #define _TRISB_TRISB0_POSN 0x0 #define _TRISB_TRISB0_POSITION 0x0 #define _TRISB_TRISB0_SIZE 0x1 #define _TRISB_TRISB0_LENGTH 0x1 #define _TRISB_TRISB0_MASK 0x1 #define _TRISB_TRISB1_POSN 0x1 #define _TRISB_TRISB1_POSITION 0x1 #define _TRISB_TRISB1_SIZE 0x1 #define _TRISB_TRISB1_LENGTH 0x1 #define _TRISB_TRISB1_MASK 0x2 #define _TRISB_TRISB2_POSN 0x2 #define _TRISB_TRISB2_POSITION 0x2 #define _TRISB_TRISB2_SIZE 0x1 #define _TRISB_TRISB2_LENGTH 0x1 #define _TRISB_TRISB2_MASK 0x4 #define _TRISB_TRISB3_POSN 0x3 #define _TRISB_TRISB3_POSITION 0x3 #define _TRISB_TRISB3_SIZE 0x1 #define _TRISB_TRISB3_LENGTH 0x1 #define _TRISB_TRISB3_MASK 0x8 #define _TRISB_TRISB4_POSN 0x4 #define _TRISB_TRISB4_POSITION 0x4 #define _TRISB_TRISB4_SIZE 0x1 #define _TRISB_TRISB4_LENGTH 0x1 #define _TRISB_TRISB4_MASK 0x10 #define _TRISB_TRISB5_POSN 0x5 #define _TRISB_TRISB5_POSITION 0x5 #define _TRISB_TRISB5_SIZE 0x1 #define _TRISB_TRISB5_LENGTH 0x1 #define _TRISB_TRISB5_MASK 0x20 #define _TRISB_TRISB6_POSN 0x6 #define _TRISB_TRISB6_POSITION 0x6 #define _TRISB_TRISB6_SIZE 0x1 #define _TRISB_TRISB6_LENGTH 0x1 #define _TRISB_TRISB6_MASK 0x40 #define _TRISB_TRISB7_POSN 0x7 #define _TRISB_TRISB7_POSITION 0x7 #define _TRISB_TRISB7_SIZE 0x1 #define _TRISB_TRISB7_LENGTH 0x1 #define _TRISB_TRISB7_MASK 0x80 // Register: EECON1 extern volatile unsigned char EECON1 @ 0x088; #ifndef _LIB_BUILD asm("EECON1 equ 088h"); #endif // bitfield definitions typedef union { struct { unsigned RD :1; unsigned WR :1; unsigned WREN :1; unsigned WRERR :1; unsigned EEIF :1; }; } EECON1bits_t; extern volatile EECON1bits_t EECON1bits @ 0x088; // bitfield macros #define _EECON1_RD_POSN 0x0 #define _EECON1_RD_POSITION 0x0 #define _EECON1_RD_SIZE 0x1 #define _EECON1_RD_LENGTH 0x1 #define _EECON1_RD_MASK 0x1 #define _EECON1_WR_POSN 0x1 #define _EECON1_WR_POSITION 0x1 #define _EECON1_WR_SIZE 0x1 #define _EECON1_WR_LENGTH 0x1 #define _EECON1_WR_MASK 0x2 #define _EECON1_WREN_POSN 0x2 #define _EECON1_WREN_POSITION 0x2 #define _EECON1_WREN_SIZE 0x1 #define _EECON1_WREN_LENGTH 0x1 #define _EECON1_WREN_MASK 0x4 #define _EECON1_WRERR_POSN 0x3 #define _EECON1_WRERR_POSITION 0x3 #define _EECON1_WRERR_SIZE 0x1 #define _EECON1_WRERR_LENGTH 0x1 #define _EECON1_WRERR_MASK 0x8 #define _EECON1_EEIF_POSN 0x4 #define _EECON1_EEIF_POSITION 0x4 #define _EECON1_EEIF_SIZE 0x1 #define _EECON1_EEIF_LENGTH 0x1 #define _EECON1_EEIF_MASK 0x10 // Register: EECON2 extern volatile unsigned char EECON2 @ 0x089; #ifndef _LIB_BUILD asm("EECON2 equ 089h"); #endif /* * Bit Definitions * */ #define _DEPRECATED __attribute__((__deprecated__)) #ifndef BANKMASK #define BANKMASK(addr) ((addr)&07Fh) #endif extern volatile __bit CARRY @ (((unsigned) &STATUS)*8) + 0; #define CARRY_bit BANKMASK(STATUS), 0 extern volatile __bit DC @ (((unsigned) &STATUS)*8) + 1; #define DC_bit BANKMASK(STATUS), 1 extern volatile __bit EEIE @ (((unsigned) &INTCON)*8) + 6; #define EEIE_bit BANKMASK(INTCON), 6 extern volatile __bit EEIF @ (((unsigned) &EECON1)*8) + 4; #define EEIF_bit BANKMASK(EECON1), 4 extern volatile __bit GIE @ (((unsigned) &INTCON)*8) + 7; #define GIE_bit BANKMASK(INTCON), 7 extern volatile __bit INTE @ (((unsigned) &INTCON)*8) + 4; #define INTE_bit BANKMASK(INTCON), 4 extern volatile __bit INTEDG @ (((unsigned) &OPTION_REG)*8) + 6; #define INTEDG_bit BANKMASK(OPTION_REG), 6 extern volatile __bit INTF @ (((unsigned) &INTCON)*8) + 1; #define INTF_bit BANKMASK(INTCON), 1 extern volatile __bit IRP @ (((unsigned) &STATUS)*8) + 7; #define IRP_bit BANKMASK(STATUS), 7 extern volatile __bit PS0 @ (((unsigned) &OPTION_REG)*8) + 0; #define PS0_bit BANKMASK(OPTION_REG), 0 extern volatile __bit PS1 @ (((unsigned) &OPTION_REG)*8) + 1; #define PS1_bit BANKMASK(OPTION_REG), 1 extern volatile __bit PS2 @ (((unsigned) &OPTION_REG)*8) + 2; #define PS2_bit BANKMASK(OPTION_REG), 2 extern volatile __bit PSA @ (((unsigned) &OPTION_REG)*8) + 3; #define PSA_bit BANKMASK(OPTION_REG), 3 extern volatile __bit RA0 @ (((unsigned) &PORTA)*8) + 0; #define RA0_bit BANKMASK(PORTA), 0 extern volatile __bit RA1 @ (((unsigned) &PORTA)*8) + 1; #define RA1_bit BANKMASK(PORTA), 1 extern volatile __bit RA2 @ (((unsigned) &PORTA)*8) + 2; #define RA2_bit BANKMASK(PORTA), 2 extern volatile __bit RA3 @ (((unsigned) &PORTA)*8) + 3; #define RA3_bit BANKMASK(PORTA), 3 extern volatile __bit RA4 @ (((unsigned) &PORTA)*8) + 4; #define RA4_bit BANKMASK(PORTA), 4 extern volatile __bit RB0 @ (((unsigned) &PORTB)*8) + 0; #define RB0_bit BANKMASK(PORTB), 0 extern volatile __bit RB1 @ (((unsigned) &PORTB)*8) + 1; #define RB1_bit BANKMASK(PORTB), 1 extern volatile __bit RB2 @ (((unsigned) &PORTB)*8) + 2; #define RB2_bit BANKMASK(PORTB), 2 extern volatile __bit RB3 @ (((unsigned) &PORTB)*8) + 3; #define RB3_bit BANKMASK(PORTB), 3 extern volatile __bit RB4 @ (((unsigned) &PORTB)*8) + 4; #define RB4_bit BANKMASK(PORTB), 4 extern volatile __bit RB5 @ (((unsigned) &PORTB)*8) + 5; #define RB5_bit BANKMASK(PORTB), 5 extern volatile __bit RB6 @ (((unsigned) &PORTB)*8) + 6; #define RB6_bit BANKMASK(PORTB), 6 extern volatile __bit RB7 @ (((unsigned) &PORTB)*8) + 7; #define RB7_bit BANKMASK(PORTB), 7 extern volatile __bit RBIE @ (((unsigned) &INTCON)*8) + 3; #define RBIE_bit BANKMASK(INTCON), 3 extern volatile __bit RBIF @ (((unsigned) &INTCON)*8) + 0; #define RBIF_bit BANKMASK(INTCON), 0 extern volatile __bit RD @ (((unsigned) &EECON1)*8) + 0; #define RD_bit BANKMASK(EECON1), 0 extern volatile __bit RP0 @ (((unsigned) &STATUS)*8) + 5; #define RP0_bit BANKMASK(STATUS), 5 extern volatile __bit RP1 @ (((unsigned) &STATUS)*8) + 6; #define RP1_bit BANKMASK(STATUS), 6 extern volatile __bit T0CS @ (((unsigned) &OPTION_REG)*8) + 5; #define T0CS_bit BANKMASK(OPTION_REG), 5 extern volatile __bit T0IE @ (((unsigned) &INTCON)*8) + 5; #define T0IE_bit BANKMASK(INTCON), 5 extern volatile __bit T0IF @ (((unsigned) &INTCON)*8) + 2; #define T0IF_bit BANKMASK(INTCON), 2 extern volatile __bit T0SE @ (((unsigned) &OPTION_REG)*8) + 4; #define T0SE_bit BANKMASK(OPTION_REG), 4 extern volatile __bit TMR0IE @ (((unsigned) &INTCON)*8) + 5; #define TMR0IE_bit BANKMASK(INTCON), 5 extern volatile __bit TMR0IF @ (((unsigned) &INTCON)*8) + 2; #define TMR0IF_bit BANKMASK(INTCON), 2 extern volatile __bit TRISA0 @ (((unsigned) &TRISA)*8) + 0; #define TRISA0_bit BANKMASK(TRISA), 0 extern volatile __bit TRISA1 @ (((unsigned) &TRISA)*8) + 1; #define TRISA1_bit BANKMASK(TRISA), 1 extern volatile __bit TRISA2 @ (((unsigned) &TRISA)*8) + 2; #define TRISA2_bit BANKMASK(TRISA), 2 extern volatile __bit TRISA3 @ (((unsigned) &TRISA)*8) + 3; #define TRISA3_bit BANKMASK(TRISA), 3 extern volatile __bit TRISA4 @ (((unsigned) &TRISA)*8) + 4; #define TRISA4_bit BANKMASK(TRISA), 4 extern volatile __bit TRISB0 @ (((unsigned) &TRISB)*8) + 0; #define TRISB0_bit BANKMASK(TRISB), 0 extern volatile __bit TRISB1 @ (((unsigned) &TRISB)*8) + 1; #define TRISB1_bit BANKMASK(TRISB), 1 extern volatile __bit TRISB2 @ (((unsigned) &TRISB)*8) + 2; #define TRISB2_bit BANKMASK(TRISB), 2 extern volatile __bit TRISB3 @ (((unsigned) &TRISB)*8) + 3; #define TRISB3_bit BANKMASK(TRISB), 3 extern volatile __bit TRISB4 @ (((unsigned) &TRISB)*8) + 4; #define TRISB4_bit BANKMASK(TRISB), 4 extern volatile __bit TRISB5 @ (((unsigned) &TRISB)*8) + 5; #define TRISB5_bit BANKMASK(TRISB), 5 extern volatile __bit TRISB6 @ (((unsigned) &TRISB)*8) + 6; #define TRISB6_bit BANKMASK(TRISB), 6 extern volatile __bit TRISB7 @ (((unsigned) &TRISB)*8) + 7; #define TRISB7_bit BANKMASK(TRISB), 7 extern volatile __bit WR @ (((unsigned) &EECON1)*8) + 1; #define WR_bit BANKMASK(EECON1), 1 extern volatile __bit WREN @ (((unsigned) &EECON1)*8) + 2; #define WREN_bit BANKMASK(EECON1), 2 extern volatile __bit WRERR @ (((unsigned) &EECON1)*8) + 3; #define WRERR_bit BANKMASK(EECON1), 3 extern volatile __bit ZERO @ (((unsigned) &STATUS)*8) + 2; #define ZERO_bit BANKMASK(STATUS), 2 extern volatile __bit nPD @ (((unsigned) &STATUS)*8) + 3; #define nPD_bit BANKMASK(STATUS), 3 extern volatile __bit nRBPU @ (((unsigned) &OPTION_REG)*8) + 7; #define nRBPU_bit BANKMASK(OPTION_REG), 7 extern volatile __bit nTO @ (((unsigned) &STATUS)*8) + 4; #define nTO_bit BANKMASK(STATUS), 4 #endif // _PIC16F84A_H_ |
Burada, tüm register tanımlarının nasıl yapıldığına dikkat ediniz 🙂 Bizim Gömülü C yazı dizisini tamamen okuyan herkes için burada ne yapıldığını anlamak kolay olacaktır. Yine de anlamadığınız yerler olursa sorunuz 🙂 Şimdi merhaba dünya kodumuzdaki register’ları özellikle inceleyelim.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
... // Register: PORTB extern volatile unsigned char PORTB @ 0x006; #ifndef _LIB_BUILD asm("PORTB equ 06h"); #endif ... // Register: TRISB extern volatile unsigned char TRISB @ 0x086; #ifndef _LIB_BUILD asm("TRISB equ 086h"); #endif |
Burada demişler ki PORTB diye bir değişken yaratıyoruz ve bu bellekteki 0x006 numaralı adrese karşılık düşüyor. Yine benzer şekilde TRISB diye bir değişken yaratıyoruz ve bu bellekteki 0x086 numaralı adrese karşılık düşüyor. Bu bilgileri datasheet’teki bellek haritasından aynen doğrulamak mümkün. Zaten oradan bakıp yazmışlar 🙂 Ama ayıbetmişler… Çünkü tüm register’lar için kafadan değişken tanımlanmış. Bu, ödemesi ağır bir bedel. Ayrıca @ operatörü standart bir operatör değil. Dolayısıyla bu tanımlar, platform bağımlılığı yaratmakta ve kodların taşınabilirliğini negatif etkilemekte. Bunlara bir ayar çekmemiz kaçınılmaz duruyor 🙂
Şimdi, bu noktaya geldikten sonra, geleneğimizi uygulayalım ve yine header kullanmaksızın merhaba dünya kodumuzu yeniden yazalım 🙂
1 2 3 4 5 6 7 8 |
#define myTRISB (*( volatile unsigned char*)0x086) #define myPORTB (*( volatile unsigned char*)0x006) void main() { myTRISB = 0; myPORTB = 1; while(1); } |
Gördüğünüz üzere, kodda ne bir kütüphane kullandık, ne de bir değişken! Zaten kodumuzda, merhaba dünya kodundaki gereksiz şeylerin hiç birisi yok. Register tanımlarını da çok çok daha verimli şekilde yaptığımızı gururla huzurlarınıza sunarım 🙂 0 byte değişken kullanarak, yine ledimizi yaktık!
Buradan gerekli ibretleri lütfen alalım 🙂 Sanıyorum ki, “Abi PIC programlama yaparken Hi-Tech compiler CSS’den iyiymiş yaa” filan gibi geyiklere artık bolca gülersiniz. Hiçbirine ihtiyacımız yok, sadece tekerleği yeniden icad etmemek için onları araç olarak kullanıyoruz 😉
Sonraki yazımda bu temellerin üzerine, giriş çıkış işlemleriyle ilgili detaylı bir yapı inşa etmeyi planlıyorum. Dilerim sizler için de faydalı ve eğlenceli bir yazı olmuştur.
Şimdi devam…
Önceki Sayfa Sonraki Sayfa