Skocz do zawartości

Elvis

Użytkownicy
  • Zawartość

    2596
  • Rejestracja

  • Ostatnio

  • Wygrane dni

    189

Wszystko napisane przez Elvis

  1. Cortex-M3 to ciekawa platforma. Natomiast linux daje możliwości, których goły RTOS nie oferuje. Wszystko oczywiście można zrobić - linux to też program jak każdy inny. Chodzi raczej o nakład pracy. Wczoraj się zmobilizowałem i uruchomiłem kamerkę. Przed końcem konkursu artykuł powinien powstać, więc sens wykorzystania linuxa w robocie będzie lepiej widać.
  2. Ostatnio z braku czasu testy musiałem odłożyć. Jak chodzi o pierwsze pytanie, to u mnie jest jakieś 54MB wolnej pamięci RAM. Do tego 900MB Flash. Więc raczej sporo. Dostęp do PWM jest przez rejestry, nie znalazłem gotowego modułu w linux-ie. Oczywiście taki moduł można samemu napisać. Natomiast co do wykorzystania modułu do budowy robota, to na pewno nie jest to temat łatwy i dobry dla początkujących. Natomiast, czy jest to za duża armata, można polemizować. Jest to na pewno mniejszy kaliber niż montowanie PC w robocie. Pobór prądu jest całkiem znośny, silniki biorą i tak dużo więcej prądu. A jeśli jesteśmy minimalistami, to po co w ogóle używać mikrokontrolerów? Większość opisywanych na forum robotów spokojnie poradziłaby sobie bez mikrokontrolera, nawet mostek H nie jest w pełni wykorzystywany. Moim zdaniem chodzi o poznanie czegoś nowego, zdobycie doświadczenia. Nie tylko budowanie konstrukcji minimalistycznych.
  3. Aktywne pętle to nie najlepszy pomysł. Po pierwsze jeśli chcemy, aby były dokładne musimy wyłączyć przerwania. Inaczej błędy będą dowolnie duże - w zależności ile razy wywołane zostanie przerwanie. Kolejny problem to pobór prądu. Procesor bierze dużo prądu, gdy czeka w pętli. Dla ARM najlepszym rozwiązaniem jest RTOS i co prawda mniejsza dokładność odmierzania czasu, ale za to wykorzystanie mocy procesora. Jeśli potrzebujemy bardzo krótkie, dokładne pętle (czasy pojedynczych µs), to można zrobić jak na AVR. Najlepiej jest napisać to w asemblerze, inaczej kompilator (dokładniej - optymalizator) popsuje nam wyliczenia.
  4. Co do pkt.3 to moduł KAmodBTM222 ma już zainstalowany stabilizator na 3.3V. Więc nie trzeba nic dodawać, wystarczy odpowiednio ustawić zworkę.
  5. W tekście usunąłem informację o dużej dokładności. Błąd może wynosić 100µs, ale przy dłuższych opóźnieniach (rzędu setek ms) nie ma to znaczenia. Nie chciałem modyfikować licznika timera ponieważ w dalszych częściach ten sam timer będzie wykorzystywany do kilku funkcji. Nadal wolny jest Timer1, więc jeśli potrzebujemy bardzo dokładnie mierzyć czasy rzędu mikrosekund można go wykorzystać.
  6. Poprzednia lekcja Lekcja 6 - Zegary i przerwania (ciąg dalszy) Następna lekcja W poprzedniej lekcji uruchomiliśmy PLL oraz Timer0. Jeśli było to nieco niezrozumiałe, nie ma czym się przejmować. Pętli PLL nie będziemy więcej ruszać. Wystarczy wywołać procedurę uruchamiającą i można o PLL (na razie) zapomnieć. Podobnie będzie z Timer0. Zmienimy tylko częstotliwość wywoływania przerwań i więcej nie będziemy się zagłębiać w działanie timer-a. Obecna konfiguracja to wywoływanie przerwania co 1s. Jest to zdecydowanie za rzadko. Ustalmy jakiś inny okres - przykładowo 100us (czyli 10.000 razy na sekundę). W tym celu zmieniamy linijkę: T0_MR0 = 1500; Od tej pory nie będziemy wracali do procedury timer0_init(). Nasze przerwanie jest wołane 10000 na sekundę, więc do sterowania diodami raczej się nie nadaje. Wykorzystamy je jako licznik czasu. Nowa procedura będzie wyglądała następująco: volatile unsigned int t0_counter = 0; void timer0(void) __attribute__ ((interrupt ("IRQ"))); void timer0(void) { if (T0_IR&0x01) { if (t0_counter) t0_counter--; } T0_IR = 0xff;//0x01; VICVectAddr = 0; } Zmienna t0_counter przechowuje nasz licznik. Jest on co 100us zmniejszany o 1. Gdy osiągnie 0 jest zatrzymywany. Do czego nam się to może przydać? Wreszcie możemy napisać znane z AVR procedury delay_us() oraz delay_ms(). Niestety nie będziemy mogli odmierzać czasu z dokładnością do pojedynczych mikrosekund. void delay_100us(unsigned int dly) { t0_counter = dly; while (t0_counter); } void delay_ms(unsigned int ms) { t0_counter = ms*10; while (t0_counter); } Procedury działają praktycznie tak samo - ustawiają naszą zmienną licznikową, po czym czekają, aż zostanie wyzerowana. Bardzo ważne jest zadeklarowanie zmiennej t0_counter jako volatile. Inaczej optymalizator może wyrzucić nasze pętle while z kodu. Teraz możemy napisać bardzo prosty program migający diodami co pół sekundy: int main(void) { GPIO1_IODIR |= LED_MASK; pll_init(); timer0_init(); while (1) { led_swap(); delay_ms(500); } } Pełny kod znajdziemy w pliku program10.zip Program10.zip
  7. O ile dobrze widzę Ron=0,5Ohm, czyli przy 1A będzie spadek 0,5V.
  8. Poczytajcie dokładniej o SPI oraz przetwornikach C/A - to co piszecie nie zadziała. Można natomiast wykorzystać rozwiązanie analogowe. Przy nadajniku dać przetwornik napięcie->częstotliwość, za odbiornikiem częstotliwość->napięcie. Jeśli dobierzemy częstotliwości w zakresie możliwości modułów HM-T/R, powinno działać. Jak chodzi o przetworniki C/A i A/C to jedynym wyjściem jest zastosowanie mikrokontrolerów po obu stronach. To że oba moduły używają SPI nie oznacza, że po połączeniu nastąpi transmisja czegokolwiek sensownego.
  9. Nie wszystkie obsługują. Najszybsze jakie testowałem to CC1100 - prędkość do 500kbps. Natomiast tanie/proste moduły pracuję tylko z niskimi prędkościami. Nie jest to duży problem, bo takie moduły nie służą do przesyłania plików (np. filmów), do tego lepiej wykorzystać wifi. Mają za to mały pobór prądu i niską cenę. Jeśli przesyłamy niewiele danych (np. odczytujemy stan czujników, albo wysyłamy komendę dla robota), to nawet 1200bps wystarczy. [ Dodano: 24 Lip 10 09:19 ] Według noty producenta moduły HM-T obsługują do 4800bps (9600 max).
  10. Ja bym radził wczytać się w dokumentację procesora - User's Manual (http://ics.nxp.com/support/documents/microcontrollers/pdf/user.manual.lpc2109.lpc2114.lpc2119.lpc2124.lpc2129.lpc2194.lpc2210.lpc2212.lpc2214.lpc2220.lpc2290.lpc2292.lpc2294.pdf). Co prawda tylko 390 stron (nowsze procesory mają po 600), ale jest tam wszystko. Na początek może nie jest to łatwe do czytania, ale można się przyzwyczaić.
  11. Spokojnie, nie ma obawy, da się mieć kilka kompilatorów. Ja mam C++Builder6, VisualStudio, AVR Studio i jeszcze kilka na raz. Problem polega na tym, że nie wszystkie mogą być domyślnymi. Większość (jak np. C++ Builder) nie potrzebują ścieżki do plików - IDE poradzi sobie bez nich. Instalator dodaje je, żeby można było kompilować z linii poleceń. Ważne pytanie, jakich windowsów używasz? [ Dodano: 23 Lip 10 07:01 ] Spróbuj z wiersza poleceń uruchomić: arm-elf-gcc --version Jak nie zadziała to jeszcze: c:\winarm\bin\arm-elf-gcc --version
  12. Chyba brakuje ścieżki do kompilatora w zmiennej PATH: C:\WinARM\utils\bin;C:\WinARM\bin
  13. Prawdopodobnie miałeś "make" zainstalowany na komputerze przed instalacją WinARM. Odinstaluje ten program, albo zmień kolejność wyszukiwania w ścieżce dostępu.
  14. Bascom-a nie znam, ale program wygląda poprawnie. Musisz tylko zmniejszyć prędkość transmisji. Zamiast: $baud = 9600 Spróbuj np. 1200 $baud = 1200
  15. Poprzednia lekcja Lekcja 5 - Zegary i przerwania Następna lekcja Poznaliśmy już podstawy sterowania liniami I/O, czas zapoznać się z zegarami oraz obsługą przerwań. Procesory LPC211x mają bardzo rozbudowany układ zegarowy oraz układ przerwań. W kursie poznamy jedynie fragment, najpotrzebniejszy w praktyce. W przypadku procesorów AVR działanie było bardzo proste - zewnętrzny kwarc, albo wbudowany układ RC generował częstotliwość z którą pracował układ. Procesory ARM mają znacznie bardziej skomplikowane działanie zegara. Zewnętrzny rezonator ma częstotliwość 12MHz (w przypadku naszej płytki). Częstotliwość ta jest następnie zwiększana w pętli PLL. Dopiero taki sygnał jest podawany na procesor (zegar główny CCLK). Układy peryferyjne (np. porty I/O, timery itd) są taktowane za pomocą innego sygnału, oznaczanego PCLK. Jest to częstotliwość CCLK podzielona przez pewną wartość - po resecie wynosi ona 4. Na razie nie używamy pętli PLL, więc procesor pracuje z następującymi ustawieniami: • Fosc = 12MHz • CCLK = 12MHz • PCLK = 3MHz Znając częstotliwość taktowania wykorzystamy Timer0 do cyklicznego wywoływania procedury. Ustawimy zegar tak, aby co 1s wywoływał procedurę obsługi przerwania. W niej będziemy naprzemiennie zapalać diody D0 oraz D1. Najpierw napiszemy procedurę obsługi przerwania: void timer0(void) __attribute__ ((interrupt ("IRQ"))); void timer0(void) { if (T0_IR&0x01) { led_swap(); } T0_IR = 0xff; VICVectAddr = 0; } Instrukcja if sprawdza, przyczynę wywołania przerwania. Ostatnie dwie instrukcje zerują flagi przerwań. Procesory ARM7 mają kilka rodzajów przerwań. Warte uwagi jest jeszcze przerwanie "szybkie" FIQ - jest tylko jedno, za to jego obsługa następuje o kilka taktów zegara szybciej. Nam, aż tak bardzo na czasie nie zależy, więc wystarczą nam przerwania IRQ. Układ timera można skonfigurować na bardzo wiele sposobów. Wykorzystamy bardzo prosty: ustalimy licznik główny na czas odpowiadający 1s, po osiągnięciu przez licznik tej wartości będzie wywoływana nasza procedura przerwania. Zegar konfigurujemy następująco: T0_TCR = 2; T0_PR = 0; T0_MR0 = 12000000/4; T0_MCR = 3; T0_TCR = 1; T0_TCR - rejestr kontrolny timera. 2 resetuje zegar, 1 uruchamia T0_PR - ustala presklaler. Zero oznacza brak, czyli timer działa z częstotliwością PCLK. T0_MR0 - jest to wartość do której liczy timer. Wpisujemy równą częstotliwości PCLK, czyli przerwanie wystąpi po dokładnie 1s. T0_MCR - rejestr ustala działanie timera. 3 oznacza, że po osiągnięciu wartości wpisanej do T0_MR0 wywołane zostanie przerwanie, a timer zacznie liczyć od początku Więcej informacji o rejestrach i ich wartościach znajdziemy w dokumentacji procesora. Cała procedura inicjalizująca timer wygląda następująco: void timer0_init(void) { VICIntSelect &= ~0x10; VICIntEnable = 0x10; VICVectCntl0 = 0x24; VICVectAddr0 = (unsigned long)timer0; T0_TCR = 2; T0_PR = 0; T0_MR0 = 12000000/4; T0_MCR = 3; T0_TCR = 1; asm volatile ( "STMDB SP!, {R0} \n" "MRS R0, CPSR \n" "BIC R0, R0, #0xC0 \n" "MSR CPSR, R0 \n" "LDMIA SP!, {R0}" ); } Początek konfiguruje wektor przerwań. Układy LPC z rodziny ARM7 mają bardzo skomplikowane przerwania. Na szczęście w nowszych układach (Cortex) zostało to już uproszczone. Tutaj pozostaje przeczytać dokumentację procesora, albo wykorzystać kod na razie nie wnikając jak działa. Fragment w asemblerze uruchamia przerwania, jest to odpowiednik sei() w AVR. Większość komercyjnych środowisk ma do tego gotowe procedury, niestety darmowy gcc wymaga sięgnięcia do asemblera. W pliku program08.zip znajdziemy pełny program. Efekt działania to dwie migające na przemian diody. Uruchamiamy pętlę PLL Praca z częstotliwością 12MHz nie jest niczym nadzwyczajnym, zwykła ATMega bywa szybsza. Czas więc nieco przyspieszyć. Ustawimy PLL, tak aby mnożył częstotliwość rezonatora przez 5. Otrzymamy wtedy parametry pracy: • Fosc = 12MHz • CCLK = 60MHz • PCLK = 15MHz Procedura konfiguracji pętli PLL: void pll_init() { MAM_MAMTIM = 3; MAM_MAMCR = 2; SCB_PLLCFG = 0x24; SCB_PLLCON = 0x1; SCB_PLLFEED = 0xAA; SCB_PLLFEED = 0x55; while (!(SCB_PLLSTAT&0x0400)); SCB_PLLCON = 0x3; SCB_PLLFEED = 0xAA; SCB_PLLFEED = 0x55; } Pierwsze dwie instrukcje konfigurują pamięć Flash procesora - jest ona wolniejsza niż rdzeń, więc musimy ustawić podzielnik jej zegara na 3. Dalszy ciąg procedury konfiguruje i uruchamia pętlę PLL. Najważniejsza jest linia: SCB_PLLCFG = 0x24; Wartość 0x24 ustala mnożnik M=5, P=3. Jeśli chcemy uzyskać inną częstotliwość pracy możemy te wartości zmienić. Informacja o sposobie obliczania jest dostępna w dokumentacji procesora. Warto zapamiętać, że częstotliwość pracy wybiera program. Bywa to bardzo wygodne, ponieważ im wyższa prędkość pracy, tym wyższy pobór prądu. Procesory ARM mogą w trakcie działania zmieniać swoją prędkość działania. Pozostaje zmienić ustawienia naszego timera - zgodnie z nową częstotliwością taktowania: T0_MR0 = 60000000/4; Pełny program znajdziemy w archiwum program09.zip. Działa jak poprzedni, jednak procesor pracuje już z pełną prędkością. Autor kursu: Elvis Pomoc przy edycji materiałów wideo, formatowaniu i publikacji: Treker Program09.zip Program08.zip
  16. adam30010 jeśli się na czymś nie znasz, to lepiej się nie odzywaj. Sterowanie silnika krokowego przez stabilizator napięcia to nie jest dobry pomysł, jak napisał Sabre silniki krokowe steruje się prądowo. Tzw. chopper jest dobrą metodą aby ograniczyć prąd. Można kupić gotowe układy sterujące np. mostek L298 + sterownik z chopperem L297. Duży wybór oferuje również allegromicro http://www.allegromicro.com/en/
  17. Poprzednia lekcja Lekcja 4 - Porty I/O (ciąg dalszy) Następna lekcja Sterowanie diodami - usprawnianie programu Poprzednia wersja programu działała całkiem ładnie, jednak w pętli głównej 8 razy (tyle ile jest diod) powtarzał się ten sam kod. Nie jest to dobra metoda programowania. Po pierwsze program jest długi (wyobraźmy sobie sterowanie 100 diodami). Po drugie, niewygodnie jest program modyfikować - zmiana na zapalanie diod parami wymaga przeglądania całej pętli głównej. Jeśli wielokrotnie wykonujemy taki sam (lub prawie taki sam) program, zamiast kopiowania kodu, możemy zastosować pętlę. W naszym przypadku pętla będzie miała 8 kroków (każdy, aby zapalić kolejną diodę). Pozostaje pytanie, jak zapisać, kiedy ma się zapalić która dioda. Moglibyśmy wykorzystać operacje na bitach oraz fakt, że diody są przyłączone do kolejnych linii. Jednak w ten sposób bardzo ciężko będzie zmienić program. Użyjemy więc tablicy. Będziemy w niej przechowywać maski zapalanych diod w kolejnych krokach pokazu. const unsigned int LEDS_PATTERN[8] = { LED_0, LED_1, LED_2, LED_3, LED_4, LED_5, LED_6, LED_7 }; Definiujemy tablicę 8 liczb. Są to maski kolejno zapalanych LED-ów. Do programu dodamy licznik leds_index. Będzie on wskazywał, który indeks w tablicy mamy aktualnie "wyświetlać". int leds_index = 0; while (1) { leds_set(LEDS_PATTERN[leds_index]); leds_index++; if (leds_index>=8) leds_index = 0; delay(DELAY); } Instrukcja leds_set(LEDS_PATTERN[leds_index]); odczytuje maskę z tablicy LEDS_PATTERN i za pomocą procedury leds_set() zapala odpowiednie diody. Następnie leds_index jest zwiększane o jeden, aby przejść do kolejnego etapu wyświetlania. Za pomocą IF-a sprawdzamy, czy nie wyszliśmy poza zakres tablicy. Jeśli tak, rozpoczynamy animację od nowa. W pliku program03.zip znajdziemy pełny kod programu. Zmieniając dane w tablicy LEDS_PATTERN[] możemy zmienić układ zapalanych diod. Możemy jednocześnie zapalać kilka LED-ów, używając znanego operatora |. Poniżej widzimy działanie programu dla tablicy LEDS_PATTERN[]. Cała reszta programu pozostaje bez zmian. const unsigned int LEDS_PATTERN[8] = { 0, LED_3|LED_4, LED_2|LED_5, LED_1|LED_6, LED_0|LED_7, LED_1|LED_6, LED_2|LED_5, LED_3|LED_4 }; W pliku program04.zip znajdziemy jeszcze jedną wersję programu. Tym razem diody są zapalane najpierw w kolejności zdefiniowanej w tablicy LEDS_PATTERN[], a później od końca. Jest to przykład na wykorzystanie dodatkowej zmiennej do przechowywania kierunku zmian. W podobny sposób sterujemy kierunkiem pracy silnika krokowego. Wejścia - obsługa przycisku Gdy mamy już dość zapalania i gaszenia diodek, czas coś odczytać z linii procesora. Odnajdujemy na schemacie oraz płytce przycisk S2 - INT1. Jest on podłączony (przez zworkę INT1) do linii P0.14 procesora. Programowa obsługa wejścia jest bardzo prosta. Po resecie, linie procesora ustawiane są jako wejścia, więc nie musimy ich konfigurować (za pomocą rejestru IO0DIR). Pozostaje jedynie odczytać stan wejścia. Służą do tego rejestry: IO0PIN dla portu 0 oraz IO1PIN dla portu 1. Jeśli linia połączona jest z masą, odczytamy na odpowiednim miejscu bit o wartości 0. Jeśli ma potencjał bliski napięciu zasilania (3.3V), to odczytamy 1. Aby z 32 bitów składających się na rejestr wybrać interesujący używamy operatora koniunkcji (&). Czyli wyrażenie IO0PIN & _BV(14) daje wartość 0, gdy na linii P0.14 jest napięcie 0V (masa), oraz 1 gdy podłączymy napięcie 3.3V. Na naszej płytce rezystor R5 łączy zasilanie (3.3V) z linią P0.14. Więc, gdy S2 jest rozłączony, na linii P0.14 pojawi się logiczne 1. Po naciśnięciu S2, linia P0.14 zostanie zwarta do masy. Otrzymamy więc wartość 0. Czas napisać program, który odczyta stan przycisku S2. Pełny kod znajdziemy w pliku program05.zip while (1) { if (GPIO0_IOPIN & _BV(14)) leds_set(LED_0); if ((GPIO0_IOPIN & _BV(14))==0) leds_set(LED_4); } W języku C warunek jest prawdziwy, gdy jest dowolną liczbą różną od 0. Stąd pętla nieskończona while (1) ; ma jako warunek 1, czyli zawsze logiczną prawdę. Równie dobrze można napisać while (-5), czy while (1020). Instrukcja warunkowa "if" warunek przyjmuje w nawiasach (). Jest on obliczany jako liczba, i jeśli wynosi 0 instrukcja po if jest pomijana, jeśli cokolwiek różnego od 0 to wykonywana. if (GPIO0_IOPIN & _BV(14)) - linia P0.14 będzie podłączona do 3.3V to instrukcja po "if" zostanie wykonana (czyli leds_set(LED_0)). Natomiast if ((GPIO0_IOPIN & _BV(14))==0) działa odwrotnie - wykona instrukcję po "if" jeśli P0.14 połączymy z masą. Program działa następująco: 1) Gdy S2 jest rozwarty, na P0.14 mamy napięcie 3.3V, pierwszy if jest spełniony, wykonywana jest instrukcja po nim - zapalana jest dioda D0 2) Gdy S2 zostanie przyciśnięty, na P0.14 pojawi się potencjał masy (0V), drugi if będzie spełniony - zapali się dioda D4. Czekanie na naciśnięcie przycisku Sposób obsługi przycisków pokazany w poprzednim przykładzie jest prosty ma jednak dużą wadę. Kod zapalający diodę wykonywany jest wiele razy. Często nie chcemy, aby program tak działał - chcemy wykryć naciśnięcie przycisku, wykonać działanie dokładnie raz (np. uruchomić robota). Do czekania na zdarzenie posłuży nam pętla while(). Wykonywana jest ona tak długo, jak jest parametr jest prawdziwy (różny od zero). while (GPIO0_IOPIN & _BV(14)) ; Taka pętla będzie się wykonywać (i nic nie robić ; oznacza pustą instrukcję) tak długo, jak przycisk S2 będzie zwolniony (nie naciśnięty). Warunek działa jak poprzednio. S2 jest zwolniony, na P0.14 jest napięcie 3.3V, odczytujemy wartość bitu 1, więc warunek jest prawdziwy. Aby poczekać na zwolnienie przycisku, zmieniamy warunek pętli. while ((GPIO0_IOPIN & _BV(14))==0) ; Pętla główna wygląda następująco: while (1) { while (GPIO0_IOPIN & _BV(14)) ; leds_index++; if (leds_index>=8) leds_index = 0; leds_set(LEDS_PATTERN[leds_index]); while ((GPIO0_IOPIN & _BV(14))==0) ; } Najpierw czekamy na naciśnięcie S2. Zapalamy kolejną diodę (kod wygląda znajomo - był opisywany wcześniej). Następnie czekamy na zwolnienie S2. Drgania styków Osoby, które wcześniej programowały obsługę mikroprzełączników zwrócą uwagę, że program ma błąd - przy naciskaniu i zwalnianiu przycisku generowanych jest wiele bardzo krótkich impulsów. Są one efektem drgania styków wewnątrz przełącznika. Powinniśmy je eliminować za pomocą kilkukrotnego odczytu stanu przycisku. Jednak na naszej płytce ewaluacyjnej program działa w pełni poprawnie. Dzieje się tak za sprawą kondensatora C9. Eliminuje on drgania styków sprzętowo. Jest to układ wart zapamiętania - czasem zamiast komplikować program, można po prostu dodać kondensator. Pełny kod źródłowy przykładu znajdziemy w pliku program06.zip. W pliku program07.zip znajdziemy nieco inny program - działa podobnie, jednak zamiast zapalać kolejne diody, przy kolejnych naciskaniach S2 zapala lub gasi diodę D0. Schemat działania jest identyczny jak omawianego przykładu. Program07.zip Program06.zip Program05.zip Program04.zip Program03.zip
  18. Poprzednia lekcja Lekcja 3 - Porty I/O Następna lekcja Procesor LPC2114 jest układem w pełni 32-bitowym. Dzięki temu porty I/O również mogą być 32-bitowe. W przypadku rodziny AVR porty oznaczane były kolejnymi literami A,B,C, itd. (PORTA, PORTB, PORC, itd.). Każdy port miał maksymalnie 8 linii (bo procesor był 8-bitowy). W przypadku LPC2114 porty są oznaczane numerami. Mamy więc port 0 oraz 1. Każdy może mieć maksymalnie 32 linie (w rzeczywistości mają nieco mniej). W dokumentacji, linie oznaczane są na dwa sposoby: 1) P0[5] - oznacza linię 5 portu 0, P1[16] - linię 16 portu 1 itd. 2) PORT0.2 - oznacza linię 2 portu 0, PORT1.20 - linię 20 portu 1 itd. W pliku LPC2114_2124_datasheet.pdf znajdujemy na stronie 5 rysunek opisujący wszystkie wyprowadzenia naszego procesora. Po resecie (uruchomieniu) procesora, wszystkie piny są skonfigurowane jako wejścia. Zamiast czytać dokumentację procesora, czasem łatwiej jest odczytać dane o portach I/O ze schematu układu. W dokumentacji naszej płytki możemy znaleźć schemat, poniżej widzimy symbol procesora oraz opisane jego wyprowadzenia: Warto poświęcić chwilę na dokładniejsze przeczytanie schematu naszej płytki. Na początek zajmiemy się sterowaniem diodami LED. Jak widać są one podłączone do linii nazwanych LCD0, LCD1, LCD2, itd. (nazwa wynika stąd, że te same linie posłużą nam później do sterowania wyświetlaczem LCD. Z poziomu procesora sterujemy liniami za pomocą logicznych zer oraz jedynek. Ustawienie bitu, czyli logicznej jedynki powoduje pojawienie się napięcia zasilającego procesor (3.3V) na odpowiednim wyprowadzeniu. Natomiast wyzerowanie bitu, czyli logiczne zero powoduje zwarcie pinu do masy (GND). Następujący kod: IO0SET = _BV(2); Powoduje, że linia 2 portu 0 zostanie ustawiona, czyli pojawi się na niej napięcie 3.3V. Aby wyzerować stan linii używamy następującego kodu: IO0CLR = _BV(2); Widzimy więc, że inny rejestr jest używany do ustawiania bitów (IO0SET), inny do zerowania (IO0CLR). W AVR wykorzystywany był jeden rejestr (np. PORTA), a zapalanie gaszenie bitów wykonywane było za pomocą operacji na bitach (|= oraz &= ). Za pomocą jednej instrukcji można ustawić więcej niż jeden bit. Służy do tego operator alternatywy |. IO0SET = _BV(2)|_BV(3)|_BV(4); Powoduje jednoczesne ustawienie logicznej jedynki na liniach P0.2, P0.3 oraz P0.4. Niemal identycznie można wyzerować kilka bitów na raz. IO0CLR = _BV(2)|_BV(3)|_BV(4); Uwaga! Kod znany z AVR, czyli IO0SET |= _BV(2); też zadziała, ale jest błędny. Może powodować niepoprawne działanie programu. Zagadka: kto zgadnie kiedy i jaki może powstać błąd? Podobnie jak w przypadku AVR, przed użyciem linii, musimy ustalić kierunek jej działania (wejście/wyjście). Służy do tego rejestr IO0DIR (oraz IO1DIR). Ustawienie bitu powoduje przełączenie linii w tryb wyjścia, wygaszenie (domyślnie) ustawia linię jako wejście. Do rejetru IO0DIR używamy zwykłych operacji bitowych (|= oraz &=). Pierwszy program Nasz pierwszy przykładowy program wygląda następująco (Program01.zip): #include "lpc2114.h" #define _BV(x) (1<<(x)) int main(void) { volatile int i; GPIO1_IODIR |= _BV(16)|_BV(17); while (1) { GPIO1_IOSET = _BV(16); GPIO1_IOCLR = _BV(17); for (i=0;i<100000;i++) ; GPIO1_IOCLR = _BV(16); GPIO1_IOSET = _BV(17); for (i=0;i<100000;i++) ; } } Program definiuje makro _BV() - jest ono bardzo wygodne i znane wszystkim programującym w C na AVR. Instrukcja GPIO1_IODIR |= _BV(16)|_BV(17); sprawia, że linie 16 oraz 17 portu 1 pracują jako wyjścia. IO1SET = _BV(16) sprawia, że na linii P1.23 pojawia się napięcie 3.3V. Efektem jest zapalenie diody D0 (na schemacie ma numer 16, ale na płytce 0). IO1CLR = _ BV(17) gasi diodę D1. GPIO1_IOSET = _BV(16); GPIO1_IOCLR = _BV(17); for (i=0;i<100000;i++) 1) Zapala diodę D0 2) Gasi diodę D1 3) Czeka pewien czas w pętli (niestety nie mamy dostępnej funkcji _delay_ms() - w kolejnych lekcjach nauczymy się jak samemu ją dodać) Następny blok instrukcji gasi diodę D0, zapala D1 i ponownie czeka pewien czas. Całość zamknięta jest w nieskończonej pętli - w efekcie program w nieskończoność (no może do wyłączenia zasilania) na zmianę zapala diodę D0 i D1. Program drugi - więcej diod Pierwszy program miał kilka istotnych wad. Przede wszystkim sterował tylko 2 pierwsze diody. Pozostałe świeciły cały czas. Teraz czas na Program02.zip. Dodamy obsługę wszystkich diod. Pierwszy krok to zdefiniowane stałych - poprzednio odwoływaliśmy się do linii sterujących LED-ami bezpośrednio. Kod _BV(17) nie jest zbyt czytelny. Dla dwóch diod mogliśmy zapamiętać, która jest podłączona do której linii, jednak dla 8 może już być trudniej. Zamiast uczyć się schematu na pamięć zdefiniujemy, gdzie są podłączone kolejne diody: #define LED_0 _BV(16) #define LED_1 _BV(17) #define LED_2 _BV(18) #define LED_3 _BV(19) #define LED_4 _BV(20) #define LED_5 _BV(21) #define LED_6 _BV(22) #define LED_7 _BV(23) Dzięki temu program będzie bardziej czytelny. Definiujemy również "maskę", czyli wartość odpowiadającą wszystkim liniom z podłączonymi diodami. Dzięki temu łatwiej będzie ustawić wszystkie linie sterujące LED-ami w tryb wyjścia. #define LED_MASK (LED_0|LED_1|LED_2|LED_3|LED_4|LED_5|LED_6|LED_7) Gdy mamy już zdefiniowane stałe, kolejnym krokiem jest utworzenie procedur, czyli fragmentów programu, które będą wywoływane wielokrotnie, z różnymi parametrami. Pierwsza procedura posłuży nam do zapalania wybranych diod, a wygaszania pozostałych. void leds_set(unsigned int leds) { GPIO1_IOCLR = LED_MASK; GPIO1_IOSET = leds; } Najpierw gasimy wszystkie diody (zdefiniowana wcześniej maska ułatwia to zadanie). Następnie zapalamy wybrane.Druga procedura pomocnicza zapewni opóźnienia. void delay(unsigned int d) { volatile int i; for (i=0;i<d;i++) ; } Warto zwrócić uwagę na słowo kluczowe volatile. Bez niego optymalizator próbowałby przyspieszyć działanie naszej pętli opóźniającej. Mając przygotowane procedury, program główny jest już bardzo prosty: GPIO1_IODIR |= LED_MASK; while (1) { leds_set(LED_0); delay(DELAY); leds_set(LED_1); delay(DELAY); leds_set(LED_2); delay(DELAY); leds_set(LED_3); delay(DELAY); leds_set(LED_4); delay(DELAY); leds_set(LED_5); delay(DELAY); leds_set(LED_6); delay(DELAY); leds_set(LED_7); delay(DELAY); } Najpierw wszystkie linie, do których podłączone są diody ustawiamy jako wyjścia, następnie w nieskończonej pętli zapalamy kolejne diody. Następna część opublikowana zostanie już w najbliższą niedzielę, a jej tematem również będą porty I/O. Autor kursu: Elvis Pomoc przy edycji materiałów wideo, formatowaniu i publikacji: Treker Program02.zip Program01.zip
  19. To prawda, że wszyscy handlowcy przekonują obecnie do kupowania Cortex-Mx. Są to nowe procesory, więc obiecują niesamowite cuda. Powód wybrania starszego procesora jest bardzo prosty - cena. Za ~89 zł dostajemy procesor z całkiem sporą pamięcią programu (128KB), szybki (60MHz), ale co najważniejsze dostajemy gotową, uruchomioną płytkę ewaluacyjną z układami peryferyjnymi. Podobna płytka pod procesor Cortex-M3 jest znacznie droższa. Jak chodzi o procesor, to nie zachęcam nikogo do używania LPC2114 we własnych projektach (głównie ze względu na dwa napięcia zasilające). Lepiej użyć chociażby LPC214x, czy LPC176x. Jak chodzi o Cortex-M3, to w przypadku LPC176x jest bardzo łatwo przejść z LPC2114 - nawet nazwy rejestrów są w większości identyczne.
  20. Poprzednia lekcja Lekcja 2 - Kompilator Następna lekcja Do kompilacji programów będziemy wykorzystywać zestaw programów dostępny pod nazwą WinARM. Bezpośredni link do wersji 20060606 wykorzystywanej w kursie znajduje się tutaj: http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects/WinARM-20060606.zip (95MB). WinARM podobnie jak WinAVR bazuje na darmowym (licencja GNU) kompilatorze GCC. Kursy dotyczące gcc przydadzą się również w przypadku WinARM, więc osoby zainteresowane pogłębianiem wiedzy o działaniu kompilatora zachęcam do poszukiwania informacji o gcc dostępnych w internecie. Instalacja Archiwum WinARM-20060606.zip zawiera folder WinARM. W przykładach zakładam, że rozpakowujemy archiwum w katalogu głównym dysku C, czyli powstaje folder C:\WinARM, a w nim wszystkie dostarczone programy. Zanim przystąpimy do pracy musimy skonfigurować zmienne systemowe (ścieżki dostępu). Opis będzie dotyczył Windows XP, jednak w nowszych systemach konfiguracja powinna przebiegać podobnie. Najpierw sprawdzamy, czy na komputerze jest już zainstalowany program „make”. Jeśli jest może to powodować problemy z kompilacją. Warto przed instalacją sprawdzić, czy program już jest, żeby w razie problemów łatwiej było ustalić ich przyczynę. W celu sprawdzenia obecności programu uruchamiamy okienko wiersza poleceń klikając Start->Wszystkie programy->Akcesoria->Wiersz polecenia W okienku wpisujemy „make” i naciskamy enter. Powinien pojawić się komunikat „Nazwa 'make' nie jest rozpoznawana jako polecenie wewnętrzne lub zewnętrzne, program wykonywalny lub plik wsadowy”. Jeśli „make” jest już zainstalowany zobaczymy komunikat podobny do następującego: Jeśli make nie ma na komputerze dalsza instalacja powinna przebiegać bez problemu. Jeśli jest, czasem mogą pojawić się problemy z kompilacją. Zamykamy okienko wiersza poleceń. Aby system mógł odnaleźć nasz kompilator, do ścieżki systemowej musimy dodać fragment: ;C:\WinARM\utils\bin;C:\WinARM\bin Otwieramy panel sterowania (menu Start->panel sterowania), a w nim wybieramy ikonkę „System”. W oknie właściwości systemu wybieramy zakładkę „Zaawansowane” i klikamy przycisk „zmienne systemowe”. W nowo otwartym okienku odnajdujemy zmienną PATH: Wybieramy ją i klikamy Edytuj. Następnie do wartości zmiennej dopisujemy: ;C:\WinARM\utils\bin;C:\WinARM\bin Ważne jest, aby nie pominąć średnika ( między kolejnymi fragmentami. Zamykamy wszystkie okienka i uruchamiamy ponownie wiersz poleceń (jeśli poprzednio zostawiliśmy otwarty, musimy go zamknąć i otworzyć ponownie). Najpierw wpisujemy: make - powinien pojawić się komunkat: Następnie wpisujemy: arm-elf-c++ powinniśmy otrzymać komunikat „arm-elf-c++: no input files”. Teraz mamy gotowy do działania kompilator dla procesorów ARM. Edytor Do edycji plików będziemy wykorzystywali dostarczony z WinARM edytor „Programers notepad 2”. Jeśli mamy w systemie już zainstalowany ten program, i tak warto skorzystać z dostarczonego w pakiecie, ponieważ ma on skonfigurowane opcje przydatne podczas kompilacji (dodane skróty do uruchamiania kompilatora). Otwieramy folder: C:\WinARM\pn Znajduje się w nim plik pn.exe. Będziemy często z niego korzystać więc, warto utworzyć skrót na pulpicie do tego programu. Po uruchomieniu pojawia się okno podobne do następującego: Pierwszy projekt Program może być tworzony z wielu plików. Taki zestaw plików określamy jako projekt. Oba terminy będą używane zamiennie. Pobieramy plik Program01.zip (link na dole artykułu), rozpakowujemy do wybranego przez nas folderu i otwieramy wybierając opcję File->Open Project(s)... Okno powinno wyglądać następująco: W lewej części widoczne jest okno „Projects” w nim widzimy nasz projekt (nazwany Program01) oraz pliki składowe projektu: program01.c - nasz pierwszy program w C lpc2114.h - plik nagłówkowy z definicjami wykorzystywanymi w naszych programach Makefile - plik z instrukcją jak kompilować nasz program LPC2114-ROM.ld - informacja o rozmieszczeniu danych w pamięci procesora crt0.S - plik z kodem w asemblerze, wykonuje działania systemowe Nas głównie interesuje pierwszy plik (program01.c), czasem będziemy zaglądać do pliku lpc2114.h (ale nie modyfikować go), plik Makefile jest interesujący głównie gdy tworzymy nowy projekt. Kompilacja i programowanie Przed kompilacją otwieramy plik program01.c (to ważne!). Następnie wybieramy z menu Tools wybrać opcję „[WinARM_C] make all” (można też nacisnąć Ctrl+F7). W oknie Output (widocznym na dole ekranu) pojawią się komunikaty kompilatora. Jeśli wszystko przebiegnie prawidłowo na końcu będzie: Errors: none -------- end -------- > Process Exit Code: 0 > Time Taken: 00:02 Co oznacza, że nasz program został poprawnie skompilowany. Czas wgrać nasz program do procesora i zobaczyć jak działa. Do programowania będziemy używać łącza RS232 oraz oprogramowania dostarczonego z WinARM. Kabel RS232 podłączamy do gniazda COM0 płytki ZL1ARM. Otwieramy plik Makefile i odszukujemy fragment: LPC21ISP = lpc21isp_beta LPC21ISP_PORT = com1 LPC21ISP_BAUD = 115200 LPC21ISP_XTAL = 12000 Tutaj jest podana konfiguracja portu. Jeśli zamiast COM1 wykorzystujemy inny port, należy go zmienić. Jak sprawdzić który port COM mamy podłączony do komputera? W Panelu Sterowania wybieramy System, a następnie zakładkę Sprzęt. Klikamy przycisk „Manager urządzeń” i szukamy opcji „Porty (COM i LPT). U mnie dostępny jest port COM1. Gdy ustawimy parametry portu w pliku Makefile, zapisujemy plik i poleceniem „[WinARM_C] make programm” (lub naciskając ctrl+F5) programujemy układ. Pierwszy program jest bardzo prosty - na zamianę zapala i gasi diody LED0 i LED1. W następnej lekcji zobaczymy, jak działa nasz program. Czas, który minie do ponownej lekcji proponujemy na zakup (jeśli oczywiście ktoś chce) oraz zapoznanie się z płytką ZL1ARM dostępną w Kamami. Może sami bawiąc się kodem zauważycie, które polecenia powodują konkretne zachowanie modułu? Przypominamy, że jeśli przy składaniu zamówienia podacie hasło DIODA, dostaniecie 10% rabatu oraz programator gratis! Problemy i niejasności dotyczące tej części prosimy zadawać w tym temacie. Autor kursu: Elvis Pomoc przy edycji materiałów wideo, formatowaniu i publikacji: Treker Program01.zip
  21. CrossStudio używa gcc, więc możesz spróbować takiej składni: asm volatile ( "STMDB SP!, {R0} \n" "MRS R0, CPSR \n" "BIC R0, R0, #0xC0 \n" "MSR CPSR, R0 \n" "LDMIA SP!, {R0}" );
  22. Lekcja 1 - Wstęp Następna lekcja Niniejszy kurs ma na celu ułatwienie rozpoczęcia przygody z procesorami ARM. Przeznaczony jest raczej dla osób, które już mają pewne doświadczenie w programowaniu i znają podstawy języka C. Podczas kursu będę raczej koncentrować się na aspektach charakterystycznych dla programowania ARM, niż na ogólnym opisie języka C. Osoby zaczynające przygodę z językiem C namawiam do nauki programowania na PC. Jest to o wiele łatwiejsza i szybsza metoda nauki. Programy kompilują i uruchamiają się natychmiast (odpada konieczność programowania procesora), istnieje możliwość debugowania programów. Dodatkowo przypominamy, że zagadnienia samego języka C są szczegółowo omawiane w poprzednim kursie: http://kursC.forbot.pl Na potrzeby kursu postanowiłem wykorzystać gotową płytkę ewaluacyjną. Powód jest prosty - chodzi o naukę programowania, nie o rozwiązywanie problemów sprzętowych. Wybór padł na płytkę ZL1ARM dostępną w sklepie internetowym Kamami. Uwaga! Niestety płytka została wycofana z produkcji. Gotowa płytka ma kilka istotnych zalet: ucząc się, mamy pewność, że część elektroniczna jest sprawna, jeśli coś nie działa, szukamy błędu w programie, a nie połączeniu układów programy przykładowe zostały przetestowane w takim samym układzie jak dostępny dla każdego czytelnika, więc powinny działać zaraz po wgraniu procesory ARM dostępne są w obudowach SMD o dość małym rastrze (0,5mm) więc ich lutowanie ręczne nie jest łatwe dla początkujących do poznania procesora mamy już gotowe peryferia płytkę można rozszerzać o nowe układy, podłączając je np. na płytce prototypowej Płytka ZL1ARM zawiera gotowy układ mikroprocesorowy o następujących cechach: • procesor LPC2114 (128kB pamięci programu, 16kB pamięci RAM, prędkość do 60MHz) • 8 diod LED (wraz z układem buforującym) • 2 porty RS232 (wraz z konwerterami napięć oraz układem resetowania) • 1 mikroprzełącznik z opcją działania jako przerwanie • potencjometr do testowania przetwornika A/C • termistor podłączony do wejścia A/C • stabilizatory napięć: 1.8V, 3.3V, 5V • kwarc 12MHz • gniazdo JTAG • gniazdo do podłączenia wyświetlacza LCD • wyprowadzenie wszystkich pinów procesora Do pełnego wykorzystania kursu potrzebne będą: 1) płytka ZL1ARM (dostępna w Kamami) 2) zasilacz 9-12V (np. zasilacz uniwersalny) 3) przewód RS232 lub/oraz przejściówka RS232<->USB 4) multimetr Procesor LPC2114 nie jest układem nowym. Ma kilka poważnych wad (największa do dwa napięcia zasilania 1,8V oraz 3,3V), jednak do nauki programowania jest w zupełności wystarczający. Programowanie nowszych układów (np. LPC214x) jest bardzo podobne. Więcej informacji o procesorze LPC2114 znaleźć można na stronie producenta oraz w datasheet z ogólnym opisem procesora. Natomiast pełny opis możliwości procesora zawiera dokument zatytułowany LPC21xx and LPC22xx User manual. Kurs dostępny będzie jako kolejne lekcje, poziom trudności będzie wzrastał wraz z poznawaniem kolejnych funkcji. Zachęcam do własnych eksperymentów oraz dyskusji, które tematy zostały opisane zbyt ogólnie lub zbyt dokładnie. Autor kursu: Elvis Pomoc przy edycji materiałów wideo, formatowaniu i publikacji: Treker
  23. Dzisiaj zamówiłem takie procesorki w kamami. Jak przyjdą zrobię płytkę i napiszę jak chodzą
  24. Wkleiłem link do obudowy HVQFN33 bo była najtańsza. Nigdy takich nie lutowałem - może ktoś ma z tym doświadczenie? Da się polutować "domowym" sposobem?
×
×
  • Utwórz nowe...