Ankieta: Jaki system operacyjnym preferujesz
Ankieta jest zamknięta.
Windows 60.78% 31 60.78%
Linux 31.37% 16 31.37%
MAC/OS 7.84% 4 7.84%
Inny 0% 0 0%
Razem 51 głosów 100%
*) odpowiedź wybrana przez Ciebie [Wyniki ankiety]

Odpowiedz 
 
Ocena wątku:
  • 1 Głosów - 5 Średnio
  • 1
  • 2
  • 3
  • 4
  • 5
Programowanie ARM, nauka, środowiska programistyczne IDE
SP6VGX Offline
Tomek
***

Liczba postów: 108
Dołączył: 03-11-2012
Post: #28
RE: Programowanie ARM, nauka, środowiska programistyczne IDE
Witam

Mialem napisac juz cos troche wczesniej, ale niestety praca. Wiec bedzie troche ogolnie odnosnie roznych wpisow.

Jesli chodzi o same zalozenia to docelowo ma byc to nauka dla poczatkujacych, co nie oznacza ze niestety bedzie wymagana choc by podstawowa znajomosc C lub innego jezyka o podobnej do C skladni. Dla wielu osob moze to wydac sie nudne, ale tutaj droga jest otwarta i mozna pociagnac roznolegle jakies bardziej zaawansowane projekty.

Odnosnie IDE to tutaj jesli ktos uzywa jakiegos ulubionego to nie jest to jakakolwiek przeszkoda, nie musi go zmieniac. Jesli juz pisze nie bedzie potrzebowal pomocy na poziomie podstawowym. Istotnym elementem jest aby takie IDE wspieralo projekty typu Makefile - niestety ale predzej czy pozniej spotkamy sie z takimi projektami i w przypadku checi zmian w nich i kompilacji bedzie trzeba uczyc sie czegos nowego.

Natomiast kompilator to jakie by nie wybrac IDE (poza komercyjnymi w stylu IAR, Keil) to mamy rozne wersje GCC. Jest to calkiem sensowny darmowy kompilator ktory w razie potrzeb mozemy przekompilowac pod wlasne potrzeby. Przykladowo sam takiego uzywam na Mac OS X z opcjami dla newlib takimi jakie potrzebowalem...

https://github.com/sp6vgx/vgx-arm-toolchain

Tutaj nie zgodze sie z tym ze na MAC OS X byl jakis problem, bo od dlugiego okresu uzywam na nim Eclipse. Owszem wczesniej bylo troche gorzej i trzeba bylo troche recznie podlubac Wink ale w sumie od wielu lat mozna sie opierac o wiele opisow konfiguracji z dowolnego systemu.

Zreszta objektywnie patrzac na darmowe srodowiska to one wszystkie maja sporo wad (ale rekompensuje to brak wydatkow), chyba jedynym sensownym darmowym jest Atmel Studio - oparte o Visual Studio, ale to ogranicza niestety do ARM-ow od Atmela (pomijajac AVR-y)

Odnosnie wbudowanych bootloaderow (RS/USB/Can itp.) nie ma sensu IMHO dyskutowac, jest to ulatwienie ktore posiada obecnie wiekszosc procesorow. Jednak do nauki programowania jest to srednio wygodne, a bez debuggera w wiekszych procesorach jest bardzo trudno.

Teraz jest niby do wyboru NXP i ST. Ja bym stawial na ST (mimo bledow) - zestawy Discovery sa bardzo popularne i jak widac wiele osob moze takie zestawy posiadac. Sa one patrzac na cene posiadaja one calkiem ciekawe dodatkowe uklady - wiec odpada lutowanie czy podpinanie czegos na kabelkach. Natomiast w przyszlosci pod dany zestaw bedzie mozna stworzyc plyte bazawa z roznymi dodatkowymi modulami np. DDS-y itp. czyli to co interesuje krotkofalowcow.

Teraz jesli STM32F4 to dlaczego akurat ten model czyli rdzen Cortex M4F. Predzej czy pozniej bedziemy chcieli zrobic cos wiekszego czyli jakiegos SDR-a itp. W tym rdzeniu mamy wiec dostepne instrukcje DSP ktore mocno przyspieszaja takie obliczenia, do tego FPU czyli koprocesor matematyczny... W porownaniu np. do Cortex-ow M3 od ST posiadaja tez o wiele fajniej rozwiazane przypisywanie alternatywnych funkcji peryferiow do pinow. Spora ilosc RAM-u czy Flash to jakas tam dodatkowa zaleta...

