Asembler AVR gaszenie diod

0

Polecenie to :"zapal wszystkie diody. Po kliknięciu przycisku diody mają się gasić po jednej od prawej strony do lewej. Kiedy zgaśnie ostatnia - zaczynamy od nowa. Obsłużyć odbicia.

Działało do momentu gaszenia diod jednak nie gasły po jednej, po ile chciały i w dziwnych kombinacjach. Tutaj wchodzi obsługa odbić, której dotyczy ma pogrubiony kod. Niestety kompletnie nie jestem go pewna, a już nie mam gdzie tego sprawdzić. Próbowałam emulatory żeby sprawdzić zachowanie ale żaden nie chciał się poprawnie zainstalować i działać.

Mam taki oto kod

.include "m32def.inc"
.org 0 
jmp start 
.org 2
jmp procedure_przerwanie  

;konfiguracja 
start:		 
CLI	 
	ldi R16, HIGH(RAMEND)	
	out SPH, R16			
	ldi R16, LOW(RAMEND)
	out SPL, R16
        LDI R16, 0xFF		
	OUT DDRA, R16		
	OUT PORTA, R16
        SBI PORTD, 2			
	LDI R16, (1<<ISC01)	
        OUT MCUCR, R16		
	LDI R16, (1<<INT0)		
	OUT GICR, R16		                                                                
	LDI R17, 0XFF
        SEI					
main:					
loop:
        LDI R16, 0x00		
	OUT PORTA, R16		
	call procedure_delay	
	OUT PORTA, R17		
	call procedure_delay
rjmp main
procedure_delay:
ldi  r18, 5
    ldi  r19, 75
    ldi  r20, 191
L1: dec  r20
    brne L1				
    dec  r19
    brne L1
    dec  r18
    brne L1
    nop
ret		
procedure_przerwanie:
IN R30, SREG			    
PUSH R30

PUSH R27
PUSH R28			     
PUSH R29
LSL R17
SBRC R16, 1
LDI R18, 2				
RCALL procedure_delay
POP R29
POP R28				
POP R27
POP R30
OUT SREG, R30
RETI	

Mam wątpliwości co do części kodu "procedure_przerwanie. Czy jest poprawna?

0

Jaki to dokładnie procesor. Różne AVR'y mają na nóżkach mapowane różne porty. Fajnie jak zamieściłbyś schemat układu.
Dlaczego nie używasz jakiegoś pliku nagłówkowego ze stałymi i definicjami dla tego układu. Dzięki temu czytelność "pinów" i kodu była by 100 razy bardziej klarowna?

0

Szczerze mówiąc nie robię tego ponieważ kompletnie nie rozumiem tego języka i nie potrafię w nim programować. Wszystko co tu mam to próby dopięcia kodów znalezionych w sieci + treść wykładów.

schemat.jpg

Kolorowe kropki to oznaczenia moich podpięć. Jest tylko źle kliknięta ta zielona z lewej strony bo powinna być pod INT0.

0

Jak się domyślam to jakieś zadanie "na zaliczenie" lub coś podobnego. Jeśli tak to proszę prześlij dokładną treść zadania.

0

Niestety nie ma dokładnej treści. Były ogólnie podane wytyczne, że mamy zapalić wszystkie diody, później wybrać sobie przycisk i przy jego naciśnięciu ma gasnąć po jednej diodzie od prawej do lewej strony. Jak zgasną wszystkie to ma się zaczynać od nowa wszystko.

0

