PIC mikrodenetleyicilerinde çoklu görev


17

Bu günlerde çoklu görev önemlidir. Mikrodenetleyicilerde ve gömülü programlamada bunu nasıl başarabileceğimizi merak ediyorum. Bir PIC mikrodenetleyicisine dayanan bir sistem tasarlıyorum. Ürün yazılımını C kullanarak MplabX IDE'de tasarladım ve sonra C # kullanarak Visual Studio'da bir uygulama tasarladım.

Paralel görevleri uygulamak için masaüstünde C # programlama iş parçacıkları kullanmaya alıştığımdan, benim mikrodenetleyici kodunda aynı şeyi yapmanın bir yolu var mı? MplabX IDE sağlar pthreads.hancak uygulama olmayan bir saplamadır . FreeRTOS desteği olduğunu biliyorum ama bunu kullanarak kodunuzu daha karmaşık hale getirir. Bazı forumlar, kesintilerin çoklu görev olarak da kullanılabileceğini söylüyor, ancak kesintilerin iş parçacıklarına eşdeğer olduğunu düşünmüyorum.

Bir UART'a bazı veriler gönderen bir sistem tasarlıyorum ve aynı zamanda (kablolu) ethernet üzerinden bir web sitesine veri göndermesi gerekiyor. Bir kullanıcı web sitesi üzerinden çıkışı kontrol edebilir, ancak çıkış 2-3 saniyelik bir gecikmeyle açılır / kapanır. İşte karşılaştığım sorun bu. Mikrodenetleyicilerde çoklu görevler için herhangi bir çözüm var mı?


İş parçacıkları yalnızca bir işletim sistemi çalıştıran işlemcilerde kullanılabilir, çünkü iş parçacıkları işlemin bir parçasıdır ve işlemler yalnızca işletim sistemlerinde kullanılır.
TicTacToe

@Zola evet haklısın. Peki denetleyiciler durumunda ne olacak?
Uçak


1
Neden gerçek çoklu göreve ihtiyaç duyduğunuzu açıklayabilir ve yazılımınızı bir round-robin görev yaklaşımı veya bir select () döngüsüne veya benzeri bir şeye dayanarak makul şekilde uygulayamaz mısınız?
whatsisname

2
Daha önce de söylediğim gibi uart'a veri gönderiyorum ve aynı zamanda ethernet'e veri gönderiyorum. Bunun dışında, zamanla birlikte SD karttaki verileri de kaydetmem gerekiyor, bu yüzden evet DS1307 RTC ve EEPROM da dahil. Şimdiye kadar sadece 1 UART'ım var ama birkaç gün sonra 3 UART modülünden veri gönderip alacağım. Web sitesi ayrıca uzak bir yerde kurulu 5 farklı sistemden veri alacaktır. Bunların hepsi paralel olmalı ama doğru değil paralel değil birkaç saniye gecikmeli. !
Uçak

Yanıtlar:


20

Önleyici ve işbirlikçi olmak üzere iki ana çok görevli işletim sistemi türü vardır. Her ikisi de birden fazla görevin sistemde tanımlanmasına izin verir, fark görev değiştirmenin nasıl çalıştığıdır. Tabii ki tek çekirdekli işlemci ile bir seferde sadece bir görev yürütülüyor.

Her iki çoklu görevli işletim sistemi türü, her görev için ayrı bir yığın gerektirir. Yani bu iki şeyi ifade eder: birincisi, işlemcinin yığınların RAM içinde herhangi bir yere yerleştirilmesine izin vermesi ve bu nedenle yığın işaretçisini (SP) hareket ettirmek için talimatlara sahip olması - yani düşük uçta olduğu gibi özel amaçlı bir donanım yığını yoktur. PIC en. Bu PIC10, 12 ve 16 serilerini terk eder.

Neredeyse tamamen C'ye bir işletim sistemi yazabilirsiniz, ancak SP'nin hareket ettiği görev değiştirici montajda olmalıdır. Çeşitli zamanlarda PIC24, PIC32, 8051 ve 80x86 için görev değiştiriciler yazdım. Bağırsaklar, işlemcinin mimarisine bağlı olarak oldukça farklıdır.

