Obecnie używamy 32-bitowego mikrokontrolera PIC32. Działa dobrze dla naszych potrzeb, ale badamy również inne mikrokontrolery, które mogą lepiej do nas pasować + mamy inne projekty, dla których wybieramy MCU. W tym celu wybraliśmy mikrokontroler SAM DA oparty na ARM, który jest taki sam 32-bitowy, ale oparty na ARM (bardziej popularny niż PIC32 - z punktu widzenia branży).
Teraz dla PIC32 używamy MPLAB, ale dla kory ARM-M0 będziemy używać Atmel Studio. Będziemy używać języka C na obu platformach. Chodzi mi o to, że będziemy używać dwóch 32-bitowych mikrokontrolerów (tej samej firmy), ale o różnych architekturach. Będzie to wymagało od nas nauki dwóch różnych urządzeń i zwiększy naszą „krzywą uczenia się” + czas dostawy. Ale z drugiej strony myślę, że ponieważ w obu przypadkach będziemy używać języka C, krzywa uczenia się dla ARM nie powinna być słyszalna i warto również zbadać ten procesor.
Moje główne pytanie dotyczy tego, jak duża jest różnica w architekturze, gdy programujemy w języku C, ponieważ zapewnia ona abstrakcję wewnętrznych elementów mikrokontrolera. I jakie są główne różnice w MPLAP i Atmel Studio , zważywszy C-język programowania.
źródło
Odpowiedzi:
To dość uparty temat. Mogę mówić za siebie (AVR, ARM, MSP430).
Różnica 1 (najbardziej znacząca) dotyczy urządzeń peryferyjnych. Każdy MCU ma podobny UART, SPI, timery itp. - wystarczy zarejestrować nazwy i bity są różne. Przez większość czasu był to główny problem, z którym musiałem sobie poradzić, przenosząc kod między chipami. Rozwiązanie: napisz sterowniki za pomocą wspólnego interfejsu API, aby aplikacja była przenośna.
Różnica 2 to architektura pamięci. Jeśli chcesz umieścić stałe w flash na AVR, musisz użyć specjalnych atrybutów i funkcji specjalnych, aby je odczytać. W świecie ARM po prostu rezygnujesz ze wskaźnika, ponieważ istnieje pojedyncza przestrzeń adresowa (nie wiem, jak radzą sobie z tym małe PIC, ale zakładam, że są bliżej AVR).
Różnica 3 to deklaracja i obsługa przerwań.
avr-gcc
maISR()
makro. ARM ma tylko nazwę funkcji (jak niektóre UART_Handler () - jeśli używasz nagłówków CMSIS i kodu startowego). Wektory przerwań ARM można umieszczać w dowolnym miejscu (w tym RAM) i modyfikować w czasie wykonywania (bardzo przydatne, jeśli masz na przykład dwa różne protokoły UART, które można przełączać). AVR ma tylko opcję użycia wektorów w „głównej pamięci flash” lub w „sekcji bootloadera” (więc jeśli chcesz inaczej obsługiwać przerwania, musisz użyćif
instrukcji).Różnica 4 - tryby uśpienia i kontrola mocy. Jeśli potrzebujesz najniższego zużycia energii, musisz wykorzystać wszystkie funkcje MCU. Może to bardzo różnić się między MCU - niektóre mają bardziej zgrubne tryby oszczędzania energii, niektóre mogą włączać / wyłączać poszczególne urządzenia peryferyjne. Niektóre MCU mają regulowane regulatory, dzięki czemu można je uruchamiać przy niższym napięciu przy niższej prędkości itp. Nie widzę łatwego sposobu osiągnięcia tej samej wydajności na MCU (powiedzmy) z 3 globalnymi trybami mocy i innym z 7 trybami mocy i indywidualne sterowanie zegarem peryferyjnym.
Najważniejszą rzeczą przy dbaniu o przenośność jest wyraźne podzielenie kodu na części zależne od sprzętu (sterowniki) i części niezależne od aplikacji (aplikacja). Możesz opracować i przetestować ten ostatni na zwykłym komputerze PC ze sztucznym sterownikiem (np. Konsolą zamiast UART). To mnie uratowało wiele razy, ponieważ 90% kodu aplikacji zostało ukończone, zanim prototypowy sprzęt wyszedł z pieca rozpływowego :)
Moim zdaniem dobrą rzeczą w ARM jest „monokultura” - dostępność wielu kompilatorów (gcc, Keil, IAR ... żeby wymienić tylko kilka), wielu darmowych i oficjalnie obsługiwanych IDE (przynajmniej dla NXP, STM32, Silicon Labs, Nordic), wiele narzędzi do debugowania (SEGGER - szczególnie Ozone, ULINK, OpenOCD ...) i wielu dostawców chipów (nawet nie zacznę ich nazywać). PIC32 ogranicza się głównie do Microchip (ale ma to znaczenie tylko wtedy, gdy nie lubisz ich narzędzi).
Jeśli chodzi o kod C. Jest w 99% taki sam,
if
instrukcja jest taka sama, pętla działa w ten sam sposób. Jednak powinieneś dbać o rozmiar natywnego słowa. Na przykład,for
pętla na AVR jest najszybsza, jeśli używaszuint8_t
licznika, podczas gdy na ARMuint32_t
jest najszybszym typem (lubint32_t
). ARM musiałby za każdym razem sprawdzać przepełnienie 8-bitowe, jeśli używałeś mniejszego typu.Wybór MCU i / lub dostawcy ogólnie dotyczy głównie polityki i logistyki (chyba że masz bardzo wyraźne ograniczenia techniczne, na przykład: wysoka temperatura - użyj MSP430 lub Vorago). Nawet jeśli aplikacja może działać na dowolnym urządzeniu, a tylko 5% kodu (sterowników) musi zostać opracowane i obsługiwane przez cały okres użytkowania produktu - jest to nadal dodatkowa opłata dla firmy. Wszystkie miejsca, w których pracowałem, miały ulubionego sprzedawcę i linię MCU (np. „Wybierz dowolną Kinetis, o ile chcesz, chyba że istnieje bardzo dobry powód, aby wybrać coś innego”). Pomaga to również, jeśli masz inne osoby, które proszą o pomoc, więc jako menedżer unikałbym posiadania 5-osobowego działu rozwoju, w którym wszyscy używali zupełnie innego układu.
źródło
Użyłem kilku MCU od czterech różnych producentów. Głównym zadaniem za każdym razem jest zapoznanie się z urządzeniami peryferyjnymi.
Na przykład sam UART nie jest zbyt skomplikowany i łatwo znajduję port sterowników. Ale ostatni raz zajęło mi prawie dzień, aby uzyskać zegary, piny I / O przerywają, włączają itp.
GPIO może być bardzo złożone. Bit-set, bit-clear, bit-toggle, Włączanie / wyłączanie funkcji specjalnych, tri-state. Następnie otrzymasz przerwania: dowolna krawędź, wzrost, spadek, poziom niski, poziom wysoki, samooczyszczanie lub nie.
Potem jest I2C, SPI, PWM, Timery i dwa tuziny innych rodzajów urządzeń peryferyjnych, każdy z własnym włączaniem zegara i za każdym razem, gdy rejestry różnią się nowymi bitami. Dla wszystkich z nich przeczytanie arkusza danych zajmuje wiele godzin, jak ustawić, który bit w jakich okolicznościach.
Ostatni producent miał wiele przykładów kodu, które uważałem za bezużyteczne. Wszystko było abstrakcyjne. Ale kiedy go prześledziłem, kod przeszedł sześć! poziomy wywołań funkcji, aby ustawić bit GPIO. Fajnie, jeśli masz procesor 3GHz, ale nie masz MCU 48 MHz. Mój kod ostatecznie był pojedynczą linią:
Próbowałem użyć bardziej ogólnych sterowników, ale zrezygnowałem. Na MCU zawsze zmagasz się z cyklami miejsca i zegara. Odkryłem, że warstwa abstrakcji jako pierwsza wychodzi z okna, jeśli generujesz określony przebieg w procedurze przerwania o częstotliwości 10 kHz.
Więc teraz mam wszystko działające i nie planuję ponownie się przełączać, chyba że z bardzo, bardzo dobrego powodu.
Wszystkie powyższe należy zamortyzować, ile produktów sprzedajesz i ile oszczędzasz. Sprzedaż miliona: oszczędność 0,10 na zmianę na inny typ oznacza, że możesz wydać 100 000 na roboczogodziny. Sprzedając 1000 masz tylko 100 do wydania.
źródło
OUTPUT_HI(n)
które daje równoważne identyfikacyjnyGPIOD->bssr |= 0x400;
, jeślin
jest stała, jak 0x6A, ale wywołania prosty podprogramu, gdyn
jest w stanie nie stały. To powiedziawszy, większość interfejsów API dostawców, jakie widziałem, waha się między miernymi a okropnymi.To bardziej opinia / komentarz niż odpowiedź.
Nie chcesz i nie powinieneś programować w C. C ++, jeśli jest używany we właściwy sposób , jest znacznie lepszy. (OK, muszę przyznać, że przy niewłaściwym użyciu jest znacznie gorszy niż C.) To ogranicza cię do układów, które mają (nowoczesny) kompilator C ++, który jest mniej więcej wszystkim, co jest obsługiwane przez GCC, w tym AVR (z niektóre ograniczenia, filo wspomina o problemach z niejednorodną przestrzenią adresową), ale wyklucza prawie wszystkie PIC (PIC32 może być obsługiwany, ale nie widziałem jeszcze żadnego przyzwoitego portu).
Kiedy programujesz algorytmy w C / C ++, różnica między wspomnianymi opcjami jest niewielka (z wyjątkiem tego, że 8 lub 16-bitowy układ będzie miał poważną wadę, gdy wykonasz dużo 16, 32 lub więcej bitów). Kiedy potrzebujesz ostatniej uncji wydajności, prawdopodobnie będziesz musiał użyć asemblera (własnego lub kodu dostarczonego przez dostawcę lub osobę trzecią). W takim przypadku możesz ponownie rozważyć wybrany układ.
Podczas kodowania do sprzętu możesz albo użyć warstwy abstrakcji (często dostarczanej przez producenta), albo napisać własną (na podstawie arkusza danych i / lub przykładowego kodu). Istniejące abstrakcje C w edytorze IME (mbed, cmsis, ...) są często funkcjonalnie (prawie) poprawne, ale strasznie zawodzą pod względem wydajności (sprawdź, czy oldfarts zawierają około 6 warstw pośrednich dla operacji na zestawie pinów), użyteczności i przenośności. Chcą udostępnić Ci całą funkcjonalność konkretnego układu, co w prawie wszystkich przypadkach nie będzie ci potrzebne, a raczej nie będziesz się tym przejmować, a to zablokuje Twój kod dla tego konkretnego dostawcy (i prawdopodobnie tego konkretnego układu).
To jest, gdy C ++ może zrobić znacznie lepiej: po prawidłowym wykonaniu zestaw pinów może przejść przez 6 lub więcej warstw abstrakcji (ponieważ dzięki temu możliwy jest lepszy (przenośny!) Interfejs i krótszy kod), ale jednocześnie zapewnia interfejs niezależny od celu dla prostych przypadków i nadal skutkować tym samym kodem maszynowym, jak w przypadku asemblera .
Fragment stylu kodowania, którego używam, który może wywołać entuzjazm lub odwrócić się z przerażeniem:
W rzeczywistości jest jeszcze kilka warstw abstrakcji. Jednak ostateczne użycie diody LED, powiedzmy, aby ją włączyć, nie pokazuje złożoności ani szczegółów celu (dla żarłocznego uno lub niebieskiej pigułki ST32 kod byłby identyczny).
Kompilator nie jest zastraszany przez wszystkie te warstwy, a ponieważ nie ma w nim żadnych funkcji wirtualnych, optymalizator przejrzy wszystko (niektóre szczegóły pominięto, na przykład włączenie zegara peryferyjnego):
Tak napisałbym to w asemblerze - JEŚLI zdałem sobie sprawę, że rejestrów PIO można używać z przesunięciami ze wspólnej bazy. W tym przypadku prawdopodobnie bym to zrobił, ale kompilator jest znacznie lepszy w optymalizacji takich rzeczy niż ja.
O ile mam odpowiedź, to: napisz warstwę abstrakcji dla swojego sprzętu, ale zrób to we współczesnym C ++ (koncepcje, szablony), aby nie zaszkodziło to twojej wydajności. Dzięki temu możesz łatwo przejść na inny układ. Możesz nawet zacząć opracowywać losowy układ, na którym się układasz, masz rodzinę, masz dobre narzędzia do debugowania itp. I odkładasz ostateczny wybór na później (gdy będziesz miał więcej informacji o wymaganej pamięci, szybkości procesora itp.).
IMO jednym z błędów we wbudowanym rozwoju jest wybór chipa jako pierwszego (to pytanie często zadawane na tym forum: dla którego chipa powinienem wybrać ... Najlepsza odpowiedź brzmi ogólnie: to nie ma znaczenia).
(edycja - odpowiedź na „Czy pod względem wydajności C lub C ++ byłby na tym samym poziomie?”)
Dla tych samych konstrukcji C i C ++ są takie same. C ++ ma znacznie więcej konstrukcji do abstrakcji (tylko kilka: klasy, szablony, constexpr), które, jak każde narzędzie, mogą być użyte dla dobra lub zła. Aby dyskusje były bardziej interesujące: nie wszyscy zgadzają się co jest dobre, a co złe ...
źródło
Jeśli dobrze rozumiem, chcesz wiedzieć, jakie specyficzne dla architektury funkcje platformy „wyskakują” w środowisku języka C, co utrudnia pisanie łatwego w utrzymaniu, przenośnego kodu na obu platformach.
C jest już dość elastyczny, ponieważ jest „przenośnym asemblerem”. Wszystkie wybrane platformy mają dostępne kompilatory GCC / komercyjne, które obsługują standardy językowe C89 i C99, co oznacza, że możesz uruchamiać podobny kod na wszystkich platformach.
Istnieje kilka uwag:
Niektóre platformy / kompilatory mogą lepiej ukrywać to „ograniczenie” niż inne. Np. W AVR musisz użyć określonych makr, aby odczytać dane ROM. W PIC24 / dsPIC dostępne są również dedykowane instancje tblrd. Jednak dodatkowo niektóre części mają również funkcję „widoczności przestrzeni programowej” (PSVPAG), która pozwala na mapowanie strony FLASH do pamięci RAM, dzięki czemu natychmiastowe adresowanie danych jest dostępne bez tblrd. Kompilator może to zrobić dość skutecznie.
ARM i MIPS są Von Neumann, dzięki czemu mają obszary pamięci dla ROM, RAM i urządzeń peryferyjnych spakowanych na 1 magistrali. Nie zauważysz żadnej różnicy między odczytem danych z pamięci RAM lub „ROM”.
Z drugiej strony PIC24 pozwala operacjom ALU na odczyt i zapis danych bezpośrednio przez adresowanie pośrednie (nawet przy modyfikacjach wskaźnika ..). Ma to pewne cechy z architektury podobnej do CISC, więc 1 instrukcja może wykonać więcej pracy. Ta konstrukcja może prowadzić do bardziej skomplikowanych rdzeni procesora, niższych zegarów, wyższego zużycia energii itp. Na szczęście dla Ciebie ta część jest już zaprojektowana. ;-)
Różnice te mogą oznaczać, że PIC24 może być „mocniejszy” we / wy operacji we / wy niż podobnie taktowany układ ARM lub MIPS. Jednak możesz uzyskać znacznie wyższą część ARM / MIPS zegara dla tych samych ograniczeń cena / pakiet / projekt. Sądzę, że dla praktycznych terminów dużo „uczenia się platformy” polega na opanowaniu tego, co architektura może, a czego nie może zrobić, jak szybko potrwa kilka zestawów operacji itp.
Różnice te można nieco zawrzeć w sterownikach urządzeń, ale ostatecznie wbudowane oprogramowanie sprzętowe ma wysoki poziom sprzężenia ze sprzętem, więc czasami nie można uniknąć pracy niestandardowej.
Ponadto, jeśli opuszczasz ekosystemy Microchip / były Atmel, może się okazać, że części ARM wymagają większej konfiguracji, aby je uruchomić. Mam na myśli w kategoriach; włączanie zegarów do urządzeń peryferyjnych, a następnie konfigurowanie urządzeń peryferyjnych i „włączanie” ich, osobne konfigurowanie NVIC itp. To tylko część krzywej uczenia się. Kiedy pamiętasz, aby wykonać wszystkie te czynności, w odpowiedniej kolejności, pisanie sterowników urządzeń dla wszystkich tych mikrokontrolerów będzie w pewnym momencie wydawać się podobne.
źródło
Tak i nie. Z perspektywy programisty idealnie ukrywasz szczegóły zestawu instrukcji. Ale to do pewnego stopnia już nie ma znaczenia, urządzenia peryferyjne, które są celem pisania programu, nie są częścią zestawu instrukcji. W tym samym czasie nie można po prostu porównać części flash 4096 bajtów w tych zestawach instrukcji, szczególnie jeśli używasz C, ilość zużycia pamięci flash / pamięci jest silnie określona przez zestaw instrukcji i kompilator, niektórzy nigdy nie powinni widzieć kompilatora (kaszel PIC kaszel) ze względu na ilość marnotrawstwa tych zasobów zużywaną przez kompilację. Inne zużycie lampy błyskowej jest mniejsze. Wydajność jest również problemem w przypadku używania języka wysokiego poziomu, a wydajność ma znaczenie w aplikacjach MCU, więc może mieć znaczenie między wydatkowaniem 3 USD za płytę na MCU lub 1 USD.
Jeśli chodzi o ułatwienie programowania (przy całkowitym koszcie produktu), powinieneś być w stanie pobrać pakiet programisty dla mcu, tak aby architektura zestawu instrukcji była czymś, czego nigdy nie widzisz, więc jeśli jest to twoja główna troska, to nie jest problemem. Nadal kosztuje Cię to tyle, ile koszt produktu związany z korzystaniem z tych bibliotek, ale czas na wprowadzenie na rynek może być krótszy, uważam, że korzystanie z bibliotek zajmuje więcej czasu / pracy w porównaniu do bezpośredniego komunikowania się z urządzeniami peryferyjnymi.
Podsumowując, zestawy instrukcji są najmniejszymi zmartwieniami, przejdźmy do prawdziwych problemów.
źródło