To ja na początek zrezygnowałbym z obsługi przerwania bo w tym przypadku to jest niepotrzebne a skoro to tylko "na zaliczenie" to nie ma się co maltretować i lepiej zrobić łatwiej a dobrze niż niepotrzebnie kombinować. W tym przypadku nie musisz martwić się o "iskrzenie" przycisku bo on i tak będzie aktywny dopiero po zakończeniu procedury migania.
W przypadku korzystania z przerwania naciskanie przycisku nie stosując debouncing`u ( zapobieganie skutkom iskrzenia styków podczas fizycznego naciskania przycisku ) powodowało jego wielokrotne wywoływanie a tym samym przechodzenie programu w dziwne nieoczekiwane stany - tym bardziej, że nie widzę aby w obsłudze przerwania było CLI, które na czas jego realizacji blokuje ponowne jego wywoływanie.

Zrobiłbym główną pętlę, której zadaniem jest jedynie oczekiwanie na naciśnięcie przycisku.


  ; skonfiguruj odpowiednio porty do odczytu i zapisu

start:
  ; zapal wszystkie diody

main_loop:

  ; sprawdzanie przycisku i jeśli naciśnięty to skok do procedury  miganie_diodami

  jmp main_loop ; powrót do stanu początkowego i oczekiwania na naciśnięcie klawisza

miganie_diodami:

  ; tutaj cała procedura migania diodami
  jmp start ; powrót do stanu początkowego i oczekiwania na naciśnięcie klawisza


0

btw. :) ... mnie zastanawia że to już drugie zadanie, ale tylko Ty jesteś z tej swojej "grupy/klasy" na tym forum. To znaczy że inni oddają zadania na zaliczenie bez problemów? Nie jestem tutaj zbyt długo na tym forum ale zauważyłem że są pewne tendencje, tzn zadania z uczelni jakie dostają studenci i pytają o rozwiązanie, te pytania się potwarzają, a to znaczy że idą podobym tokiem uczenia. Ty masz jakieś abstrakcje w porównaniu do nich. Asembler, do tego znajomość architektur ARM, x86, AVR xD. W ogóle to na jakim jesteś kierunku że zajmujecie się AVR, x8086?

4

Prawdę mówiąć z AVR nigdy nie miałem do czynienia jeśli chodzi o programowanie ale z nudów zerknąłem do dokumentacji, opisu instrukcji itd. Z ciekawości. Moim zdaniem żeby zuzyskać sekwencyjne zapalanie i gaszenie od lewej strony to trzeba zrobić coś w tym stylu jak w kodzie niżej. Obecnie albo gasisz wszystkie bity 0x00 albo zapalsz wszystkie 0xff. Pewnie są instrukcje które mogą to zrobić bardziej optymalnie niż takie kombinowanie. Ale chodzi o ideę.

 LDI R17, 0 // rejestr tymczasowy dla obliczania przesunięć od 0 do 7
 LDI R19, 0x00 // rejest tymczasowy do instrukcji OUT PORTA która ma wygasić bity (nie wiem czy tutaj 0 oznacz wartość 0 czy bit 0 dlatego 0x00)
loop:
 LDI R16, (1 << [R17]) // albo wartość zapisana w .data .set gdzieś gdzie można ją potem aktualizować jak tutaj rejest
// ogólnie idea jest taka że trzeba zwiększać wskaźnik R17 (wartość R17) o 1 aż do wartości 7 i robić przesunięcie
// w ten sposób uzyskuje się liczby 1, 2, 4, 16, 32, 64, 128, które binarnie zapalają bity na pozycjach właśnie od 0 do 7
// obecnie macie ustaw 0xFF czyli zapal wszystkie bity 1111 1111 albo wszystko zgaś. W ten sposób można uzyskać sekwencję
 OUT PORTA, R16 
 CALL delay; // opóźnienie  z którego wychodzisz ret żeby tutaj wrócić do pętli
 OUT PORTA, R19  // ustawi bity na 0 tj wygaś bity (i diody)
 CALL delay // opóźnienie z którego wychodzisz ret żeby tutaj wrócić do pętli
 INC R17
 CMP R17, 7
 BRNE loop // Branch if not eaul - wróć do poczatku petli loop jesli R17 < 7

A sekwencja procedura przerwanie może powinna zaczynać tak

PUSH R30 // jesli to ma byc rejestr tymczasowy, czyli zapisz aktualna wartość tego rejestru
IN R30, SREG // teraz w rejestrze R30 jest zawartość SREG
SBR R30, 0x80 // ustaw 7 bit na 1 ( pytanie czy to jest potrzebne, trzeba by sprawdzić wartości w debugerze SREG )
PUSH R30 // odloz na stos jeszcze to
// i tutaj nie wiem po co są odłożone jeszcze rejestry 27-29 ale to może mieć związek z SPL SPH czyli stack pointer
... 
POP R30
// SBRC R30, 7 // Skip if Bit in Register is Cleared 
OUT SREG, R30 // powinno ustawić bit 7 na 1 bo zapisana była wartość 0x80 przed wrzuceniem na stos
POP R30 // przywróć pierwotną zawartość rejestru
// mozna chyba tez wyjść RET jeśli bity chyba na GICR  
RETI //Return from Interrupt 

Ale nie bierz tego na serio, bo sam ledwo ogarniam te tematy. Po prostu z ciekawości i nudów przejrzałem trochę dokumentaje żeby zrozumieć co robi ten kod... i takie są moje luźne spostrzeżenia na ten temat.

0

@goose_: dzięki :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1