İkinci gereklilik, birden fazla yığın sağlamak için yeterli RAM olmasıdır. Genellikle bir yığın için en az birkaç yüz bayt gerekir; ancak görev başına yalnızca 128 bayt olsa bile, sekiz yığın 1K bayt RAM gerektirir - yine de her görev için aynı boyut yığınını ayırmanız gerekmez. Geçerli görevi yerine getirmek için yeterli yığına ve iç içe geçmiş alt yordamlarına yapılan tüm çağrılara ihtiyaç duyduğunuzu, ancak ne zaman gerçekleşeceğini asla bilemeyeceğiniz için bir kesinti çağrısı için alan yığınının gerektiğini unutmayın.

Her görev için ne kadar yığın kullandığınızı belirlemek için oldukça basit yöntemler vardır; örneğin, tüm yığınları belirli bir değere (örneğin 0x55) başlatabilir ve sistemi bir süre çalıştırabilir ve sonra belleği durdurabilir ve inceleyebilirsiniz.

Ne tür PIC'ler kullanmak istediğinizi söylemiyorsunuz. PIC24'lerin ve PIC32'lerin çoğunun çok görevli bir işletim sistemi çalıştırmak için bolca yeri olacaktır; PIC18'in (RAM'de yığınları olan tek 8 bit PIC) maksimum RAM boyutu 4K'dır. Yani bu oldukça havalı.

İşbirlikli çoklu görevle (ikisinin daha basit), görev değiştirme yalnızca görev kontrolünü işletim sistemine "bıraktığında" yapılır. Bu, görevin, bir G / Ç isteği veya zamanlayıcı çağrısı gibi bekleyeceği bazı işlevleri gerçekleştirmek için bir işletim sistemi yordamı çağırması gerektiğinde gerçekleşir. Bu, işletim sisteminin yığınları değiştirmesini kolaylaştırır, çünkü tüm kayıtların ve durum bilgilerinin kaydedilmesi gerekli değildir, SP sadece başka bir göreve geçirilebilir (çalışmaya hazır başka görev yoksa, boş bir yığın verilen kontrol). Geçerli görevin bir işletim sistemi çağrısı yapması gerekmiyor, ancak bir süredir çalışıyorsa, sistemin yanıt vermesini sağlamak için kontrolü gönüllü olarak bırakması gerekir.

İşbirlikçi çoklu görev ile ilgili sorun, görev asla kontrolü bırakmazsa, sistemi domuz tutabilir. Yalnızca o ve denetim verilen kesinti yordamları çalışabilir, böylece işletim sistemi kilitli görünecektir. Bu, bu sistemlerin "işbirlikçi" boyutudur. Yalnızca bir görev anahtarı gerçekleştirildiğinde sıfırlanan bir bekçi köpeği zamanlayıcısı uygulanırsa, bu hatalı görevleri yakalamak mümkündür.

Windows 3.1 ve öncesi kooperatif işletim sistemleriydi, bu yüzden performansları bu kadar büyük değildi.

Önleyici çoklu görevlerin uygulanması daha zordur. Burada, görevleri manuel olarak kontrolünden vazgeçmek gerekmez, bunun yerine her göreve maksimum çalışma süresi verilebilir (10 ms diyelim) ve sonra varsa bir sonraki çalıştırılabilir göreve bir görev anahtarı gerçekleştirilir. Bunun için bir görevin keyfi olarak durdurulması, tüm durum bilgilerinin kaydedilmesi ve ardından SP'nin başka bir göreve geçirilmesi ve başlatılması gerekir. Bu, görev değiştiriciyi daha karmaşık hale getirir, daha fazla yığın gerektirir ve sistemi biraz yavaşlatır.

Hem işbirlikçi hem de önleyici çoklu görev için, çalışan görevi geçici olarak önleyecek herhangi bir zamanda kesintiler meydana gelebilir.

Supercat'in bir yorumda belirttiği gibi, kooperatif çoklu görevinin avantajlarından biri, kaynakların paylaşılmasının daha kolay olmasıdır (örn. Çok kanallı bir ADC gibi donanım veya bağlantılı bir listeyi değiştirmek gibi bir yazılım). Bazen iki görev aynı kaynağa aynı anda erişmek isteyebilir. Önleyici zamanlama ile, işletim sisteminin bir görevi kullanarak bir görevin ortasında görevleri değiştirmesi mümkün olacaktır. Bu nedenle , başka bir görevin gelmesini ve aynı kaynağa erişmesini önlemek için kilitler gereklidir. İşbirlikçi çoklu görev ile bu gerekli değildir, çünkü görev kendisini ne zaman işletim sistemine geri bırakacağını kontrol eder.


3
İşbirlikçi çoklu görevin bir avantajı, çoğu durumda kaynaklara erişimi koordine etmek için kilitlerin kullanılmasına gerek olmamasıdır. Görevlerin, kontrolü bıraktıkları her zaman kaynakları paylaşılabilir bir durumda bırakmalarını sağlamak yeterli olacaktır. Eğer bir görev başka bir görevin ihtiyaç duyduğu bir kaynak üzerinde kilit tutarken bir görev kapatılabilirse, önleyici çoklu görev çok daha karmaşıktır. Bazı durumlarda, ikinci görev, kooperatif sistemi altında olduğundan daha uzun süre engellenebilir, çünkü kilidi tutan görev sistemin görevini
devralıyordu

1
... (önleyici sistemde) kilidi gerektirecek eylemi bitirmeye yönelik tam kaynaklar, böylece korunan nesneyi ikinci görev için kullanılabilir hale getirmek.
supercat

1
İşbirlikli çoklu görevliler disiplin gerektirse de, zamanlama gereksinimlerinin yerine getirilmesini sağlamak bazen bir kooperatif çoklu görev altında önleyici olandan daha kolay olabilir. Bir görev anahtarı üzerinde çok az sayıda kilit tutulması gerekeceğinden, görevlerin teslim edilmeden 10 ms'den fazla gitmemesi gereken beş görevli yuvarlak görevli bir görev anahtarı sistemi, "Görev X acilen olursa, çalıştırılması gerekiyor, bir sonraki çalıştır ", X görevinin çalışmaya başlamadan önce sinyal verdikten sonra asla 10ms'den fazla beklemesine gerek kalmayacaktır. Buna karşılık, bir görev X için hangi kilide ihtiyaç
duyarsa

1
... gerekecek ama serbest bırakmadan önce önleyici bir anahtarlayıcı tarafından kapatılıyor, X, CPU zamanlayıcı ilk görevi çalıştırmak için işe yarayana kadar yararlı bir şey yapamayabilir. Zamanlayıcı, öncelikli ters çevrmeyi tanımak ve işlemek için mantık içermedikçe, ilk görevin işini bitirmesine ve kilidi serbest bırakmasına izin verilmesi biraz zaman alabilir. Bu tür problemler çözümsüz değildir, ancak bunları çözmek kooperatif bir sistemde önlenebilecek çok karmaşıklık gerektirir. Kooperatif sistemleri bir gotcha dışında harika çalışıyor: ...
supercat

3
Süreklilikleri kodlarsanız, kooperatifte birden fazla desteğe ihtiyacınız yoktur. Temelde, kodunuz işlevlere ayrılmıştır void foo(void* context), denetleyici mantığı (çekirdek) sıranın bir işaretçisini ve işlev işaretçisi çiftini çeker ve birer birer çağırır. Bu işlev, değişkenlerini ve benzerlerini depolamak için bağlamı kullanır ve daha sonra kuyruğa bir süreklilik ekleyebilir. Bu işlev, diğer görevlerin CPU'daki anlarına izin vermek için hızlı bir şekilde geri dönmelidir. Bu, yalnızca tek bir yığın gerektiren olay tabanlı bir yöntemdir.
cırcır ucube

16

Diş çekme bir işletim sistemi tarafından sağlanır. Gömülü dünyada genellikle bir işletim sistemimiz yoktur ("çıplak metal"). Bu, aşağıdaki seçenekleri bırakır:

  • Klasik ana sorgulama döngüsü. Ana işlevinizde bir görev (1) vardır ve bu görev 1'i yapar, ardından görev 2'yi yapar ...
  • Ana döngü + ISR bayrakları: Kritik zaman işlevini yerine getiren ve daha sonra görevin servise ihtiyaç duyduğu bir bayrak değişkeni aracılığıyla ana döngüyü uyaran bir ISR'niz var. Belki de ISR dairesel bir arabellek içine yeni bir karakter yerleştirir ve sonra ana döngüye veri hazır olduğunda verileri işlemesini söyler.
  • Tüm ISR: Buradaki mantığın çoğu ISR'den yürütülür. Birden fazla öncelik seviyesine sahip bir ARM gibi modern bir kontrolörde. Bu, güçlü bir "iş parçacığı benzeri" şema sağlayabilir, ancak hata ayıklamak için kafa karıştırıcı olabilir, bu nedenle yalnızca kritik zamanlama kısıtlamaları için ayrılmalıdır.
  • RTOS: Bir RTOS çekirdeği (bir zamanlayıcı ISR tarafından kolaylaştırılır), birden çok yürütme iş parçacığı arasında geçiş yapılmasına izin verebilir. FreeRTOS'tan bahsettiniz.

Uygulamanız için işe yarayacak yukarıdaki şemaların en basitini kullanmanızı tavsiye ederim. Açıkladığınız şeyden, paketleri üreten ve dairesel tamponlara yerleştiren ana döngü olurdu. Daha sonra arabellek gönderilinceye kadar önceki bayt gönderildiğinde tetiklenen bir UART ISR tabanlı sürücüye sahip olun, sonra daha fazla arabellek içeriği bekler. Ethernet için de benzer yaklaşım.


3
Bu çok yararlı bir cevaptır, çünkü sorunun kökünü ele alır (çözüm olarak iş parçacıkları yerine küçük bir gömülü sistemde çoklu görev nasıl yapılır). Orijinal soruya nasıl uygulanabileceğine dair bir paragraf, belki de senaryo için her birinin artılarını ve eksilerini de içeren mükemmel olurdu.
David

8

Herhangi bir tek çekirdekli işlemcide olduğu gibi, gerçek yazılım çoklu görev yapmak mümkün değildir. Bu nedenle, birden fazla görev arasında tek yönlü geçiş yapmaya dikkat etmelisiniz. Farklı RTOS bununla ilgileniyor. Onlar bir zamanlayıcı var ve bir sistem kene dayalı onlar size çoklu görev yeteneği vermek için farklı görevler arasında geçiş yapacak.

Bunu yapmakla ilgili kavramlar (bağlam tasarrufu ve geri yükleme) oldukça karmaşıktır, bu yüzden bunu manuel olarak yapmak muhtemelen zor olacak ve kodunuzu daha karmaşık hale getirecektir ve daha önce hiç yapmadığınız için içinde hatalar olacaktır. Buradaki tavsiyem tıpkı FreeRTOS gibi test edilmiş bir RTOS kullanmak olacaktır.

Kesmelerin bir düzeyde çoklu görev sağladığını söylemiştiniz. Bu biraz doğru. Kesme, mevcut programınızı herhangi bir noktada kesintiye uğratır ve kodu orada yürütür, düşük öncelikli 1 göreviniz ve zamanlayıcının bir zaman diliminde biten yüksek öncelikli başka bir görevinizle karşılaştırılabilir.

Böylece, tekrar eden bir zamanlayıcı için UART üzerinden birkaç paket gönderecek bir kesme işleyicisi yazabilirsiniz, sonra programınızın geri kalanının birkaç milisaniye çalışmasını ve sonraki birkaç baytı göndermesini sağlayalım. Bu şekilde bir çeşit sınırlı görev yapabileceksiniz. Ama aynı zamanda kötü bir şey olabilecek oldukça uzun bir kesintiniz olacak.

Tek çekirdekli bir MCU'da aynı anda birden fazla görev yapmanın tek gerçek yolu, çekirdekten bağımsız olarak çalıştıklarında DMA ve çevre birimlerini kullanmaktır (DMA ve MCU aynı veriyolunu paylaşır, bu nedenle her ikisi de etkindir). DMA, baytları UART'a karıştırırken, çekirdeğiniz işleri ethernet'e göndermekte özgürdür.


2
teşekkürler, DMA ilginç geliyor. Kesinlikle arayacağım.!
Uçak

Tüm PIC serilerinde DMA yoktur.
Matt Young

1
PIC32 kullanıyorum;)
Uçak 16

