Elektroda.pl
Elektroda.pl
X

Search our partners

Find the latest content on electronic components. Datasheets.com
Elektroda.pl
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

AVR [ATMEGA48] , Assambler - samoistna zmiana taktowania uC

guuciek 18 Sep 2010 12:43 2076 11
IGE-XAO
  • #1
    guuciek
    Level 14  
    Witam .
    Napisałem ( prawie) sobie na ww procesor program w asm i bascomie który ma robić za zamek otwierany kartą . W bascomie wszystko działa w 100 % według założenia jednak w asm mam taki problem że układ sam zmienia swoje taktowanie (ponad 2x) przez co wszystkie funkcje czasowe nie pracują jak powinny . Nie było by w tym nic niezwykłego ( ustawiony prescaler ) gdyby nie fakt że 1 raz po wgraniu programu bez odłączania zasilania wszystkie funkcje czasowe działają poprawnie ( zrobiłem sobie na początku kodu zaraz po starcie pętlę która ma zaświecać i gasić diodę co 3 sec ) . Jednak gdy zresetuję układ przez odłączenie zasilania , po ponownym uruchomieniu tego samego kodu !! wszystkie czynności wykonują się ponad 2 razy wolniej . P Odkreślam że w programie ani fusach nic nie zmieniałem w tym czasie .
    próbowałem podczas startu zerować bajt prescalera , oraz kalibracji oscylatora ale dalej jest to samo . Nie mam pojęcia co może powodować takie spowolnienie pracy procesora skoro czas odliczany jest przy pomocy timera więc podstawa czasowa jest brana z oscylatora ( używam wewnętrzny oscylator 8 Mhz ) .

    Wrzucam kawałek kodu w ASM :

    Code:


    .include "m48def.inc"         ; dane dla kompilatora AVRstudio

    .def flagi = r2
    .def wyswietlacz = r3
    .def klawiatura = r4
    .def sloty = r8
    .def zro = r6                ; przechowujemy tutaj wartośc zero do porównania instrukcją CP
    .def status = r7
    .def liczbakart = r5
    .def i = r16
    .def o = r17
    .def p = r18
    .def slep = r19 
    .def daneeram = r20
    .def adreseram = r21
    .def dane = r22
    .def u = r23
    .def r = r24
    .def zadanie = r25

    .EQU IOKARTY    = PC0         ; Linia danych karty
    .EQU KLAPA       = PC1         ; Krańcówka otwarcia klapy ( 1 - klapa zamknięta , 0 - klapa otwarta)
    .EQU ZAMEK       = PC2         ; Otwieranie zamka
    .EQU RXD       = PD0         ; Uart - dane odbierane ( Transmisja rs232 , do wykorzystania )
    .EQU TXD       = PD1         ; Uart - dane wysyłane  ( Transmisja rs232 , do wykorzystania )
    .EQU OK       = PD2         ; Przycisk SET na klawiaturze .    
    .EQU STYKKARTY   = PD3         ; Krańcówka obecności karty ( 0 - karta obecna , 1 - karta nieobecna )
    .EQU WEJSCIE1    = PD5         ; Wejście logiczne do wykozystania     
    .EQU WEJSCIE2    = PD6         ; Wejście logiczne do wykozystania
    .EQU DS       = PD7         ; Linia danych wyświetlacza i klawiatury . 
    .EQU KIERUNEK    = PB0         ; Kierunek transmisji ( 0 - transmisja do wyświetlacza (wyświetlacz świeci jeśli ustawiony)  , 1 - wyświetlacz gaśnie , wybrano odczyt z klawiatury ) 
    .EQU STCP       = PB1         ; Linia potwierdzenia wyświetlacza i klawiatury
    .EQU SHCP       = PB2         ; Linia zegara wyświetlacza i klawiatury
    .EQU WYJSCIE1    = PB3         ; Wyjście tranzystorowe ( max 0,5 A ) - do wykorzystania
    .EQU WYJSCIE2    = PB4         ; Wyjście tranzystorowe ( max 0,5 A ) - do wykorzystania
    .EQU WYJSCIE3    = PB5         ; Wyjście tranzystorowe ( max 0,5 A ) - do wykorzystania
    .EQU RSTKARTY    = PB6         ; Linia resetu karty
    .EQU ZEGARKARTY = PB7         ; Linia zegara karty
                            ; Dodatkowo zostały wyprowadzone 2 linie przetwornika analogowo-cyfrowego do wykorzystania
                            ; Stałe adresów dostępu do pamięci 24c02 ( nogi E0 , E1 , E2  zwarte do masy )   
    .EQU adresW     = 0xA0         ; Adres zapisu do pamięci
    .EQU adresR     = 0xA1        ; Adres odczytu z pamięci

    .dseg
    danekarty:   .byte 16
    zapisanekarty:   .byte 128

    .cseg

    ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!   ATmega 48 / 88 / 168 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    ;!! W wyżej wymienionych procesorach rejestry wejścia/wyjścia o adresach od 0x60 do 0xFF   !!
    ;!!  nie mogą być odczytywane i zapisywane przy pomocy instrukcji OUT oraz IN . Rejestry   !!
    ;!! te obsługuje się jak zwykłą pamięć S-RAM czyli przy pomocy funkcji STS , ST , LD , LDS !!
    ;!! Najprostszym sposobem na sprawdzenie czy dany rejest należy do ww grópy jest zobaczenie!!
    ;!! czy w oknie we\wy ( po prawej stronie ) dla interesującego nas rejestru są podane oba  !!
    ;!! adresy , jśli taknie jest to rejestr ma adres powyżęj 0x60 . Dla rejestrów które mają  !!
    ;!! wyswietlone oba adresy TRZEBA użyć instrukcji IN , OUT ponieważ tylko  one działają dla!!
    ;!! tej grópy rejestrów. Trzeba na to uważać ponieważ bardzo trudno jest znaleźć taki błąd !!   
    ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    ; program
     
    .org 0x00                  ; PC = 0 , przerwania reset lub podania zasialania i załączenia ukłądau 
       rjmp start                ; przeskakujemy adresy przerwań (od 0x01 do 0x19) . Atmega48 ma 26 przerwań.
          
    .org 0x02                   ;adres przerwania INT1
    ;   rjmp karta                ;jeśli nastąpi przerwanie z INT1 to przeskakujemy do podfuncji karta
     
    .org 0x10                  ;adres pzerwania przepełnienia timer0
       rjmp czas                ;przeskakujemy do podfunkcji czas



    .org 0x30

    start:
    ;   clr i
    ;   sts clkpr , i
    ;   ldi i , 160
    ;   sts osccal , i
       
       ldi i , high(ramend)      ;wpisujemy do rejestru SP (wskaźnik stosu) adres ostatniej komórki pamięci SRAM 
       out sph , i               ; funkcja high(x) oznacza odczytanie starszego bajtu adresu
       ldi i , low(ramend)         ; funkcja low(x) oznacza odczytanie młodszego bajtu adresu
       out spl , i               ; ramend jest to etykieta zawarta w paczce z informacjami o procesorze dla AVRStudio
                            ; (patrz 1 linijka kodu) w której zapisany jest adres ostatniej komórki SRAM dla danego procesora   
       
       ldi i , 0B00110110          ; ustawianie portu c   
       out ddrc , i
       ldi i , 0B00110101
       out portc , i
       
       ldi i , 0b10000000         ;ustawianie portu d
       out ddrd , i
       ldi i , 0b11101100
       out portd , i

       ldi i , 0B11111111          ;ustwaianie portu b
       out ddrb , i
       ldi i , 0b00000111
       out portb , i

       ldi i , 0b00000100          ;ustwiamy przerwanie INT1 na zmianę stanu
       sts eicra, i            
       
       ldi i , 0b00000010         ;i odblokowujemy przerwania
       out eimsk , i      

       
                            ; ustawiamy sprzetowy I2C
       ldi i , 2               ; wpisujemy do rejestru TWBR wartość 2 , co powoduje ustawienie częstotliwości lini SCl na 400 kHz
       sts twbr , i             ; czyli takiej z jaką pracuje kontroler I2C w procesorze . Pamięć 24c02 od atmela może być taktowana do 1Mhz  . Dokładny opis oraz wzór na str.216 noty katalogowej
                      
       ldi i , 1       
       sts timsk0 , i
                            ; odblokowujemy przerwanie dla timer0 .  Będziemy nim obliczać długie odstępy czasowe
                            ; Timer0 to timer 8-bitowy czyli zlicza od 0 do 255 . My będziemy go używać wraz z prescalerem ( dzielnik częstotliwości)   
                            ; o wartości 1024 . Możemy obliczyć ilość przepełnień w ciągu 1 sec dzieląć częstotliwość taktowania procesora przez prescaler oraz maksymalną liczbę zliczeń
                            ; 80000000 Hz \ 1024 \ 256 = 30,51 przepełnień timera w ciągu sec czyli jedno przepełnienie będzie trwało 1 \ 30,51 sec ~ 0,03 sec
                      

       clr i                   ;rejestr GPIOR0 używamy jako rejestru z flagami , jest to wolny rejestr z adresowaniem bitowym 
       out GPIOR0 , i            ; Bit rejestru - wykorzystanie 
                            ; 0 - timer1 odliczył wskazany czas (1) ,  jeszcze nie odliczono (0)
                            ; 1 - karta jest poprawna (1) , karta niepoprawna (0)
                            ; 2 - wykryto wsadzenie karty, nalezy ja odczytać (1) , nie wykryto / odczytano kartę (0)   
                            ; 3 - dopisz kartę do pamięci (1) , NIE DOPISUJ (0)
                            ; 4 - wyświetl kropkę na wyświetlaczu (1) , nie wyświetlaj (0)
                            ; 5 - stan przycisku Set
                            ; 6 - flaga odczytania zapisanych kart do SRAM , należy przepisać dane z ERAM do SRAM (0) , dane już odczytane (1)
                            ; 7 - wybór pamięci EEPROM , pamięć wewnętrzna (0) , pamięć zewnętrzna 24c02 (1)
       
       
       ldi i , 0b01011111         ; wyłączamy wszytkie nieuzywane moduły ( SPI ,  UART , ADC . nieużywane timery ) aby nie pobierały one prądu .
       sts prr , i
       


    ;#########################################################################################################################
       cbi gpior0 , 7             ;wybór pamięci EEPROM , pamięć wewnętrzna (0) , pamięć zewnętrzna 24c02 (1)            ##
    ;#########################################################################################################################

       
       sei                  ; załączamy globalne zezwolenie przerwań

          

    ;**************************************************** program własciwy **************************************************

     
    s1:
       ldi slep  , 90            ; oczekujemy około 3 sec  --> slep(90) * 0,0333 sec ( max czas jaki możemy odczekać to około 8,5 sec (slep = 255))
       rcall odliczaj            ; pzeskakujemy do funkcji odczekiwania czasu

       sbic portb , 3                              ; zmiana stanu wyjscia diody
       rjmp u1

       sbis portb , 3
       sbi portb , 3
       rjmp s1

    u1:   cbi portb , 3
       rjmp s1






    i używane podfunkcje :



    Code:
    ;*************************************  funkcja odliczaj - załączanie timer0 i czekanie na zakończenie odliczania ***********************            
    

    odliczaj:
        cbi gpior0 , 0                ; zerujemy flagę odliczania
        ldi i , 5
        out tccr0b , i                ; wpisujemy do tccr0b 5 co znaczy załączenie timera0 i ustawienie prescalera na 1024
        nop
        nop
    od1:
        nop
        nop
        nop
        nop
        sbis gpior0 ,0                ; sprawdzamy czy timer odliczył czas     
        rjmp od1                    ; jesli nie to wracamy do od1
        cbi gpior0 , 0                ; zerujemy flagę odliczenia
        ret                            ; powracamy z funkcji     




    Code:


    ;**************************** Przerwanie Czas - zlicza czas do wartości ustalonej przez wzór  t = (1\3) * slep [sec]   gdzie t = czas po jakim funkcja ustawi flagę ***************************

    czas:   
       dec slep
       cp slep , zro
       breq czas2
       reti
    czas2:
       clr i
       out tccr0b , i
       sbi gpior0 , 0
       reti




    Fajnie było by gdyby ktoś mógł wskazać na co należy zwrócić uwagę .
    Ustawienia fusów prezentują się tak :
    LOW : 0xD2
    HIGh : 0xD7
    EXT : 0x01

    Komentarze zawierają sporo błędów językowych ale przepuszczę je przez Worda gdy program będzie działał jak należy .



    Kolejną intrygującą rzeczą jest to że po zapisaniu programu razem z pamięcią EEPROM program się zawiesza ( nie startuje ) , chociaż w żaden sposób nie odnosiłem się do tej pamięci , i nie były załączone żadne przerwania .
    Wszystko zaczynało jednak działać gdy najpierw wgrałem EEprom , zresetowałem układ , a następnie wgrałem program . Wtedy wszystko ruszało tak jak powinno . Oczywiście przy ustawionym fusie EESAVE . Sprawdziłem jednak czy dane z eeprom się nie skasowały, i tak jak być powinno wszystkie były na swoim miejscu.

    I to chyba tyle .
    Byłbym wdzięczny gdyby ktoś potrafił wytłumaczyć mi te 2 zagadki .
    Priorytetem jest pierwsza , druga można traktować w kategorii ciekawostki jako że nie przeszkadza ona przy pisaniu programu , jeśli czynności wykona się prawidłowo .

    Ps. Gdy skończę kod w asm to podzielę się na elektrodzie całym kodem zarówno w bascomie jak i assamblerze z dokładnymi komentarzami , wykresami funkcji , schematami płytkami itd..
    Pozdrawiam .
    Kamery 3D Time of Flight - zastosowania w przemyśle. Darmowe szkolenie 16.12.2021r. g. 10.00 Zarejestruj się
  • IGE-XAO
  • IGE-XAO
  • #3
    guuciek
    Level 14  
    Procesor jest w smd wiec nie ma podstawki a poza tym innych nie mam z powodu sytuacji na rynku , ale w bascomie wszystkie waitms itd działają poprawnie ... też mam tam zaprzęgnięty 1 timer i tez działa jak powinien .

    Skierowałem pytanie na forum bo już nie mam żadnych logicznych pomysłów.
  • #4
    Nawigator
    Level 33  
    Używasz r6=zro jako zerowy ale nie widzę jego inicjalizacji.
    Najlepiej dać krótką procedurę zerowania rejestrów roboczych.
    Poza tym konieczna jest procedura wyłączania watchdoga jak go nie używasz.
    Stos na Ramend jest ustawiany sprzętowo więc tutaj niepotrzebnie.

    N.
  • #5
    Andrzej__S
    Level 28  
    guuciek wrote:

    Code:

    czas:   
       dec slep
       cp slep , zro
       breq czas2
       reti
    czas2:
       clr i
       out tccr0b , i
       sbi gpior0 , 0
       reti


    Nawigator wrote:

    Używasz r6=zro jako zerowy ale nie widzę jego inicjalizacji.
    Najlepiej dać krótką procedurę zerowania rejestrów roboczych.

    W zasadzie w powyższym kodzie można całkowicie pominąć instrukcję cp slep , zro i wtedy rejestr zro stanie się zbędny (chyba, że jest używany w innym miejscu kodu, którego autor nie przedstawił). Instrukcja DEC ustawi flagę Z w SREG w momencie osiągnięcia przez rejestr slep wartości zerowej, więc BREQ zadziała prawidłowo. Instrukcja CP byłaby potrzebna wtedy, gdyby rejestr zro miał wartość różną od zera.
  • #6
    guuciek
    Level 14  
    Rejestr ten jest używany jeszcze w paru miejscach .
    Po zmianie dalej jest to samo . Przydał by się ktoś od sztuk magicznych może on by coś poradził na to :) Bo ja już za bardzo nie widzę co może jeszcze mieszać .


    Kod który wrzuciłem to część która jest wykorzystywana podczas sprawdzania tych odstępów czasowych . Mogę wrzucić nieużywaną resztę kodu jeśli potrzeba , jednak od razu mówię że jest tego ponad 1000 linii i nie powinien on mieć wpływu na działanie programu ponieważ nie jest wykonywany .
  • #7
    Nawigator
    Level 33  
    Brak jest również obsługi rejestru stanu SREG w przerwaniu a rejestr roboczy " i " uzywany jest także w pętli main.

    N.
  • #8
    guuciek
    Level 14  
    Rejest i jest używany w pierwszej części kodu ale jest to jeszcze PRZED załączeniem timera ( po odliczeniu timer sam się wyłącza ) więc nie powinno to przeszkadzać i nie przeszkadza dopóki nie zresetuję zasilania , potem dzieje się coś czego nie rozumiem.

    Co do obsługi SREG , mógłby Pan jakoś rozwinąć swoją wypowiedź ponieważ nie za bardzo rozumiem .
  • Helpful post
    #9
    Nawigator
    Level 33  
    Rejestr SREG zawiera podstawowe flagi mówiące o stanie procesora potrzebne do kontroli wykonywania programu (warunki, skoki itd.)
    Na czas obsługi przerwania należy zapamiętać te flagi gdyż kod w przerwaniu je oczywiscie może zmienić. Po powrocie z przerwania procesor podejmuje program w miejscu wystąpienia przerwania i musi wiedzieć co sie działo wcześniej.
    Czyli
    Code:

    czas:   
                            push i
                            in i, SREG
                            push i
       dec slep
       cp slep , zro
       breq czas2
                             pop i
                             out SREG, i
                             pop i
       reti
    czas2:
       clr i
       out tccr0b , i
       sbi gpior0 , 0
                             pop i
                             out SREG, i
                             pop i
       reti


    N.
  • #10
    guuciek
    Level 14  
    Witam. I znalazł się poszukiwany magik :)
    Problem 1 rozwiązany dzięki pomocy Pana Nawigator .
    Jednak taką małą poprawkę trzeba wstawić do jego kodu .
    2 i 3 funkcji przepisu - powinno być OUT sreg , i zamiast IN .

    Teraz pozostaje 2 problem , gdyby ktoś miał jakieś pomysły to proszę pisać ponieważ jestem ciekaw co jeszcze mogłem zrobić źle . Program zawiesza się jeszcze przed ustawieniami portów , tego jestem pewien .

    Pozdrawiam i dziękuję za pomoc .
  • #11
    Nawigator
    Level 33  
    Faktycznie konieczna była poprawka z in na out ale pisałem z głowy czyli z niczego.
    Co do wieszania się programu znajdź w sieci init watchdoga oraz zerowanie jego flagi w MCUSR, do tego procesora.
    Opis w nocie AVR132 na stronie Atmela.
    Poza tym odblokowałeś przerwanie INT1 a nie widzę w tym fragmencie kodu jego obsługi.
    N.
  • #12
    guuciek
    Level 14  
    Ponieważ jest on w komentarzu bo nie był potrzebny do sprawdzenia .

    Pozdrawiam .