Teraz co do SPL-a to juz go odradzalem i powiem dlaczego.

To tylko tak na pierwszy rzut oka wyglada czytelniej, nie ma sie co oklamywac ze szybko dojdziemy do jakiejs specyficznej konfiguracji danych peryferiow. Tutaj bedzie nieuniknione zagladanie do Reference Manual, a potem przebijanie sie przez kod SPL-a (ktory nie nalezy do najlatwiejszych w analizie) - aby sprawdzic jak wypelnic strukture. Niestety pomijajac juz to ze SPL nie jest rozwijany to nie ma do niego jakiejkolwiek sensownej dokumentacji. Pomijam juz tutaj nadmiar kodu czy bledy w SPL-u...

Kolejna sprawa to trzymanie sie RS232 na obecne czasy traci troche myszka Wink jak mamy procesory z wbudowanym USB. Tutaj mimo ze biblioteka od ST nie uzywa SPL-a to jej uzywanie nie jednej osobie przyspozylo sporo siwych wlosow. Czyli mamy to samo co w SPL, bardzo malo czytelny kod.

Po prostu nie oszukujmy sie ciagle odpalony Reference Manual do ktorego zagladamy jest niezbedny (bo nikt nie zapamieta tego co tam jest).

Jak bylo wspomniane wczesniej jest jeszcze biblioteka libopencm3 ktora zaproponowalem Adamowi i Piotrowi jako kompromis - aby zwlaszcza poczatkujacych nie pchac od razu na "gleboka wode". Jest o wiele sensowniej napisana w stosunku do SPL, wspomniane USB odpala sie bardzo latwo, kod dla poczatkujacego jest na pewno znacznie bardziej czytelny.

Tak krotko wspominajac jeszcze o Assemblerze - bo o tym bylo. W przypadku ARM-ow i obecnych kompilatorow C/C++ nie ma to najmniejszego sensu. Generalnie architektora jak i zestaw instrukcji tych procesorow optymalizowany jest pod kompilatory C. Obecnie chyba tylko na starszych PIC-ach sensowne jest uzywanie w pelni ASM (ze wzgledu na pewne upierdliwosci jak rozdzial pamieci danych od pamieci programu itp.), ewnetualnie na najmniejszych AVR-ach (Tiny) gdzie zalezy nam na objetosci kodu. Choc i ostatnio na PIC-u napisalem calosc w C bo wystarczylo to do danego zastosowania...

Optymalizacja jest na tyle dobra ze trudno jest w ASM napisac taki kod. Owszem czasami mozna uzywac wstawek, ale sa to skrajne przypadki i wiekszosc z nas nigdy nie bedzie miala nawet takiej potrzeby.

Osobiscie jej nie uzywalem ale postanowilem ostatnio w firmie ja przetwestowac i powstaja na niej dwa projekty. Co moge powiedziec - moze tez ma pewne braki, ale pisze sie w niej calkiem wygodnie - dokumentacja moze tez nie jest idealna (Doxygen), ale w porownaniu do SPL-a istnieje Smile No i sama biblioteka wspiera tez procesory innych producentow.

Z wiekszych projektow jakie na niej bazuja to Autopilot do dronow itp. Paparazzi UAV:
https://wiki.paparazziuav.org/wiki/Main_Page

Do tego w o wiele latwiej w niej skonfigurowac czestotliwosci na szynach, PLL itd. niz w SPL. Tak samo o wiele bardziej intuicyjne sa handlery dla przerwan.