6

Diğer cevaplar zaten en çok kullanılan seçenekleri (ana döngü, ISR, RTOS) açıkladı. İşte uzlaşma olarak başka bir seçenek: Protothreads . Temelde bir RTOS'u "taklit etmek" için ana döngü ve bazı C makrolarını kullanan dişler için çok hafif bir lib'dir. Tabii ki tam bir işletim sistemi yok, ancak "basit" iş parçacıkları için yararlı olabilir.


Windows için kaynak kodunu nereden indirebilirim? Bence sadece linux için kullanılabilir.!
Uçak

@CZAbhinav İşletim sisteminden bağımsız olmalı ve en son indirmeyi buradan alabilirsiniz .
erebos

Şu anda windows'dayım ve MplabX kullanarak, burada yararlı olduğunu düşünmüyorum. Her neyse teşekkürler.!
Uçak

Prototipleri duymadım, ilginç bir teknik gibi geliyor.
Arsenal

@CZAbhinav Neden bahsediyorsun? C kodu ve işletim sisteminizle ilgisi yok.
Matt Young

3

Minimum zaman dilimli RTOS için temel tasarımım birkaç mikro ailede çok fazla değişmedi. Temelde bir durum makinesini süren bir zamanlayıcı kesintisidir. Ana hizmetteki switch deyimi kullanıcı görevleri iken kesme hizmeti rutini OS çekirdeğidir. Aygıt sürücüleri, G / Ç kesintileri için kesme servis yordamlarıdır.

