Gömülü cihazlarda TDD'yi nasıl yapabilirim?


17

Programlama konusunda yeni değilim ve AVR'de düşük seviyeli C ve ASM ile bile çalıştım, ancak gerçekten daha büyük ölçekli gömülü bir C projesinin etrafında başımı alamıyorum.

Ruby'nin TDD / BDD felsefesi ile yozlaşmış olduğum için insanların böyle kod yazma ve test etme yöntemlerini anlayamıyorum. Bunun kötü bir kod olduğunu söylemiyorum, bunun nasıl çalışabileceğini anlamıyorum.

Düşük seviyeli bir programlamaya daha fazla girmek istedim, ancak buna nasıl yaklaşacağım hakkında hiçbir fikrim yok, çünkü alıştığım tamamen farklı bir zihniyete benziyor. İşaretçi aritmetiği veya bellek ayırmanın nasıl çalıştığını anlamakta zorlanmıyorum, ancak Ruby ile karşılaştırıldığında karmaşık C / C ++ kodunun nasıl göründüğünü gördüğümde, inanılmaz derecede zor görünüyor.

Kendime zaten bir Arduino kartı sipariş ettiğim için, düşük C seviyesine daha fazla girmeyi ve işleri düzgün bir şekilde nasıl yapacağımı gerçekten çok isterim, ancak üst düzey dillerin kurallarının hiçbiri geçerli değil gibi görünüyor.

Gömülü aygıtlarda veya sürücüler veya özel önyükleyici gibi şeyler geliştirirken TDD yapmak bile mümkün müdür?


3
Merhaba Darth, C korkunuzu aşmanıza gerçekten yardımcı olamayız, ancak gömülü cihazlarda TDD ile ilgili soru burada: Konu yerine bunu sormak için sorunuzu gözden geçirdim.

Yanıtlar:


18

Öncelikle, yazmadığınız kodu anlamaya çalışmanın, kendiniz yazmaktan 5 kat daha zor olduğunu bilmelisiniz. C'yi üretim kodunu okuyarak öğrenebilirsiniz, ancak yaparak öğrenmekten çok daha uzun sürecektir.

Ruby'nin TDD / BDD felsefesi ile yozlaşmış olduğum için insanların böyle kod yazma ve test etme yöntemlerini anlayamıyorum. Bunun kötü bir kod olduğunu söylemiyorum, bunun nasıl çalışabileceğini anlamıyorum.

Bu bir beceridir; daha iyi olursun. Çoğu C programcısı insanların Ruby'yi nasıl kullandığını anlamıyor, ancak bu yapamayacakları anlamına gelmiyor.

Gömülü aygıtlarda veya sürücüler veya özel önyükleyici gibi şeyler geliştirirken TDD yapmak bile mümkün müdür?

Konuyla ilgili kitaplar var:

resim açıklamasını buraya girin Bir yaban arısı bunu yapabilirse, sen de yapabilirsin!

Diğer dillerden uygulamaların genellikle işe yaramadığını unutmayın. TDD oldukça evrenseldir.


2
Gömülü sistemlerim için gördüğüm her TDD, yalnızca kendi başıma kolayca bulabileceğim hataları çözmesi kolay sistemlerde hatalar buldu. Asla yardıma ihtiyacım olan şeyi, diğer yongalarla zamana bağlı etkileşimleri ve interrupt etkileşimlerini bulamazlar.
Kortuk

3
Bu, ne tür bir sistem üzerinde çalıştığınıza bağlıdır. İyi bir donanım soyutlaması ile birleştiğinde yazılımı test etmek için TDD kullanmanın aslında o zamana bağlı etkileşimleri çok daha kolay bir şekilde taklit etmeme izin verdiğini buldum. İnsanların sık sık baktığı diğer fayda, otomatikleştirilen testlerin herhangi bir zamanda yapılabileceği ve yazılımın çalıştığından emin olmak için birisinin mantık analizörü ile cihazda oturmasını gerektirmeyebilmesidir. TDD sadece şu anki projemde hata ayıklamamı sağladı. Genellikle, beklemediğimiz hatalara neden olan fark etmenin kolay olduğunu düşündüğümüz hatalardır.
Nick Pascucci

