Forum: Mikrocontroller und Digitale Elektronik Hilfe Bei Assembler Program


von Stefa N. (exa)


Angehängte Dateien:

Lesenswert?

Hallo zusammen, mein Programm soll folgendes Erledigen:
Ich habe 64 digitale Ausgänge die ich zu 8 Segmenten á 8 Ausgänge 
zusammenfasse. Die Segmente werden von einem ATmega32 an PortC in einem 
Timer-Interrupt ausgewertet. Die Segmente werden nacheinander von PortA 
aktiviert. Das Ergebnis soll mir an PortB angezeigt werden. Ein 
Beispiel:

Segment 1 aktiviert , Ausgang 1 = 1 , Ergebnis sollte 1 sein.
Segment 1 aktiviert , Ausgang 8 = 1 , Ergebnis sollte 8 sein.
Segment 2 aktiviert , Ausgang 9 = 9 , Ergebnis sollte 9 sein.
Segment 2 aktiviert , Ausgang 16 = 16 , Ergebnis sollte 16 sein.
...

Nun mein Problem:
PortA = 0b00000001 , Ausgang 1 = 1 , Ergebnis = 9
PortA = 0b00000001 , Ausgang 8 = 1 , Ergebnis = 16

PortA = 0b00000010 , Ausgang 9 = 1 , Ergebnis = 17
PortA = 0b00000010 , Ausgang 16 = 1 , Ergebnis = 24

PortA = 0b00000100 , Ausgang 17 = 1 , Ergebnis = 25
PortA = 0b00000100 , Ausgang 24 = 1 , Ergebnis = 32

PortA = 0b00001000 , Ausgang 25 = 1 , Ergebnis = 33
PortA = 0b00001000 , Ausgang 32 = 1 , Ergebnis = 40

PortA = 0b00010000 , Ausgang 33 = 1 , Ergebnis = 41
PortA = 0b00010000 , Ausgang 40 = 1 , Ergebnis = 48

PortA = 0b00100000 , Ausgang 41 = 1 , Ergebnis = 49
PortA = 0b00100000 , Ausgang 48 = 1 , Ergebnis = 54

PortA = 0b01000000 , Ausgang 49 = 1 , Ergebnis = 55
PortA = 0b01000000 , Ausgang 54 = 1 , Ergebnis = 64

PortA = 0b10000000 , Ausgang 55 = 1 , Ergebnis = 1
PortA = 0b10000000 , Ausgang 64 = 1 , Ergebnis = 8

Ich finde meinen Fehler nicht. Das Assembler-Programm habe ich 
angehängt. Mir ist klar, dass man einen Interrupt so kurz wie möglich 
halten sollte, und die Auswertung separat machen sollte. Dieses Programm 
habe ich nur geschrieben, um dessen korrekte Funktionsweise zu 
überprüfen.
Ich hoffe ihr könnt mir helfen.
MfG
EXA

von Lutz (Gast)


Lesenswert?

timer0_overflow:
  push  r16
  in  r16, SREG    ;SREG Register retten
  push  r16
  ldi  r18, 0b00000001    ;Erstes Segment auswählen

wird am Anfang des overflow-irq r18 immer auf 1 intialisiert, obwohl es 
am ende nach links verschoben wird?

von Lutz (Gast)


Lesenswert?

brcc  auslesen

sorry, hab ich übersehen

von Herr M. (herrmueller)


Lesenswert?

Ich vermute, bei
sbrs  r17, 0    ;Segmenteingang 1 den
ldi    r16, 0x01  ;Wert 1 zuweisen
sbrs  r17, 1    ;Segmenteingang 2 den
ldi    r16, 0x02  ;Wert 2 zuweisen
sbrs  r17, 2    ;Segmenteingang 3 den
...

wolltest Du springen, wenn das Bit gelöscht ist.

Ausserden würde ich zur Sicherheit bei dem ROL Befehl vorher das Carry 
löschen, da es sonst von der vorhergehenden Addition übernommen wird.