Temel yapı aşağıdaki gibidir:

unsigned char tick;

void interrupt HANDLER(void) {
    device_driver_A();
    device_driver_B();
    if(T0IF)
    {
        TMR0 = TICK_1MS;
        T0IF = 0;   // reset timer interrupt
        tick ++;
    }
}

void main(void)
{
    init();

    while (1) {
        // periodic tasks:
        if (tick % 10 == 0) { // roughly every 10 ms
            task_A();
            task_B();    
        }
        if (tick % 55 == 0) { // roughly every 55 ms
            task_C();
            task_D();    
        }

        // tasks that need to run every loop:
        task_E();
        task_F();
    }
}

Bu temelde işbirlikçi bir çoklu görev sistemidir. Görevler asla sonsuz döngüye girmeyecek şekilde yazılır, ancak umursamıyoruz çünkü görevler bir olay döngüsünde çalışır, böylece sonsuz döngü örtük olur. Bu, javascript veya go gibi olaya yönelik / engellenmeyen dillere benzer bir programlama stilidir.

RC verici yazılımımda bu mimari tarzının bir örneğini görebilirsiniz (evet, aslında RC uçakları uçmak için kullanıyorum, bu yüzden uçaklarımı çarpmamı ve potansiyel olarak insanları öldürmemi önlemek için biraz güvenlik açısından kritik öneme sahip): https://github.com / slebetman / pic-txmod . Temel olarak 3 görevi vardır - durum bilgisi olan aygıt sürücüleri (ppmio sayfalarına bakın) olarak uygulanan 2 gerçek zamanlı görev ve karıştırma mantığını uygulayan 1 arka plan görevi. Temelde web sunucunuza benzer, çünkü 2 G / Ç iş parçacığı vardır.


