Skocz do zawartości

kermit

Użytkownicy
  • Zawartość

    26
  • Rejestracja

  • Ostatnio

  • Wygrane dni

    3

kermit wygrał w ostatnim dniu 30 lipca 2013

kermit ma najbardziej lubianą zawartość!

Reputacja

16 Dobra

O kermit

  • Ranga
    3/10

Informacje

  • Płeć
    Mężczyzna
  • Lokalizacja
    łódź

Ostatnio na profilu byli

Blok z ostatnio odwiedzającymi jest wyłączony i nie jest wyświetlany innym użytkownikom.

  1. Jeśli już zdecydowałeś się na stm-y to tutaj masz fajny spis oprogramowania wspomagających pracę z nimi: http://www.st.com/web/catalog/tools/FM147/CL1794/SC961/SS1533
  2. Jeśli posiadasz już płytkę od STM to polecam zacząć właśnie od niej ponieważ róznice między poszczególnymi uC z rdzeniem cortex od róznych producentów są niewielkie i przy wyborze producenta bardziej polecam patrzeć na dokumentację,środowisko programistyczne i popularność rodziny uC(co przekłada się na lepsze wsparcie ze strony użytkowników tych mikrokontrolerów), do tego za wyborem tej płytki przemawia wbudowany debugger, który przy nauce programowania oferuje nieocenioną pomoc.Aktualnie bawię się mikrokontrolerem LPC1114 na płytce którą otrzymałem wraz z książką "mikrokontrolery lpc1100 pierwsze kroki"(której zdecydowanie nie polecam) i co do nich to dokumentacja może na początku wydać się trochę nieczytelna(ale idzie się przywyczaić) w porównaniu z dokumentacją np. atmela .Co prawda na twojej płytce znajduje się uC z rdzeniem cortex-m0 ale pózniejsza migracja na cortex-m3 nie sprawi ci żadnych problemów, ponieważ ze strony programisty te mikrokontrolery niewiele się różnią, jedynie posiadają zazwyczaj trochę więcej peryferiów jak znudzi ci się ta plytka lub zabraknie mocy obliczeniowej to stm oferuje również bardzo fajne płytki z uC na rdzeniu cortex-m3 po bardzo atrakcyjnej cenie(sam przymierzam się do zakupue).Do tej płytki polecałbym też zakup jakiejś dobrej ksiązki o tych mikrokontrolerach.
  3. Witam wszystkich Dzisjaj chciałbym przedstawić moją konstrukcję - robota minisumo. Powstał on ponad rok temu, ale dopiero teraz znalazłem chwilę aby przedstawić go forumowej społeczności Niestety do tej pory nie startował on w żadnych zawodach, ze względu na brak czasu choć możliwe, że niedługo uda mi się pojawić na jakiś zawodach. Głównym celem dla którego wiesio został powołany do życia była chęć zdobycia doświadczenia w projektowaniu układów elektronicznych i mogę śmiało powiedzieć, że podczas pracy nad tą konstrukcją zdobyłem naprawdę dużo doświadczenia. Niestety ze względu na wcześniejszą awarie komputera utraciłem wszystkie pliki które utworzyłem podczas projektowania łącznie ze schematem, projektem płytki i rysunkami. 1.Mechanika "zbroja" czyli obudowa robota została zaprojektowana w programie google sketchup, ale ze względu na problemy podczas eksportowania plików .DXF z tegoż programu, musiałem przeprosić się z inventorem i ostatecznie w inventorze wygenerowałem pliki .dxf który był potrzebny do wycięcia obudowy laserem. Wykonana jest ona z blachy aluminiowej(a dokładniej stopu 6061, czyli najtańszego ).Następnie została ona powyginana w warunkach domowych do aktualnego kształtu . W wypadku silników, kół i mocowań do tematu podszedłem dośc mało ambitnie(delikatnie mówiąc).Silniki to pololu Hp z przekładnią 1:50, koła również z pololu zazwyczaj spotykane w linefollower-ach, prawdę mówiąc średnio nadają się do minisumo, ale jako że na zawodach jeszcze nie zagościłem to nie mogłem sprawdzić ich w boju .Mocowania silników jak widać są trochę patenciarskie, ale to ze względu, że jedno z oryginalnych mocowań zaginęło w ferworze pracy i niestety do tej pory figuruje na liście zaginionych. Jednak w razie gdyby robot miał startować w zawodach to jest możliwośc zamontowania oryginalnego mocowania bo choć te opaski na ktorych aktualnie tryzmają się silniki sprawują się nadzwyczaj dobrze to w przypadku konfrontacji z innymi robotami to nie wymagał bym od nich cudów. Elektronika: elektronika byłą częścią której poświęciłem najwięcej uwagi i czasu płytka została zaprojektowana w programie eagle, a następnie wykonana w satlandzie. Choć starałem się ją zaprojektować bez błędów to niestety wkradł się jeden mały chochlik przy gnieździe ISP ze względu na to, że nie doczytałem że atmega128 której użyłem ma troszkę inaczej wyprowadzone piny do programowania niż większość mikrokontrolerów z rodziny ATMEGA Procesorem który steruje wiesiem jest atmega128, ale jest to strzelanie do muchy z armaty i spokojnie wystarczył by jakiś zdecydowanie mniejszy, no ale chociaż nie miałem problemów z wyroutowaniem płytki. Czujniki przeciwnika to 2 sharpy cyfrowe (GP2Y340K) o zasięgu do 40cm, przydało by się ich trochę więcej, ale niestety są troszkę drogie i ostatecznie zdecydowałem się na dwa. Czujniki linii to typowe dla takich konstrukcji transoptory odbiciowe (KTIR340K) podłączone za pomocą komparatora do mikrokontrolera co w zamyśle miało odciązyć mikrokontroler od obsługi przetwornika AC, ale prawdę mówiąc w przypadku mojego robota to rozwiązanie ma więcej wad niż zalet a to ze względu na to, że przedni i tylny czujnik są w innej odległości nad podłożem przez co cięzko jest ustawić odpowiedni próg za pomocą potencjometru(jest tylko jeden na wszystkie kanały).Aha no i jak widać na zdjęciach czujniki są troszkę głupio rozmieszczone bo wkońcu doszedłem do wniowsku, że lepiej by było gdyby były w narożnikach. energie wiesiowi zapewnia akumualtor litowo-polimerowy o napięciu znamionowym 11,1V i pojemności 500mAh.Silniki zasilane są bezpośrednio z akumulatora a częśc cyfrowa zasilana jest z 3 stabilizatorów z których jeden zasila procesor, drugi mostki H i czujniki a trzeci zapewnia napięcie 3,3v dla akcelerometru. Silnikami sterują mostki H TB6612 po jednym na silnik (kanały zostały połączone dla zwiększenia prądu ktorym mogą sterować). Na płytce znajduję się również akcelermetr który miał służyć do wykrywania zderzeń z przeciwnikiem ale nie został on jeszcze oprogramowany. Do głównej płytki za pomocą taśmy FFC podłączana jest płytka na której znajdują się 4 diody LED i 2 przyciski służące do sterowania robotem . Wyprowadziłem również złączne na którym dostępny jest UART gdzie można podłączyć moduł bluetooth. Software: Program był częscią której poświęciłem zdecydowanie najmniej uwagi i prawdę mówiąc to jest częśc którą można by jeszcze dopracować i dlatego narazie go nie publikuje, ale jeśli ktoś by chciał to mogę go przesłać. Podsumowanie: Jak widać w konstrukcji można by dużo poprawić, ale na usprawiedliwienie powiem, że był to mój drugi robot, wcześniej zbudowałem prostego linefollowera i konstrukcja wiesia była jak na tamten czas dośc dużym wyzwaniem któremu udało mi się sprostać i mimo wszystko jestem z tego projektu zadowolony, ponieważ spełnił on podstawowe zadanie jakim było zdobycie doświadczenia co pozwoli na ulepszenie konstrukcji w przyszłości filmiki dodam jutro, ponieważ dziś nie miałem dostępu do aparatu z kamerą pozdrawiam __________ Komentarz dodany przez: Treker Temat poprawiłem i opublikowałem.
  4. jeśli chcesz wprowadzić wartość portu do zmiennej to wystarczy taka operacja: twoja zmienna = PORTx; i dalej możesz robić w programie z tą zmienną co chcesz.
  5. miałem dokładnie taki sam problem tylko z innym akcelerometrem i w moim wypadku pomogło zapisywanie danych osobno do każdego rejestru konfiguracyjnego: mniej więcej coś takiego: i2c start ctrl_reg1 dane do zapisania i2c stop i2c start ctrl_reg2 dane do zapisania i2c stop i tak dalej nie wiem czy u ciebie coś to pomoże, aha no i łatwiej by było gdybyś przedstawił kod i napisał coś więcej o układzie.
  6. witam! Od jakiegoś czasu bawię się modułem IMU gy-80: schemat: podłączam go do płytki z mikrokontrolerem lpc1114 , a dokładniej: http://www.kamami.pl/dl/litecomplpc1114.pdf moduł IMU jak i płytka z mikrokontrolerem zasilane są z portu USB napięciem 5V ponieważ posiadają one własne stabilizatory 3,3V, moduł posiada również translator napięć I2C z 3,3V na 5V teoretycznie mogło by to sprawiać problemy, ponieważ mikrokontroler chodzi pod napięciem 3,3v lecz komunikacja z magnetometrem oraz barometrem działa bez problemu. Problematyczne okazało się uruchomienie akcelerometru, ponieważ odczytywana wartośći z reejstrów w których powinny znajdować się wyniku pomiarów ciągle równe są zero.Wszystkie odczyty wysyłam przez przejsciówke UART do aplikacji stworzonej w labview lub ostatnio do terminala (Hercules)ale w obu wypadkach odczytywane wyniki równe są 0.Odczytywanie sygnatury działa bez problemu, więc połączenia jak i sam układ wydaje się sprawny.Męczę się już z tym akcelerometrem od 4 dni niestety nie mogę nic wskurać. Konfiguracja układu też wydaje sie poprawna chociaż w takim stopniu aby można było odczytać jakiekolwiek wyniki. Może ktoś miał już doczynienia z tym akcelerometrem i byłby w stanie mi pomóc. oto kod częśc kodu odpowiedzialna za konfigurację akcelerometru oraz odczyty wyników: //definicje dla akcelerometru #define adxl_adr 0xA6 #define data_format_reg 0x31 #define range_2g 0 #define range_4g (1<<0) #define range_8g (1<<1) #define range_16g (1<<0)|(1<<1) #define power_reg 0x2D #define data_rate_reg 0x2C #define x_lsb_data 0x32 #define ID_reg 0 void accel_config(uint8_t range); void accel_read(uint8_t *buffer); uint8_t accel_read_id(void); // ciała funkcji void accel_config(uint8_t range) { I2CWriteLength = 7; I2CMasterBuffer[0]=adxl_adr; I2CMasterBuffer[1]=data_rate_reg; I2CMasterBuffer[2]=((1<<0)|(1<<1)|(1<<2)); //data rate 12,5hz I2CMasterBuffer[3]=data_format_reg; I2CMasterBuffer[4]=range; I2CMasterBuffer[5]=power_reg; I2CMasterBuffer[6]=(1<<3); //włączenie pomiarów I2CEngine(); } void accel_read(uint8_t *buffer) { uint8_t i; I2CWriteLength = 2; I2CReadLength = 6; I2CMasterBuffer[0]=adxl_adr; I2CMasterBuffer[1]=x_lsb_data; I2CMasterBuffer[2]=adxl_adr+1; I2CEngine(); for(i=0;i<=5;i++) { *buffer++ = I2CSlaveBuffer[i]; } } uint8_t accel_read_id(void) { I2CWriteLength = 2; I2CReadLength = 1; I2CMasterBuffer[0]=adxl_adr; I2CMasterBuffer[1]=ID_reg; I2CMasterBuffer[2]=adxl_adr+1; I2CEngine(); return I2CSlaveBuffer[0]; } w funkcji main dane wysyłane są na port szeregowy do terminala: /* =============================================================================== Name : main.c Author : Version : Copyright : Copyright (C) Description : main definition =============================================================================== */ #ifdef __USE_CMSIS #include "LPC11xx.h" #endif #include <cr_section_macros.h> #include <NXP/crp.h> // Variable to store CRP value in. Will be placed automatically // by the linker when "Enable Code Read Protect" selected. // See crp.h header for more information __CRP const unsigned int CRP_WORD = CRP_NO_CRP ; // TODO: insert other include files here #include "drivers/uart.h" #include "drivers/gy80.h" #include "redlib/stdlib.h" uint8_t hmc[5]; uint8_t bmp_p[3]; uint8_t bmp_t[3]; uint8_t accel[5]; int16_t data; char itoa_buf[10]; int main(void) { uart_init(); while(1) { if(uart_get_data() == 97) { uart_put_data(170); break; } } SysTick_Config(48000); I2CInit(0); //HMC_config(avg_4|datarate_70hz,gain_7,cont_mode); //bmp_init(); accel_config(range_16g); while(global_timer<=25); while(1) { accel_read(accel); //uart_put_data(accel_read_id()); uart_put_string("\n"); uart_put_string("-------------"); //put x data data=(accel[0]<<8 | accel[1]); itoa(data,itoa_buf,10); uart_put_string("data x"); uart_put_string(itoa_buf); uart_put_string("\n"); //put y data data=(accel[2]<<8 | accel[3]); itoa(data,itoa_buf,10); uart_put_string("data y: "); uart_put_string(itoa_buf); uart_put_string("\n"); //put z data data=(accel[4]<<8 | accel[5]); itoa(data,itoa_buf,10); uart_put_string("data z: "); uart_put_string(itoa_buf); uart_put_string("\n"); uart_put_string("-------------"); global_timer=0; while(global_timer<=200); } } pozdrawiam!!!
  7. Witam w kolejnej części kursu programowania mikrokontrolerów AVR w języku assembler, dziś zajmiemy się przerwaniami. Co to są przerwania? Jak nazwa sugeruje ich istota polega przerwaniu czegoś, w naszym wypadku chodzi o przerwanie wykonywania programu i skok do tzw. Procedury obsługi przerwania(ISR)- czyli wydzielonego fragmentu programu do którego wykonywany jest skok, gdy jakiś układ zgłasza przerwanie. Układem, który zgłasza przerwanie może być jeden z modułów peryferyjnych w naszym mikrokontrolerze, np. Przetwornik analogowo - cyfrowy o którym pisałem w poprzedniej części. Po co nam te przerwania: -Otóż przerwania mogą bardzo przyspieszyć wykonywanie naszego programu, aby to udowodnić przedstawię przykład z przetwornikiem analogowo-cyfrowym, otóż jak pamiętamy z poprzedniej części kursu, aby odczytać wartość napięcia jaką zmierzy przetwornik musieliśmy czekać w pętli aż wartość w rejestrze ADC zostanie uaktualniona, jako że przetwornik ten działa dość wolno to większość czasu nasz program tkwił w pętli sprawdzając, czy pomiar jest już gotowy, taka pętla bardzo spowalnia wykonywanie naszego programu. W takim wypadku najlepszym rozwiązaniem jest użycie przerwania od przetwornika ADC, gdy przetwornik wykona pomiar zgłasza przerwanie, wykonywanie programu głównego jest wstrzymywane, następuje skok do procedury obsługi przerwania gdzie wartość pomiaru wpisywana jest do rejestru ogólnego przeznaczenia, po napotkaniu rozkazu "reti" dokonywany jest powrót do miejsca w którym był program zanim nastąpiło przerwanie. Przerwania dzielimy na wewnętrzne, czyli takie które przychodzą od modułów wewnętrznych mikrokontrolera(przetwornik A/C, timery, UART, TWI itp.) oraz przerwania zewnętrzne, czyli takie które wyzwalane jest przez jakiś układ zewnętrzny, przerwanie takie wyzwalane jest poprzez podanie sygnału na odpowiednie wejście mikrokontrolera(wejścia te oznaczane są jako INTn, np. W naszej atmedze32 mamy 3 takie wejścia- INT0(PD2), INT1(PD3) oraz INT2(PB2) ). jak konfigurujemy przerwania zewnętrzne? Aby skonfigurować przerwania zewnętrzne musimy skonfigurować rejestry MCUCR oraz GICR Opis pierwszego z nich możemy znaleźć na stronie 66 noty aplikacyjnej atmegi32 jak widzimy 4 bardziej znaczące bity są zaznaczone szarym kolorem i nie są one przeznaczone do konfiguracji przerwań zewnętrznych Bity które nas interesują to te o nazwie ISC01 oraz ISC00 służą one do określenia jaki sygnał na wejściu INT1 ma wyzwalać przerwanie, ustawiamy je wg. Tej tabelki: jako że na wejściu INT0 mamy przycisk, który po wciśnięciu zwiera to wejście do masy to wybieramy wyzwalanie przerwania zboczem opadającym, czyli ustawiamy bit ISC01 bity ISC11 oraz ISC10 odpowiadają za ustawienie przerwania INT1, jako że my nie używamy tego wejścia to pozostawiamy je w spokoju. Kolejnym rejestrem do ustawienia jest rejestr GICR, którego struktura prezentuje się tak: I tutaj akurat wielkiej filozofii nie ma, bity INT0,1,2 służą do włączania poszczególnych przerwań zewnętrznych. Ustawiamy bit INT0. Ćwiczenie 1: Gdy wiemy już co musimy poustawiać pora wkońcu coś zrobić : montujemy układ według powyższego schematu i przystępujemy do pisania programu. W pierwszym programie użyjemy przerwania zewnętrznego, będzie ono wywoływane poprzez zwarcie [przyciskiem] linii PB3 do masy. W procedurze obsługi przerwania znajdzie się instrukcja która zapali naszą diodę, wykona opóźnienie o 1s i powróci do programu głównego. Możliwe, że niektórym bardziej doświadczonym osobom w tym momencie zapaliła się w głowie czerwona lampka, czytając o tym, że w przerwaniu ma być wykonywane opóźnienie. Otóż przerwania i opóźnienia nie są zbyt dobrym połączeniem i należy pamiętać, że w przerwaniach raczej nie powinno się używać opóźnień, ani długich pętli, wyjątkiem są bardzo proste programy lub takie gdzie używamy tylko jedno przerwanie, czyli w naszym wypadku nie będzie problemu, ale warto zapamiętać tą regułę na przyszłość Nasz program będzie wyglądał tak: .nolist .include "m32def.inc" .list .cseg .org 0 //tablica wektorów przerwań jmp start .org 0x0002 rjmp EXT_INT0; External Interrupt Request 0 .org 0x0004 reti ; External Interrupt Request 1 .org 0x0006 reti; External Interrupt Request 2 .org 0x0008 reti; Timer/Counter2 Compare Match .org 0x000a reti; Timer/Counter2 Overflow .org 0x000c reti; Timer/Counter1 Capture Event .org 0x000e reti; Timer/Counter1 Compare Match A .org 0x0010 reti; Timer/Counter1 Compare Match B .org 0x0012 reti; Timer/Counter1 Overflow .org 0x0014 reti; Timer/Counter0 Compare Match .org 0x0016 reti; Timer/Counter0 Overflow .org 0x0018 reti; Serial Transfer Complete .org 0x001a reti; USART, Rx Complete .org 0x001c reti; USART Data Register Empty .org 0x001e reti; USART, Tx Complete .org 0x0020 reti; ADC Conversion Complete .org 0x0022 reti; EEPROM Ready .org 0x0024 reti; Analog Comparator .org 0x0026 reti; 2-wire Serial Interface .org 0x0028 reti; Store Program Memory Ready start: //początek programu cli //wyłączenie przerwań ldi R16, HIGH(RAMEND) out SPH, R16 ldi R16, LOW(RAMEND) out SPH, R16 sei //włączenie przerwań sbi DDRC, 0 //ustawienie linii do której podłączona jest dioda jako wyjście cbi DDRD, 2 //ustawienie linii PD2 jako wejście, zwarcie tej linii do masy wyzwoli przerwanie sbi PORTD, 2 //podciągnięcie linii PD2 do napięcia zasilania ldi R16, (1<<ISC01) //ustawienie sygnału wyzwalającego przerwanie INT0- zbocze opadające out MCUCR, R16 ldi R16, (1<<INT0) //zezwolenie na przerwanie od INT0 out GICR, R16 main: cbi PORTC, 0 //zgaś diodę rjmp main EXT_INT0: //procedura obsługi przerwania INT0 sbi PORTC, 0 //zapal diode LED ; ============================= ; podprogram opózniający ; 1 sekunda: ; ----------------------------- ; delaying 999999 cycles: ldi R17, $09 WGLOOP0: ldi R18, $BC WGLOOP1: ldi R19, $C4 WGLOOP2: dec R19 brne WGLOOP2 dec R18 brne WGLOOP1 dec R17 brne WGLOOP0 ; ----------------------------- ; delaying 1 cycle: nop ; ============================= reti //rozkaz powrotu z przerwania Jak widać w naszym programie pojawiło się coś nowego, a mianowicie tablica wektorów(adresów) przerwań, z racji tego, że w programie używamy tylko i wyłącznie jednego przerwania(INT1), to z jednego adresu(0x002) wykonywany jest skok do etykiety "EXT_INT0" pełna tablica wektorów przerwań, którą wklejamy do programu wygląda tak(w zależności od przerwania jakiego używamy, zastępujemy w odpowiednim miejscu rozkaz reti , rozkazem rjmp "etykieta procedury obsługi przerwania"): jmp start .org 0x0002 reti; External Interrupt Request 0 .org 0x0004 reti; External Interrupt Request 1 .org 0x0006 reti; External Interrupt Request 2 .org 0x0008 reti; Timer/Counter2 Compare Match .org 0x000a reti; Timer/Counter2 Overflow .org 0x000c reti; Timer/Counter1 Capture Event .org 0x000e reti; Timer/Counter1 Compare Match A .org 0x0010 reti; Timer/Counter1 Compare Match B .org 0x0012 reti; Timer/Counter1 Overflow .org 0x0014 reti; Timer/Counter0 Compare Match .org 0x0016 reti; Timer/Counter0 Overflow .org 0x0018 reti; Serial Transfer Complete .org 0x001a reti; USART, Rx Complete .org 0x001c reti; USART Data Register Empty .org 0x001e reti; USART, Tx Complete .org 0x0020 reti; ADC Conversion Complete .org 0x0022 reti; EEPROM Ready .org 0x0024 reti; Analog Comparator .org 0x0026 reti; 2-wire Serial Interface .org 0x0028 reti; Store Program Memory Ready start: Wcześniej mówiłem, że w przerwaniu następuje skok do jakiegoś wydzielonego miejsca w programie i wykonanie tzw. Procedury obsługi przerwania(tak nazywamy część programu wykonywaną, gdy nastąpi przerwanie), a teraz chciałbym nadmienić, że skok do procedury obsługi przerwania nie jest bezpośredni, tylko najpierw następuje skok do tablicy wektorów, a dokładniej do którego adresu w tej tablicy, można wyczytać w tabeli 18 noty aplikacyjnej atmegi32 tablica ta wygląda tak: i jak widać adres do którego zostanie wykonany skok zależy od przerwania jakie ma być wykonane.U nas skok zostaje wykonany do adresu 0x002, skąd dopiero następuje skok do miejsca w którym zawarta jest nasza procedura obsługi przerwania, w naszym programie miejsce to wskazuje etykieta EXT_INT0. W razie gdybyśmy w programie używali innych przerwań wystarczy w odpowiednim miejscu tablicy zastąpić instrukcje "reti" fragmentem który prezentuje się tak: "rjmp" "etykieta procedury obsługi przerwanie". Ćwiczenie 2 – przerwania wewnętrzne W tym ćwiczeniu wykorzystamy ten sam układ co w poprzednim, czyli diodę LED podłączoną anodą do portu PC0 oraz przycisk z jednej strony podłączony do PD2 a z drugiej do potencjału masy. Tym razem w programie wykorzystamy dwa przerwania jedno które już znamy, czyli przerwanie zewnętrzne INT0 oraz jedno przerwanie wewnętrzne, które będzie pochodziło od timera1(16-bitowy, czyli wartość do której zlicza jest równa 65536). Zadanie programu będzie jak zwykle niezbyt skomplikowane- za pomocą przycisku S1 wyzwalamy przerwanie INT0 w którego procedurze obsługi znajdzie się instrukcja zapalenia diody, następnie po przepełnieniu timera1 nastąpi przerwanie w którym dioda zostanie zgaszona. Przepełenienie timera nastąpi po 65535 x 8(wart. Preskalera) = 524280 cykli co przy taktowaniu 1mhz daje ok. 0,52s(po tym czasie dioda zostanie zgaszona ) tak prezentuje się nasz program: .nolist .include "m32def.inc" .list .cseg .org 0 jmp start .org 0x0002 rjmp EXT_INT0; External Interrupt Request 0 .org 0x0004 reti ; External Interrupt Request 1 .org 0x0006 reti; External Interrupt Request 2 .org 0x0008 reti; Timer/Counter2 Compare Match .org 0x000a reti; Timer/Counter2 Overflow .org 0x000c reti; Timer/Counter1 Capture Event .org 0x000e reti; Timer/Counter1 Compare Match A .org 0x0010 reti; Timer/Counter1 Compare Match B .org 0x0012 rjmp OVF1; Timer/Counter1 Overflow .org 0x0014 reti; Timer/Counter0 Compare Match .org 0x0016 reti; Timer/Counter0 Overflow .org 0x0018 reti; Serial Transfer Complete .org 0x001a reti; USART, Rx Complete .org 0x001c reti; USART Data Register Empty .org 0x001e reti; USART, Tx Complete .org 0x0020 reti; ADC Conversion Complete .org 0x0022 reti; EEPROM Ready .org 0x0024 reti; Analog Comparator .org 0x0026 reti; 2-wire Serial Interface .org 0x0028 reti; Store Program Memory Ready //koniec tablicy start: //początek programu cli //wyłączenie przerwań ldi R16, HIGH(RAMEND) out SPH, R16 ldi R16, LOW(RAMEND) out SPH, R16 sbi DDRC, 0 //ustawienie linii do której podłączona jest dioda jako wyjście cbi DDRD, 2 //ustawienie linii PD2 jako wejście, zwarcie tej linii do masy wyzwoli przerwanie sbi PORTD, 2 //podciągnięcie linii PD2 do napięcia zasilania ldi R16, (1<<ISC01) //ustawienie przerwań zewnętrnych out MCUCR, R16 ldi R16, (1<<INT0) out GICR, R16 sei //włączenie przerwań ldi R16, (1<<CS11) //ustawienie preskalera out TCCR1B, R16 ldi R16, (1<<TOIE1) //zezwól na przerwania od przepełnienia timera1 out TIMSK, R16 ldi R16,0 //ustawienie trybu działania timera w tryb normalny out TCCR1A, R16 main: //pętla główna nop rjmp main EXT_INT0: //procedura obsługi przerwania INT0 sbi PORTC, 0 //zapal diode clr R16 //wyzerowanie zawartości timera1 out TCNT1H, R16 out TCNT1L, R16 reti //powrót z przerwania OVF1: //procedura obsługi przerwania od przepełnienia timera1,nastąpi ono 65535 x 8= ok.0.52s po wyzerowaniu licznika timera1 cbi PORTC, 0 //zgaś diodę reti //powrót z przerwania nowe instrukcje które wystąpiły w programie: sei – "global interrupt enable" – globalne zezwolenie na przerwania clr – "clear register" – wyczyść "nazwa rejestru ogólnego przeznaczenia" reti – "interrupt return" - powrót z procedury obsługi przerwania Na koniec chciałbym poinformować o dwóch ważnych zagadnieniach: -Przerwania mają swoje priorytety.Znaczy to, że jeśli w tym samym momencie zostaną zgłoszone 2 lub więcej przerwań to jako pierwsze zostanie wykonane to przerwanie, które jest wyżej w tablicy wektorów(czyli przerwanie, które ma niższy adres ) -w przypadku gdy używamy przerwań musimy zaimplementować w programie stos, ponieważ w momencie skoku do procedury obsługi przerwania adres powrotny jest odkładany właśnie na stos i gdy program napotka instrukcję "reti" adres ten jest zdejmowany ze stosu i następuje powrót do miejsca w którym był program zanim nastąpiło przerwanie. Zakończenie: Jako, że kończy się wrzesień a wraz z nim niestety, również mój wolny czas, to jak narazie była to ostatnia część naszego kursu, możliwe że za jakiś czas pojawią się następne części, ale jak narazie kurs można uważać za zakończony.Mam nadzieję, że kurs był pomocny przy pierwszych krokach z językiem assembler.W razie jakiś pytań lub jeśli ktoś zobaczył by jakieś błędy - proszę pisać na w temacie lub na PW. Pozdrawiam!!!
  8. Część 4 - ADC W dzisiejszesj części naszego kursu zajmemy się konfiguracją przetwornika analogowo-cyfrowego, czyli ADC(analog to digital coverter). Czym on jest i po co nam on? Otóż przetwornik analogowy/cyfrowy jest to jeden z modułów naszego mikrokontrolera.Po co nam on?Jak zapewne wiecie nasze mikrokontrolery są układami cyfrowymi-znaczy to, że rozumieją one jedynie wartośći 0 i 1, wymyślono więc coś takiego jak przetwornik analogowo-cyfrowy służy on nam do zamiany sygnału analogowego, czyli sygnału który może przyjmować dowolną wartość( w przypadku gdy taki sygnał będziemy bezpośrednio podawać na wejście naszego przetwornika, może on wynosić max. 5V) na sygnał cyfrowy, czyli sygnał zrozumiały dla procesora, dzięki temu w programie możemy potem operować na wartościach, które zostały przetworzone/zmierzone przez nasz przetwornik. Jak go uruchomimy? Podobnie jak inne peryferia, czyli musimy skonfigurować rejestr/y odpowiadające za jego działanie. W przypadku przetwornika rejestrami odpowiedzialnymi za jego działanie są rejestry ADMUX oraz ADCSRA. Informacje o tych rejestrach możemy znaleźć od strony 214 noty aplikacyjnej, ale polecam zacząć lekture od strony 201, ponieważ znajdziemy tam wszystkie informacje o przetworniku A/C w naszej atmedze. Poza rejestrami odowiedzialnymi za konfigurację ADC, powinniśmy zwrócić uwagę na jeszcze jeden rejestr a mianowicie- rejestr ADC w którym, po dokonaniu pomiaru, możemy znaleźć jego wynik, z racji tego że nasz przetwornik jest 10-bitowy, a rejestry mamy jedynie 8-bitowe to rejestr ADC podzielony jest na 2 rejestry- ADCH(starsza część słowa) i ADCL(młodza część) . Co to znaczy, że przetwornik jest 10-bitowy? Otóż znaczy to, że wynik pomiaru może być przedstawiony jako wartość od 0 do 1024,a jak to się ma do rzeczywistego poziomu napięcia, który mierzony jest na wejściu przetwronika? możemy to wyliczyć ,ze wzoru: Wynika z tego, że Vin = Aref/1024 x ADC(wartośc jaką możemy znaleźć w rejestrze ADC). Przy okazji widzimy do czego służy nam pin AREF, podajemy na niego napięcie odniesienia dla naszego przetwornika. Teraz przechodzimy do konfiguracji : rejestr ADMUX wygląda tak: MUX0,1,2,3,4 – Odpowiadają za wybór kanału ADC na którym będziemy dokonywali pomiaru, nasza atmega32 ma tych kanałów 8, wszystkie są podłączone do portu A, ale pomiaru dokonywać możemy naraz tylko na jednym z tych kanałów. Tabelkę z tym które bity należy ustawić, aby wybrać żądany kanał znajdziemy na stronie 215 noty aplikacyjnej. ADLAR – Jak już pisałem dysponujemy przetwornikiem 10-bitowym, a bit ten odpowiedzialny jest za prezentację wyniku w rejestrach ADCH i ADCL, jeśli ustawimy ten bit na "1" to 2 najmniej znaczące bity znajdą się w rejestrze ADCL, a 8 bitów bardziej znaczących w rejstrze ADCH, jeśli bit ten pozostawimy ustawiony na "0" to 2 najbardziej znaczące bity znajdą się w rejestrze ADCH, a 8 mniej znaczących znajdzie się w rejestrze ADCL.Najlepiej tłumaczy to ilustracja, która mozna znaleźć w nocie aplikacyjne, a wygląda ona tak: Ale co to znaczy z punkt widzenia programisty? Bit ADLAR ustawiamy, gdy wystarczy nam 8-bitowa rozdzielczość pomiaru, ponieważ wtedy możemy znaleźć wszystkie bardziej znaczące bity w rejestrze ADCH i nie musimy zawracać sobie głowy pozostałymi 2bitami z rejestru ADCL.W naszym wypadku 8-bitowa rozdzielczośc(w wypadku gdy Aref = 5v, to rozdielczośc wynosi 0,019V na działkę) pomiaru jest jak najbardziej wystarczająca, więc ustawiamy ten bit na "1". REFS0,1- bity odpowiedzialne za wybór napięcia odniesienia, ustawiamy je według tabelki W naszym wypadku mamy na pinie AREF mamy,a przynajmniej powinniśmy mieć kondensator 100nF podłączony do masy, więc ustawiamy bit REFS0 Kolejnym rejestrem, który należy skonfigurować jest rejestr ADCSRA: ADEN – co tu dużo pisać, ustawienie tego bitu włącza ADC ADSC – ustawienie tego bitu powoduje rozpoczęcie pomiaru ADATE – ustawienie tego bitu powoduje wybranie innego sposobu (niż ustawienie bitu ADSC) wyzwalania pomiaru, proponuję otworzyć stronę 218 noty aplikacyjnej atmegi32 i samemu sprawdzić co mamy do wyboru w przykładowych programach nie korzystałem z tego, ale jeśli ktoś jest ambitny to polecam poeksperymentować ADIF – jest to tzw. "flaga" ustawienie tego bitu następuje automatycznie, gdy pomiar został zakończony i rejestry ADC(ADCH i ADCL) zaaktualizowane. ADIE – zezwolenie na wywołanie przerwania przez przetwornik, gdy pomiar zostanie zakończony. Jako, że narazie nie mówiłem o przerwaniach, to nie będziemy ustawiali tego bitu, ale użycie przerwania, zamiast czekanie w pętli na ustawienie flagi ADIF jest lepszym rozwiązaniem. ADPS2,1,0- bity odpowiedzialne za wybór preskalera dla zegara przetwornika preskaler musimy wybrać tak, aby częstotliwość dla pomiaru 10-bitowego zawierała się w przedziale 50-200khz , lub dla pomiaru z dokładnością 8-bitów ,delikatnie powyżej 200khz.W swojej atmedze częstotliwość mam ustawioną na domyślną( 1mhz), więc wybrałem preskaler 8 co daje częstotliwośc 1mhz/8 =125khz. Gdy wiemy co nieco o przetworniku to pora napisać pierwszy program Ćwiczenie 1 – regulacja jasności świecenie diody LED temat ćwieczenia może wydawać się wam znajomy, ponieważ podobne ćwiczenie wykonaliśmy w poprzedniej części kursu, ale tym razem jasność świecenia diody LED będziemy regulowali za pomocą potencjometru .Wyjście naszego potencjometru będzie podłączone do wejścia przetwornika A/C, do regulacji jasności świecenia diody użyjemy PWM. Tak będzie to wyglądało na schemacie: a teraz pora na program: .nolist .include "m32def.inc" .list .cseg .org 0 cli //wyłączenie przerwań ldi R16, HIGH(RAMEND) //implementacja stosu out SPH, R16 ldi R16, LOW(RAMEND) out SPL, R16 //konfiguracja PWM sbi DDRB, 3 //ustawienie jako wyjście linii na której będzie generowany sygnał PWM ldi R16, (1<<WGM00)|(1<<WGM01)|(1<<COM01)|(1<<CS00) //załadownie do R16 wartości która posłuży do konfiguracji timera0 out TCCR0, R16 //załadowanie wartośći z R16 do TCCR0 sbi DDRC,0 //ustawienie stanu niskiego na PC0(katoda diody LED) cbi PORTC, 0 //konfiguracja ADC ldi R16,(1<<REFS0)|(1<<ADLAR) //załadowanie do R16 wartości która posłuży do konfiguracji rejestru ADMUX out ADMUX, R16 //załadowanie wartośći z R16 do ADMUX ldi R16,(1<<ADEN)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADSC) out ADCSRA, R16 main: //program glowny sbic ADCSRA, ADIF //jeśli bit ADIF w rejestrze ADCSRA nie jest ustawiony(konwersja nie gotowa) to pomiń następną instrukcje rcall load_OCR //wywołaj poprogram który załaduje wartość z przetwornika ADC do rejestru OCR0 rjmp main //instrukcja zostanie wykonana gdy konwersja nie jest jeszcze gotowa ,tym samym pętla ta wykonuje się, gdy oczekujemy na konwersje load_OCR: //podprogram ładujący wartość z rejestru ADCH do rejestru OCR0 in R16, ADCH //załaduj wartośc z ADCH do R16 out OCR0, R16 //załaduj wartośc z R16 do OCR0 sbi ADCSRA, ADSC //ustaw bit ADSC w rejestrze ADCSRA , powoduje to wyzwolenie kolejnej konwersji ADC ret //powrót do podprogramu main Idea programu jest bardzo prosta, najpierw przetwornik dokonuje pomiaru napięcia na wyjściu potencjometru, następnie wartość tego pomiaru ładowana jest do rejestru OCR0, więc czym większe napięcie na wejściu przetwornika tym większe wypełenienie PWM, co przekłada się na jaśniejsze światło diody LED. Ćwiczenie 2 - obsługa transopotora odbiciowego: W tym ćwiczeniu zajmiejmy się obsługą transoptora obciciowego(cny70), czyli czujnika jaki często używany jest w konstrukcjach robotów FTL.Najpierw zmontujmy układ wg. poniższego schematu: zmontowany układ wygląda tak: gdy zmontujemy układ przechodzimy do programu, jego zadaniem będzie zapalenie diody LED, kiedy czujnik umieścimy nad czarną powierzchnią(w moim przypadku jest to kawałek izolacji) i gaszenie jej gdy czujnik umieścimy nad białą powierzchnią(kartka). Program który wykona to zadanie prezentuje się tak: .nolist .include "m32def.inc" .list .cseg .org 0 .def calib = R17 //nadanie symbolicznej nazwy rejestrowi R17 cli //wyłączenie przerwań ldi R16, HIGH(RAMEND) //implementacja stosu out SPH, R16 ldi R16, LOW(RAMEND) out SPL, R16 sbi DDRC, 0 //ustawienie jako linia wyjściowa PC0 //konfiguracja ADC ldi R16,(1<<REFS0)|(1<<ADLAR)|(1<<MUX0)|(1<<MUX1)|(1<<MUX2) //załadowanie do R16 wartości która posłuży do konfiguracji rejestru ADMUX,wybranie kanału 8 przetwornika(bity mux) out ADMUX, R16 //załadowanie wartośći z R16 do ADMUX ldi R16,(1<<ADEN)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADSC) //załadowanie do R16 wartości która posłuży do konfiguracji ADCSRa out ADCSRA, R16 //załadowaine R16 do ADCSRA calibration: //podprogram który kalibruje nasz czujnik sbis ADCSRA, ADIF //jeśli bit ADIF jest ustawiony(pomiar gotowy) pomiń następną instrukcje rjmp calibration //jeśli bit ADIF jest wyzerowany(pomiar nie jest gotowy) instrukcja zostanie wykonany in calib, ADCH //załaduj wartośc rejestru ADCH do rejestru calib_value(R17) sbi ADCSRA, 6 //uruchom następny pomiar main: //program główny sbis ADCSRA, ADIF //jeśli bit ADIF w rejstrze ADCSRA jest ustawiony(pomiar gotowy)pomiń następną instrukcje rjmp main //instrukcja zostanie wykonana, jeśłi bit ADIF jest wyzerowany(pomiar trwa) in R16, ADCH //załaduj wartość pomiaru(znajdującej się w ADCH) do R16 cp calib, R16 //porównaj wartośc R16 z calib_value(R17) brge LED_on //skocz do etykiety LED_off jeśli wynik porównania to większy, bądź równy rjmp LED_off //jeśli wartość aktualnego pomiaru jest mniejsza od calib(R17),skocz do podprogramu wylaczajacego diode LED_on: //podprogram zapalajacy diode i wyzwalający następny pomiar sbi PORTC,0 //zapal diode sbi ADCSRA, ADSC //ustaw bit ADSC w rejestrze ADCSRA- wyzwala to kolejny pomiar rjmp main //skocz do programu głównego LED_off: //podprogram zapalający diodę i wyzwalający następny pomiar cbi PORTC,0 //wygas diode sbi ADCSRA, ADSC //ustaw bit ADSC w rejestrze ADCSRA - wyzwala to kolejny pomiar rjmp main //skocz do programu głównego Jak widzimy w programie występuje etykieta o nazwie "calibration" jest to podprogram, który po uruchomieniu zasilania lub resecie mikrokontrolera mierzy wartość z jaką będzie porównywał póżniejsze wyniki, więc aby cały program działał poprawnie podczas uruchomienia mikrokontrolera lub po jego resecie musimy czujnik trzymać ok. 2mm nad powierzchnią kartki. W podprogramie "calibration" możemy również zobaczyć ładowanie zmierzonej wartości do rejestru o nazwie "calib" jest to nazwa która nadałem rejestrowi R17 poprzez dyrektywę: ".def " Nowe instrukcje, które pojawiły się w programie: BRGE - Branch if Greater or Equal- skocz jeśli większe, bądź równe "do etykiety" IN – in port – w wolnym tłumaczeniu - załaduj "do rejestru ogólnego przeznaczenia" " z rejestru systemowego" CP – compare – porównaj "nazwa rejestru ogólnego przeznaczenia" z "nazwa rejestru ogólnego przeznaczenia" Na koniec - efekt działania naszego programu: To by było tyle na dzisjaj. Aha no i oczywiście- jeśli ktoś zauważy jakieś błędy w artykule to proszę pisać Pozdrawiam
  9. @up ,ze względu na organizację pamięci SRAM ,nie możemy bezpośrednio załadować danej do rejestrów systemowych ,musimy użyć rejestrów pośredniczących jakimi są rejestry ogólnego przeznaczenia(w naszym wypadku R16)
  10. Cześć 3- PWM(Pulse width modulation) Witam w 3 częśći kursu programowania mikrokontrolerów AVR w języku assembler. Dzisjaj zajmiemy się konfiguracją PWM, ale zanim to zrobimy wypadało by dowiedzieć się co to jest to całe Pwm-z otóz angielskiego pulse width modulation, czyli modulacja szerokością impulsu jest to tryb timera wbudowanego w nasz mikrokontroler (w naszym wypadku wykorzystamy timer0, czyli 8-bitowy)która pozwala nam na generowanie impulsów o konkretnej długości. Mówiąc prościej jest to zmienianie stanu wyjścia z 0 na 1 lub odwrotnie z określoną częstotliwościa .My użyjemy tzw. Sprzętowego PWM, ale można je także zrealizować programowo, wyglądało by to tak, że co określony czas zmienialibyśmy stan wyjścia na którym miałoby być generowane nasze PWM Jak to działa? Otóż po uruchomieniu timera0 jeden z jego rejestrów ,a dokładniej TCNT0 jest inkrementowany(powiększany o 1) w każdym takcie sygnału zegarowego, sygnał ten może pochodzić z wewnętrznego lub zewnętrznego oscylatora w zależności czym taktujemy nasz mikrokontroler, może on być również dzielony za pomocą tzw. Preskalera, co powoduje, że TCNT0 nie będzie inkrementowany w każdym takcie, tylko co ilość ustawioną w naszym preskalerze(do wyboru mamy 8,64,256,1024).Jako, że timer0 jest timerem 8-bitowym to rejestr TCNT0 jest inkrementowany, aż osiągnie wartośći 255, gdy ją osiągnie jest zerowany i odliczanie zaczyna się od nowa.W trybie PWM który nas interesuje, rejestr TCNT0 po każdej inkrementacji jest porównywany z wartością rejestru OCR0 i jeśli TCNT0 zrówna się z wartością jaką zapiszemy do OCR0 to wyjście tego timera -PB3 zostaje wyzerowane lub ustawione (w zależności od tego co ustawiliśmy w rejestrze TCCR0 ) i pozostaje w tym stanie(logiczne 1 lub 0) aż rejestr TCNT0 osiągnie wartość maksymalną(255). Jak to uruchomić? Może już sie domyślacie, bo w podobny sposób ustawialiśmy nasze wejśćia/wyjścia, czyli ustawialiśmy bity w rejestrach odpowiedzialnych za ich działanie, więc musimy w rejestrze odpowiadającym za działanie timera0 ustawić odpowiednie bity, rejestrem przeznaczonym do konfiguracji timera0 jest TCCR0, (zachęcam teraz do otworzenia noty aplikacyjnej naszej atmegi32 i poszukania informacij nt. Tego rejestru -na stronie nr. 327 noty aplikacyjnej możemy znalezć tabelkę z nazwami wszystkich rejestrów i numerami stron na których możemy znależć o nich informacje) tak wygląda struktura naszego rejestru TCCR0: bit numer 0 – pozostawiamy go w spokoju po przeczytaniu pierwszego zdania w nocie katalogowej nt. Tego bitu(strona 80) . bity numer 6,3(WGM00 , WGM01)odpowiadają za tryb działania timera, w naszym wypadku będzie to fast PWM, więc ustawiamy oba te bity na 1 bity numer 5,4 – wybieramy z ich pomocą jaka ma być reakcja kiedy TCCR0 = OCR0 tzn. Czy po zrównaniu się wartości tych dwóch rejestrów, wyjście OC0(PB3) ma być ustawione, (tryb odwracający) czy wyzerowane(tryb nieodwracający) – w naszym wypadku ustawimy tryb nieodwracający, czyli ustawiamy tylko bit COM01. bity numer 2,1,0 – za ich pomocą ustawiamy wartośc preskalera o którym wspomniałem już wcześniej. Dla naszych zastosowań najlepiej wybrać jak najmniejszy dzielnik(za chwilę dowiecie się dlaczego), czyli wartośc preskalera ustawimy na 0, poprzez ustawienie bitu CS00 Czas na przejście do konkretów: ćwiczenie 1 – regulowanie jasności świecenia diody LED układ jest bardzo prosty , dioda LED katodą do PC0 ,a anodą przez rezystor do wyjścia naszego timera, czyli do PB3(w nocie aplikacyjnej przy opisie wyprowadzeń przy "PB3", możemy zobaczyć napis "OC0" ). Aby dioda świeciła na PC0 musimy ustawić stan niski, na PB3 będzie generowane PWM. gdy zmontujemy już nasz układzik pora na, przejście do programu, co będzie on robił? Zadaniem naszego programu będzie zapalenie diody LED tyle ,że nasza dioda będzie świeciła z jasnością taką jaką ustawimy w programie, a dokładniej z wartością jaką zapiszemy do rejestru OCR0(jako ,że timer jest 8bitowy ,wartośc ta może wynosić 0-255)i tak np. Jeśli załadujemy do tego rejestru 127 to nasza dioda będzie świeciła z 127/255 czyli około 1/2 swojej mocy Może niektórzy mają teraz wątpliwości bo jak to możliwe -regulować jasnośc świecenia diody LED za pomocą wyjścia cyfrowego, otóż wykorzystamy jedną z właściwości ludzkiego oka ,które nie jest wstanie rejestrować zmian które następują z częstotliwością większą niż 50Hz, jeśli dioda byłaby włączana i wyłączana z mniejszą częstotliwośćią, wiedizlibysmy jedynie jej mruganie, gdy częstotliwość będzie większa ,bądź równa 50hz nie będziemy wstanie dostrzec mrugania diody, tylko wydawało nam się będzie, że dioda świeci światłem ciągłym.Dlatego też dla takich zastosowań ustawilismy jak najmienszy preskaler, ponieważ nczym wyższa jest częsotliwość przełączania tym światło diody jest bardziej "jednolite" Tak będzie wyglądał program w całej okazałości .nolist .include "m32def.inc" .list .cseg .org 0 cli //wyłączenie przerwań ldi R16, HIGH(RAMEND) //zaimpelementowanie stosu out SPH, R16 ldi R16, HIGH(RAMEND) out SPL, R16 ldi R16, (1<<PC0)|(1<<PC1)|(1<<PC2) //ustawienie linii do których podłączone są diody jako wyjściowe out DDRC, R16 //ustawienie linii PC0,PC1,PC2 jako wyjściowe out PORTC, R16 //wygaszenie diod na liniach PC0,PC1,PC2 poprzez ustawienie na nich stanu wysokiego(diody podłączone są katodami //,więc ustawienie stanu niskiego zapala je ,a wysokiego wygasza) //ustawienia pwm sbi DDRB, 3 //ustawiennie lini PB3 jako wyjście (na tej linii bedzie generowany sygnał pwm) ldi R16, (1<<WGM00)|(1<<WGM01)|(1<<COM01)|(1<<CS00) //załadowanie do R16 ,wartości ktora posłuży do ustawienia pwm(poprzez załadowanie tej wartośći do TCCR0) out TCCR0, R16 //załadowanie R16 do TCCR0 cbi PORTC, 0 //włączenie diody,w tym momencie dioda nie świeci ,ponieważ OCR0=0,czyli na PB3 panuje stan niski ldi R16, 125 out OCR0, R16 main: //pętla nop //nic nie rób rjmp main Program starałem się jak najdokładniej opisać komentarzami, więc mam nadzieję, że nie będzie większych problemów z jego zrozumieniem. jeśli chcemy obliczyć częstotliwość pwm jaka generowana jest na wyjściu naszego timera ,możemy skorzystać ze wzoru, który można znależć w nocie aplikacyjnej atmegi32 na stronie 75 F_clock i/o to częstotliwośc naszego mikrokontrolera , w moim wypadku jest ona ustawiona na częstoliwość domyślną, czyli 1mhz. N to wartośc preskalera, jaką ustawiliśmy za pomocą rejestru TCCR0(bity CS00,CS01,CS02). Dla zapoznania się trochę z tematem radzę, pobawić się troche wartością rejestru OCR0 oraz ustawieniami timera(np. spróbujcie zmienić tryb generowania PWM z nieodwracającego na odwracający i zaobserwować co się wtedy jak wtedy ma się jasność świecenia diody do wartości rejestru OCR0, spróbujcie też zwiększyć wartość preskalera np. Na 1024) Ćwiczenie 2 – zabawy z diodą RGB: Jeśli już trochę obeznaliście się z tematem pwm, pora przejśc do następnego programu który przygotowałem, tym razem wykorzystamy diodę RGB ze wspolną anodą i jak widzimy aby zapalić jeden z kolorów naszej diody musimy ustawić stan niski na katodzie odpowiadającej za dany kolor . montujemy układ wg. Powyższego schematu i przystępujemy do pisania programu. Tym razem zadanie jest trochę bardziej skomplikowane spowodujemy, aby każdy kolor naszej diody był po kolei najpierw powoli rozjaśniany, następnie powoli wygaszany. Program będzie wyglądał tak: .nolist .include "m32def.inc" .list .cseg .org 0 cli //wyłączenie przerwań ldi R16, HIGH(RAMEND) //zaimpelementowanie stosu out SPH, R16 ldi R16, HIGH(RAMEND) out SPL, R16 ldi R16, (1<<PC0)|(1<<PC1)|(1<<PC2) //ustawienie linii do których podłączone są diody jako wyjściowe out DDRC, R16 //ustawienie linii PC0,PC1,PC2 jako wyjściowe out PORTC, R16 //wygaszenie diod na liniach PC0,PC1,PC2 poprzez ustawienie na nich stanu wysokiego(diody podłączone są katodami //,więc ustawienie stanu niskiego zapala je ,a wysokiego wygasza) //ustawienia pwm sbi DDRB, 3 //ustawiennie lini PB3 jako wyjście (na tej linii bedzie generowany sygnał pwm) ldi R16, (1<<WGM00)|(1<<WGM01)|(1<<COM01)|(1<<CS00) //załadowanie do R16 ,wartości ktora posłuży do ustawieni pwm(poprzez załadowanie tej wartośći do TCCR0) out TCCR0, R16 //załadowanie R16 do TCCR0 main: cbi PORTC, 0 //zapalenie diody podłączonej pod PC0 rcall wave //wywołanie podprogramu który spowoduje -najpierw powolne rozjaśnienie,a następnie powolne wygaszenie zapalonej diody sbi PORTC,0 //zgaszenie diody podłączonej pod PC0 cbi PORTC,1 //zapalenie diody podłączonej pod PC1 rcall wave //wywołanie podprogramu wave sbi PORTC,1 //zgasenie diody podłączonej pod PC1 cbi PORTC,2 //zapalenie diody podłączonej pod PC2 rcall wave //wywołanie podprogramu wave sbi PORTC,2 //zgaszenie diody podłączonej pod PC2 rjmp main wave: //poprogram powodujący powolne rozjaśnianie diody led ,a następnie powolne wygaszanie ldi R16, 0 //ładujemy do R16, 0 wave_up: //część programu odpowiadająca za rozjaśnianie diody inc R16 //zwiększenie zawartości R16 out OCR0, R16 //załadownie zawartości R16 do OCR0 rcall delay //wywołanie podprogramu opózniającego cpi R16,255 //porówaninie zawartości R16 z maksymalną wartością jaka może znajdować się w rejestrze OCR0 brlo wave_up //jeśli R16<255 następuje skok do Wave_up ,następnie ponowna inkrementacja OCR0 co powoduje rozjaśnienie diody wave_down: //poprogram odpowiadający za powolne wygaszanie diody dec R16 //dekrementacja R16(zmniejszenie o 1) out OCR0, R16 //załadowanie R16, do OCR0 rcall delay //opóznienie cpi R16,1 //porówanienie zawartości R16 z 1 brsh wave_down //jeśli R16>1(czyli dioda nadal świeci) następuje skok do etykiety wave_dowm ret //instrukcja wykonywana ,gdy R16<1(czyli kiedy R16 wynosi 0 ,wcześniej ładowalismy zawartośc R16 do OCR0 ,więc wart. w tym rejestrze tez równa jest 0) delay: //poprogram opóżniający o 10ms ; ============================= ; delay loop generator ; 10000 cycles: ; ----------------------------- ; delaying 9999 cycles: ldi R17, $21 WGLOOP0: ldi R18, $64 WGLOOP1: dec R18 brne WGLOOP1 dec R17 brne WGLOOP0 ; ----------------------------- ; delaying 1 cycle: nop ; ============================= ret tak jak poprzednio program starałem się ubrać w jak najdokładniejsze komentarze. Chciałbym jedynie trochę powiedzieć o podprogramie wave, otóż powoduje on najpierw płynne rozjaśnianie diody Led, poprzez zwiększanie wartości rejestru OCR0(poprzez inkrementacje R16 i następnie zaladowania wartości z R16 do OCR0), jeśli wartość OCR0 osiągnie maximum (sprawdzana jest wartośc R16 która w poprzednich cyklach została załadowana do OCR0)następuje wykonywanie częsci programu("wave_down"), która powoduje powolne wygaszanie diody, działa to analogicznie do poprzedniej częsci z tym, że wartość OCR0 jest teraz dekrementowana. Potem następuje powrót do etykiety "main" i ustawienie stanu wysokiego na katodzie koloru który był aktualnie włączony(poprzez ustawienie stanu wysokiego wyłączamy daną diode/kolor) potem następuje ustawienie stanu niskiego(włączanie jej) na katodzie kolejnego koloru oraz wykonanie podprogragmu wave. W poprzednim programie nie pojawiły sie żadne nowe instrukcje za to tym już tak, są to: cpi - "Compare with Immediate" -porównaj bezpośrednio"nazwa rejestru(np. R16 lub R20)" "liczba z którą ma byc porównana wartośc rejestru" brlo - "Branch if Lower" – skocz jeśli równe "do etykiety" brsh - "Branch if same or higher" – skocz jeśli równe bądź większe"do etykiety" Na zakończenie jeszcze słowo o innych timerach, ponieważ w atmedze32 mamy ich 3(timer0,timer1,timer2), my konfigurowaliśmy timer0, który jest 8-bitowy i wg. Nomenklatury atmela timery zawsze są numerowane tak: timer0 - zawsze jest 8-bitowy ,timer1- zawsze jest 16-bitowy,timer2-zawsze jest 8-bitowy i tak dalej.Jeśli już dowiedzieliście się, że w naszej atmedze dysponujemy timerem 16-bitowym(jego rejestr – czyli TCNT1 zlicza nie do 255(8-bitów) tylko do 65536(16-bitów),czyli możemy uzyskać większą rozdzielczośc generowanej częstotliwośći PWM. Do tego mamy sporo więcej trybów działania tego timera ) to jako zadanie domowe dla tych ambitniejszych, proponuję, aby spróbować go skonfigurować . P.s tak jak ostatnio starałem sie, aby do artykuły nie wkradły się błędy,ale jeśli ktoś jakieś zauważy, czy to faktyczne, czy językowe proszę śmiało pisać. Pozdrawiam __________ Komentarz dodany przez: Treker Kolejny raz zwracam na interpunkcje i spacje.
  11. Jak pisałem już w pierwszej części ,umiejętność programowania w asm jest przydatna ,więc zawsze warto umieć posługiwać się tym językiem ,nawet programując w C (np. czasem zachodzi konieczność napisania wstawki). A co do tego ,że w artykule jest kogel mogel ,po części masz trochę racji, ale nie do końca się z tobą zgadzam, ponieważ czytanie, że gdzieś tam występują takie a takie rejestry czy że mamy architekturę 8-bitową itp. bez napisania co to oznacza z punktu widzenia programisty, nie jest zbyt pouczające i w sumie po przeczytaniu suchych faktów o architekturze i tak nam niewiele daje. edit:to widać mamy trochę odmienne zdanie bo wg. mnie asm jest właśnie takim językiem ,że jak załapiemy na czym polega idea programowania w tym języku niewiele trzeba ,aby swobodnie go używać. Wydaję mi się, że trochę demonizujesz ten język __________ Komentarz dodany przez: Treker Interpunkcja. Spacje po, a nie przed...
  12. Witam w drugiej częśći kursu programowania w języku assembler, w pierwszej części trochę was oszczędziłem, ale dziś bierzemy się ostro do roboty. W tej lekcji dowiemy się co to są etykiety, zajmiemy się opóznieniami, zaimpelementujemy do programu obsługę stosu oraz nauczymy sie obsługiwać przycisk(tzw. microswitch). Ćwiczenie 1- przycisk i etykiety: w poprzedniej części jedynie zapaliliśmy naszą diodę led podłączoną anodą(+) do linii PC0, dzisjaj wykonamy już bardziej ambitne zadanie i sprawimy, że nasza dioda zapali się dopiero po przyciśnięciu przycisku, a zgaśnie gdy przycisk zostanie zwolniony. Montujemy układ wg. Poniższego schematu: Lewa strona układu, pozostała bez zmian względem schematu który widzieliśmy w pierwszej części kursu, natomiast po prawej stronie jak widzimy przybył nam jeden microswitch oraz kondensator o pojemności 100nF, po co nam ten kondensator? Otóż microswitche mają to do siebie, że po wciśnięciu przez kilkanaście/kilkadziesiąt milisekund występuje tzw. Efekt drgania styków, który objawia się tym, że zamiast czystego zwarcia, styki przycisku są zwieranie i rozwieranie, zjawisko takie może być bardzo nie pożądane, więc najlepiej za wczasu nauczyć się je eliminować .Jest jeszce druga tzw. Programowa metoda eliminacji drgania styków, ale o tym powiem póżniej. Gdy zmontujemy nasz układ, uruchamiamy AVR studio, tworzymy nowy projekt i przystępujemy do pisania programu: .nolist .include "m32def.inc" .list .cseg .org 0 cbi DDRA,0 // ustawiamy jako linie WEJŚĆIOWĄ pin do którego podlączony jest microswitch sbi PORTA, 0 //podciągamy go przez wewnętrzny opornik do napięcia zasilania sbi DDRC,0 //ustawiamy jako linie WYJŚCIOWĄ pin do którego podłączonona jest nasza dioda LED main: cbi PORTC, 0 //wyłącz diodę led sbic PINA, 0 //jeśli bit nr0 w rejestrze PINA jest wyzerowany(przycisk wciśnięty) pomiń następną instrukcję rjmp main //jeśli bit jest ustawiony (przycisk zwolniony)instrukcja nie zostanie pominięta i nastąpi skok do main rjmp LED_on // jeśli bit jest wyzerowany skocz do funkcji która zapali diode LED_on: sbi PORTC, 0 //ustaw bit nr0 w rejestrze PORTC (zapal diodę led) sbic PINA,0 //jeśli bit nr0 w rejestrze PINA jest wyzerowany(przycisk zwarty do masy,czyli wscisniety),pomin nastepna instrukcje rjmp main //jesli przycisk został zwolniony(bit nr0 w rejestrze PINA jest ustawiony)zostanie wykonany skok do funckji main rjmp LED_on //jesli przycisk jest nadal wcisniety(bit nr0 w rejestrze PINA jest wyzerowany) to nastąpi skok do etykiety LED_on najpierw wpisaliśmy dyrektywy dla kompilatora i ustawiliśmy nasze piny, czyli linię nr0 portu A jako linię wejściową, czyli musieliśmy wyzerować bit o numerze 0 w rejestrze DDRA (wszystkie bity w rejestrach I/O są domyślnie ustawione jako zero, ale lepiej nie ufać wartościom domyślnym i wyzerować konkretny bit), następnie podciągamy nasza linie wejściową poprzez rezystor wewnętrzny do napięcia zasilania(gdy przycisk będzie zwolniony bit nr0 w rejestrze PINA, będzie ustawiony), robimy to poprzez ustawienie(wpisanie wartości 1, używamy instrukcji sbi) bitu 0 rejestru PORTA, ostatnim krokiem jest ustawienie linii 0 portu C do której jest podłączona nasza dioda, jako linie wyjściową . omówmy teraz pętle główną: pętla główna(main) sprawdza nam czy przycisk został wciśnięty(bit nr0 w rejestrze PINA wyzerowany), jeśli tak, następuje skok(instrukcja rjmp) do podprogramu "LED_on" który włącza diode, oraz sprawdza czy przycisk nie został zwolniony(czyli bit nr0 w rejestrze PINA ustawiony ), jeśli został, wykonywany jest skok do pętli "main" i po kolei, co oznaczają poszczególnie linie: " main: "- jest to tzw. Etykieta, czyli adres w pamięci programu, jest nam ona potrzebna wtedy gdy chcemy za pomocą instrukcji (w tym wypadku "rjmp") wykonać skok do miejsca w programie gdzie została użyta ta etykieta. Etykiety zapisujemy ze znakiem dwukropka na końcu . rjmp - Relative Jump- skocz do etykiety "nazwa etykiety" sbic – Skip if Bit in I/O Register Cleared – nie wykonuj następnej instrukcji jeśli bit w rejestrze wejścia/wyjścia jest wyzerowany "którym rejestrze", "numer bitu" Cwiczenie 2 -opóżnienia, stos Pora abyśmy nauczyli się powodować opóznienia w programie, do tego posłuży nam ten sam układ który wykorzystywaliśmy w ćwiczeniu poprzednim, zmienimy tylko trochę jego działanie, otóż gdy przycisk zostanie wciśnięty-dioda zacznie mrugać (z opózniem 250ms) .nolist .include "m32def.inc" .list .cseg .org 0 cli //wyłączenie przerwań ldi R16, HIGH(RAMEND) //załadowanie adresu końca pamięci[stała RAMEND - zdefiniowana w pliku m32def.inc](starszej jego częśći) SRAM do R16 out SPH, R16 //załadowanie zawartości rejestru R16 do SPH(starszej częśći) rejestru który przechowuje tzw. wskaźnik końca stosu ldi R16, LOW(RAMEND) //załadowanie (mlodszej czesci) adresu konca pamieci sram do R16 out SPL, R16 //przepisanie R16 do SPL -rejestru który przechowuje wskażnik końca stosu(młodszej czesci) cbi DDRA,0 // ustawiamy jako linie WEJŚĆIOWĄ pin do którego podlączony jest microswitch sbi PORTA, 0 //podciągamy go przez wewnętrzny opornik do napięcia zasilania sbi DDRC,0 //ustawiamy jako linie WYJŚCIOWĄ pin do którego podłączonona jest nasza dioda LED main: cbi PORTC, 0 //wyłącz diodę led sbis PINA, 0 //jeśli przycisk jest zwolniony pomiń następną instrukcje rjmp LED_on //instrukcja wykonywana gdy przycisk jest wciśnięty, skok do etykiety LED_on rjmp main //instrukcja wykonywana gdy przycisk jest zwolniony ,skok do etykiety main LED_on: //tutaj następuje skok gdy przycisk został wciśnięty = bit nr0 w PINA wyzerowany sbi PORTC, 0 //zapal diodę LED rcall delay //wywołaj podprogram opózniający cbi PORTC, 0 //zgaś diode LED rcall delay //wywołaj podprogram opózniający sbic PINA,0 //pomin następna instrukcje jeśli bit nr 0 w PINA jest wyzerowany = przycisk wicsniety rjmp main //instrukcja wykonywana gdy przycisk zostanie zwolniony ,skok do etykiety main rjmp LED_on //instrukcja wykonywana gdy przycisk jest wcisniety, skok do etykiety LED_on delay: ; ============================= ; delay loop generator ; 250000 cycles: ; ----------------------------- ; delaying 249999 cycles: ldi R16, $A7 WGLOOP0: ldi R17, $02 WGLOOP1: ldi R18, $F8 WGLOOP2: dec R18 brne WGLOOP2 dec R17 brne WGLOOP1 dec R16 brne WGLOOP0 ; ----------------------------- ; delaying 1 cycle: nop ; ============================= ret na początku program może wyglądać na trochę skomplikowany, ale starałem się opisac go jak najdokładniej komentarzami i myślę, że to powinno wystarczyć do zrozumienia idei jego działania.Tutaj chciałbym wyjaśnić tylko kilka spraw które mogą być nie jasne otóż po pierwsze: po co nam stos ? Fragment kodu odpowiedzialny za jego implementację w programie: cli //wyłączenie przerwań ldi R16, HIGH(RAMEND) //załadowanie adresu końca pamięci[stała RAMEND - zdefiniowana w pliku m32def.inc](starszej jego częśći) SRAM do R16 out SPH, R16 //załadowanie zawartości rejestru R16 do SPH(starszej częśći) rejestru który przechowuje tzw. wskaźnik końca stosu ldi R16, LOW(RAMEND) //załadowanie (mlodszej czesci) adresu konca pamieci sram do R16 out SPL, R16 //przepisanie R16 do SPL -rejestru który przechowuje wskażnik końca stosu(młodszej czesci) otóż jest on nam potrzebny ,ponieważ używamy pózniej instrukcji rcall jest to instrukcja która wykonuje skok do podprogramu (podobnie jak instrukcja rjmp) tyle, że po napotkaniu w podprogramie instrukcji ret następuje powrót do adresu(powiększonego o 1) z którego nastąpił skok do podprogramu. W naszym programie instrukcje "rcall" używamy do wywołania podprogramu opózniającego, po wykonaniu tego podprogramu (i co za tym idzie opóznienia trwającego 250ms)po powrocie wykonywana jest instrukcja "sbic PINA,0", czyli do powrotu nie potrzebujemy etykiety. I jeszcze słów kilka o podprogramie opózniającym, otóż nie napisałem go sam, ponieważ w przypadku assemblera pisanie pętli opózniających jest bardzo czasochłonne, użyłem do tego specjalnego programu który generuje nam kod pożądanego opóznienia, program ten jest darmowy i można go pobrać ze strony: http://www.home.unix-ag.org/tjabo/avr/AVRdelayloop.html gdy uruchomimy program wybieramy jakie potrzebujemy opóznienie, jakie jest taktowanie naszego mikrokontrolera oraz jakich rejestrów chcemy użyć w pętli, następnie klikamy przycisk "go" i w polu obok zostaje wygenerowana pętla opózniająca, wystarczy skopiować ją do naszego programu i gotowe. Jeżeli będziemy używali tego podprogramu częśćiej niz raz możemy dodać nad nim etykietę(w przypadku naszego programu to "delay:") a na końcu rozkaz "ret". Za pomocą pętli opóżniających możemy również programowo eliminiować drgania styków, a polega to na tym, że po wykryciu wciśnięcia przycisku w programie następuje opóżnienie trwające co najmniej 50ms, następnie po raz kolejny sprawdzamy czy przycisk jest wciśnięty,jeśli tak program idzie dalej, jeśli nie następuje powrót do pętli sprawdzającej stan przycisku. nowe instrukcje które pojawiły się w programie: rcall - Relative Call Subroutine - skok względny do podprogramu,"etykieta do której ma odbyć się skok" ret – powrót z podprogramu do miejsca z którego nastąpił skok dec - decrement – dekrementuj(pomniejsz o 1), "nazwa rejestru" brne – branch if not equal – skocz jeśli nie jest równe, " do etykiety " nop – no operation – nic nie rób Sbis - Skip if Bit in I/O Register Set, czyli pomiń następną instrukcje, jeśli bit w rejestrze I/O jest ustawiony "nazwa rejestru", "numer bitu" . Na zakończenie słów kilka o symulatorze: w AVR studio 4 mamy dostępny również symulator, który bardzo pomaga w diagnozowaniu błędów w programie, oraz gdy chcemy zobaczyć jak będzie przebiegało jego wykonywanie, aby uruchomić symulator musimy skompilować program-> na górnym pasku wybrać debug-> w rozwiniętym pasku wybrać start debugging-> klikamy F11 -> przeskakująca strzałka wskazuje nam instrukcję która zostanie wykonana w następnym kroku . Stan rejestrów możemy modyfikować i podglądać po prawej stronie okna programu,po lewej stronie widzimy informacje dt. Jednsotki centrlanej przydatne pdfy: http://www.atmel.com/Images/doc1022.pdf - poradnik dla użytkownika assemblera AVR ,możemy znależć tu wsyztskie instrukcjie wypisane w tabeli http://www.atmel.com/images/doc0856.pdf - zdecydowanie bardziej rozbudowany przewodnik, jak widać mamy opisaną po kolei każdą instrukcje i nie tylko. http://www.atmel.com/Images/doc2503.pdf - nota aplikacyjna naszej atmegi32 Starałem się uniknąć błędów w artykule, ale jeśli jednak jakiś znajdziecie, czy to rzecozwy, czy ortograficzny to proszę śmiało pisać. To by było na tyle, do zobaczenia w następej części.
  13. Już poprawione przepraszam za te błędy, wkradły się one do artykułu z powodu zmęczenia.
  14. Lekcja1: wstęp i pierwszy programik poniższy artykuł jak już się pewnie domyślacie będzie dotyczył programowania mikrokontrolerów z rodziny AVR w języku assembler, czyli języku niskiego poziomu( takim języku który kompilator tłumaczy bezpośrednio na kod bajtowy).Zdecydowałem sie go napisać ,ponieważ chciałbym udowodnić, że jest to język naprawdę ciekawy i użyteczny a zarazem prosty -możliwe, że niektórzy czytający w tym momencie czują się delikatnie zmieszani, ponieważ często moża wyczytać na różncyh forach, że assembler jest językiem nielogicznym czy niezrozumiałym. W tym kursie postaram się udowodnić, że te negatywne opinie nt. Assemblera są co najmniej nie prawdziwe i po poznaniu jego prostych podstaw docenimy jego zalety. No więc po tym krótkim wstępie jako że, pisanie długich wywodów za dobrze mi nie idzie to wypisze punkt po punkcie jakie moim zdaniem są zalety oraz wady assemblera. Zalety: -prosta składnia, właściwie jedyne co musimy zapamiętać to tzw. Mnemoniki, czyli 2, 3 lub 4 literowy kod, oznaczjący konkrteną instrukcje dla naszego procesora, niektórym może wydawać się to awykonalne, ale w rzeczywistości gdy uświadomimi sobie, że mnemoniki są skrótem jakiejś konkretnej instrukcji (np. JMP - jump, CP - compare ) to robi się zdecydowanie łatwiej.Ogólnie mówiąc mamy zdecydowanie mniej róznych dziwnych znaczków, które występują w innych językach, a które często jest trudniej zapamiętać niz mnemoniki. -Teraz UWAGA ,bardzo ważna zaleta asemblera – ucząc się tego języka, tak naprawdę uczymy się nie tylko języka, ale też architektury samego mikrokotnrolera ,co w przyszłości może baaardzo nam sie przydać np. Przy programowaniu w innych językach czy nawet przy programowaniu innych mikrokontrolerów. -Assembler jest świetną podstawą do programowania uC(mikrokontrolera/-ów) w językach wysokiego poziomu, które najpierw są tłumaczone, przez kompilator właśnie do assemblera , dzieki znajomości tego języka łatwiej nam będzie przesiąść się na języki wysokiego poziomu, oraz łatwiej będzie o optymalizację i wykrywanie błędów w takim kodzie (np. C) -assembler daje naprawdę małą objętość kodu wynikowego, żeby zająć pamięć przynajmniej połowy np. Poczciwej atmegi32 muslielibyśmy się naprawdę okropnie namęczyć. -daje nam bardzo dobrą kontrole nad pracą uC, przez co możemy w nim pisać programy które sterują jakimś bardzo czasowo krytycznym procesem. Wypisałem te zalety które akurat przyszły mi do głowy, zapewne można by ich jeszcze trochę wypisać, ale ich odkrywanie pozostawie wam No i dla równowagi wady a właściwie jedna, ale dosyć spora wada: -pisanie bardziej skomplikowanych programów zajmuje zdecydowanie więcej czasu, niż w językach wysokopoziomowych nastała pora, aby przejść dalej, a mianowicie w czym będziemy pisali : -Do pisania programów w asm wybrałem dosyć juz leciwe, ale proste w obsłudze i przejrzyste AVR studio 4, ponieważ do pisania naszych programów nic bardziej skomplikowanego nie jest nam potrzebne, a następne wersje AVR studio mają całą mase niepotrzebnych bajerów które właśćiwie powoduja głównie spowolnienie komputera i zajmują kilkanaście razy więcej miejsca na dysku. A co będzimy programowali?: -programowali będziemy jeden z najpopularniejszych mikrokontrolerów z rodziny AVR, a mianowicie Atmege32 zamontowaną na klasycznej płytce stykowej, wybrałem takie rozwiązanie z tego względu, że w odróżnieniu od płytek rozwojowych jest ono dosyć tanie i jeśli ktos nie posaida jeszcze płytki stykowej to raczej nie powinienn mieć problemu z nabyciem takowej, a taka płytka przyda nam się napewno jeszcze wiele razy. Czym programujemy: -ja będe używał programatora AVR isp mkII, ale jeśli ktos posiada inny programator też nie powinienn mieć problemu. Ok, to by było na tyle, pora wziąść się do roboty : pierwszy układ który zmontujemy będzie chyba najprostszy z możliwych, czyli zasilanie, programator oraz dioda podłączona anodą do portu PC0, a katodą przez rezystor do minusa zasilania Gdy już zmontujemy układ według powyższego schematu pora przystąpić do pisania programu, którego zadaniem będzie zapalenie naszej diody LED, myślę, że uruchomienie avr studio jest na tyle banalne i było już wielokrotnie opisywane co gdzie i jak, że mogę pominąć tą część. Przystępujemy więc do pisania programu ,pierwsze co wpisujemy to 5 dyrektyw dla komilatora które będą występowały praktycznie w każdym pisanym przez nas programie asm: .nolist .include "m32def.inc" .list .cseg .org 0 oznaczją one : .nolist – informuje kompilator aby nie kompilował następujących po niej wierszy .include "m32def.inc" – jest to plik który zawiera definicje nazw rejestrów ,co ułatwia bardzo pisanie programów, bo nie musimy odnosić się do fizycznych adresów rejestrów, tylko możemy używać ich nazw (czyli np. Zamiast pisać 0x14, możemy napisać DDRC) .list –informuje kompilator, aby kompilował następujące po tej dyrektywie wiersze. .cseg – czyli skrót od "code segment", informuje kompilator ,że będziemy pisali w segmencie programu. .org 0 – nakazuje, aby program był umieszczony w pamięci począwszy od adresu zerowego. No więc wiemy już co oznaczją dyrektywy teraz pora na program właśćiwy który będzie wyglądał tak: wersja I: .nolist .include "m32def.inc" .list .cseg .org 0 sbi DDRC, 0 //ustawiamy pin 0 portu C jako wyjściowy sbi PORTC, 0 //ustawiamy stan wysoki na pinie 0 portu C najprostszy z możliwych programów, czyli ustawienie bitu w rejestrze I/O* DDRC(data direction register) oraz PORTC, pierwszą instrukcją ustawiamy PORTC 0 jako port wyjściowy ,drugą ustawiamy go w stan wysoki ,aby nasza dioda mogła się zapalić.Następne kroki to : klikamy przycisk F7 na klawiaturze ,aby skomilować program-> wybieramy tools na górze okna programu-> w rozwiniętym okienku wybieramy "program avr"-> klikamy "connect-> w okienku które sie nam pojawi wybieramy nasz programator-> gdy połączymy się z programatorem,wyskoczy nam okno dialogowe w którym wchodzimy w zakładkę program-> w podpunkcie "flash" wybieramy ścieżke do naszego pliku z programem(rozszerzenie .hex) -> klikamy "program" Co dokładnie oznaczają instrukcje, oraz jaka jest ich składnia?: sbi – "set bit in I/O" -czyli ustaw bit w rejestrze "wejśćia/wyjścia"(którym ,czyli np. DDRC , PORTC,lub PINC ) ,"o numerze"(rejestry są ośmiobitowe ,więc wybieramy z przedziału 0-7, w zależności który pin danego portu chcemy ustawić) przeciwieństwem instrukcji "sbi" jest instrukcja "cbi" która zeruje dany pin. Asembler jest językiem w którym wielkość liter nie ma znaczenia, więc instrukcja sbi = SBI, tak samo jest z nazwami rejestrów ddrc = DDRC Wersja II: teraz pora na drugą wersje programu który rówież zapali naszą diodę LED: .nolist .include "m32def.inc" .list .cseg .org 0 ldi R16,0b00000001 out DDRC, R16 out PORTC, R16 najpierw ładujemy stałą 0b00000001 do jednego z rejestrów ogólnego przeznaczenia* w tym wypadku R16 ,następnie zawartość rejestru R16 ładujemy do rejestrów I/O DDRC(tym sposobem w rejestrze DDRC bit nr0 został ustawiony, czyli linia ta została ustawiona jako wyjściowa), następnie tą samą stałą z rejestru R16 ładujemy do rejestru PORTC(sytuacja analogiczna jak z rejestrem DDRC ,tyle że tym razem ustawiamy bit w PORTC, co skutkuje ustawieniem stanu wysokiego na linii 0 portu C) i tu jeszcze jedna sprawa, użyłem w tym wypadku zapisu binarnego stałej ładowanej do rejestru ,aby było widać dokładnie który bit ustawiamy, ale równie dobrze moglibyśmi użyć zapisu szesnastkowego, wyglądało by to tak: .nolist .include "m32def.inc" .list .cseg .org 0 <code/> ldi R16,0x01 out DDRC, R16 out PORTC, R16 lub mojego ulubionego zapisu, wg mnie najwygodzniejszego: .nolist .include "m32def.inc" .list .cseg .org 0 ldi R16, (1<<PINC0) out DDRC, R16 out PORTC, R16 znaczenie poszczególnych instrukcji: ldi – "load immediate" -[załaduj bezpośrednio] "do rejestru ogólnego przeznaczenia" , "stałą K" out – "out port"- [załaduj] "do rejestru" "z rejestru" *od dłuższego czasu używam już słowa rejestr(ogólnego przeznaczenia , wejścia/wyjśćia) i może niektórzy którzy dopiero zaczynją zabawę z mikrokontrolerami mogą nie wiedzieć czym są te rejestry otóż już wyjaśniam: rejestry są to niewielkie komórki pamięci w przypadku rejestrów systemowych(np. Używane przez nas DDRC, PORTC itp.) słuzą one sterowania konkretnymi peryferiami naszego mikrokontrolera np. W przypadku rejestru DDRn ustawienie w nim bitów powoduje ustawienie linii danego portu jako wyjśćie, a zerowanie jako wejśćie . Rejestry ogólnego przeznaczenia(od R16 do R31) są do użytku programisty, można w nich przechowywać, zmienne lub stałe które wykorzystywane będą w programie. Ze względu ,że nasze mikrokontrolery AVR są 8-bitowe to też wszystkie rejestry w nich są również 8-bitowe co oznacza, że największą wartość dziesiętną jaką możemy zaladować do pojedyńczego rejestru to 255. komentarze: wpisać komentarz w języki assembler możemy na 3 soposoby: ";" - znak średnika pozwala nam na napisanie komentarza do końca wiersz "//" -a analogicznie jak w przypadku średnika "/*"możemy pisać komentarz aż do odwołania znakiem "*/" . Zakończenie: Ok więc na tym zakończymy pierwszą część kursu, jeśli się on spodoba opublikuje następne jego części .Prosze pisać co można by jeszcze poprawić, dopisać itp. __________ Komentarz dodany przez: Treker Proszę poprawić interpunkcję. Spacje stawiamy po, a nie przed znakiem interpunkcyjnym.
  15. @nes86 - no i o to chodziło ,teraz wszystko śmiga bez problemu wielkie dzięki za pomoc
×
×
  • Utwórz nowe...