gruss
 HerrMueller

von Stefa N. (exa)


Lesenswert?

@ Lutz:
Ja, am Anfang setze ich r18=1. Das steht aber auserhalb der 
'auslesen:'-Schleife und sollte daher nur einmal am Anfang passieren.

@  Herr Mueller:
Sorry hätte ich dazu sagen sollen, PortC wird über externe PullUps auf 
0xFF gehalten und wenn eine Ausgang durchschaltet dann wird der 
entsprechende Pin auf LOW gezogen. (Interne PullUps konnten nicht 
verwendet werden, da der Widerstandswert für meine Zwecke zu hoch ist)

MfG
EXA

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Stefan P. schrieb:

> Die Segmente werden nacheinander von PortA
> aktiviert. Das Ergebnis soll mir an PortB angezeigt werden.

Du tappst in diese Falle:
http://www.mikrocontroller.net/articles/AVR-Tutorial:_IO-Grundlagen#Stolperfalle_bei_Matrixtastaturen_etc.

(Link zeigt Bug in der Forumsoftware. Gehe zum Abschnitt "Stolperfalle 
bei Matrixtastaturen etc.")

z.B. an dieser Stelle:
1
auslesen:    
2
    out   PORTA, r18    ;Segment aktivieren
3
    in    r17, PINC    ;Segment einlesen

von Stefa N. (exa)


Lesenswert?

Stefan B. schrieb:
> Du tappst in diese Falle:
> 
http://www.mikrocontroller.net/articles/AVR-Tutorial:_IO-Grundlagen#Stolperfalle_bei_Matrixtastaturen_etc.

Auch mit einem zusätzlichem NOP funktionierts leider nicht. Ein weiterer 
komischer Effekt, den ich mir nicht erklären kann ist folgender:
Das Ergebnis wird mir an PortB durch LEDs angezeigt. Beim Auslesen von 
Segment 1-6 (PA0-5) und Segment 8 (PB7) flackern die LEDs, da ja im 
Interrupt ausgelesen wird. Soweit ist das ja ok. Beim Abfragen von 
Segment 7 (PA6) leuchten die LEDs kontinuirlich. Wenn ich Segment 7 
auslese sollten die LEDs doch auch flackern, da ja ebenfalls im 
Interrupt ausgelesen wird.
Dafür finde ich absolut keine Erklärung.
Bei der Simulation im AVR-Studio funktioniert alles wunderbar.

MfG
EXA

von Stefa N. (exa)


Lesenswert?

Hat keiner eine Idee? Ich kann mir nicht erklären, wo der Fehler liegen 
soll.
MfG
EXA

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Ich habe versucht das per Simulation nachzuvollziehen, bin aber daran 
schon kläglich gescheitert.

Im ASM-Code ist eine Eingabe über PINC nötig und aus dem Begleittext der 
Frage wird überhaupt nicht darauf eingegangen, welche Eingaben an PINC 
zu welcher Ausgabe und zu welchem Ergebnis führen sollen. Die komplette 
Simulation wäre daher 8 Segmentmöglichkeiten * 256 Zustände an PINC = 
2048 Möglichkeiten groß - zuviel für mich.

Und ich habe dieses Wunschergebnis nicht verstanden.
> Segment 1 aktiviert , Ausgang 8 = 1 , Ergebnis sollte 8 sein.
> Segment 2 aktiviert , Ausgang 9 = 9 , Ergebnis sollte 9 sein.
                                    ^

Ich würde weitermachen, wenn ich die Rechenvorschrift für

Ausgang = f(Segment, PINC)
Ergebnis = f(Segment, PINC)

in einfachen Worten lesen könnte und sie mir nicht aus möglicherweise 
falschen ASM-Code zusammenreimen müsste ;-)

von Reinhard R. (reirawb)


Lesenswert?

@Stefan P. (exa)
Immer unter der Voraussetzung, daß ich nicht völlig auf einem falschen 
Dampfer bin:

Wie ist denn elektronisch/physikalisch die Verbindung zwischen Port A 
und Port C aufgebaut? Da sind doch -wenn ich Dich richtig verstanden 
habe- noch irgendwelche Schaltungselemente/Register dazwischen. 
Vielleicht postest Du mal die Schaltung?
So wie ich das verstanden habe, kann da auch von jedem Segment nur ein 
Ausgang aktiv sein?

Das Ganze ist mir noch recht unklar, kann aber auch daran liegen, daß 
ich mich altersmäßig langsam der Senilitätsgrenze nähere :-)

Reinhard

von Reinhard R. (reirawb)


Lesenswert?

Was mir auf die Schnelle aufgefallen ist:

Du sammelst das Ergebnis für Segment 1 und gibst es auf Port B aus. Dann 
springst Du wieder zurück und sammelst das Ergebnis für Segment 2 und 
gibst dieses auf Port B aus. Dazwischen liegen geschätzt 40 
Prozessortakte, dann wird schon das nächste Segmentergebnis ausgegeben. 
Du hast schnelle Augen... :-)

Vielleicht solltest Du bei jedem Interrupt nur ein Segment ausgeben und 
Dir das Ergebnis erst mal ansehen?

Reinhard

von Stefa N. (exa)


Angehängte Dateien:

Lesenswert?

Ok ich gebe zu, mein erster Post war nicht leicht verständlich ...
Ich habe mal den Schaltplan vom Prinzip her angehängt.
Ich habe 8 Segmente mit je 8 Eingängen. Die restlichen Segmente sind 
paralell zu Segment1 geschaltet und werden ebenfalls über PortA + 
Transistor aktiviert. Zur besseren Übersichtlichkeit ist im Schaltplan 
nur ein Segment eingezeichnet.
Von allen 64 Eingängen ist IMMER höchstens EINER durchgeschaltet !
Am Ausgang PortB will ich jetzt sehen, welcher der 64 Eingänge 
geschalten ist, oder ob garkeiner durchgeschalten ist.
PortC wird durch externe PullUPs auf HIGH-Pegel gehalten. Schaltet jetzt 
einer der Eingänge eines Segments durch sieht der AVR am entsprechenden 
Pin LOW-Pegel.

Wenn also PINC = 0b11111111 (also kein Eingang geschaltet) soll Ausgang 
= 0 sein. Ansonsten soll folgende Beziehung gelten:
PortB = Ausgang
Ausgang = Nummer des durchgeschalteten Eingangs
Ausgang = (Nummer des aktiven Segments - 1)*8 + Nummer des Pins der 
LOW-Pegel sieht
Ausgang = (LOG2(PortA) - 1)*8 + LOG2(invertierter PortC)

An PortA wird ein nach links rotierendes Register ausgegeben und damit 
die Segmente nacheinander aktiviert.
Der Befehl LOG2() erwartet als Argument eine Konstante, bei mir ändern 
sich jedoch die Zustände an PortA und PortC.
Deshalb wird (LOG2(PortA) - 1)*8 folgendermaßen erledigt:
1
ldi  r18, 0b00000001  ;Erstes Segment auswählen
2
auslesen:    
3
out  PORTA, r18
4
sbrc  r18, 0  ;Bei aktiviertem 1ten Segment den
5
ldi  r20, 0  ;Ausgang SEGMENTEINGANG + 0 definieren
6
sbrc  r18, 1  ;Bei aktiviertem 2ten Segment den
7
ldi  r20, 8  ;Ausgang SEGMENTEINGANG + 8 definieren
8
sbrc  r18, 2  ;Bei aktiviertem 3ten Segment den
9
ldi  r20, 16  ;Ausgang SEGMENTEINGANG + 16 definieren
10
sbrc  r18, 3  ;Bei aktiviertem 4ten Segment den
11
ldi  r20, 24  ;Ausgang SEGMENTEINGANG + 24 definieren
12
sbrc  r18, 4  ;Bei aktiviertem 5ten Segment den
13
ldi  r20, 32  ;Ausgang SEGMENTEINGANG + 32 definieren
14
sbrc  r18, 5  ;Bei aktiviertem 6ten Segment den
15
ldi  r20, 40  ;Ausgang SEGMENTEINGANG + 40 definieren
16
sbrc  r18, 6  ;Bei aktiviertem 7ten Segment den
17
ldi  r20, 48  ;Ausgang SEGMENTEINGANG + 48 definieren
18
sbrc  r18, 7  ;Bei aktiviertem 8ten Segment den
19
ldi  r20, 54  ;Ausgang SEGMENTEINGANG + 54 definieren
20
clc
21
rol  r18  ;Nächstes Segment auswählen
22
brcc  auslesen