Ayrıca geliştirme ve test dışı hedeflere izin verir.
cp.engr

Gömülü Olmayan C için TDD'yi anlamak için bu kitabı takip edebilir miyim ? Herhangi bir kullanıcı alanı C programlama için?
17'de

15

Burada çok çeşitli cevaplar ... çoğunlukla konuyu çeşitli şekillerde ele alıyor.

25 yılı aşkın süredir çeşitli dillerde gömülü düşük seviyeli yazılım ve bellenim yazıyorum - çoğunlukla C (ancak Ada, Occam2, PL / M ve yol boyunca çeşitli montajcılar ile).

Uzun bir düşünce ve deneme yanılma sürecinden sonra, sonuçları oldukça hızlı bir şekilde alan ve test sarmalayıcıları ve koşum takımları oluşturmak oldukça kolay olan bir yönteme karar verdim.

Yöntem böyle bir şeye gider:

  1. Kullanmak istediğiniz her ana çevre birimi için bir sürücü veya donanım soyutlama kod birimi yazın. Ayrıca işlemciyi başlatmak ve her şeyi ayarlamak için bir tane yazın (bu, dost ortamı sağlar). Tipik olarak küçük gömülü işlemcilerde - AVR'niz bir örnektir - hepsi küçük 10 - 20 gibi birimler olabilir. Bunlar başlatma birimleri, ölçeklenmemiş bellek tamponlarına A / D dönüşümü, bitsel çıkış, buton girişi (sadece açılma yok örneklenmiş), darbe genişlik modülasyon sürücüleri, UART / basit seri sürücüler kullanım kesintileri ve küçük I / O tamponları olabilir. Birkaç tane daha olabilir - örn. EEPROM, EPROM veya diğer I2C / SPI aygıtları için I2C veya SPI sürücüleri.

  2. Donanım soyutlama (HAL) / sürücü birimlerinin her biri için bir test programı yazıyorum. Bu seri bağlantı noktasına (UART) ve işlemci başlatmasına dayanır - bu nedenle ilk test programı sadece bu 2 birimi kullanır ve sadece bazı temel giriş ve çıkışları yapar. Bu, işlemciyi başlatabildiğimi ve temel hata ayıklama desteği seri G / Ç çalıştığımı test etmemi sağlıyor. Bu işe yaradığında (ve ancak o zaman), diğer HAL test programlarını geliştiriyorum ve bunları bilinen iyi UART ve INIT birimlerinin üzerine inşa ediyorum. Bu yüzden bitsel girişleri okumak ve bunları seri hata ayıklama terminalimde güzel bir biçimde (onaltılık, ondalık, her neyse) görüntülemek için test programlarına sahip olabilirim. Daha sonra EEPROM veya EPROM test programları gibi daha büyük ve daha karmaşık şeylere geçebilirim - Çalıştırmak, çalıştırmak ve sonucu görmek için bir test seçebilmem için bu menünün çoğunu yönlendiriyorum. SCRIPT yapamıyorum ama genellikle yapmıyorum

  3. Tüm HAL'ımı çalıştırdıktan sonra, düzenli bir kronometre almanın bir yolunu buluyorum. Bu tipik olarak 4 ila 20 ms arasında bir orandadır. Bu düzenli olmalı, kesilmeyle oluşturulmalıdır. Sayaçların devrilmesi / taşması genellikle bunun nasıl yapılabileceğidir. Kesme işleyici daha sonra bir bayt boyutu "semafor" İNCELEME. Bu noktada, gerektiğinde güç yönetimi ile de uğraşabilirsiniz. Semafor fikri, değeri> 0 ise "ana döngüyü" çalıştırmanız gerektiğidir.

  4. YÖNETİCİ ana döngüyü çalıştırır. Hemen hemen bu semaforun 0 olmayan hale gelmesini bekler (bu detayı soyutluyorum). Bu noktada, bu keneleri saymak için sayaçlarla oynayabilirsiniz (çünkü kene oranını biliyorsunuz) ve böylece geçerli yürütme kene 1 saniye, 1 dakika ve diğer ortak aralıklar için olup olmadığını gösteren bayrakları ayarlayabilirsiniz. kullanmak isteyebilir. Yönetici semaforun> 0 olduğunu bildiğinde, her "uygulama" süreç "güncelleme" fonksiyonundan tek bir geçiş gerçekleştirir.

  5. Uygulama süreçleri etkin bir şekilde yan yana oturup bir "güncelleme" kene tarafından düzenli olarak çalıştırmak olsun. Bu sadece yürütme tarafından çağrılan bir işlevdir. Bu, giriş, küçük bir iş parçası ve çıkış yapan tüm uygulamalara dayanan çok basit bir evde yetiştirilen RTOS ile etkili bir şekilde çok görevlidir. Uygulamaların kendi durum değişkenlerini koruması gerekir ve adaleti zorlayacak önleyici bir işletim sistemi olmadığından uzun süreli hesaplamalar yapamazlar. ZORUNLU uygulamaların çalışma süresi (kümülatif olarak) ana kene süresinden daha kısa olmalıdır.