Przyklad konfiguracji zegarow dla nietypowego kwarcu (akurat taki zostal kiedys wsadzony na plyty w firmie, a obecnie przepisuje kod bo ten w SPL-u ktos kiedys napisal tak tragicznie ze nie ma go co poprawiac):
Kod:
const struct rcc_clock_scale rcc_hse_26mhz_3v3[RCC_CLOCK_3V3_END] = {
    { /* 48MHz */
        .pllm = 26,
        .plln = 96,
        .pllp = 2,
        .pllq = 2,
        .hpre = RCC_CFGR_HPRE_DIV_NONE,
        .ppre1 = RCC_CFGR_PPRE_DIV_4,
        .ppre2 = RCC_CFGR_PPRE_DIV_2,
        .power_save = 1,
        .flash_config = FLASH_ACR_ICE | FLASH_ACR_DCE |
                FLASH_ACR_LATENCY_3WS,
        .ahb_frequency  = 48000000,
        .apb1_frequency = 12000000,
        .apb2_frequency = 24000000,
    },
    { /* 84MHz */
        .pllm = 26,
        .plln = 336,
        .pllp = 4,
        .pllq = 7,
        .hpre = RCC_CFGR_HPRE_DIV_NONE,
        .ppre1 = RCC_CFGR_PPRE_DIV_2,
        .ppre2 = RCC_CFGR_PPRE_DIV_NONE,
        .flash_config = FLASH_ACR_ICE | FLASH_ACR_DCE |
                FLASH_ACR_LATENCY_2WS,
        .ahb_frequency  = 84000000,
        .apb1_frequency = 42000000,
        .apb2_frequency = 84000000,
    },
    { /* 120MHz */
        .pllm = 26,
        .plln = 240,
        .pllp = 2,
        .pllq = 5,
        .hpre = RCC_CFGR_HPRE_DIV_NONE,
        .ppre1 = RCC_CFGR_PPRE_DIV_4,
        .ppre2 = RCC_CFGR_PPRE_DIV_2,
        .power_save = 1,
        .flash_config = FLASH_ACR_ICE | FLASH_ACR_DCE |
                FLASH_ACR_LATENCY_3WS,
        .ahb_frequency  = 120000000,
        .apb1_frequency = 30000000,
        .apb2_frequency = 60000000,
    },
    { /* 168MHz */
        .pllm = 26,
        .plln = 336,
        .pllp = 2,
        .pllq = 7,
        .hpre = RCC_CFGR_HPRE_DIV_NONE,
        .ppre1 = RCC_CFGR_PPRE_DIV_4,
        .ppre2 = RCC_CFGR_PPRE_DIV_2,
        .flash_config = FLASH_ACR_ICE | FLASH_ACR_DCE |
                FLASH_ACR_LATENCY_5WS,
        .ahb_frequency  = 168000000,
        .apb1_frequency = 42000000,
        .apb2_frequency = 84000000,
    },
};