LOG2(invertierter PortC) wird folgendermaßen realisiert:
1
in  r17, PINC    ;Segment einlesen
2
clr  r16
3
sbrs  r17, 0    ;Segmenteingang 1 den
4
ldi  r16, 0x01  ;Wert 1 zuweisen
5
sbrs  r17, 1    ;Segmenteingang 2 den
6
ldi  r16, 0x02  ;Wert 2 zuweisen
7
sbrs  r17, 2    ;Segmenteingang 3 den
8
ldi  r16, 0x03  ;Wert 3 zuweisen
9
sbrs  r17, 3    ;Segmenteingang 4 den
10
ldi  r16, 0x04  ;Wert 4 zuweisen
11
sbrs  r17, 4    ;Segmenteingang 5 den
12
ldi  r16, 0x05  ;Wert 5 zuweisen
13
sbrs  r17, 5    ;Segmenteingang 6 den
14
ldi  r16, 0x06  ;Wert 6 zuweisen
15
sbrs  r17, 6    ;Segmenteingang 7 den
16
ldi  r16, 0x07  ;Wert 7 zuweisen
17
sbrs  r17, 7    ;Segmenteingang 8 den
18
ldi  r16, 0x08  ;Wert 8 zuweisen

Später soll das Ergebnis via UART an den Rechner geschickt werden. Die 
Ausgabe an PortB mit den LEDs dient nur während der Entwicklungsphase 
zur Kontrolle, ob das Program so läuft wie ich mir das vorstelle.
Ich hoffe Ihr könnt damit mehr anfangen, wie mit dem ersten Post :-)
MfG
EXA

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Atmega32... PORTC... Wie ist eigentlich die JTAG Fuse eingestellt?

von Stefa N. (exa)


Angehängte Dateien:

Lesenswert?

Daran hab ich auch schon gedacht. Müsste aber passen. Bild von den Fuses 
unter PonyProg im Anhang.
MfG
EXA

von Reinhard R. (reirawb)


Lesenswert?

Hier addierst Du r16 und r20, gibst aber r16 aus, egal ob Addition oder 
nicht:
1
       breq  jump         ;Addition überspringen und somit Ausgang = 0
2
       add   r16, r20     ;Ausgang = SEGMENTEINGANG + r20
3
jump:  out   PORTB, r16   ;Ausgang schreiben
4
       rol   r18          ;Nächstes Segment auswählen
5
       brcc  auslesen
Versuch mal die Sprungmarke hinter den OUT-Befehl zu setzen, dann wird 
auch nur das Additionsergebnis ausgegeben:
1
       breq  jump         ;Addition überspringen und somit Ausgang = 0
2
       add   r16, r20     ;Ausgang = SEGMENTEINGANG + r20
3
       out   PORTB, r16   ;Ausgang schreiben
4
jump:  rol   r18          ;Nächstes Segment auswählen
5
       brcc  auslesen

Viel Erfolg.

Reinhard

von Stefa N. (exa)


Lesenswert?