1
Gerçekten bu 'kooperatif çoklu görev' olarak adlandırmayacağım, çünkü bu, birden fazla şey yapmak zorunda olan diğer mikrodenetleyici programlardan çok farklı değildir.
whatsisname

2

Sorunun özellikle gömülü bir RTOS kullanımını sorduğunu takdir etsem de, bana sorulan daha geniş sorunun "gömülü bir platformda çoklu görevlerin nasıl gerçekleştirileceği" olduğu ortaya çıkıyor.

En azından şimdilik gömülü bir RTOS kullanmayı unutmanızı şiddetle tavsiye ederim. Bunu öneriyorum çünkü önce basit görev zamanlayıcıları ve durum makinelerinden oluşan son derece basit programlama teknikleriyle görev 'eşzamanlılığını' nasıl elde edeceğinizi öğrenmenin gerekli olduğunu düşünüyorum.

Kavramı son derece kısaca açıklamak için, yapılması gereken her iş modülünün (yani her 'görev'), o modülün bazı şeyleri yapması için periyodik olarak çağrılması gereken ('işaretli') özel bir işlevi vardır. Modül kendi mevcut durumunu korur. Daha sonra modül işlevlerini çağıran bir ana sonsuz döngü (zamanlayıcı) vardır.

Ham illüstrasyon:

for(;;)
{
    main_lcd_ui_tick();
    networking_tick();
}


...

// In your LCD UI module:
void main_lcd_ui_tick(void)
{
    check_for_key_presses();
    update_lcd();
}

...