int main(void)
{
    //PLL Clock 168MHz
    rcc_clock_setup_hse_3v3(&rcc_hse_26mhz_3v3[RCC_CLOCK_3V3_168MHZ]);

Dla standardowych kwarcow wystepujacych w roznych zestawach wystarczy tylko wywalac rcc_clock_setup_hse_3v3 z odpowiednio juz zdefiniowana w bibliotece struktura.

Przykladowe nazwy zdefiniowane dla przerwan ktore tylko uzywamy w kodzie:

Kod:
void dma2_stream0_isr(void)
void sys_tick_handler(void)
void i2c1_ev_isr(void)
void i2c1_er_isr(void)

Oraz przykladowa konfiguracja ADC + DMA:
Kod:
    /**************************************************​******************************
     *  Configure DMA2 Stream0
     **************************************************​******************************/

    dma_stream_reset(DMA2, DMA_STREAM0);

    dma_set_peripheral_address(DMA2, DMA_STREAM0, (uint32_t)&ADC1_DR);

    dma_set_number_of_data(DMA2, DMA_STREAM0, 4);

    dma_set_memory_address(DMA2, DMA_STREAM0, (uint32_t)&ADCValue);
    dma_set_transfer_mode(DMA2, DMA_STREAM0, DMA_SxCR_DIR_PERIPHERAL_TO_MEM);
    dma_enable_memory_increment_mode(DMA2, DMA_STREAM0);
    dma_set_peripheral_size(DMA2, DMA_STREAM0, DMA_SxCR_PSIZE_16BIT);
    dma_set_memory_size(DMA2, DMA_STREAM0, DMA_SxCR_MSIZE_16BIT);
    dma_set_priority(DMA2, DMA_STREAM0, DMA_SxCR_PL_HIGH);
    dma_enable_circular_mode(DMA2, DMA_STREAM0);
    dma_channel_select(DMA2, DMA_STREAM0, DMA_SxCR_CHSEL_0);
    dma_set_peripheral_burst(DMA2, DMA_STREAM0, DMA_SxCR_PBURST_SINGLE);
    dma_set_memory_burst(DMA2, DMA_STREAM0, DMA_SxCR_MBURST_SINGLE);

    dma_enable_transfer_complete_interrupt(DMA2, DMA_STREAM0);

    nvic_set_priority(NVIC_DMA2_STREAM0_IRQ, 1);
    nvic_enable_irq(NVIC_DMA2_STREAM0_IRQ);
    dma_enable_stream(DMA2, DMA_STREAM0);


    /**************************************************​******************************
     *  Configure ADC1
     **************************************************​******************************/

    uint8_t adc_channels[] = { ADC_CHANNEL12 };

    adc_power_off(ADC1);

    adc_disable_scan_mode(ADC1);
    adc_disable_external_trigger_regular(ADC1);

    adc_set_sample_time_on_all_channels(ADC1, ADC_SMPR_SMP_480CYC);

    adc_set_continuous_conversion_mode(ADC1);

    adc_set_regular_sequence(ADC1, 1, adc_channels);
    
    adc_set_multi_mode(ADC_CCR_MULTI_INDEPENDENT)​;

    ADC_CCR &= ~(ADC_CCR_DMA_MASK | ADC_CCR_DDS);
    ADC_CCR |= ADC_CCR_DMA_MODE_1 | ADC_CCR_DDS;

    adc_enable_dma(ADC1);
    adc_set_dma_continue(ADC1);

    adc_set_resolution(ADC1, ADC_CR1_RES_12BIT);
    adc_set_right_aligned(ADC1);

    adc_power_on(ADC1);

    adc_start_conversion_regular(ADC1);

Oczywiscie na wspomniana biblioteke sie nie upieram, ale uczenie sie SPL-a stanowczo odradzam bo to IMHO tylko marnowanie czasu. Predzej czy pozniej staniemy przed problemem na ktory poswiecimy o wiele wiecej czasu niz bysmy to poswiecili na nauke pisania na rejestrach.

Jeszcze co do J-Linka to pomijajac juz sam debugger, to cale dostarczane oprogramowanie jest na dobrym poziomie. GDBServer o wiele lepiej dziala niz OpenOCD... do tego nie wiem jak teraz wyglada sytuacja, ale kiedys OpenOCD wspieralo SWD tylko dla STLinka... wiec jest to pewna wygoda (mniejsze zlacze do programowania na plytce niz standardowy JTAG)... do tego dostepne SWO czyli takie uproszczone Trace (Serial Wire Output trace port.). Jest to w sumie cos podobnego do RS-a gdzie mozemy sobie wysylac jakies pomocnicze dane do debugowania itp. nie marnujac na to innych peryferiow.

----

Teraz co do samej nauki. Wiem ze to bedzie moze bardzo banalne i dla wielu osob analiza np. jakiegos projektu moze byc ciekawa. Ale... Rozne projekty sa roznie pisane - wiec po co z gory uczyc zlych przyzwyczajen. Wiele z nich jest pisanych w SPL-u ktory byl kiedys promowany w tym na uczelniach (co IMHO bylo sporym bledem).

IMHO aby uwzglednic tych najbardziej poczatkujacych, osoby ktore uzywaly Arduino itp. Warto zaczac od podstaw typu miganie dioda led. Przy okazji omawiajac od podstaw czyli konfiguracji zegarow, a jak bedzie potrzeba obiasniajac i jakies niejasne sprawy w samym C (dla totalnie poczatkujacych - to oczywiscie jesli beda jakies pytania co do zamieszczonego kodu).

Potem zobaczymy jaki jest ogolny poziom i w razie potrzeby bedzie go mozna zwiekszyc, choc ja mysle ze trzeba sie dostosowac dla osoby najbardziej "zielonej" - jesli zalezny nam aby wiecej krotkofalowcow zachecic do tych procesorow. Ktore obecnie nawet patrzac na cene i oferowane mozliwosci sa o wiele bardziej atrakcyjne od Arduino czy AVR-ow. Przykladowo w popularnym AVT za niecale 13zl kupimy procesor z rdzeniem CortexM3, CAN, USB, 3 x UART, 2 x I2C, 2 x SPI, 2 x ADC....

Natomiast jak caly projekt sie uda oczywiscie nic nie stoi na przeszkodzie by poleciec w projekty o wiele bardziej ciekawsze czyli np. jakis wspomniany SDR i DSP. Jednak tutaj nie bede ukrywal ze to przynajmniej na poczatku bedzie wymagalo w pewnym stopniu odswiezenia sobie wiadomosci z matematyki. Takie podstawy to bedzie troche przypomnienie sobie liczb zespolonych, troche calek itp. Oczywiscie tej matematyki bedzie niezbedne minimum, ale nie ma sie co oklamywac ze bez matematyki uda sie cos zrobic - tak aby byla to wiedza przydatna do wykorzystania w wlasnych projektach, a nie powielenie jakiegos zaprezentowanego rozwiazania.

W miedzyczasie zapewne jeszcze omowi sie jakiegos RTOS-a bez ktorego czasem trudno w wiekszych projektach.



DODANE PO PEWNYM CZASIE

Jeszcze tak rozwine wspomniany temat USB (akurat udalo mi sie znalezc przyklad kodu dla bibliotek od ST). Nie wspomne ze w przypadku uzywania cudow od ST mozna znalezc wiele tematow ze cos dziala wolno lub dziala zle Smile

Tak wyglada implementacje wirtualnego portu COM dla biblioteki ST:

https://github.com/znuh/STM32_VCP

Tutaj natomiast libopencm3:
https://github.com/libopencm3/libopencm3...usb_cdcacm

Praktycznie na poczatek potrzebna niewielka wiedza o USB tzn. podstawy o endpointach itp.

Mozna pisac calosc bez bibliotek oczywiscie - ale tutaj mamy przebicie sie przez cala specyfikacje USB aby zrozumiec jak wszystko dziala, a jest to dosc spora lektura...

tutaj maly przyklad obslugi samego przerwania USB w STM32F103 (co potrzeba obsluzyc) i jest to zaledwie maly fragment prostej biblioteki jaka kiedys pisalem:

Kod:
void USB_LP_CAN1_RX0_IRQHandler(void)
{

    uint16_t istr = (uint16_t)USB->ISTR;

    if (istr & USB_ISTR_CTR)
    {
        uint8_t ep = istr & USB_ISTR_EP_ID;
        uint8_t type = (istr & USB_ISTR_DIR) ? 1 : 0;

        if (type)
        {
            type += (USB->EPR[ep] & USB_EP0R_SETUP) ? 1 : 0;
        }
        else
        {
            USB->EPR[ep] = ((USB->EPR[ep] & (USB_EP0R_EP_TYPE | USB_EP0R_EP_KIND | USB_EP0R_EA)) | (USB_EP0R_CTR_RX | USB_EP0R_CTR_TX)) & ~USB_EP0R_CTR_TX;
        }

#ifdef DEBUG
            SWO_PrintString("USB: User Callback [ep=%d][type=%d] ", ep, type);
#endif
        if (USB_Ctx.user_callback_ctr[ep][type])
        {
#ifdef DEBUG
            SWO_PrintString("FOUND\n");
#endif
            USB_Ctx.user_callback_ctr[ep][type](ep);
        }
        else
        {
#ifdef DEBUG
            SWO_PrintString(" NOT FOUND\n");
#endif
            USB->EPR[ep] = ((USB->EPR[ep] & (USB_EP0R_EP_TYPE | USB_EP0R_EP_KIND | USB_EP0R_EA)) | (USB_EP0R_CTR_RX | USB_EP0R_CTR_TX)) & ~USB_EP0R_CTR_RX;
        }
    }
    else if (istr & USB_ISTR_SUSP)
    {
#ifdef DEBUG
            SWO_PrintString("USB: SUSP\n");
#endif
        USB->ISTR &= ~USB_ISTR_SUSP;
        USB->CNTR |= USB_CNTR_FSUSP;
        if (USB_Ctx.user_callback_suspend)
        {
            USB_Ctx.user_callback_suspend();
        }
    }
    else if (istr & USB_ISTR_WKUP)
    {
#ifdef DEBUG
            SWO_PrintString("USB: WKUP\n");
#endif
        USB->ISTR &= ~USB_ISTR_WKUP;
        USB->CNTR &= ~USB_CNTR_FSUSP;
        if (USB_Ctx.user_callback_resume)
        {
            USB_Ctx.user_callback_resume();
        }
    }

    else if (istr & USB_ISTR_RESET)
    {
#ifdef DEBUG
            SWO_PrintString("USB: RESET\n");
#endif
        USB_Ctx.pm_top = 0x40;
        USB_Reset();

        USB->ISTR &= ~USB_ISTR_RESET;
    }


}

W sumie mozna sobie wyciagnac wnioski co jest czytelniejsze Smile Mozna tez zerknac na obsluge tego przerwania w bibliotece od ST Smile

No i jeszcze wracajac do robienia czegos konkretnego - czyli proponowane bajery typu cos do Husara itp. To jak wspomnialem to chyba nie w ramach kursu...

Przynajmniej ja nie chcial bym aby kurs polegal na tym ze potem powstaje taki naglowek jak tutaj:

https://github.com/STM32-SDR/STM32-SDR/b...c/PSKMod.c

Kod:
// PSK code
//
// Based on code by Moe Weatley; changes may have been made
// for the STM32-SDR project. Original license below:

/* ////////////////////////////////////////////////////////////////////
     PSK31Core Library for transmission and reception of PSK31 signals
        using a PC soundcard  or .wav files.
                       Copyright 2000, Moe Wheatley, AE4JY

Wiec jaki ma sens przepisywanie napisanych przez kogos funkcji bez ich zrozumienia, to nie prowadzi do jakiegokolwiek zwiekszenia innowacyjnosci rozwiazan...

Niestety trzeba tez uciec troche od tego jak programujemy na PC... tutaj mamy rozne peryferia ktore mozemy ze soba sprzegac, wykorzystywac DMA... dlatego przekladanie czegos na wygodne klasy jak w PC nie ma czasami sensu...

Chodzi mi o cos takiego co proponuje kolega SQ6DGT:
Kod:
Usart rs1 = new Usart(USART1, 115200, 8, 1, 200);

gdzie argumenty to nr. USART, prędkość, bity danych, bity stopu, timeout w ms. I potem:

if(rs1.error()) { .... } czy
if(rs1.success()) { ....}
if(rs1.hasData()) {
rs1.read(&buf, 200); gdzie 200 - maks długość, a dla sprawdzenia statusu operacji:
}

ladnie wyglada i tyle... po co sprawdzac to w petli glownej ? Po to w rdzeiach Cortex jest NVIC by korzystac z przerwan, z mozliwosci ich priorytetow, wywlaszczania itp. moze czasami lepiej uzyc aby przeslac te dane przez DMA do "software FIFO" w RX i z pamieci do UART z automatu przy TX, zwlaszcza przy sporej ilosci danych zaczyna widac zalety i wtedy taka klasa traci sens. Natomiast napisanie takiej klasy aby byla uniwersalna i umozliwiala wszelkie warianty laczenia peryferiow to ogromny naklad pracy i malo prawdopodobne by zrobic to elastycznie. Owszem mozna pisac obiektowo bo to niewielki naklad, ale trzeba zostawic przyzwyczajenia z pisania pod systemy operacyjne typu Windows/Linux daleko z tylu...

Takie klasy mozna budowac stosujac jakiegos RTOS-a, ale tez w ograniczonym zakresie. Zreszta bedzie mozna tez napisac wlasny maly "ala" RTOS, a w zasadzie to pokazac jak przelaczac konteksty tak aby miec watki z osobnym stosem itd.

Przynajmniej mi zalezy na tym aby ktos inny poznal jak cos zbudowac od zera czy to bedzie miganie dioda, wlasny RTOS czy demodulator AM/FM/SSB i wiedzial jak kazdy najmniejszy fragment dziala...

Tomek - SP6VGX (SWL: SP-0316-JG)
QTH: Warszawa, LOKATOR: KO02NG
http://www.sp6vgx.pl/
(Ten post był ostatnio modyfikowany: 30-06-2016 2:58 przez SP6VGX.)
30-06-2016 2:58
Znajdź wszystkie posty użytkownika Odpowiedz cytując ten post
Odpowiedz 


Wiadomości w tym wątku
RE: Programowanie ARM, nauka, środowiska programistyczne IDE - SP6VGX - 30-06-2016 2:58

Skocz do:


Użytkownicy przeglądający ten wątek: 2 gości