r16 wird am anfang auf 0 gesetzt. Ist kein Eingang durchgeschaltet 
bleibt r16 = 0 und ich will auch 0 als Ausgabe bekommen. Wenn ich die 
Sprungmarke hinter den OUT-Befehl setzte bekomme ich als Ausgabe den 
Wert vom vorhergehenden Interrupt und nicht 0.
MfG
EXA

von Reinhard R. (reirawb)


Lesenswert?

Stefan P. schrieb:
> r16 wird am anfang auf 0 gesetzt. Ist kein Eingang durchgeschaltet
> bleibt r16 = 0 und ich will auch 0 als Ausgabe bekommen. Wenn ich die
> Sprungmarke hinter den OUT-Befehl setzte bekomme ich als Ausgabe den
> Wert vom vorhergehenden Interrupt und nicht 0.

Richtig, aber wenn Du den einen eingeschalteten Schalter detektiert und 
auf Port B ausgegeben hast, wird r16 -und damit wieder der Port B nach 
ca. 40 Takten, im nächsten Schleifendurchgang innerhalb des gleichen 
Interrupts- wieder auf 0 gesetzt. Reichen Dir ca. 40 Takte um die LEDs 
und damit den Schalter zu erkennen?

Oder sehe ich da wieder was falsch?

Reinhard

von Stefan B. (stefan) Benutzerseite


Lesenswert?

48+8 = 56 nicht 54 ;-)

von Michael (Gast)


Lesenswert?

Mal ne dumme Frage: Ist der Atmega so verschaltet wie auf dem Plan im 
Post von 18:58 Uhr? Also ist wirklich der Reset-Pin nicht angeschlossen? 
Ich verwende nur PICs aber da ist das ne dumme Idee keinen Pull-Up zu 
verwenden. Vielleicht kommt das Problem daher. Aber wie gesagt, ich hab 
von Atmegas keine Ahnung.

von spess53 (Gast)


Lesenswert?

Hi

>Ist der Atmega so verschaltet wie auf dem Plan im Post von 18:58 Uhr? A

Hoffentlich nicht. Es fehlen z.B. alle Vorwiderstände für die Leds.

MfG Spess

von Alex M. (Gast)


Lesenswert?

Hi Stefan,
1)im Schaltplan würde ich die Dioden(LED) bei 0- Leuchten lassen, weil 
die Porttreiber des AVR's bei Low den höheren Strom abdrücken können.
2) Vorwiderstände für die LED's sind Pflicht !
3) Den maximal zulässigen Portstrom nicht überschreiten!
4) Gesamtstrom zw. VCC und GND nicht überschreiten !

von Stefan B. (stefan) Benutzerseite


Angehängte Dateien:

Lesenswert?

In der Simulation sehe ich keine Probleme, Code ist im Anhang. Beachte 
alle Tipps der Vorredner bzgl. Hardware. Da liegt es anscheinend im 
Argen. AVcc und GND Pin 31 auch beschalten.

von Michael (Gast)


Lesenswert?

>Hi
>
>>Ist der Atmega so verschaltet wie auf dem Plan im Post von 18:58 Uhr? A
>
>Hoffentlich nicht. Es fehlen z.B. alle Vorwiderstände für die Leds.
>
>MfG Spess

Deshalb schrieb ich ja "dumme Frage" da ich mir auch denk, dass das nur 
schematisch sein soll damit man weiß, was wo dran hängt. Aber grade den 
Pull-Up am Reset-Pin vergisst man im Eifer des Gefechts mal schnell. ;)

von Stefa N. (exa)


Angehängte Dateien:

Lesenswert?

Reinhard R. schrieb:
> Richtig, aber wenn Du den einen eingeschalteten Schalter detektiert und
> auf Port B ausgegeben hast, wird r16 -und damit wieder der Port B nach
> ca. 40 Takten, im nächsten Schleifendurchgang innerhalb des gleichen
> Interrupts- wieder auf 0 gesetzt. Reichen Dir ca. 40 Takte um die LEDs
> und damit den Schalter zu erkennen?

