Bu tuhaf görünmüyor. Normal MCU kodu aslında böyle görünüyor.
Burada sahip olduğunuz, bellek eşlemeli çevre birimleri kavramına bir örnektir . Temel olarak, MCU donanımının kendisine atanan MCU'nun SRAM adres alanında özel konumları vardır. Bu adreslere yazarsanız, n adresine yazılan bayt bitleri , periferik m'nin davranışını kontrol eder .
Temel olarak, bazı bellek bankalarının kelimenin tam anlamıyla SRAM hücresinden donanıma giden çok az kablosu vardır. Bu bite bu bayta "1" yazarsanız, bu SRAM hücresini mantıksal bir yüksekliğe ayarlar ve bu da donanımın bir kısmını açar.
MCU'nun başlıklarına bakarsanız, <-> adres eşlemelerinin büyük büyük tabloları vardır. TCCR1B
Vb ... gibi şeyler derleme zamanında bu şekilde çözülür.
Bu bellek haritalama mekanizması MCU'larda son derece yaygın olarak kullanılmaktadır. Arduino'daki ATmega MCU, PIC, ARM, MSP430, STM32 ve STM8 MCU serilerinin yanı sıra hemen tanımadığım birçok MCU kullanıyor.
Arduino kodu, MCU kontrol kayıtlarına dolaylı olarak erişen işlevlerle garip şeylerdir. Bu biraz "daha hoş" görünmekle birlikte, aynı zamanda çok daha yavaştır ve daha fazla program alanı kullanır.
Gizemli sabitlerin hepsi ATmega328P veri sayfasında çok ayrıntılı olarak açıklanmıştır , bu da bir arduino üzerinde oklüzal olarak geçiş yapan pimlerden daha fazlasını yapmak istiyorsanız gerçekten okumalısınız.
Yukarıda bağlantı verilen veri sayfasından alıntılar seçin:
Yani, örneğin, TIMSK1 |= (1 << TOIE1);
bit ayarlar TOIE1
içinde TIMSK1
. Bu, ikili dosya 1'i ( 0b00000001
) TOIE1
bitler tarafından sola kaydırarak TOIE1
, bir başlık dosyasında 0 olarak tanımlanarak elde edilir. Bu, daha sonra bu değeri TIMSK1
biraz yüksek bir değere ayarlayan geçerli değerine bit yönlü ORed olur .
Bit 0 belgelerine bakıldığında TIMSK1
, bunun olarak tarif edilir görebilirsiniz
Bu bit bir taneye yazıldığında ve Durum Kaydındaki I bayrağı ayarlandığında (genel olarak kesmeler etkinleşir), Zamanlayıcı / Sayaç1 Taşma kesmesi etkinleştirilir. TIFR1'de bulunan TOV1 Bayrağı ayarlandığında karşılık gelen Kesme Vektörü (Bkz. ”Kesintiler” sayfa 57) yürütülür.
Diğer tüm satırlar aynı şekilde yorumlanmalıdır.
Bazı notlar:
Bunun gibi şeyler de görebilirsiniz TIMSK1 |= _BV(TOIE1);
. _BV()
Bir olduğu yaygın olarak kullanılan makro aslen uygulanması libc AVR . daha iyi okunabilirlik avantajıyla _BV(TOIE1)
fonksiyonel olarak aynıdır (1 << TOIE1)
.
Ayrıca, gibi satırlar da görebilirsiniz: TIMSK1 &= ~(1 << TOIE1);
veya TIMSK1 &= ~_BV(TOIE1);
. Bu zıt işlevi vardır TIMSK1 |= _BV(TOIE1);
o ki, getopts bit TOIE1
içinde TIMSK1
. Bu, tarafından üretilen bit maskesini alarak, _BV(TOIE1)
üzerinde bitsel bir NOT işlemi gerçekleştirerek ( ~
) ve daha sonra TIMSK1
bu NOTed değeriyle (0b11111110) ANDing yaparak elde edilir.
Not olduğunu bu durumlarda Sonuç olarak, gibi şeylerin değeri (1 << TOIE1)
veya _BV(TOIE1)
tamamen çözümlenir derleme zamanında onlar işlevsel basit sabite azaltmak ve dolayısıyla zamanında bilgi işlem için hiçbir yürütme zaman alabilir bu yüzden.
Düzgün yazılmış kod genellikle , kayıtların ne yapmak için atandığını ayrıntılı olarak kodla birlikte satır içinde yorumlar içerir. İşte son zamanlarda yazdığım oldukça basit bir yumuşak SPI rutini:
uint8_t transactByteADC(uint8_t outByte)
{
// Transfers one byte to the ADC, and receives one byte at the same time
// does nothing with the chip-select
// MSB first, data clocked on the rising edge
uint8_t loopCnt;
uint8_t retDat = 0;
for (loopCnt = 0; loopCnt < 8; loopCnt++)
{
if (outByte & 0x80) // if current bit is high
PORTC |= _BV(ADC_MOSI); // set data line
else
PORTC &= ~(_BV(ADC_MOSI)); // else unset it
outByte <<= 1; // and shift the output data over for the next iteration
retDat <<= 1; // shift over the data read back
PORTC |= _BV(ADC_SCK); // Set the clock high
if (PINC & _BV(ADC_MISO)) // sample the input line
retDat |= 0x01; // and set the bit in the retval if the input is high
PORTC &= ~(_BV(ADC_SCK)); // set clock low
}
return retDat;
}
PORTC
PORTC
ATmega328P içindeki çıkış pinlerinin değerini kontrol eden kayıttır. PINC
yazmaçtır girdi değerleri PORTC
mevcuttur. Temel olarak, digitalWrite
ya da digitalRead
işlevlerini kullandığınızda bu gibi şeyler dahili olarak gerçekleşir . Bununla birlikte, arduino "pin numaralarını" 50 saat döngü alanında bir yere götüren gerçek donanım pin numaralarına dönüştüren bir arama işlemi vardır. Tahmin edebileceğiniz gibi, hızlı gitmeye çalışıyorsanız, sadece 1 gerektiren bir işlemde 50 saat döngüsünü boşa harcamak biraz saçma.
Yukarıdaki fonksiyon muhtemelen 8 biti transfer etmek için 100-200 saat döngüsü alanında bir yere sahiptir. Bu 24 pin yazma ve 8 okuma gerektirir. Bu, digital{stuff}
fonksiyonları kullanmaktan çok, çok daha hızlıdır .