Yukarıdaki yaklaşım kolaylıkla genişletilebilir, böylece eşzamansız olarak çalışan iletişim yığınları eklenebilir ve iletişim iletileri daha sonra uygulamalara teslim edilebilir (her birine "rx_message_handler" olan yeni bir işlev eklersiniz ve hangi uygulamanın gönderileceği).

Bu yaklaşım, adlandırmayı düşündüğünüz hemen hemen her iletişim sistemi için çalışır - birçok tescilli sistem, açık standart iletişim sistemleri için çalışabilir (ve yaptı), hatta TCP / IP yığınları için bile çalışır.

Ayrıca, iyi tanımlanmış arayüzlere sahip modüler parçalar halinde inşa edilmesi avantajına sahiptir. Parçaları istediğiniz zaman içeri ve dışarı çekebilir, farklı parçaları değiştirebilirsiniz. Yol boyunca her noktada, bilinen iyi alt katman parçalarına (aşağıdaki şeyler) dayanan test koşum takımı veya işleyiciler ekleyebilirsiniz. Bir tasarımın kabaca% 30 ila% 50'sinin genellikle oldukça kolay eklenen özel yazılı birim testlerinden faydalanabileceğini buldum.

Bunu bir adım daha ileri götürdüm (bunu yapan başka birinden çaldım bir fikir) ve HAL katmanını PC için eşdeğerle değiştirdim. Örneğin, bir bilgisayarda C / C ++ ve winforms ya da benzerlerini kullanabilir ve DİKKATLİ bir kod yazarak her arabirimi taklit edebilirsiniz (örn. EEPROM = PC belleğine okunan bir disk dosyası) ve ardından tüm gömülü uygulamayı bir PC'de çalıştırabilirsiniz. Dostça bir hata ayıklama ortamı kullanma yeteneği büyük miktarda zaman ve çaba tasarrufu sağlayabilir. Sadece gerçekten büyük projeler genellikle bu tür çabaları haklı çıkarabilir.

Yukarıdaki açıklama, gömülü platformlarda işleri nasıl yaptığım konusunda benzersiz olmayan bir şeydir - benzer olan birçok ticari kuruluşla karşılaştım. Uygulama şekli genellikle uygulamada büyük ölçüde farklıdır, ancak ilkeler çoğu zaman aynıdır.