Prinzipiell gesehen hast du natürlich Recht. Die LEDs dienen aber nur 
zum Debuggen, die kommen später weg und das Ergebnis wird via UART an 
den Rechner geschickt.
Der Schaltplan von 18:58 Uhr war nur schematisch, damit Ihr wisst, wie 
wo was dranhängt. Den kompletten Schaltplan vom Versuchsaufbau hab ich 
mal angehängt. Die Fuses könnt ihr weiter oben im Thread anschaun.

Stefan B. schrieb:
> In der Simulation sehe ich keine Probleme ...
Das ist es ja gerade was ich nicht verstehe, Simulation ist bei mir auch 
ok aber aumf Chip gehts dann nicht ...
Trotzdem bis hier hin schon mal vielen Dank für eure Unterstützung :)
MfG
EXA

von Stefa N. (exa)


Lesenswert?

Also wenn ich folgfende Stelle im Quellcode ändere
1
push  r16
2
in  r16, SREG    ;SREG Register retten
3
push  r16
4
ldi  r18, 0b00000001    ;Erstes Segment auswählen
5
auslesen:    
6
ldi  r18, 0b00000001  <<<<<<Neue Zeile <<<<<<<
7
out  PORTA, r18    ;Segment aktivieren
8
nop
9
in  r17, PINC    ;Segment einlesen
10
clr  r16
und den Rest nicht verändere, dann bekomme ich die richtige Ausgabe an 
PortB! Allerdings wird dann auch nur Segment1 ausgelesen. Anscheinend 
hat es also etwas mit dem rotieren von r18 zu tun. Aber wie soll ich das 
anders lösen? Die Rechenvorschrift für r18 wäre:
r18 = 2^x wobei x=[0;7]
Ich habe auch schon versucht, den ROL-Befehl folgendermaßen 
auszudrücken:
1
clc
2
ldi  r19, 2
3
mul  r18, r19
4
mov  r18, r0
5
cpi  r18, 128
6
brne  auslesen
Hat aber auch nicht funktioniert.

MfG
EXA

von Holger B. (Gast)


Lesenswert?

Hallo Stefan,
in der Simulation läuft dein Programm nur, wenn man den Zustand von Port 
C genau zwischen der Ausgabe auf Port A und dem Einlesen von Port C 
ändert.
Macht man diese Änderung zu spät, kommt es zu Deinem Effekt. Nehme an, 
das der Transistor nicht schnell genug sperrt. Füge entsprechende NOP's 
ein um Ihm mehr Zeit zu geben.
Bsp.

  out    PORTA, r18    ;Segment aktivieren
  .
Mehr Nop's

         in    r17, PINC    ;Segment einlesen

von Stefa N. (exa)


Lesenswert?

Hallo Holger, unglaublich, es funktioniert !!! Klasse ! Habe 21 mal NOP 
eingefügt (mehr geht nicht, weil rjmp ja nur 63 Zeilen springen kann). 
Vielen Dank du hattest Recht. 21 NOPs im Code sind ja nicht gerade 
befriedigend, werde mich morgen mal auf die Suche eines geeigneten 
Transistios oder FETs? machen. Momentan verwende ich den BC547A, habt 
ihr irgendwelche Vorschläge?
Danke für die Unterstützung, ohne euch hätt ich's nicht soweit geschafft 
... :)
MfG
EXA

von Holger B. (Gast)


Lesenswert?

Hallo Stefan,
freut mich das es klappt. Du kannst die Nop's auch durch eine 
Warteschleife ersetzen. Hatte mit weniger NOP's gerechnet.
Denke mal darüber nach, die Zuordnung der Werte durch Addition bzw 
Schieben in Schleifen zu lösen (Das Programm wird Kürzer) ;-)

Wenn es wirklich sicher ist,dass nur ein Eingang betätigt werden kann,
dann sollte die Ausgabe auf Port B erst am Ende der Auswertung erfolgen.
Damit vermeidest Du das "flackern" der LED's.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.