Elektroda.pl
Elektroda.pl
X
SterControl
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

ASM, przepełnianie stosu?

28 Sie 2007 07:44 2832 28
  • Poziom 27  
    Witam, najpierw program:

    Cytat:
    .include "m88def.inc"
    ; procek ustawiam na prace z wewnętrznego oscylatora 8MHz
    ; tryb sleep ustawiam jako POWER DOWN

    ; PD3 (INT1) jest przyciskiem (wejscie)
    ; PB1 (OC1A) to wyjscie sterujace reglem
    ; PB5 to wyjscie sterujące MOSFETami zalaczajacymi zasilanie regla
    ; PD5 (OC0B) to wyjscie sterujace LEDem
    ; ADC6 - pomiar pradu
    ; PC1, pomiar napiecia baterii (dziala gdy PC2,3,4 sa w stanie niskim)
    ; PC2, PC3, PC4 sa wejsciami, zmieniaja swoj stan tylko przy pomiarze napiecia (wyjscie, 0)
    ; PC5 (ADC5), pomiar temperatury
    ; BOD ustawiam na 101 (2,7V)

    .org 0x0
    rjmp Resetuj ; Reset handler
    nop ;rjmp IRQ_INT0 ; IRQ0 Handler
    rjmp IRQ_INT1 ; przerwanie od przycisku
    nop ;rjmp PCINT0 ; PCINT0 Handler
    nop ;rjmp PCINT1 ; PCINT1 Handler
    nop ;rjmp PCINT2 ; PCINT2 Handler
    nop ;rjmp WDT ; Watchdog Timer Handler
    nop ;rjmp TIM2_COMPA ; Timer2 Compare A Handler
    nop ;rjmp TIM2_COMPB ; Timer2 Compare B Handler
    nop ;rjmp TIM2_OVF ; Timer2 Overflow Handler
    nop ;rjmp TIM1_CAPT ; Timer1 Capture Handler
    nop ;rjmp TIM1_COMPA ; Timer1 Compare A Handler
    nop ;rjmp TIM1_COMPB ; Timer1 Compare B Handler
    nop ;rjmp TIM1_OVF ; Timer1 Overflow Handler
    nop ;rjmp TIM0_COMPA ; Timer0 Compare A Handler
    nop ;rjmp TIM0_COMPB ; Timer0 Compare B Handler
    nop ;rjmp TIM0_OVF ; Timer0 Overflow Handler
    nop ;rjmp SPI_STC ; SPI Transfer Complete Handler
    nop ;rjmp USART_RXC ; USART, RX Complete Handler
    nop ;rjmp USART_UDRE ; USART, UDR Empty Handler
    nop ;rjmp USART_TXC ; USART, TX Complete Handler
    nop ;rjmp ADC ; ADC Conversion Complete Handler
    nop ;rjmp EE_RDY ; EEPROM Ready Handler
    nop ;rjmp ANA_COMP ; Analog Comparator Handler
    nop ;rjmp TWI ; 2-wire Serial Interface Handler
    nop ;rjmp SPM_RDY ; Store Program Memory Ready Handler
    .org 0x1A

    Resetuj: ; zainicjalizuj stos:
    cli
    ldi R16, low(RAMEND)
    out SPL, R16
    ldi R16, high(RAMEND)
    out SPH, R16

    ldi R16, 0
    ldi R17, 0
    ldi R18, 0
    ldi R19, 0
    ldi R20, 0
    ldi R21, 0
    ldi R22, 0
    ldi R23, 0
    ldi R24, 0
    mov R15, R24

    wdr
    ldi R16, (1<<WDE)|(1<<WDCE)
    sts 0x60, R16
    ldi R16, (1<<WDP0)|(1<<WDP3)|(1<<WDCE)
    sts 0x60, R16

    rcall led_1

    ; ==================== ustawiam przerwanie od INT1 =========================
    ldi R16, 0b00000000 ; ustawiam przerwanie przy niskim stanie INT1
    sts EICRA, R16
    ldi R16, 0b00000010
    sts 0x3D, R16 ; odblokowuje przerwanie od INT1
    ; ==========================================================================

    ; ================== ustawiam tryb sleep jako POWER save ===================
    ldi R16, 0b00000111 ; 101 -power down
    sts SMCR, R16 ; ustawiam SMCR
    rjmp spij
    ; ==========================================================================

    IRQ_INT1:
    cli ; blokujemy przerwania

    ldi r16,0b00000000
    out SMCR,r16 ;wylacz SE (zezwolenie na uspienie)

    sbi EIFR, INTF1 ; kasujemy wywołanie
    ;cbi PORTB, 5 ; wylaczam urzadzenie <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    ; 1a) jesli jest to poczatek programu (R20=0) to wykonujemy procedure startu
    ; 1b) jesli program juz dziala (R20=1) to sprawdzamy czy ktos nie chce zmienic wypelnienia lub wylaczyc
    ; 2a) zmieniamy wypelnienie
    ; 2b) wylaczam (sleep)

    ; 1)
    rcall Czekaj ; jesli w ciagu okreslonego czasu przycisk zostanie puszczony to wlaczamy
    in R16, PIND
    sbrs R16, 3 ; jesli nadal bedzie trzymany to zmieniamy wypelnienie
    rjmp zmien_PWM

    sbrc R20, 0
    rjmp wylacz

    ldi R20, 1 ; ustawiam znacznik pracy układu
    ; petla główna programu, ustawiamy generator (T1) na PWM (WGM10, WGM13)(COM1A0)(cs10, CS11) 50Hz OC1A i zmieniamy wypełnienie
    ; najpierw sprawdzamy temperature i na piecie

    sbi DDRB, 1 ; ustawiam wyjscie B1

    ; jesli sa dobre to wlaczam generator PWM
    sbi PORTB, 5
    sbi DDRB, 5

    ldi R16, 0b10000001 ; wgm 0b10000001 (61Hz) 0b10000010 (30,5Hz)
    sts TCCR1A, R16
    ldi R16, 0b1011 ; 0b1011
    sts TCCR1B, R16 ; prescaler 64
    ldi R16, 19 ; wypelnienie poczatkowe (19)
    sts OCR1AL, R16


    rcall napiecie

    rcall temperatura

    rcall Czekaj
    rcall Czekaj
    rcall Czekaj
    ;rcall Czekaj
    ;rcall Czekaj
    lds R16, OCR1AL
    inc R16
    inc R16
    sts OCR1AL, R16


    nic:
    wdr
    sei
    rcall czekaj
    rcall czekaj
    rcall czekaj
    rcall czekaj
    rcall prad
    rjmp nic


    zmien_PWM:
    ;sprawdzamy czy zalaczono uklad, jesli nie to wylaczamy
    sbrs R20, 0
    rjmp wylacz ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

    rcall Czekaj ; jesli w ciagu okreslonego czasu przycisk zostanie puszczony to wylaczamy

    zmieniam:
    sbrs R20, 0
    rjmp wylacz

    ; sprawdzam czy PWM jest podzielne przez 2 bez reszty, jesli tak to zwiekszam pwm, inaczej zmiejszam
    mov R16, R21
    lsr R16

    in R16, SREG
    sbrs R16, 0
    rjmp PWM_PLUS

    lds R16, OCR1AL
    cpi R16, 23 ; nastawa minimum 22 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    brlo pozniej
    dec R16
    sts OCR1AL, R16
    pozniej:

    rcall Czekaj
    ; tu zmieniamy wypelnienie
    ; poniewaz zostało zmienione raz wypelnienie, oczekujemy na ponowna zmiana
    in R16, PIND
    sbrs R16, 3
    rjmp zmien_PWM
    ; dodajemy obsługe zmiany wypelnienia w zaleznosci od R21 (+/-)
    inc R21

    rcall Czekaj
    rcall Czekaj
    rcall Czekaj
    sei
    rjmp nic

    PWM_PLUS:
    lds R16, OCR1AL
    cpi R16, 25 ; nastawa maximum 25
    brsh pozniej

    inc R16
    sts OCR1AL, R16
    rjmp pozniej

    temperatura:
    ldi R16, 0b11000101 ; odpalam przetwornik ADC5 z Uref=1,1V i badam napiecie na czujniku temperatury
    sts ADMUX, R16
    ldi R16, 0b11000011
    sts ADCSRA, R16

    petla_temperatury: ; czekam na zakonczenie konwersji
    lds R16, ADCSRA
    sbrs R16, ADIF
    rjmp petla_temperatury

    lds R16, ADCL
    lds R17, ADCH

    ldi R22,0b00010000
    sts ADCSRA, R22
    rcall oblicz_temperature
    ret


    ; ===================================== mierze napiecie ==========================================
    napiecie:
    ; najpierw ustawiam stan niski na PC2, PC3, PC4
    cbi PORTC,2
    cbi PORTC,3
    cbi PORTC,4
    sbi DDRC,2
    sbi DDRC,3
    sbi DDRC,4

    ldi R16, 0b11000001 ; odpalam przetwornik ADC1 z Uref=1,1V i badam napiecie na ADC1 poprzez rezystory R9, R10
    sts ADMUX, R16
    rcall czekaj ; czekamy na Uref
    ldi R16, 0b11000011
    sts ADCSRA, R16

    petla_napiecia: ; czekam na zakonczenie konwersji
    lds R16, ADCSRA
    sbrs R16, ADIF
    rjmp petla_napiecia

    lds R16, ADCL
    lds R17, ADCH

    ldi R22,0b00010000 ; zeruje flage konca konwersju
    sts ADCSRA, R22

    ; 790j-krytyczne(6,5V), 875j-1_d(7,2V), 899j-2_d(7,4V), 923j-3_d(7,6V), 947j-4_d(7,8V), 972j-5_d(8V)
    ldi R18, 0x16 ; wpisuje 790
    ldi R19, 0x03
    cp R16, R18
    cpc R17, R19
    brsh napiecie_OK ; skocz jesli wieksze niz minimalne

    rjmp wylacz

    napiecie_OK: ; tutaj dodac kod wizualizujacy napiecie
    ldi R18, 0xb3 ; wpisuje 947
    ldi R19, 0x03
    cp R16, R18
    cpc R17, R19
    brsh blysk_5 ; skocz jesli wieksze niz 8V
    rjmp moze_4

    blysk_5:
    rcall LED_5
    ret
    ; ==============================================================
    moze_4:
    ldi R18, 0x9b ; wpisuje 923
    ldi R19, 0x03
    cp R16, R18
    cpc R17, R19
    brsh blysk_4 ; skocz jesli wieksze niz 7,8V
    rjmp moze_3

    blysk_4:
    rcall LED_4
    ret
    ; ==============================================================
    moze_3:
    ldi R18, 0x83 ; wpisuje 899
    ldi R19, 0x03
    cp R16, R18
    cpc R17, R19
    brsh blysk_3 ; skocz jesli wieksze niz 7,6V
    rjmp moze_2

    blysk_3:
    rcall LED_3
    ret
    ; ==============================================================
    moze_2:
    ldi R18, 0x6b ; wpisuje 875
    ldi R19, 0x03
    cp R16, R18
    cpc R17, R19
    brsh blysk_2 ; skocz jesli wieksze niz 7,4V
    rjmp moze_1

    blysk_2:
    rcall LED_2
    ret
    ; ==============================================================
    moze_1:
    ldi R18, 0x52 ; wpisuje 850
    ldi R19, 0x03
    cp R16, R18
    cpc R17, R19
    brsh blysk_1 ; skocz jesli wieksze niz 7,2V
    rjmp koniec_pomiaru

    blysk_1:
    rcall LED_1
    ret
    ; ==============================================================


    koniec_pomiaru:

    lds R16, OCR1AL ; poniewaz mamy niskie napiecie, zwiekszamy PWM
    inc R16
    sts OCR1AL, R16

    cbi DDRC,2 ; wyłączamy podciaganie do masy rezystora pomiarowego
    cbi DDRC,3
    cbi DDRC,4
    ret
    ;

    ===========================================================================================
    prad: ; sprawdzam czy prad jest jest odpowiedni (czy silnik pracuje) pomiar ADC6 (prog 17mV=15j)
    ldi R16, 0b11000110 ; odpalam przetwornik ADC6 z Uref=1,1V i badam prad
    sts ADMUX, R16
    ldi R16, 0b11000011
    sts ADCSRA, R16

    ldi R16, 0
    mov R15, R16

    petla_pradu: ; czekam na zakonczenie konwersji
    lds R16, ADCSRA
    sbrs R16, ADIF
    rjmp petla_pradu

    lds R16, ADCL
    lds R17, ADCH
    add R15, R16
    ldi R22,0b00010000
    sts ADCSRA, R22

    rcall czekaj

    ldi R16, 0b11000011
    sts ADCSRA, R16

    petla_pradu1: ; czekam na zakonczenie konwersji
    lds R16, ADCSRA
    sbrs R16, ADIF
    rjmp petla_pradu1

    lds R16, ADCL
    lds R17, ADCH
    add R15, R16
    ldi R22,0b00010000
    sts ADCSRA, R22

    rcall czekaj

    ldi R16, 0b11000011
    sts ADCSRA, R16

    petla_pradu2: ; czekam na zakonczenie konwersji
    lds R16, ADCSRA
    sbrs R16, ADIF
    rjmp petla_pradu2

    lds R16, ADCL
    lds R17, ADCH
    add R15, R16
    ldi R22,0b00010000
    sts ADCSRA, R22

    rcall czekaj

    ldi R16, 0b11000011
    sts ADCSRA, R16

    petla_pradu3: ; czekam na zakonczenie konwersji
    lds R16, ADCSRA
    sbrs R16, ADIF
    rjmp petla_pradu3

    lds R16, ADCL
    lds R17, ADCH
    add R15, R16
    ldi R22,0b00010000
    sts ADCSRA, R22

    mov R16, R15
    cpi R16,10
    brsh prad_OK ; skocz jesli wieksze niz minimalne
    rjmp wylacz
    prad_OK:
    ret

    ; ===============================================================================================
    Czekaj: ; ustawiam opoznienie 250ms CTC naT1 zliczam do 2000dec=07D0hex
    ; Timer1 ustawiam w trybie CTC(WGM01) z preskalerem 64(CS00, CS02) i zliczam do 2000 co daje nam 250ms
    ; OCIE01 ustawiam przerwanie
    ldi R16, 0b10
    out TCCR0A, R16
    ldi R16, 0b101
    out TCCR0B, R16 ; prescaler 1024
    ldi R16, 255
    out OCR0A, R16

    nic1:
    in R16, TIFR0
    sbrc R16, OCF0A ; czekamy na ustawienie flagi
    rjmp koniec ; jesli zostanie ustawiona to wychodzimy z petli nic1
    nop
    rjmp nic1

    koniec:
    ldi R16, 0
    sts 0x44, R16
    sts 0x45, R16
    sts 0x47, R16
    sbi TIFR0, OCF0A ; zerujemy flage
    ret
    ; ====================================koniec petli opozniajacej===================================

    oblicz_temperature: ; temperatura nie powinna przekraczac 65st.C czyli 0,65V (0,65*1024/1,1=605j)
    ldi R18, 0x5d ; wpisuje 605
    ldi R19, 0x02
    cp R16, R18
    cpc R17, R19
    brsh wylacz ; skocz jesli wieksze niz wartosc maksymalna
    ret

    wylacz:
    ldi R20, 0
    ldi R21, 0
    out PORTB, R20
    out PORTC, R20
    out PORTD, R20
    out DDRB, R20
    out DDRC, R20
    out DDRD, R20

    ldi R16, 0 ; wgm 0b10000001 (61Hz) 0b10000010 (30,5Hz)
    sts TCCR1A, R16
    ldi R16, 0 ; 0b1011
    sts TCCR1B, R16 ; prescaler 64

    spij:
    rcall Czekaj
    in R16, PIND ; czekam na puszczenie przycisku
    sbrs R16, 3
    rjmp spij

    ; ================== ustawiam tryb sleep jako POWER save ===================
    ldi R16, 0b00000111 ; 101 -power down
    sts SMCR, R16 ; ustawiam SMCR
    ; ==========================================================================
    nop
    sei
    sleep



    LED_1: ; program zapalajacy diode na jakis czas
    ; ================LED ON ====================
    sbi DDRD, 5 ;
    sbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED OFF ===================
    cbi PORTD, 5
    cbi DDRD, 5 ;
    ; ===========================================
    ret

    LED_2: ; program zapalajacy diode na jakis czas
    ; ================LED ON ====================
    sbi DDRD, 5 ;
    sbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED OFF ===================
    cbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED ON ====================
    sbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED OFF ===================
    cbi DDRD, 5 ;
    cbi PORTD, 5 ;
    ; ===========================================
    ret

    LED_3: ; program zapalajacy diode na jakis czas
    ; ================LED ON ====================
    sbi DDRD, 5 ;
    sbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED OFF ===================
    cbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED ON ====================
    sbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED OFF ===================
    cbi PORTD, 5 ;
    ; ===========================================
    ; ===========================================
    rcall czekaj
    ; ================LED ON ====================
    sbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED OFF ===================
    cbi DDRD, 5 ;
    cbi PORTD, 5 ;
    ; ===========================================
    ret

    LED_4: ; program zapalajacy diode na jakis czas
    ; ================LED ON ====================
    sbi DDRD, 5 ;
    sbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED OFF ===================
    cbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED ON ====================
    sbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED OFF ===================
    cbi PORTD, 5 ;
    ; ===========================================
    ; ===========================================
    rcall czekaj
    ; ================LED ON ====================
    sbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED OFF ===================
    cbi PORTD, 5 ;
    ; ===========================================
    ; ===========================================
    rcall czekaj
    ; ================LED ON ====================
    sbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED OFF ===================
    cbi DDRD, 5 ;
    cbi PORTD, 5 ;
    ; ===========================================
    ret

    LED_5: ; program zapalajacy diode na jakis czas
    ; ================LED ON ====================
    sbi DDRD, 5 ;
    sbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED OFF ===================
    cbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED ON ====================
    sbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED OFF ===================
    cbi PORTD, 5 ;
    ; ===========================================
    ; ===========================================
    rcall czekaj
    ; ================LED ON ====================
    sbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED OFF ===================
    cbi PORTD, 5 ;
    ; ===========================================
    ; ===========================================
    rcall czekaj
    ; ================LED ON ====================
    sbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED OFF ===================
    cbi PORTD, 5 ;
    ; ===========================================
    ; ===========================================
    rcall czekaj
    ; ================LED ON ====================
    sbi PORTD, 5 ;
    ; ===========================================
    rcall czekaj
    ; ================LED OFF ===================
    cbi DDRD, 5 ;
    cbi PORTD, 5 ;
    ; ===========================================
    ret




    Teraz opis:
    Program steruje reglem moelarskim, który steruje silnikiem BLDC.
    Dodatkowo wyświetla stan naładowania 2 akumulatorów Li-Poli zasilających regiel. Regiel dodatkowo jest wyłączany MOSFETem (oszczędzanie energii)
    Całością steruje microswitch w sposób:
    a) krótkie wciśniecie i puszczenie (do 250ms) to właczenie bądz wyłączenie urządzenia
    b) dłuższe przetrzymanie przycisku powoduje zwiększenie wypełnienia skokowo, kolejne dłuższe przytrzymanie powoduje zmniejszenie wypełnienia (kazde wcisniecie powoduje zmianę kierunku zwiększenie-zmniejszenie)
    Dodatkowo podczas włączania urządzenia stan naładowania aku jest podawany na diodzie LED poprzez jej odpowiednia ilość mrugnięć

    Opis problemu:
    Wygląda na to, że przepełnia mi się stos bo po jakiejś minucie od podłączenia zasilania i niekorzystania z przycisku nie można juz obudzić kontrolera.
    Jeśli ktoś ma ochote to byłbym wdzięczny o pomoc w znalezieniu błędu
    Dodam, że te same problemy miałem z ATiny26, więc to nie wina płytki ani kontrolera tylko moja :)
    Pozdrawiam




    Opis elementów:
    TS1 to TS2951cs-5V (stabilizator low drop low power 5V/150mA)
    IRF1 to IRF7455 (MOSFET)

    W punkcie "sterowanie" podłączony jest przycisk podciągniety R12 = 1MOhm do plusa
    W punkcie "pomiar temperatury" podłączam wyjście LM35
    LED, PWM to wyjścia odpowiednio sterujące dioda i reglem. LED może będzie 2 katodowy, do tego przewidziałem kolejne wyjście tuz nad pierwszym)
    Na dole tuż nad polami do programowania jest także indukcyjność oznaczona prostokątem i literką L leżącą na diolnych pinachm, więc niewiele jej widać.
    R8 steruje GATE mosfetu a R13(nad nim) podciąga ją do masy
    R9-R10 to dzielnik napięciowy do pomiaru napięcia ogniw, włączany tylko w czasie pracy poprzez zwarcie R10 do masy (wczesniej odpowiednie wyprowadzenia kontrolera maja wysoką impedancję - pracuja jako wejścia)
    C2 to tantal 10uF filtrujący zasilanie
    Dodatkowo jest mierzony prąd pobierany przez regiel poprzez pomiar spadku napięcia na ścierzce 1mm X 35mm biegnącej tuż pod plusem zasilania i rezystorami R12, R9. (1A=17mV=15kwantów)
  • SterControl
  • Poziom 42  
    Witam,

    po pierwsze to masz strasznie dużo błedów, które własnie wpływają na to, że stos się przepełnia i to nie dziwne.

    na czym polegają twoje błędy? - otóż cały program, który pokazałes jest nimi usiany jak dobrymi skwarkami ....

    pokaże to na drobnym wyrywku:

    Cytat:
    napiecie_OK: ; tutaj dodac kod wizualizujacy napiecie
    ldi R18, 0xb3 ; wpisuje 947
    ldi R19, 0x03
    cp R16, R18
    cpc R17, R19
    brsh blysk_5 ; skocz jesli wieksze niz 8V
    rjmp moze_4

    blysk_5:
    rcall LED_5
    ret
    ; ==============================================================
    moze_4:
    ldi R18, 0x9b ; wpisuje 923
    ldi R19, 0x03
    cp R16, R18
    cpc R17, R19
    brsh blysk_4 ; skocz jesli wieksze niz 7,8V
    rjmp moze_3

    blysk_4:
    rcall LED_4
    ret


    zobacz co ty tutaj robisz:

    pierwsze polecenie rjmp moze_4

    a w procedurze moze_4 masz z kolei rozejście się na:

    brsh blysk_4 które kończy się RET'em - ale gdzie ten RET ma prowadzić? pobiera jakiś adres powrotu ze stosu - nie wiadomo jaki i dalej coś sobie robi o czym już nikt nie wie. W moze_4 istnieje możliwość skoku rjmp moze_3 i tam podobna sytuacja chociaż to już następuje jakaś jakby dziwna rekurencja błędów - aż się troszkę dziwię że ten program w ogóle działa chociaż minutę

    poprostu całkowicie nieumiejętnie posługujesz się rozkazami rcall i rjmp ale być może czegoś nie wiesz - to pytaj.

    tak dla przypomnienia tylko rcall powoduje skok do procedury która musi być zakończona rozkazem ret. Poprostu procek przy rcall odkłada na stos adres powrotu tuż za rozkazem rcall i polecenie ret na końcu procedury powoduje zdjęcie ze stosu tego adresu i skok do tegoż własnie miejsca

    natomiast rjmp to skok bezwarunkowy do dowolnego miejsca w programie ale nigdy nie można nim skakać do procedury która kończy się rozkazem ret. (no chyba że sobie ręcznie sterujesz stosem - ale to już inny temat i nie opłaca się tak robić na dłuższą metę)

    mam nadzieję, że teraz rozumiesz dlaczego całość idzie w maliny? jak spbie przeanalizujesz to takich miejsc gdzie robisz skok do jakby jakiejś procedury rozkazem rjmp a na jej końcu masz często dwie możliwości - jedna to skok do jeszcze innego miejsca rjmp a druga to właśnie nieszczęsne trafienie procka na nieprzewidzianego ret'a

    o ile z procedury (o ile to procedura) robiłbyś tylko skok rjmo moze_3 i tam dalej byłoby normalne sterowanie to byłoby to jakoś ok - a tak? w procedurze moze_3 mamy dokładnie taką samą sytuację itd itd

    dzięki czemu robisz kilka skoków czasem rjmp a czasem brsh po którym zadziała ret i chyba dlatego czasem działa ci ten program minutę a czasem krócej a czasem dłużej - zależy od tego ile razy i kiedy trafia na te dziwne rozgałęzienia

    popraw to wszystko

    aha i jeszcze jedno używaj etykiet .EQU zamiast wpisywać na żywca argumenty to bardzo ułatwi ci życie i będziesz mniej błędów robił w przyszłości - bo ładowanie do pamięci sts aż się prosi wcześniej o zdeklarowanie własnie w jej obszarze zmiennej (etykiety) i będziesz później używał sts Zmienna1, R16 zamiast sts 0x65, R16

    powodzenia
  • Poziom 27  
    Sprecyzuj o co Ci chodzi, jeśli o wartości to sie nie sugeruj tym co jest w opisie, bo zmieniałem je kilkukrotnie, a opis tylko sugeruje jakie powinny one być. Jeśli chodzi Ci o retY to powinny onne być, podprogram Napięcie_OK jest wywoływany funkcją rcall napiecie (ustawianie pinów do pomiaru, pomiar i jeśli wartość odpowiednia to wyświetlenie na duiodzie wartości a następnie powrót z podprogramu "napiecie")

    To co na końcu dopisałeś, etykiety, tak zawsze robię ale w moim symulatorze (a dokładniej w pliku.def M88 są błędy albo brak definicji adresów, dlatego musiałem użyć ich adresów bespośrednich. Po pracy przeanalizuje to co napisałeś, chociaż wydaje mi sie ze po rcall użycie kilku rjmp czy nawet dodatkowych rcall nie gmatwa nic w stosie jesli mamy tyle samo RETów.
  • Poziom 42  
    aha - no ok może i racja może te rety jakoś zadziałają ale przyznaj że nie masz w programie jasno wydzielonych części więc może dlatego tak ciężko się go analizuje - ale druga prawda jest taka, że każdy pisze w asm po swojemu i innej osobie ciężko go analizować ;)

    jednak ja uważam, że lepiej się trzymać jakichś zasad i dla jednej procedury do której skaczę rozkazem rcall mam jednego ret'a bo jak się narobi tyle rozgałęzień to potem sam widzisz co się dzieje ;)

    poprostu trzeba najpierw kodzik doprowadzić do ładu bo tak to ciężko się domyśleć co poeta miał na myśli jak to się mówi ;)

    pozdrówka
  • Poziom 27  
    Cytat:
    Cytat:
    napiecie_OK: ; tutaj dodac kod wizualizujacy napiecie
    ldi R18, 0xb3 ; wpisuje 947
    ldi R19, 0x03
    cp R16, R18
    cpc R17, R19
    brsh blysk_5 ; skocz jesli wieksze niz 8V
    rjmp moze_4

    blysk_5:
    rcall LED_5
    ret
    ; ==============================================================
    moze_4:
    ldi R18, 0x9b ; wpisuje 923
    ldi R19, 0x03
    cp R16, R18
    cpc R17, R19
    brsh blysk_4 ; skocz jesli wieksze niz 7,8V
    rjmp moze_3

    blysk_4:
    rcall LED_4
    ret


    Nie widze tu przepadkowych RETów... program leci sobie i jak wykrywa odpowiednia wartość to idzie rcallem do wyświetlenia jej i wreca retem do miejsca gdzie był wywoływany, trafia na kolejny ret i idzie przez pół programu w górę do wywołania rcall napiecie
    Wbrew pozorom, ten mój chaos jest uporządkowany w tym miejscu :)
  • SterControl
  • Poziom 42  
    tak tak zauważyłem to i opisałem wyżej ;) ale np pokaż mi gdzie u ciebie jest pętla główna programu - bo cięzko ją wypatrzeć ;)

    a przy okazji w takiej sytuacji jak ty masz to Napiecie i tele retów to powiem tak - ja robię sobie na końcu procedury zawsze jeden RET a przed nim etykietę Koniec_procedury: czy jakoś tam - i zamiast takich retów po drodzę daję rjmp do tejże etykiety a wtedy kod analizuje się bez najmniejszego problemu
  • Poziom 27  
    Pętla głowna to jest
    1: tryb uśpienia zaraz po właczeniu asilania migamy raz diodą i przechodzimy do snu (spij lub wylacz)
    2: Po wykryciu przerwania uruchamiamy PWM, wykonujemy pare podprogramów np rcall napiecie i przechodzimy w tryb oczekiwania na zmianę stanu przycisku poprzez przerwanie (petla nic)

    Co do RETów i RJMP to ujme to tak: jeśli mam skoczyć blisko to zawsze używam RJMP, ale jeśli skaczę poza zasięgiem RJMPa to niestety nie chcę mi sie używać dwóch RJMP tylko stosuję RCALL, dzięki temu kod jest krótszy i bezpośrednio widzę dokąd mam się przemieścić



    Jeśli możesz to zerknij na petle nic, czy przypadkiem nie może być problemu z wywołaniem przerwania... bo w tym miejscu własnie program jakby głupieje po pewnym czasie działania. Zapomniałem wyłaczyć przerwań podczas wykonywania instrukcji pomiarów prądu.... nie wstawiłem tam cli :(
    Jeśli nawet to jest przyczyna zawieszenia podczas pracy, to jeszcze zostaje przyczyna zawieszania w czuwaniu!
  • Poziom 42  
    ok ujmuj jak chcesz ale nie dziw się, że później czegoś nie zauważysz ...

    a kolejne moje spostrzeżenie - gdzież to kolega zgubił rozkaz RETI na zakończenie przerwania????

    w ogóle program obsługi przerwania to jakiś sorrki - ale koszmar. Tak się nie robi. Podstawowa zasada to krótka jasna i przejrzysta obsługa przerwań. A u ciebie dzieje się masakra - przecież gdy działa ci jakiś fragment kodu, który używa chociażby R16 a ty chyba klawiszem wywołujesz to przerwanie to wszystko idzie w maliniarnię ;) (przecież nie odkładasz na stos uzywanych w przerwaniu rejestrów a potem ich nie zdejmujesz) .... oczywiście znowu mogę się mylić bo teoretycznie można obsługiwać przerwania bez rozkazu reti ale to jest los-masakros i niezgodne z zadnymi normalnymi zasadami. A co robisz ze swoim stosem na którym odkłada się biedny adres powrotu z przerwania???? i tu cię chyba mam .... bo nie widzę żadnego pop'a po wejściu w takie nietypowe przerwanie

    - moja rada jest jedna - należy zacząć programować zgodnie z kanonami sztuki w asemblerze to będzie milion razy mniej takich problemów. Polecam zapoznać się z wieloma przykładami róznych procedur i programów asemblerowych w necie - przyjrzeć się jak to robią mistrzowie. Mi jeszcze daleko do mistrzostwa i dlatego zawsze tak robię i lubię się uczyć od lepszych od siebie zamiast czasem wyważać głową drzwi gdy są otwarte

    chyba że i z tym reti mnie jeszcze zagniesz? ;)
  • Poziom 27  
    zerknij na post powyzej, tam znalazłem jedna przyczynę zawiechy.


    no z tym co powyżej odnośnie RETI to pokarz mi gdzie w moim przerwaniu jest rcall ?? mam tylko RJMP więc nie ingeruję w stos (chyba ze sie mylę)

    Z Helpa można wyczytać:
    Syntax: Operands: Program Counter: Stack

    (i) RJMP k -2K ≤ k < 2K PC ← PC + k + 1 Unchanged

    Dlatego nie ma ani popów ani pushów, nie sa potrzebne w mojej obsłudze przerwań
    Natomiast komenda CALL potrzebuje RETI (w przerwaniu) ale ze nie bardzo wiem, gdzie program by wrócił (pewnie do kolejnych, ewentualnych przerwan, których nie przewiduje) dlatego nie stosuje wywołań tego typu

    Wogóle wielkie dzięki za pomoc, zawsze co 2pary oczu to nie 1 :)
    mam nadzieje, że pomożesz mi skończyć poszukiwania błedów
    :)
  • Poziom 42  
    jak to nie ingerujesz w stos panie kolego??? przecież wiesz, że rcall ingeruje w stos? prawda? tu się zgadzasz?

    ..... ale nie wiesz właśnie chyba tego że samo wywołanie przerwania to tak jakbyś zrobił rcall do jego obsługi - tyle tylko że ten rcall za ciebie robi sam procek i on tak jak w przypadku rcall odkłada na stos adres powrotu z obsługi przerwania - rozumiesz????

    tak więc masz np swoją pętlę główną - i w jej trakcie następuje przerwanie dowolne. Procek przerywa działanie tej pętli głównej i robi po swojemu rcall z którego powrót musi być nie ret tylko reti

    weź pod uwagę że gdy następuje wejście w przerwanie to zwyczajowo odkłada się na stos w tej procedurze obsługi rejestry które używasz ale podstawa to zapamiętanie na stose rejestru SREG

    czyli np:

    Cytat:
    INT1:
    push R16
    in R17, SREG
    push R16
    push R17
    ...
    ...\tu procedura obsługi przerwania która przy okazji korzystan np z dwóch rejestrów R16 i R17, które razem z SREG zabezpieczane są na stosie
    ....
    pop R17
    pop R16
    out SREG, R16
    pop R16
    reti


    czyli po takiej procedurze obsługi przerwania nie zniszczysz wartości rejestrów R16 i R17 których używasz w pętli głównej akurat w momencie gdy została ona przerwana ale co najważniejsze nie zniszczysz resjestru SREG a przecież od niego zależą różne rodzaje skoków brsh brlo itp itp więc gdy przerwanie nastąpi w momencie takiego skoku w pętli głównej a ty nie zadbasz o SREG w przerwaniu i zmieni się jego zawartość własnie w przerwaniu to czujesz już chyba z dreszczykiem na plecach co się może dziwnego dziać w programie???? szczególnie w twoich procedurach nic czy czuwaniu

    a samo wyłączanie i włączanie w nich przerwań to poprostu jakbyś pisał w innym języku zamiast w asemblerze bo wszystko starasz robić odwrotnie no ale to własnie może wynika z niedoczytania na temat jak działają przerwania to rozumiem. A szczególnie jeśli chodzi o ten stos. Czyli mógłbyś w całym swoim programie nie użyć choć jednego rozkazu rcall ale jeśli odbędzie się choć jedno przerwanie to już coś odłoży się na stosie i te rozkazy w wektorach przerwań własnie nieco inaczej działają bo to są takie jakby rcall'e które muszą być kończone reti


    mam nadzieję że teraz się rojaśnia ;)
  • Poziom 27  
    Rozjaśnia się, czyli po wywołaniu przerwania powinienem zacząć kod od:
    IRQ_INT1:
    in R16, SREG
    push R16

    a skończyć:
    pop R16
    out SREG, R16
    RETI

    Czyli powiedz mi gdzie po komendzie RETI wyląduje wskaźnik programu ? Wróci do adresu zaraz za rjmp IRQ_INT1 ?? To by było nieporządane, więc pewnie coś źle rozumiem

    Inna sprawa, że wywoływane u mine w pętli nic rozkaz "sei", może także przepełnić stos! O tym nie wiedziałem i dlatego kod wygląda jak wygląda :(
    Do tej pory omijałem przerwań, stąd ma niewiedza :)

    Czy wobec tego pętlę jak poniżej:
    nic:
    wdr
    sei
    rcall czekaj
    rcall czekaj
    rcall czekaj
    rcall czekaj
    rcall prad
    rjmp nic

    mogę zamienić na:
    wdr

    sei
    in R16, SREG
    push R16

    rcall czekaj
    rcall czekaj
    rcall czekaj
    rcall czekaj
    rcall prad

    pop R16
    out SREG, R16
    cli
    rjmp nic


    Czy ten zapis też nie jest dobry?
  • Pomocny post
    Poziom 42  
    oczywiście że tam nie wróci!

    zobacz - wyobraź sobie że działa ci twoja procedura nic i nagle w trakcie jej działania przychodzi przerwanie dowolne. Procek zapamiętuje dokładnie ten adres w którym aktulanie wykonuje instrukcję na stosie i przechodzi o obsługi tegoż przerwania. Gdy zakończy procedurę przerwania to rozkaz reti spowoduje zdjęcie ze stosu adresu miejsca gdzie nastąpiła przerwa w procedurze nic i dokładnie od tamtego miejsca dalej zacznie się program.

    tylko jak mówię jesli np w procedurze nic coś np załadowałeś do rejstru R16 i coś na nim robisz to jeśli nie zapamiętasz go na stosie w przerwaniu tylko sam SREG tak jak pokazałeś to zniszczysz wartość R16 w procedurze nic i gdy nastąpi do niej powrót to nie będzie już w nim tej wartości, która była wcześniej czaisz? i mogą zacząć się krzaki.

    dlatego podstawa to odłożenie na stosie i R16 i SREG. A że do odłożenia SREG potrzebujesz R16 to przez to właśnie juz potrzeba i jego samego odłożyć czyli tak jak ja napisałem wcześniej. Oczywiście jesli w przerwaniu nie użyjszesz innych rejestrów np R17 to ich nie musisz odkładać

    Cytat:
    INT1:
    push R16
    in R17, SREG
    push R16

    ...
    ...\tu procedura obsługi przerwania która przy okazji korzysta np z rejestru R16 , który razem z SREG zabezpieczane są na stosie
    ....

    pop R16
    out SREG, R16
    pop R16
    reti


    sam rozkaz sei lub cli nie rusza stosu. Tylko skok do przerwania rusza ;)
  • Poziom 27  
    no dobra ale mój program wywoływany przerwaniem wygląda tak:

    Cytat:
    IRQ_INT1:
    cli ; blokujemy przerwania

    ldi r16,0b00000000
    out SMCR,r16 ;wylacz SE (zezwolenie na uspienie)

    sbi EIFR, INTF1 ; kasujemy wywołanie
    ;cbi PORTB, 5 ; wylaczam urzadzenie <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    ; 1a) jesli jest to poczatek programu (R20=0) to wykonujemy procedure startu
    ; 1b) jesli program juz dziala (R20=1) to sprawdzamy czy ktos nie chce zmienic wypelnienia lub wylaczyc
    ; 2a) zmieniamy wypelnienie
    ; 2b) wylaczam (sleep)

    ; 1)
    rcall Czekaj ; jesli w ciagu okreslonego czasu przycisk zostanie puszczony to wlaczamy
    in R16, PIND
    sbrs R16, 3 ; jesli nadal bedzie trzymany to zmieniamy wypelnienie
    rjmp zmien_PWM

    sbrc R20, 0
    rjmp wylacz

    ldi R20, 1 ; ustawiam znacznik pracy układu
    ; petla główna programu, ustawiamy generator (T1) na PWM (WGM10, WGM13)(COM1A0)(cs10, CS11) 50Hz OC1A i zmieniamy wypełnienie
    ; najpierw sprawdzamy temperature i na piecie

    sbi DDRB, 1 ; ustawiam wyjscie B1

    ; jesli sa dobre to wlaczam generator PWM
    sbi PORTB, 5
    sbi DDRB, 5

    ldi R16, 0b10000001 ; wgm 0b10000001 (61Hz) 0b10000010 (30,5Hz)
    sts TCCR1A, R16
    ldi R16, 0b1011 ; 0b1011
    sts TCCR1B, R16 ; prescaler 64
    ldi R16, 19 ; wypelnienie poczatkowe (19)
    sts OCR1AL, R16


    rcall napiecie
    rcall temperatura

    rcall Czekaj
    rcall Czekaj
    rcall Czekaj
    ;rcall Czekaj
    ;rcall Czekaj
    lds R16, OCR1AL
    inc R16
    inc R16
    sts OCR1AL, R16


    nic:
    wdr
    sei
    rcall czekaj
    rcall czekaj
    rcall czekaj
    rcall czekaj
    rcall prad
    rjmp nic

    Więc gdzie tu mam wstawić Pushe Popy i RETI ??

    Cytuje R.Baranowskiego z książki "Mikrokontrolery AVR ATmega w praktyce":
    "(...) Od zwykłego podprogramu procedurę tę (przerwanie) odróżnia m.in. zakończenie - rozkaz RETI. Instrukcja ta jest podobna do RET, ale dodatkowo powoduje ona przywracanie obsługi przerwań. Jak pamiętamy, przy przejściu do procedury obsługi zezwolenie na przerwania zostaje automatycznie wyłączone (zerowany jest znacznik I w rejestrze SREG). Rozkaz powrotu (RETI) powinien ją zatem ponownie włączyć. Oczywiście wewnątrz procedury obsługi przerwania można uaktywnić manualnie (rozkazem SEI), ale należy to robić z rozwagą, by nie dopuścić do przepełnienia stosu w efekcie nawarstwienia wywołań" koniec cytatu

    jak mam to rozumieć?

    nie da się napisać programu bez używania rcall... nie wyobrażam sobie, powtarzać w kilku fragmentach np oczekiwania czasowego... to tak jakbym za kazdym razem wpisywał w kodzie procedure właczenia licznika i oczekiwania na zliczenie, zamiast wpisywać w kodzie tylko odwołania do licznika... po prostu kod zeżarłby 2x więcej miejsca przy prostym programie, nie mówiąc juz o bardziej skomplikowanych :)
  • Poziom 28  
    Nie znam sie zbytnio na AVR'ach ale z tego co zerknołem do opisu instrukcji to widać że RETI od RET za bardzo sie nie różni w właściwie można świadomie używać ich naprzemiennie, bo poprostu jak użyjemy RET do wyjścia z obsługi przerwania to nie zostanie automatycznie ustawiona flaga globalnego zezwolenia na przerwania.
  • Poziom 42  
    no widzisz kolego autorze - trochę ciężko ci to narazie zrozumieć bo do tej pory nie trzymałeś się pewnych zasad. Hmmm tak z grubsza to u ciebie obsługa przerwania niczym się nie różni od programu głównego nawet więcej przeplata się z nim co powoduje takie błędziorki.

    musisz uznać pewne zasady do których należą następujące kroki:

    1. definicja procka
    2. definicja etykiet .EQU o ile są potrzebne i MAKR
    3. definicja zmiennych w pamięci SRAM i EEPROM jeśli są potrzebne
    4. Definicja wektorów przerwań - to zawsze warto zrobić a w miejsca nie używanych wektorów powstawiać same reti
    5. start programu od wektora reset
    6. inicjalizacja procka i wszystkich potrzebnych rzeczy w tym stosu
    7. rozpoczęcie pętli głównej programu z której są ew wywoływane procedury
    8. miesce na procedury ich kod
    9. Procedury obsługi przerwań

    to tak bardzo ogólnie ale powinno dać się jasno wyróżnić w programie takie elementy mniej więcej

    same przerwania tak jak napisał R.Baranowski trzeba tak własnie rozumieć jak on pisze bo ja mówiłem dokładnie to samo.

    u ciebie w procedurze obsługi przerwania ciężko wstawić RETI bo jak mówiłem ty z procedury obsługi przerwania zrobiłeś w zasadzie program główny i miesza się on za pomocą tych rjmp twoich i rcall z pętlą główną - rozumiesz????

    usiądź - i postaraj się przemyśleć tak program aby "ubrać" go w te bloki funkcjonalne o jakich pisałem. po inicjalizacji zdefiniuj pętlę główną zastanów się co ma w niej być robione i jakie podprocedury mają być z niej wywoływane

    a następnie zastanów się jak ma wyglądać obsługa twojego przerwania. Przecież zobacz sam zaznaczyłeś na górze na czerwono swoją pętlę główną która o zgrozo gdzie jest? w przerwaniu. Rozumiesz? wszystko musisz zmienić wszystko i przemyśleć od nowa.

    ciężko mi szczegółowo podpowiedzieć co i jak bo nie znam projektu ani hardwar'u i nie o to chodzi. Tak tylko ogólnie zauważyłem że coś nie tak u ciebie i to bardzo z podejmowaniem pewnych działań i pewnym małym chaosem jaki wprowadzasz tymi setkami rozkazów ret. Ale wkrótce sam zobaczysz że to nie popłaca i zaczniesz pisać normalnie.

    do kolegi Dr_DEAD - sorki ale kolega chyba dużo z asemblerem do czynienia nie miał. Bo oczywiście co do meritum to się zgodzę że można ich używać naprzemiennie ze świadomością efektów drobnych niuansów w działaniu każdego z nich. Ale to jest poprostu wielka BZDURA z punktu widzenia pisania jasnych i przejrzystych programów. Pomijam jakieś wyjątki gdzie czasami wszystkie chwyty są dozwolone. Tu jednak masz do czynienia z osobą jeszcze początkującą (chodzi o autora tematu) więc proszę nie wprowadzaj mu dodatkowego mętliku w głowie
  • Poziom 27  
    OK juz mniejwięcej pojąłem istote przerwań, w pętli głównej wywalę przerwanie i zastąpię zwykłym oczekiwaniem na wciśnięcie przycisku. Przerwanie będzie tylko budzić kontroler - to wniesie przejrzystość.
    Etykiet używam, ale ten projekt był akurat prosty, wpisywanie wszystkich etykiet zajęłoby połowe tego obszaru co sam kod.
    Wszystko prócz przerwań dobrze działało, zobaczymy czy po wywaleniu przerwania z pętli głównej program się ustabilizuje, chociaż mam pewne wątpliwości, ale trzeba spróbować. Dziś przyszły nowe M88 to zdążę przelutować procka (poprzedni już zablokowałem bawiąc się FUSami w akcie desperacji) i nieco pozmieniać. Zobaczymy co z tego wyjdzie :)
    pozdrawiam i odezwę sie pewnie jutro
  • Poziom 42  
    Ch.M. napisał:
    ...Wszystko prócz przerwań dobrze działało, zobaczymy czy po wywaleniu przerwania z pętli głównej program się ustabilizuje, chociaż mam pewne wątpliwości, ...


    nie ma co mieć wątpliwości, działanie na przerwaniach i ich zrozumienie to podstawa więc warto je powoli ujarzmić w swoich projektach nawet tych małych .... na pewno wszystko będzie działało ci dobrze jeśli w tym konkretnym przypadku zrezygnujesz z przerwania ale jak mówię warto i jego użycie przemyśleć, przyda ci się to na później do obsługi bardziej skomplikowanych klawiaturek niż 2-3 przyciski naprzykład.

    tak więc niech moc będzie z tobą ;) dobrych pomysłów życzę
  • Poziom 31  
    Powiem że tak z ciekawości zerknąłem na to. Na jedna rzecz zwrócono ci już uwage. Jak masz przerwanie podczas którego używasz R16, a w całym programie masz tego R16, to nalezy wcześniej odłożyć na stos. Jeżeli robisz jakies obliczenia, to jeszcze inne rejestry trzeba też odłożyć.

    Druga rzecz która mi sie nie podoba to że funkcja reset prowadzi bezprośrednio przez funkcje z przerwania.

    Ogólnie cały szkielet programu mi sie nie podoba. I to nie jest chaos tylko bałagan.
  • Poziom 27  
    jeśli nastąpi reset to i tak mam zresetowane wszystkie rejestry prócz licznika watchdoga więc to chyba nie problem, mój program po przejściu w tryb uśpienia nie musi pamiętać danych w RAMie.
    Przepisze program wg rad i wrzuce jutro na procka, zobaczymy co on na to :) Dziś zajmuję się innym projektem 9 a przynajmniej miałem takie nadzieje)
    Pozdrawiam i do jutra :)
  • Poziom 28  
    mirekk36 napisał:

    do kolegi Dr_DEAD - sorki ale kolega chyba dużo z asemblerem do czynienia nie miał. Bo oczywiście co do meritum to się zgodzę że można ich używać naprzemiennie ze świadomością efektów drobnych niuansów w działaniu każdego z nich. Ale to jest poprostu wielka BZDURA z punktu widzenia pisania jasnych i przejrzystych programów. Pomijam jakieś wyjątki gdzie czasami wszystkie chwyty są dozwolone. Tu jednak masz do czynienia z osobą jeszcze początkującą (chodzi o autora tematu) więc proszę nie wprowadzaj mu dodatkowego mętliku w głowie

    Zasugerowanie Autorowi tego żeby zerknoł do instrukcji i zobaczył że funkcja:
    RET - Sciąga ze stosu adres powrotu i wrzuca go do PC (program counter).
    a
    RETI - Sciąga ze stosu adres powrotu i wrzuca go do PC (program counter) i modyfikuje falgę "I"
    nazywasz wprowadzaniem mętliku?
    Chcesz nauczyć Autora pisania przejżystego kodu? Naucz go dzielić program na funkcję, pokaż różne sposoby przekazywania parametrów do funkcji (najprostrza - poprzez wyznaczone rejestry, trudniejsza - poprzez softwerowy stos, najbardziej kłopotliwa - poprzez hardwerowy stos). Naucz dzielenia na moduły i pliki, powiedz do czego słyży PUBLIC i EXTERN. Ze każda funkcja tak samo jak ISR powinna odkładać używane rejestry na stos.
    Najważniejszy dla czytelności jest podział na funkcje/moduły/pliki i wszędzie ten sam system przekazywania parametrów do funkcji.
    W ASM pisze od roku.
  • Poziom 42  
    Dr_DEAD - mętlikiem nazywam tylko to żeby radzić szczególnie początkującemu zamianę reti na ret - tylko to. A jak mówiłem co do tego że działa to wten sposób jak opisujesz nie mam najmniejszych zastrzeżeń - bo dobrze piszesz ;)

    nie wspominając już o innych poradach n/t przejżystego kodu, jasno wydzielonych funkcji itp - na brak tego aspektu również własnie zwracałem mocno uwagę autorowi.

    tak więc w większości się zgadzamy. I ja również śmiało polecam autorowi postu skorzystać z porad kolegi Dr_DEAD .

    Cytat:
    Najważniejszy dla czytelności jest podział na funkcje/moduły/pliki i wszędzie ten sam system przekazywania parametrów do funkcji


    pozdrawiam
  • Poziom 21  
    Ch.M, programowałeś wcześniej w BASCOMIE? Bo z tego co zauważyłem, to przeważnie takie są efekty przesiadki z BASCOMA na ASM. Dr_DEAD ma rację, kod musi być przejrzysty. Co innego jak robisz w ASM miganie diodami, a co innego jak taki, dosyć już niemały program. Gdybyś zastosował się do wskazówek mojego przedprzedmówcy, to nie dość, że Tobie łatwiej byłoby się poruszać w kodzie i znajdować błędy, to i dla nas byłaby o niebo łatwiejsza analiza programu. Bo tak jak jest teraz, to dotarłem do 1/3 kodu i darowałem sobie, bo musiałbym pół dnia stracić na pojęcie Twojego toku rozumowania. Właśnie przy tak chaotycznych programach jest najwięcej błędów i najtrudniej jest je potem znaleźć.
  • Poziom 27  
    gufiak:
    Nie nie pisałem w bascomie, ASM od ponad roku ćwiczę, a co do skomplikowania to inaczej kod wygląda w symulatorze a inaczej na stronie www prosto skopiowany z AVRStudio.
    Program jest zawikłany bo są zagniezdzone odwołania typu gdy a(gdy a1, gdy a2), gdy b, gdy c(gdy c1, gdy c2) które mocno komplikują zrozumienie. Kod pisałem w 5minut a później edytowałem dla aktualnych potrzeb, dlatego nie jest przejrzysty. generalnie bez przerwań wszystko smiga dobrze, ale niestety układ jest zasilany z baterii które będą raczej na stałe podłaczone, więc trzeba zadbac o ich kondycje :) Do tego własnie jest potrzebne przerwanie wybudzające kontroler z bardzo głębokiego snu. W kodzie powyżej przerwanie jest rónież używane do wykrywania zmian na przycisk, gdy z tego zrezygnuję i wprowadzę sprawdzanie stanu portu, zredukuję korzystanie z przerwań i dzięki temu dowiem się czy wszystko jest winą złego kodu w funkcji obsługi przerwania, czy coś jeszcze zagmatwałem
    Obiecuje poprawić się w przejrzystości, generalnie moje programy wyglądają teraz tak:

    Cytat:
    /* ***************************************************************************************************
    **
    ** Siemens S65 Display Control, sterownik zasilacza do opasek
    ** rejestry ogolne: R16, R17, R18, R19, R20, R22, R24, R25, R30, R31
    ** rejestry zarezerwowane: R26, R27, R28, R29
    ** R7-przetrzymuje wartosc zadana cisnienia
    ** R8-przechowuje wartosc zadana stopera
    ** R9-przechowuje poziom MENU (zwolni sie)
    ** R4-przechowuje liczbe sekund (*5)
    ** R5-przechowuje kierunek zliczania
    ** R10, R11, R12, R13, R14, R15 zapisuja rozklad liczby
    ******************************************************************************************************/

    .org 0x00

    rjmp Reset
    .org 0x46
    .include "m128def.inc"
    .include "czekaj_m128.inc"
    .include "s65_LCD.inc"

    .def tlo_0 = R26
    .def tlo_1 = R27
    .def znak_0 = R28
    .def znak_1 = R29
    .def cisnienie_A = R7 ; przechowuje wartosc nastawy cisnienia

    Reset:
    .equ LCD_RESET = PB6 ; RESET
    .equ LCD_MISO = PB3
    .equ LCD_MOSI = PB2 ; DAT
    .equ LCD_CS = PB0 ; CS
    .equ LCD_SCK = PB1 ; CLK
    .equ LCD_RS = PB7 ; RS

    .equ klawisz = PIND
    .equ gora = 0 ; pinD1
    .equ dol = 1
    .equ plus = 2
    .equ minus = 3
    .equ enter = 4

    .equ ster = portC ; definicja portu sterowania
    .equ pompa = 0
    .equ zaw_plus = 1
    .equ zaw_minus = 2
    .equ buzer = 3

    ldi R16, high(RAMEND) ; zainicjalizuj stos
    out SPH, R16
    ldi R16, low(RAMEND)
    out SPL, R16
    ldi R16, 7
    out DDRC, R16
    cli
    ; ============================================================
    ldi R23, 0
    ; ladujemy wartosc poczatkowa cisnienia
    ldi R16, 0
    mov R7, R16
    mov R8, R16
    mov R9, R16

    ;rjmp Przeliczanie_jednostek ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    ; ===== czyszczenie ekranu ======
    rcall LCD_ini ;|
    rcall biale_tlo ;|
    ; ===============================
    rcall tlo_bialy

    rcall znak_czerwony
    rcall ChM_maly
    rcall czekaj_250ms
    rcall czekaj_250ms
    rcall czekaj_250ms

    rcall znak_zielony
    ;rcall Ustawienia
    ;rcall Napis_Cis
    rcall Wartosc
    rcall Miano
    rcall znak_zielony
    ;rcall Znak_Stoper
    rcall Wartosc_stoper
    rcall Znak_Minut
    ;rcall Znak_START
    rcall time_S

    rcall znak_bialy
    rcall czekaj_250ms
    rcall czekaj_250ms
    rcall czekaj_250ms
    rcall ChM_maly ; rysuję napis
    rcall INFLATE

    rjmp USTAWIAM_CISNIENIE
    itd...


    Nie, nie używam przerwań w tym kodzie, ale na pewno zaczne
    Pozdrawiam
  • Poziom 33  
    Na pierwszy rzut oka:
    1) inicjalizacja stosu niepoprawna kolejność:
    Resetuj: ; zainicjalizuj stos:
    cli
    ldi R16, low(RAMEND)
    out SPL, R16
    ldi R16, high(RAMEND)
    out SPH, R16
    -wpisać należy najpierw SPH potem SPL
    -cli tu niepotrzebne
    2) tu też cli niepotrzebne bo procesor sam w przerwaniu blokuje inne przerwania
    IRQ_INT1:
    cli ; blokujemy przerwania
    3) brak reti

    Pzdr. N.
  • Poziom 21  
    Cytat:
    wpisać należy najpierw SPH potem SPL
    Nie bardzo widzę różnicę?
  • Poziom 42  
    z tym wpisaniem wartości stosu najpierw do SPH a potem do SPL to koledze Nawigator może troszeczkę pomyliło się z taką koniecznością ładowania 16bitowych liczników do timera w czasie działania - gdzie jest rzeczywiście potrzeba zachowania odpowiedniej kolejności, ale przy stosie nie ma takiej potrzeby

    cli jest teoretycznie nie potrzebne przed inicjalizacją stosu bo po resecie procek ma wyłączone przerwania ale ja np jakoś wstawiam to cli jako pierwszy rozkaz po resecie przed inicjalizacją - tak na wszelki wypadek ;)

    natomiast co do rozkazu cli na początku obsługi przerwania to chyba kolega Nawigator nie ma racji bo procek blokuje tylko wystąpienie tego przerwania a nie wszystkich innych. Tak więc jeśli jest potrzeba zablokować inne to można i nawet trzeba ręcznie zapodać cli w przerwaniu

    pozdr
  • Poziom 19  
    mirekk36 napisał:

    natomiast co do rozkazu cli na początku obsługi przerwania to chyba kolega Nawigator nie ma racji bo procek blokuje tylko wystąpienie tego przerwania a nie wszystkich innych. Tak więc jeśli jest potrzeba zablokować inne to można i nawet trzeba ręcznie zapodać cli w przerwaniu


    Nie. Procesor sam zeruje flagę I w SREG i tym samym blokuje wszystkie przerwania.
  • Poziom 42  
    sorry - tak przyznaję że pomyliłem się jednak procki wyłączają wszystkie przerwania przy wejściu w obsługę dowolnego z nich - więc zwracam honor koledze Nawigatorowi ;)
  • Poziom 33  
    Co do kolejności wpisu/odczytu rejestrów 16 bitowych w obszarze I/O to faktycznie stos jest zazwyczaj ustawiany przed zezwoleniem na przerwania i tu nie powinno nic złego się wydarzyć. Chociaż miałem przypadek tylko już nie pamiętam w jakim procesorze że to było istotne.
    Dostęp do rejestrów 16 bitowych jest realizowany z pomocą ukrytego rejestru temporary i tutaj może jest przyczyna.
    Nota Atmela zaleca tą kolejność więc dla higieny lepiej się tego trzymać.
    Ponieważ mam na stole akurat ATmega48 więc dla sprawdzenia odczytałem SPH i SPL dla obu sposobów zapisu i rzeczywiście kolejność nie miała znaczenia.
    Natomiast popatrzyłem przy okazji do datasheet'a i w nim wyczytałem że rejestry SPH i SPL są defaultowo ustawiane wartościami RAMEND więc ich dodatkowy wpis można w ogóle pominąć! Czyli oszczędzamy 4 rozkazy. Sprawdziłem ten fakt i rzeczywiście tak jest. Oczywiście można wpisać do tych rejestrów inny adres początku stosu jakby ktoś potrzebował.
    Sprawdziłem w pdf-ach że to ułatwienie dotyczy też np. mega88/168, tiny13, tiny25/45/85 i tiny 261/461/861 podczas gdy inne procesory mają wartości SPH/SPL po resecie zerowe.
    Pzdr. N.