Umarım yukarıdakiler biraz lezzet verir ... bu yaklaşım, birkaç kB'de agresif pil yönetimi ile sürekli çalışan 100K veya daha fazla kaynak hattının canavarlarına kadar çalışan küçük gömülü sistemler için çalışır. Windows CE gibi büyük bir işletim sisteminde "katıştırılmış" çalıştırırsanız, yukarıdakilerin tümü tamamen önemsizdir. Ama bu GERÇEK gömülü programlama değil.


2
Çoğu donanım çevre birimi bir UART aracılığıyla test edemezsiniz, çünkü çoğunlukla zamanlama özellikleriyle ilgilenirsiniz. Bir ADC örnekleme hızını, bir PWM görev döngüsünü, başka bir seri çevre biriminin (SPI, CAN vb.) Davranışını veya yalnızca programınızın bir bölümünün yürütme süresini kontrol etmek istiyorsanız, bunu bir UART. Herhangi bir ciddi yerleşik yazılım testi bir osiloskop içerir - gömülü sistemleri bir tane olmadan programlayamazsınız.

1
Ah evet, kesinlikle. Bundan bahsetmeyi unuttum. Ancak UART'ınızı çalıştırıp çalıştırdığınızda, test veya test senaryoları (sorunun ne olduğu) ayarlamak, şeyleri uyarmak, kullanıcı girişine izin vermek, sonuçları almak ve dostça bir şekilde görüntülemek çok kolaydır. Bu + sizin CRO'nuz hayatı çok kolaylaştırır.
11:36

2

Seçtiğiniz örnekler gibi, birden fazla platform için uzun bir artımlı geliştirme ve optimizasyon geçmişine sahip olan kodun okunması genellikle zordur.

C ile ilgili şey, aslında platformları çok çeşitli API zenginliği ve donanım performansı (ve bunların eksikliği) üzerine yayma yeteneğidir. MacVim, günümüzde tipik bir akıllı telefondan 1000X daha az bellek ve işlemci performansına sahip makinelerde duyarlı bir şekilde çalıştı. Ruby kodunuz olabilir mi? Seçtiğiniz olgun C örneklerinden daha basit görünmesinin nedenlerinden biri de budur.


2

Son 9 yılın çoğunu C programcısı olarak geçirmenin tersine döndüm ve son zamanlarda bazı Ruby on Rails ön uçları üzerinde çalışıyorum.

C'de üzerinde çalıştığım şeyler, otomatik depoları kontrol etmek için çoğunlukla orta ölçekli özel sistemlerdir (tipik olarak birkaç yüz bin lira, birkaç milyona kadar maliyet). Örnek işlevsellik, bazı kısa yanıt süresi gereksinimleri ve daha yüksek düzeyde depo iş akışı yönetimi ile makinelere arayüz oluşturan özel bir bellek içi veritabanıdır.

Her şeyden önce şunu söyleyebilirim, TDD yapmıyoruz. Ünite testlerini tanıtan birkaç kez denedim, ancak C'de değerinden daha fazla sorun - en azından özel yazılım geliştirirken. Ancak TDD'nin C'de Ruby'den çok daha az gerekli olduğunu söyleyebilirim. Temel olarak, bunun nedeni sadece C'nin derlenmiş olması ve uyarılar olmadan derlenmesi durumunda, Rails'teki otomatik olarak oluşturulan rspec otomatik olarak üretilen iskele testlerine oldukça benzer bir test yapmışsınızdır. Birim testleri olmayan yakut mümkün değildir.

Ama söyleyeceğim şey, C'nin bazı insanların yaptığı kadar zor olması gerekmiyor. C standart kütüphanesinin çoğu anlaşılmaz işlev isimlerinin bir karmaşasıdır ve birçok C programı bu kuralı takip eder. Yapmadığımızı söylemekten memnuniyet duyuyorum ve aslında standart kütüphane işlevselliği için çok fazla paketleyici var (strncpy yerine ST_Copy, regcomp / regexec yerine ST_PatternMatch, iconv_open / iconv / iconv_close vb. Yerine CHARSET_Convert). Şirket içi C kodum bana okuduğum diğer birçok şeyden daha iyi okuyor.

