Giriş: Neden CENGT:0) Anahtar Kelime Maddeleri gömülü Cildi
gömülü sistemler geliştirmede, donanım etkileşimi yazılım ve fiziksel dünya arasındaki köprüdür. Mikrokontrolörler ve işlemciler sensörleri ile iletişim kurar, eylemciler, hafızaya dayanıklı periferiler ve harici cihazlar kayıt ve hafıza adreslerini doğru bir şekilde değiştirebilirler. C programlama dili, böyle öngörülemeyen değişiklikleri işlemek için uygun değildir.
Bu makale, C kodunızda tam olarak nerede olduğunu ve neden C++ULLT:6 gibi modern dil ilerlemelerine rağmen vazgeçilmez olduğunu öğreneceksiniz.
Anahtar Kelime aslında ne yapıyor?
Dil seviyesinde, [[DÜDÜDÜDÜDÜDÜDÜSÜSÜSÜSÜSÜSÜSÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜN
- [FONT:0]Emit a load talimat[Döneticisinin bellek adresi her zaman değişken kaynağı kodunda okunur (öğrenmeler boyunca kayıt yaptırmaz).
- [FONT:0]Bir mağaza talimat [[Dönetici: 1 ) her seferinde bellek adresi yazılır (ödüntü veya geri dönüş yazmaz).
- [FONT=0) Tam sırayı ([Döneticileri) dikkate alarak, kaynakta yazılmış olarak bu değişkene erişim sağlar, diğer [[FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=)
Bu garantiler, C programı, dış olaylara dayanan hafızaya dayalı donanım kayıtları ile etkileşime girilmesi gerektiğinde tam olarak gerekli olan şeydir. Örneğin, UART statüsü kaydı, bir byte'nin okumaya hazır olduğunu gösterebilir, ancak derleme, bu anketin değerini asla değiştirmeyeceğini varsayabilir.
Compiler Optimizasyonu Problemleri Nasıl Oluşturur
Modern C derleyicileri (GCC, Clang, IAR, ARM Compiler) sürekli propagasyon, ölü kod ortadan kaldırılması, döngü değişmez kod hareketi ve kayıt dağılımı gibi agresif optimizasyonlar uyguluyor.
int *flag = (int *)0x20000000;
while (*flag == 0) {
// wait for hardware
}
[FONT=12], derleyici, döngüyü analiz edebilir ve C soyut makineye göre doğru bir şekilde kontrol edemez; ancak gömülü sistemlerde, dış bir ajan (talış periferi) tam olarak bunu yapar.
DeclaringFL: 15) {FLT:16 olarak, program gerçek donanım durumunu görmek için derleyiciyi her iterasyona yeni bir yüke konup getirir.
Ne zaman ve nerede kullanılır: Anahtar Kelime
[[TheD 18) anahtar kelime, mevcut iplik kapsamı dışında bağımsız bir aktör tarafından değiştirilebilen herhangi bir durumda uygulanmalıdır (veya ana yürütme yolu). Klasik kullanım koşulları şunlardır:
- Memory-mapped I/O kayıtları (peripheral kayıtları)
- Bir ISR ve ana döngü arasında paylaşılan değişkenler
- Açık metal veya RTOS ortamlarında birden çok konuya erişim sağlar (tavap ile - sadece atomiklik sağlamaz)
- DMA transferleri tarafından değiştirilmiş küresel değişkenler
- POSIX benzeri ortamlardaki sinyal eller
Memory-Mapped I /O Registers
Bu, C. Most mikrokontroller harita periferik kontrol ve statü kayıtlarını işlemcinin hafıza adresini kayıt altına almak için en yaygın kullanım durumudur. Örneğin, bir ARM Cortex-M MCU'da GPIO çıktı verileri kaydı adresi olarak yaşayabilir. Accessing it through a pointer tour not the işlemcinin hafıza adresini güncelleyebilir. Örneğin, her yazı mevcut giriş seviyesini yansıtır.
#define GPIOA_ODR ( (volatile uint32_t *) 0x40020014 )
#define GPIOA_IDR ( (volatile uint32_t *) 0x40020010 )
void toggle_led(void) {
*GPIOA_ODR ^= (1 << 5); // toggle bit 5 – compiler will generate a load-modify-store
}
[FONT: 9], derleyici birden çok yazar veya yeniden sipariş edebilir, glitches veya sessiz başarısızlıklara neden olabilir.
Interrupt Service Routines tarafından Değiştirilen Değişkenler
Bir ISR, ana döngünün okuduğu küresel bir değişkeni güncellediğinde, her iki erişim de aŞFLT:24'dir. Tipik örnekler: bir bloğa tırmanma, bir UART ISR'den bir tampon doldurma veya bir tampon doldurma.
volatile uint32_t system_tick = 0;
void SysTick_Handler(void) {
system_tick++; // ISR modifies this
}
void main_loop(void) {
while (1) {
uint32_t current_tick = system_tick; // main loop reads
// ...
}
}
Eğer bu kadar çok şey değil, bir de olsa, bu da, her zaman hafızaya en son değer kazandırarak, hiçbir zaman ISR tarafından yapılan artışları görmemiştir.
DMA ve Paylaşılan Hafıza
Doğrudan bellek Access (DMA) kontrolörleri, CPU müdahalesi olmadan periferik ve hafıza arasında verileri kopyalayabilir. Tipik bir model:
- CPU, bir ADC'den bir tampon doldurma için bir DMA transferini ayarlar.
- DMA kontrolörü bir hafıza tamponuna veri yazar.
- CPU, transfer tamamlandıktan sonra tamponu okur (bir bayrak ya da bir kesme kullanarak).
Eğer buffer basit bir dizi olarak ilan edilirse, derleme yazarı, verilerin CPU tarafından asla yazılmamış olduğuna inanabilir.Buffer (veya CPU'nun gerçek DMA-yazılı değerleri garanti etmek için) olarak ilan edilmelidir.
Örnek: Bir Donanım Durumu Kayıt Ol
Orijinal örneği daha gerçekçi bir senaryoya genişletelim - bir SPI işlemi için bir statü kaydı okuyarak tamamlamak için.
// Memory-mapped SPI peripheral registers
typedef struct {
volatile uint32_t CR; // control register
volatile uint32_t SR; // status register
volatile uint32_t DR; // data register
} SPI_TypeDef;
#define SPI1_BASE 0x40013000
#define SPI1 ((SPI_TypeDef *) SPI1_BASE)
void spi_send_byte(uint8_t data) {
// Wait until transmit buffer empty (bit 1 in SR set)
while ( !(SPI1->SR & (1 << 1)) ) {
// busy wait
}
// Write data to data register
SPI1->DR = data;
// Wait for transmission to complete (bit 7 in SR set)
while ( !(SPI1->SR & (1 << 7)) ) {
// busy wait
}
}
Çünkü, s.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.c.
[Kırkların ve Sınırların Ötesinde]
[[Dind|38|> Anahtar kelime güçlü ama genellikle yanlış anlaşılmalıdır. Birkaç önemli sınırlama tanınmalıdır:
Atomik Garantiler Yok
[FONT=FONT=FONT=0)[FONT=0))) {0|0|0|x|x x x x x x x x x x x x x x x x x x x x x x x x
Hiçbir Memory Ordering Garantileri
[FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=TRNT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=
Proper Synchronization için Substitute değil
Çok hazır ortamlarda (RTOS veya SMP), [[FONTD:48) paylaşılan değişkenler için yetersizdir. Birden çok konu aynı değişkeni okuyabiliyor ve doğru senkronizasyon olmadan (mutlu işlemler, semaphores veya atom operasyonları), hala yarış koşullarını ve tutarsız hafıza görüşlerini alabilirsiniz.
[FONT:0)[DÜT: 9)
Her küresel değişkende “sadece durumda” olarak akıp giden bir şekilde serpinmeye cazip geliyor, ancak bu karşıtlıklı kod optimizasyonundan derleyici, sarışın hafıza erişim döngülerine ve gerçek tasarım problemlerini gizleyebilir.
- Sadece dışsal değişiklik olmadan tek bir iplik içinde okuyan veya yazılı olan değişkenler.
- Değişkenin donanım veya ISR tarafından dokunmadığı performans-kampif döngüler.
- Birden çok CPU veya kesintiye uğratılmış bağlamlar dahil edildiğinde uygun atom operasyonları için bir yedek olarak.
- Yazılımın değiştiremeyeceği anlamına gelir, ancak yazılımlar yalnızca bir notu ile yapılır.
Gerçek Dünya Kodu: Interrupts ve Ping-Pong Buffers ile UART RX
İki tane tampon kullanan bir UART alıcı düşünün. ISR, diğerini ana döngü süreçleri sırasında bir tampona kadar yazar.Buffers'ın olması gereken bayraklar:
#define BUF_SIZE 64
volatile char buffer_a[BUF_SIZE];
volatile char buffer_b[BUF_SIZE];
volatile int active_buffer = 0; // 0 = buffer A, 1 = buffer B
volatile int bytes_received = 0;
void UART_IRQHandler(void) {
char data = UART->DR; // hardware register
if (active_buffer == 0) {
if (bytes_received < BUF_SIZE) {
buffer_a[bytes_received++] = data;
}
} else {
if (bytes_received < BUF_SIZE) {
buffer_b[bytes_received++] = data;
}
}
}
int main(void) {
while (1) {
if (bytes_received > 0) {
// Process data from active_buffer
// Swap buffers after processing
int current_buf = active_buffer;
char *data_ptr = (current_buf == 0) ? buffer_a : buffer_b;
int count = bytes_received;
// ... process data_ptr[0..count-1] ...
// Reset and switch
bytes_received = 0;
active_buffer = current_buf ^ 1;
}
}
}
Tüm buffers ve kontrol değişkenleri ►D:57'dir, böylece ana döngü ISR tarafından yazılmış en son verileri görür. Not: Burada bile, kritik bölümler etrafında kesintiler olan bir risk yeterli olabilir.Daha karmaşık sistemler için, atom işlemleri veya kilitlemesiz teknikler.
Compiler-Specific Thinkations
Farklı derleyiciler, kenar durumlarda biraz farklı tedavi edebilir. C standart (C11, bölüm 6.7.3) minimum gereksinimleri belirtir, ancak derleyiciler daha güçlü veya daha zayıf garantiler sunabilir:
- [FONT=0)GCC/Clang:[Dönetici:[Dönetici: 6) Standart olarak, başka bir şekilde erişimleri sipariş etmezler, ancak onlar üzerinde sipariş edilmezler.
- [FONT:0)IAR Gömülü İşbench:[Dönetici: 1 ) Ek semantik sağlar: varsayılan olarak, tüm erişimler için [[Dönetici:66) nesne büyüklüğü için atom olarak muamele edilir (32 bits'e kadar) ve siparişi verilirse tehlikeli olabilir.
- [FONT:0)ARM Compiler (armcc): ) GCC'ye benzer.
- [FONT:0]MSVC:[[Dönemli, MSVC:67] Okunma ve yazar için yazma / sürüm semantics elde etti, ancak VS 2015 standart uygunluk modu ile başlayın ([DD:68) Bu siparişi ortadan kaldırır.
Her zaman derleme belgenize danışın ve doğru davranışın kritik olduğu zaman üretilen montajı test edin.
Alternatifler ve Modern Yaklaşımlar
[[70|Seseç kayıtları ve ISR iletişim için gerekli olsa da, bazı kullanım vakaları yeni dil özellikleri tarafından daha iyi hizmet edilir:
| Use Case | Recommended Tool |
|---|---|
| Reading/writing memory-mapped I/O | volatile qualified pointer |
| Variable shared between ISR and main loop (single core) | volatile + disabling interrupts when accessing multi-word variables |
| Variable shared between multiple threads (SMP, RTOS) | _Atomic (C11) or compiler intrinsics + memory barriers |
| Flag or status bit touched by both threads and ISRs | stdatomic.h with atomic_flag or atomic_int |
| DMA buffers written by peripheral, read by CPU | volatile qualified pointer (or ensure compiler doesn’t optimize via proper barriers) |
C++'da, [[Dönem:78] şablonu hem atomik hem de hafıza siparişi sağlar. ancak donanım kaydı erişim için, [[Üye:79| hala standart modeldir - C++20'ninİLFLT:80) hafızaya kadar değiştirmez.
Ortak Hatalar ve Them'dan Nasıl Kaçırmak
Donanımlara Kullanım Edilmesi:81)
Ortak bir hata, işaretli tip asurFLT:82 olarak nitelendirilmeden bir donanım kaydına işaret eder:
int *reg = (int *)0x40000000; // WRONG – not volatile
while (*reg == 0) ; // may be optimized
Doğru:
volatile int *reg = (volatile int *)0x40000000; // read volatile-qualified
Kendini Pointer'in kendisini de öylece kınayan:85.
Eğer noktanın kendisini donanım (rare) tarafından doğrulanabilir olması istiyorsanız, bunu yapabilirsiniz.(bu)- ama bu, puanlayıcı değişkenin değişebileceğini, kayıt erişimi için puan vermediğini anlamına gelir.
Bir Üste Bir Bakış:88) Bir Eleştirel Bölümde Bir Kez Zorlamasız Bir Bakış
Diyelim ki, 8bit MCU'da 64 bit karşı bir karşıtınız. ana döngü yüksek ve düşük bir tane okur. Bir ISR, iki okur tarafından değeri güncelleyebilir, bozuk bir değer verir..:90) Burada yardım etmez - atomik erişim mekanizması kullanmalısınız.
Özet Özet Özet Özet Özet Özet Özet Özet Özet Özet
C'deki anahtar kelime, gömülü sistemlerde doğru donanım etkileşimi sağlamak için temel bir araçtır.Bu, gerekli okumaları optimize etmek ve dış olaylar tarafından değiştirilebilecek bellek yerlerine yazın - donanım kayıtları, servis rutinleri veya DMA'lar için temel bir araçtır. ancak,FLT:92'nin gümüş mermi sağlamamasını engeller: atomiklik sağlamaz, bellek siparişini dışsal olaylarla değiştiremez ve çok hazır ortamlardaki uygun senkronizasyonu yerine getiremez.
Herhangi bir gömülü geliştirici için ustalık:93) bir geçişin bir parçası. derlemenin davranışı, donanım hafıza haritası ve mimarlık hafıza modeli, ve tüm ince, sert-tabug başarısızlıklarından kaçınacaksınız.
Ayrıca okuma
- [FONT:0) C referansı: [[FONT:94||Spektif (cppreference.com)).
- [FONT:0)GCC belgesi: Volatiles[Dönem: 1 )
- [FONT=0)Embedded.com: [[FONT:95|95| Neden ve Nasıl Kullanılır).
- [FONT:0)ARM Compiler: 0:96|Dönlendirici[Dönetici: 1 )