// In your networking module:
void networking_tick(void)
{
    //'Tick' the TCP/IP library. In this example, I'm periodically
    //calling the main function for Keil's TCP/IP library.
    main_TcpNet();
}

Bir ana zamanlayıcı döngüsünden periyodik olarak ana durum makine işlevlerini periyodik olarak çağırdığınız tek iş parçacıklı programlama yapısı gömülü programlamada her yerde bulunur ve bu yüzden OP'yi doğrudan kullanmaya başlamadan önce ilk olarak tanıdık ve rahat olmasını teşvik ederim RTOS görevleri / iş parçacıkları.

Donanım LCD arayüzü, dahili web sunucusu, e-posta istemcisi, DDNS istemcisi, VOIP ve diğer birçok özelliğe sahip bir tür gömülü cihaz üzerinde çalışıyorum. Bir RTOS (Keil RTX) kullanmamıza rağmen, kullanılan bireysel iş parçacığı (görev) sayısı çok azdır ve 'çoklu görev'in çoğu yukarıda açıklandığı gibi elde edilir.

Bu konsepti gösteren birkaç kütüphane örneği vermek için:

  1. Keil ağ kütüphanesi. Tüm TCP / IP yığını tek iş parçacıklı çalıştırılabilir; TCP / IP yığınını ve kitaplıktan derlediğiniz diğer ağ seçeneklerini (örneğin, web sunucusu) yineleyen main_TcpNet () öğesini düzenli olarak çağırırsınız. Bkz. Http://www.keil.com/support/man/docs/rlarm/rlarm_main_tcpnet.htm . Kuşkusuz, bazı durumlarda (muhtemelen bu cevabın kapsamı dışında), iplikleri kullanmaya faydalı veya gerekli olmaya başladığı bir noktaya ulaşırsınız (özellikle BSD soketlerini bloke ediyorsanız). (Ayrıca not: Yeni V5 MDK-ARM aslında özel bir Ethernet iş parçacığı oluşturur - ama ben sadece bir örnek sunmaya çalışıyorum.)

  2. Linphone VOIP kütüphanesi. Bağlantı kitaplığının kendisi tek iş parçacıklıdır. iterate()İşlevi yeterli bir aralıkta çağırırsınız . Bkz. Http://www.linphone.org/docs/liblinphone-javadoc/org/linphone/core/LinphoneCore.html#iterate () . (Biraz kötü bir örnek, çünkü bunu gömülü bir Linux platformunda kullandım ve linphone'un bağımlılık kütüphaneleri şüphesiz konuları doğurdu, ama yine de bir noktayı göstermek için.)

OP tarafından özetlenen belirli soruna geri dönersek, sorun, UART iletişiminin bazı ağlarla (TCP / IP üzerinden paket iletimi) aynı zamanda gerçekleşmesi gerektiği gibi görünüyor. Aslında hangi ağ kütüphanesini kullandığınızı bilmiyorum, ancak sık sık çağrılması gereken bir ana işlevi olduğunu varsayıyorum. UART veri iletimi / alımı ile ilgilenen kodunuzu, benzer şekilde yapılandırılmak üzere, bir ana işleve periyodik çağrılarla yinelenebilen bir durum makinesi olarak yazmanız gerekir.


2
Bu güzel açıklama için teşekkürler, mikroçip tarafından sağlanan TCP / IP kütüphanesini kullanıyorum ve çok büyük karmaşık bir kod. Bir şekilde parçalara ayırmayı ve gereksinimlerime göre kullanılabilir yapmayı başardım. Kesinlikle bir yaklaşımınızı deneyeceğim.!
Uçak

Eğlenin :) Bir RTOS kullanmak kesinlikle birçok durumda hayatı kolaylaştırır. Benim görüşüme göre, bir iş parçacığı (görev) kullanmak, programlama görevini bir anlamda çok daha kolay hale getirir, çünkü görevinizi bir durum makinesine bölmek zorunda kalmazsınız. Bunun yerine, görev kodunuzu sadece C # programlarınızda yaptığınız gibi yazın, görev kodunuz var olan tek şeymiş gibi yaratılır. Her iki yaklaşımı da keşfetmek önemlidir ve daha gömülü programlama yaparken, her durumda hangi yaklaşımın en iyi olduğuna dair bir fikir edinmeye başlarsınız.
Trevor Sayfa

Ayrıca diş çekme seçeneğini kullanmayı tercih ederim. :)
Uçak
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.