Fakat diğer üst düzey dillerdeki kuralların geçerli olmadığını söylediğinizde, katılmıyorum. Pek çok iyi C kodu nesne odaklı 'hissediyor'. Sık sık bir kaynağa tanıtıcı başlatma, tanıtıcıyı bağımsız değişken olarak geçen bazı işlevleri çağırmak ve sonunda kaynağı serbest bırakmak için bir desen görürsünüz. Gerçekten de, nesne yönelimli programlamanın tasarım ilkeleri büyük ölçüde insanların prosedür dillerinde yaptıkları iyi şeylerden geldi.

C'nin gerçekten karmaşık olduğu zamanlar, genellikle temelde çok düşük seviyeli olan aygıt sürücüleri ve işletim sistemi çekirdekleri gibi şeyler yaparken. Daha yüksek seviye bir sistem yazarken, C'nin daha yüksek seviye özelliklerini kullanabilir ve düşük seviye karmaşıklığından kaçınabilirsiniz.

Göz atmak isteyebileceğiniz çok ilginç bir şey Ruby için C kaynak kodudur. Ruby API belgelerinde (http://www.ruby-doc.org/core-1.9.3/) çeşitli yöntemlerin kaynak kodlarını tıklayabilir ve görebilirsiniz. İlginç olan, bu kod oldukça güzel ve zarif görünüyor - hayal edebileceğiniz kadar karmaşık görünmüyor.


... aynı zamanda C'nin üst düzey özelliklerini de kullanabilirsiniz ... ” gibi? ;-)
alk

Aygıt sürücüsü tür kodunda görmeye eğilimli bit manipülasyonu ve işaretçi işaretçi sihirbazından daha yüksek bir seviye demek istiyorum! Ve birkaç işlev çağrısının yükü hakkında endişelenmiyorsanız, gerçekten oldukça yüksek düzeyde görünen C kodu yapabilirsiniz.
asc99c

... gerçekten oldukça yüksek seviyede görünen C kodu yapabilirsiniz. ”, kesinlikle, bunu tamamen kabul ediyorum. Ama " ... daha yüksek seviye özellikler ... " C değil, ama kafanızda, değil mi?
alk

2

Yaptığım şey, aygıta bağımlı kodu aygıta bağımlı koddan ayırmak, sonra aygıttan bağımsız kodu test etmektir. İyi modülerlik ve disiplin ile, çoğunlukla iyi test edilmiş bir kod tabanı ile dolacaksınız.


2

Yapamamanın bir sebebi yok. Sorun, diğer geliştirme türlerinde olduğu gibi hoş "hazır" birim test çerçeveleri olmayabilir. Bu iyi. Bu sadece test için "kendi rulo" yaklaşımını kullanmanız gerektiği anlamına gelir.

Örneğin, A / D dönüştürücüleriniz için "sahte girişler" üretmek üzere enstrümanları programlamanız gerekebilir veya gömülü cihazınızın yanıt vermesi için bir "sahte veri" akışı oluşturmanız gerekebilir.

Eğer "TDD" kelimesini kullanmaya karşı dirençle karşılaşırsanız, EE'yi bu fikirle daha rahat ettirecek olan "DVT" (tasarım doğrulama testi) deyin.


0

Gömülü aygıtlarda veya sürücüler veya özel önyükleyici gibi şeyler geliştirirken TDD yapmak bile mümkün müdür?

Bir süre önce bir ARM CPU için birinci seviye bir bootloader yazmam gerekiyordu. Aslında bu CPU'yu satan adamlardan biri var. Ve onların bootloader'larının bootloader'ımızı başlattığı bir düzen kullandık. Ancak bu yavaştı, iki dosyayı bir yerine NOR flaşına dönüştürmemiz gerektiğinden, önyükleyicimizin boyutunu ilk önyükleyiciye dönüştürmemiz ve önyükleyicimizi her değiştirdiğimizde yeniden oluşturmamız gerekiyordu.

Bu yüzden önyükleyicilerinin işlevlerini bizimkine entegre etmeye karar verdim. Ticari kod olduğu için her şeyin beklendiği gibi çalıştığından emin olmalıydım. Bu yüzden QEMU'yu bu CPU'nun IP bloklarını taklit edecek şekilde değiştirdim (hepsi değil, sadece bootloader'a dokunanlar) ve PEM, UART, SRAM denetleyicisi ve yakında. Sonra bu CPU'yu desteklemek için bootloader'ımızı yükselttim ve bundan sonra bootloader'ımızı ve emülatörlerini veren çıkışı karşılaştırdık, bu birkaç hatayı yakalamama yardımcı oluyor. Kısmen ARM montajcısında, kısmen C'de yazılmıştır. Bundan sonra değiştirilmiş QEMU bir hatayı yakalamama yardımcı oldu, JTAG ve gerçek bir ARM CPU kullanarak yakalayamadım.

Böylece C ve birleştirici ile bile testleri kullanabilirsiniz.


-2

Evet, gömülü yazılım üzerinde TDD yapmak mümkündür. Bunun mümkün olmadığını, konuyla ilgili olmadığını veya uygulanamadığını söyleyen insanlar doğru değildir. Herhangi bir yazılımda olduğu gibi gömülü olarak TDD'den kazanılacak ciddi bir değer vardır.

Bunu yapmanın en iyi yolu, testlerinizi hedefe çalıştırmak değil, donanım bağımlılıklarınızı soyutlamak ve ana bilgisayarınızda derleyip çalıştırmaktır.

TDD yaparken, birçok test oluşturup çalıştıracaksınız. Bunu yapmanıza yardımcı olacak yazılıma ihtiyacınız var. Otomatik test keşfi ve alay oluşturma ile bunu hızlı ve kolay hale getiren bir test çerçevesi istiyorsunuz.

Şu anda C için en iyi seçenek Ceedling. İşte bu konuda yazdım hakkında bir yazı:

http://www.electronvector.com/blog/try-embedded-test-driven-development-right-now-with-ceedling

Ve Ruby'de inşa edilmiş! Yine de kullanmak için herhangi bir Ruby bilmenize gerek yok .


cevapların kendi başlarına durması beklenmektedir. Okuyucuları madde bulmak için harici kaynağa gitmeye zorlamak Stack Exchange'de kaşlarını çattı ("makaleyi okuyun veya Ceedling'e bakın"). Site kalite normlarına uydurmak için düzenlemeyi düşünün
gnat

Ceedling'in eşzamansız olayları destekleyen mekanizmaları var mı? Gerçek zamanlı gömülü uygulamaların daha zorlayıcı yönlerinden biri, modellenmesi zor olan çok karmaşık sistemlerden girdi almakla uğraşmalarıdır ...
Jay Elston

@Jay Özellikle bunu destekleyecek hiçbir şey yok. Ancak alay ile bu tür bir şey test başarı ve bunu desteklemek için bir mimari kurarak yaşadım. Örneğin, son zamanlarda kesintiye dayalı olayların bir sıraya konulduğu ve daha sonra bir "olay işleyici" durum makinesinde tüketildiği bir proje üzerinde çalıştım. Bu aslında sadece bir olay meydana geldiğinde çağrılan bir işlevdi. Bu işlevi test ederken, olayları kuyruktan çeken işlev çağrısını alay edebilirim ve böylece sistemde meydana gelen herhangi bir olayı simüle edebilir. Test sürüşü burada da yardımcı olur.
cherno
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.