Forum: Mikrocontroller und Digitale Elektronik Warum funktioniert mein AVR Assembler Code nicht?


von Sebastian (Gast)


Lesenswert?

Ich programmiere erst seit kurzem mit Assembler und brauch Hilfe. Mein 
Programm soll eine binäre Zahl am PORTC einlesen und diese Zahl dann auf 
ein 7 Segment Display anzeigen. Wenn aber ein Signal an PORTC anliegt 
passiert nichts. Ich habe alle Verbindungen geprüft also kann es nicht 
daran liegen.
1
.include "m328pdef.inc"
2
3
  ldi r16, 0b01111111 ; PB0-6 as outputs (7 segment display)
4
  out DDRB, r16
5
6
loop:
7
8
  ldi r16, PINC ; PINC read
9
10
  cpi r16, 0b0000000 ; if(PINC == 0)
11
  breq number0
12
13
  cpi r16, 0b0000001 ; if(PINC == 1)
14
  breq number1
15
16
  cpi r16, 0b0000010 ; if(PINC == 2)
17
  breq number2
18
19
  cpi r16, 0b0000011 ; if(PINC == 3)
20
  breq number3
21
22
  cpi r16, 0b0000100 ; if(PINC == 4)
23
  breq number4
24
25
  cpi r16, 0b0000101 ; if(PINC == 5)
26
  breq number5
27
28
  cpi r16, 0b0000110 ; if(PINC == 6)
29
  breq number6
30
31
  cpi r16, 0b0000111 ; if(PINC == 7)
32
  breq number7
33
34
  cpi r16, 0b0001000 ; if(PINC == 8)
35
  breq number8
36
37
  cpi r16, 0b0001001 ; if(PINC == 9)
38
  breq number9
39
40
  out PORTB, r17 ; turn on leds
41
42
  rjmp loop
43
44
number0:
45
  ldi r17, 0b01111110 ; a, b, c, d, e, f
46
  
47
number1:
48
  ldi r17, 0b00110000 ; b, c
49
50
number2:
51
  ldi r17, 0b01101101 ; a, b, d, e, g
52
53
number3:
54
  ldi r17, 0b01111001 ; a, b, c, d, g
55
56
number4:
57
  ldi r17, 0b00110011 ; b, c, f, g
58
59
number5:
60
  ldi r17, 0b01011011 ; a, c, d, f, g
61
62
number6:
63
  ldi r17, 0b01011111 ; a, c, d, e, f, g
64
65
number7:
66
  ldi r17, 0b01110000 ; a, b, c
67
68
number8:
69
  ldi r17, 0b01111111 ; a, b, c, d, e, f, g
70
71
number9:
72
  ldi r17, 0b01111011 ; a, b, c, d, f, g

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Sebastian schrieb:
> ldi r16, PINC ; PINC read

Das tut nicht das, was du planst. Du lädst hier nur die Adresse des PINC 
Register als direkten Wert in R16. Du möchtest vermutlich
1
   IN R16, PINC

von Flo (Gast)


Lesenswert?

Du springst bei einem gültigen Vergleich aus der loop raus und nie 
wieder rein.

von Karl M. (Gast)


Lesenswert?

Hallo,

Die Interrupt-Vektor-Tabelle fehlt und die Initialisierung des Stacks, 
insbesondere des Stack-Pointers SP[H:L].

von S. Landolt (Gast)


Lesenswert?

an Karl M.:

- es ist gar kein Interrupt aktiviert
- der Stack wird gar nicht benutzt
- beim ATmega328P ist der 'Initial Value' des Stackpointers ohnehin 
=RAMEND

von Megatroll (Gast)


Lesenswert?

Die AVR IDE hat einen simulator dabei, den kann man Singlesteppen und 
schauen, was so rauskommt.

von Thomas (kosmos)


Lesenswert?

Port C musst du auch noch initialisieren. Achte auf die Pullups
1
ldi r16, 0x00ff
2
out ddrc, r16

Zudem müssen auch irgendwann mal die Infos ausgegeben werden.
1
out portb, r17

Hier solltest du eigentlich alles nötige finden, was schon genannt wurde 
Stackpointer....
https://www.mikrocontroller.net/articles/AVR-Tutorial

von Dennis H. (c-logic) Benutzerseite


Lesenswert?

ohne Stack-benutzung (call/push/pop/ret) gehts auch ohne SPL/SPH.
Interrupt-Tabelle ist ohne Interrupte auch nicht notwendig.

Für simplen Code ohne Call/Ret reichts doch :)

Matthias hat ja das Hauptproblem schon beschrieben.
IN R16, PINC

Sebastian denk mal über eine Tabelle mit den Daten nach:
Z-Register mit Anfang laden.
Offset durch Zahl dargestellt aufaddieren.
LPM mit Z-Register

von c-hater (Gast)


Lesenswert?

Karl M. schrieb:

> Die Interrupt-Vektor-Tabelle fehlt und die Initialisierung des Stacks,
> insbesondere des Stack-Pointers SP[H:L].

So ein Bullshit.

Eine (vollständige) Interruptvektortabelle braucht man sowieso praktisch 
nie.

Allenfalls braucht man einzelne Interruptvektoren. Eben genau die, die 
man tatsächlich benutzt. Er benutzt exakt keinen, also braucht er auch 
keinen. Alles, was er benutzt, ist der Reset-Vektor. Und es spricht 
absolut nichts dagegen, in dem Fall, das man keine Interrupts braucht, 
genau dort mit dem main-code anzufangen. Alles andere wäre sinnlos 
verschwendeter Flash...

Dasselbe Drama gilt für den Stack. Den braucht man genau dann, wenn man 
Interrupts benutzen möchte oder Unterprogramme aufrufen möchte oder 
irgendwas pushen oder poppen möchte. Macht er aber alles nicht. also ist 
die Initialiserung des Stacks überflüssig. Verschwendet nur Rechenzeit 
(wenn auch in einem sehr überschaubaren Ausmass). Bei einem P328 wäre es 
sogar doppelt sinnlose Verschwendung, der initialisiert nämlich seinen 
Stack schon per Hardware auf RAMTOP, wie alle "neueren" AVRs es seit 
ewigen Zeiten tun...

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Thomas O. schrieb:
> ldi r16, 0x00ff
> out ddrc, r16

Was soll das Laden eines 16-bit Wertes in ein 8-Bit Register denn 
bewirken? Es sei angemerkt, das die Ports eines AVR nach Reset auf Input 
stehen. Nur eventuelle Pullups sollten konfiguriert werden:
1
 ldi r16,0xff
2
 out portc,r16
Sebastian schrieb:
> cpi r16, 0b0000000 ; if(PINC == 0)
>   breq number0

Hier genau die Stellen zählen. Es sind bei dir nur 7, der Port (und R16) 
haben aber 8.

: Bearbeitet durch User
von Thomas (kosmos)


Lesenswert?

Asche auf mein Haupt, hatte erst die Ports verwechselt der eine war ja 
schon als Ausgang und als ich das FF in 00 korrigieren wollte war ich 
wohl zu schnell.

von Peter D. (peda)


Lesenswert?

Sebastian schrieb:
> number9:
>   ldi r17, 0b01111011 ; a, b, c, d, f, g

Ab hier läuft das Programm durch den ganzen Flash, bis es wieder bei 
0x0000 ankommt.
Das Programm enthält aber noch viel mehr Mumpitz. Assembler scheint 
nicht so Dein Ding zu sein.

von npn (Gast)


Lesenswert?

Peter D. schrieb:
> Assembler scheint nicht so Dein Ding zu sein.

Kann es sein, daß er genau deshalb in einem Forum um Rat fragt?

von Roland F. (rhf)


Lesenswert?

Hallo,
Peter D. schrieb:
> Assembler scheint nicht so Dein Ding zu sein.

Wie er schon im Eröffnungsbeitrag schrieb:
> Ich programmiere erst seit kurzem mit Assembler und brauch Hilfe.

rhf

von Wolfgang (Gast)


Lesenswert?

Matthias S. schrieb:
> Sebastian schrieb:
>> cpi r16, 0b0000000 ; if(PINC == 0)
>>   breq number0
>
> Hier genau die Stellen zählen. Es sind bei dir nur 7, der Port (und R16)
> haben aber 8.

Das ist dem Compiler so etwas von egal. Auch aus "0b0" würde er ein 0x00 
generieren.

von Peter D. (peda)


Lesenswert?

Roland F. schrieb:
> Wie er schon im Eröffnungsbeitrag schrieb:
>> Ich programmiere erst seit kurzem mit Assembler und brauch Hilfe.

Auch als Anfänger darf man Tutorials lesen, das Instruction Set mal 
aufschlagen und das Gehirn einschalten.
Was z.B. erwartet man, was nach 10 "ldi r17,x" Befehlen wohl in R17 
steht?
Auch sollte die letzte Zeile eines Programms immer ein jmp oder ret 
sein.
Die Kommentare in C-Syntax lassen die Frage offen, warum er nicht gleich 
C nimmt.

Sebastian schrieb:
> breq number9
>
>   out PORTB, r17 ; turn on leds

Ein C-Complier würde Dir hier ein
"warning: 'r17' is used uninitialized in this function"
vor den Latz knallen.

Versuch erstmal, ein Programm mit nur einer Bedingung zum Laufen zu 
bringen.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Wolfgang schrieb:
> Das ist dem Compiler so etwas von egal.

Es gibt keinen Compiler bei Assembler. Und es ist nicht egal, wenn man 
z.B. die letzte Stelle wirklich abfragen möchte. Notiert man binär, ist 
es wirklich sinnvoll, auch die 8 Bit zu beachten.

von Nachdenklicher (Gast)


Lesenswert?

Kleine strukturelle Änderung hilft:

In jedem der "numberx:"-Blöcke als zweite Zeile ein "rjmp ausgabe" 
hinzufügen, vor dem out nach portb ein label "ausgabe:" einbauen, und es 
sollte schon besser laufen. Elegant ist es trotzdem nicht. ;-)

von Roland F. (rhf)


Lesenswert?

Hallo,
Peter D. schrieb:
> Roland F. schrieb:
>> Wie er schon im Eröffnungsbeitrag schrieb:
>>> Ich programmiere erst seit kurzem mit Assembler und brauch Hilfe.
>
> Auch als Anfänger darf man Tutorials lesen, das Instruction Set mal
> aufschlagen und das Gehirn einschalten.

Oder in einen (Fach-)Forum nachfragen! Denn genau dafür ist so eine 
Diskussionsplattform ja eigentlich gedacht.

Warum fühlen sich hier so oft Diskussionsteilnehmer (in diesem Fall du) 
extrem genervt von scheinbar trivialen Frage? Bleiben wir doch mal beim 
aktuellen Fall:
- Sebastian hat sich eine Aufgabe gestellt.
-> das zeigt Interesse

- er angefangen eine Lösung zu suchen und dazu ein Programm geschrieben
-> Eigeninitiative, wird hier immer gefordert.

- Das Programm funktioniert nicht, er weiß auf mangelnder Kenntnis und 
Erfahrung nicht mehr weiter und bittet um Hilfe.

Ich würde sagen, er hat alles richtig gemacht. Als Reaktion erhält er 
z.B von dir die Zurechtweisung, er solle sich doch mal Tutorials 
durchlesen und du unterstellst ihm gedankenloses Handeln.
Was soll das? Das ist destruktiv und demotivierend(1) gegenüber einer 
Person, die augenscheinlich etwas lernen will. Wenn dich solche Beiträge 
so sehr nerven, halte doch einfach mal die Hände von deiner Tastatur weg 
und antworte gar nicht. Das spart dir dann Zeit und vor allem Nerven.

rhf

P.S.:
(1) Übrigens, sollte Demotivation Ziel deines Beitrag gewesen sein, hier 
mal ein kleines "Tutorial" zur Weiterbildung von dem Fachmann für 
solche Fragen:

https://www.youtube.com/watch?v=1x6R6z1_vMk

:-))

von Martin V. (oldmax)


Lesenswert?

Hi
Also, viele unsinnige Antworten, wenige, die das Problem angehen. Ganz 
toll finde du den Hinweis, doch C zu erlernen. Zum Prinzip, Assembler 
ist eine Programmiersprache der einfachsten Art. Die Befehle sind 
simpel. Höhere Programmiersprachen sind mit komplexen Befehlen bestückt. 
Aber, Kern des Problems, man muss programeren können. Nicht jeder, der 
einen Pinsel halten kann, ist ein Picasso oder Rembrandt. Um dahin zu 
kommen, bedarf es Übung und Talent. Nun zum Thema.
En Controller holt sich aus seinem Speicher einen Befehl nach dem 
anderen. Dazu hat er ein Register, den Prrogrammcounter,. Zuerst holt er 
den Befehl LDI  und eine dazugehörige Konstante. Den Wert dieser 
Konstante schreibt er dann mit dem Befehl OUT in das Portsteuerregister. 
(DDRx). Nun kommt die Marke Loop. Den Controller interessiert diese 
Marke nicht, aber der Compiler, der aus den Assembler Anweisungen einen 
Maschinencode generiert, merkt sch den Wert des Prrogrammcounter, also 
die Adresse des aktuellen Befehles. Auch die Adressen der Marken Numbers 
hat sich der Compiler durch eine entsprechende Berechnung der dortigen 
Befehlsadresse gemerkt und ersetzt nun bei den nächsten Befehlen den 
Markennamen mit der Adresse. Also ein BREQ number3 schreibt den 
berechnete Adresse in den Prrogrammcounter. Somit wird der nächste 
Befehl an der Marke Number3 geladen und der Programmcounter auf die 
nächste Adresse gesetzt. Das dies Adresse vom Befehl LDI Konstante 4 
ist, ist dem Controller egal und so arbeitet er weiter mit erhöhen des 
Programmcounter s und dem Laden und bearbeiten der unter der 
Speicheradresse enthaltenen Befehle. Findet er keine mehr, weil die 
Speicherzelle noch nicht geschrieben ist macht er nichts, außer der 
Erhöhung des Programmcounter s. Irgendwann läuft der Programmcounter auf 
der höchste 16 Bit Adresse FFFFh und der nächste Wert wäre 10000h. Doch 
die 1 im Überlauf ist  mit 16 Bit nicht mehr abzubilden und es bleibt 
nur die 0000h übrig. Und damit beginnt dein Programm von vorn.  Das wäre 
Mal der Programmierfehler und das hat nichts mit Assembler zu tun.
Die Lösung ist ein BRNE auf den nächsten Vergleich. Dazu mußt du 
natürlich auch dort entsprechend Marken setzen. Z.B. check_2, check_3 
usw.Hinter den BRNE setzt du ein CALL Numbers und der Befehlsfolge an 
den Marken Numbers beendest du mit RET. Falls du noch Probleme hast, 
melde die ruhig mit einer PN.
Gruß oldmax

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Roland F. schrieb:
> und du unterstellst ihm gedankenloses Handeln.

Ich setze auch bei einem Anfänger voraus, daß er weiß, daß Code in genau 
der Reihenfolge abgearbeitet wird, wie er hingeschrieben wurde. Sprünge 
führen nur die Sprungbefehle (BRANCH INSTRUCTIONS) aus.
Assembler ist da gnadenlos. Wenn man Unsinn hinschreibt, führt er auch 
Unsinn aus.
Ich helfe auch Anfängern gerne, aber ein roter Faden sollte schon zu 
erkennen sein.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Angehängte Dateien:

Lesenswert?

Martin V. schrieb:
> viele unsinnige Antworten, wenige, die das Problem angehen.
Ich würde empfehlen, zuerst mal eine einzige LED blinken zu lassen. Und 
das zuallererst im Simulator!

> Um dahin zu kommen, bedarf es Übung
Danach würde ich ein Lauflicht empfehlen (erst simpel von links nach 
rechts, dann hin&her, dann mit beliebigem Muster, dann mit per Taster 
verstellbarer Geschwindigkeit).
Wenn diese "Übungen" umgesetzt wurden, löst sich das Problem, um das es 
hier im Thread geht, ganz von alleine, weil es gar nicht mehr 
auftritt.

Roland F. schrieb:
> Ich würde sagen, er hat alles richtig gemacht.
... aber er hat kein Durchhaltevermögen (obwohl er kurz vor dem Ziel 
steht) und gibt sich keine Mühe. Mag hart klingen, ist aber so.
Denn dieses Problem kann man tatsächlich mit kostenloser Software und 
frei zugänglichen Informationen selber lösen.

Ich hatte noch mehr geschrieben, das aber wieder gelöscht, weil es 
offtopic ist. Und auch deine Auslassungen helfen dem TO im Grunde nicht 
weiter, ausser dass er jetzt weiß, dass er "alles richtig gemacht" hat 
(was irgendwie auch wieder ein wenig demotivierend ist, weil es ja 
trotzdem nicht funktioniert)...

Zurück zur Sache:
was erscheint auf der Anzeige mit so einem Programmschnipsel:
1
ldi r17, 0b00110000;
2
out portc,r17;
Zeigt sie eine "1" oder ein "E"?

Und im Anhang noch ein Denkanstoß...

: Bearbeitet durch Moderator
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Lothar M. schrieb:
> was erscheint auf der Anzeige mit so einem Programmschnipsel:ldi r17,
> 0b00110000;
> out Port portc,r17;

 Nix.
 Auf deinem Monitor allerdings:
 error: PORTC: Unknown instruction or macro

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Uuups, korrigiert... ;-)

von Martin V. (oldmax)


Lesenswert?

Hi
@Lothar Warum zitierst du mich? Deiner Antwort auf das erste Zitat kann 
ich nicht so richtig folgen. Im Übrigen tut er ja genau das, was du 
vorschlägst, er will eine LED anschalten, auch wenn es eine 
7Segmentanzeige ist. Fakt ist, das Programm führt den Out-Befehl gar 
nicht aus, weil er nach dem Laden des Bitmusters nicht in die 
Programmschleife zurückfindet. Und das habe ich ausführlich beschrieben 
und wenn Stefan des Lesens mächtig ist, dann wird er verstehen, welchen 
Fehler er gemacht hat. Die Arbeit mit Controller und Assembler setzt 
schon ein wenig Kenntnisse der Hardware vorraus. Hochsprachen sind da 
nicht ganz so anspruchsvoll, aber auch da gibt es ganz gemeine Fallen, 
in die man tappen kann, wenn man die zeitliche Reihenfolge nicht 
beachtet. Und diese Fehler sind in Hochsprachen nicht so offensichtlich 
wie hier in Assembler. Mir ist es auch schon passiert, das ein 
mathematisches Ergebnis von 0 von Pascal anders geliefert wurde wie von 
Basic. Bei einer Kalenderberechnung lieferte Basic keine Null, sondern 
ein kltzekleines 0,00000000irgendwas während die gleiche Formel n Pascal 
das richtige Ergebnis lieferte. Da man ja weiß, das der Fehler 
grundsätzlich vor dem Monitor sitzt, hat es mich schon einiges an Zeit 
gekostet, bis der Fehler erkannt und mit einem kleinen Offset behoben 
werden konnte.ok, ich schweife ab.
Grundsätzlich, wer Assembler anwendet, lernt seinen Controller 
zwangsläufg so richtig kennen. Das Datenblatt ist Pflichtlektüre. 
Assembler hat also nicht nur Nach- sondern auch Vorteile. Alles eine 
Frage der Perspektive.
Gruß oldmax

von michael_ (Gast)


Lesenswert?

Lothar M. schrieb:
> Und im Anhang noch ein Denkanstoß...

...
loop:

  ldi r16, PINC       ;  Eing„nge einelesen
...

Das kann ja niemals gehen.

Ersetzen durch:

in r16,PORTC    ; Port-C einlesen

Einen Pin kann man evtl. mit SBIC/SBIS testen.

Anfänger sollten sich erst mal an fertigen einfachen Programmen 
versuchen.

Und für jeden Prozessor eine Standard Initialisierung.
Egal, ob man alle Funktionen braucht oder nicht.

Ich habe mich damals durch die Appl. AVR-242 durchgebissen.
Danach war Tasten und 7-Segment kein Problem mehr.

Vor allem den Code für die 7-Segment würde ich wie dort in Tabelle 
ablegen.
Nicht wie hier über Sprünge.
Wehe, wenn die phys. Anschlüsse nicht mit dem PORT übereinstimmen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

michael_ schrieb:
1
 ldi r16, PINC       ;  Eingnge einelesen   ...
> Das kann ja niemals gehen. Ersetzen durch:
1
  in r16,PORTC    ; Port-C einlesen
Sollten da trotzdem nicht eher die PINs eingelesen werden?
Es ist aber auch echt heiß...  ;-)

michael_ schrieb:
> erst mal an fertigen einfachen Programmen versuchen.
Und wenn schon nicht fertig, dann wenigstens einfach. Das meinte ich mit 
der blinkenden LED. In diesem Fall wäre das simpelste 
LED-Einlesen-und-Ausgeben-Programm etwa so:
1
.include "m328pdef.inc"
2
3
  ldi r16, 0b01111111 ; PB6..0 = Ausgang
4
  out DDRB, r16       ; 7-SegDisp Bit 6..0 = abcdefg
5
  
6
loop:                 ; jetzt gehts los
7
  in   r16,PINC       ; alle Pins vom PC einlesen
8
  out  PORTD,r16      ; und geradeaus auf den PD ausgeben
9
  jmp  loop           ; wieder von vorn beginnen

: Bearbeitet durch Moderator
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Lothar M. schrieb:
> Uuups, korrigiert... ;-)

 Jetzt sollte eine '1' auf der Anzeige erscheinen.
 Andernfalls wäre das ein 'E', aber mit Dezimalpunkt :-)

 Und man macht das normalerweise mit Tabellen, aber andersrum,
 d.h. Seg_a = bit0 ... DP = bit7.

von michael_ (Gast)


Lesenswert?

Lothar M. schrieb:
> in r16,PORTC    ; Port-C einlesen
> Sollten da trotzdem nicht eher die PINs eingelesen werden?
> Es ist aber auch echt heiß...  ;-)

Neieiein!
Es geht nicht. Wie willst du ein Bit in einem Register ablegen?
Also das Port einlesen, was der TO sowieso möchte, und dann evtl. die 
Pin einzeln behandeln.

Lothar M. schrieb:
> in   r16,PINC       ; alle Pins vom PC einlesen

Neieiein! Ändern.


Hier mal die Applikation AVR-242.

http://ww1.microchip.com/downloads/en/AppNotes/doc1231.pdf

von spess53 (Gast)


Lesenswert?

Hi

>Lothar M. schrieb:
>> in   r16,PINC       ; alle Pins vom PC einlesen

>Neieiein! Ändern.

Witzbold. Selbstverständlich werden die Ports über das PIN-Register 
eingelesen:

PINC – The Port C Input Pins Address

MfG Spess

von Roland F. (rhf)


Lesenswert?

Hallo Lothar,
> ... aber er hat kein Durchhaltevermögen (obwohl er kurz vor dem Ziel
> steht) ...

Schon mal auf die Idee gekommen, das er seinen Fehler "einfach nicht* 
sieht?

> Denn dieses Problem kann man tatsächlich mit kostenloser Software und
> frei zugänglichen Informationen selber lösen.

Naja, das gilt so ziemlich für jedes Problem das in diesem Forum 
behandelt wird.

> ... ausser dass er jetzt weiß, dass er "alles richtig gemacht" hat
> (was irgendwie auch wieder ein wenig demotivierend ist, weil es ja
> trotzdem nicht funktioniert)...

Lesen hilft: mit richtig meinte ich, das er sich gemäß der hier immer 
wieder geforderten Vorgehensweise bei Fragen richtig verhalten hat.

rhf

von Roland F. (rhf)


Lesenswert?

Hallo Peter,
> Ich setze auch bei einem Anfänger voraus, daß er weiß, daß Code in genau
> der Reihenfolge abgearbeitet wird, wie er hingeschrieben wurde.

Vielleicht ist das das Grundproblem hier, das einfach zu viel 
vorausgesetzt wird.

rhf

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Roland F. schrieb:
> Vielleicht ist das das Grundproblem hier, das einfach zu viel
> vorausgesetzt wird.

Das glaube ich auch. Und wenn dann noch Leute wie 'michael_' kommen, die 
nur zur Verwirrung beitragen, ist das noch weniger hilfreich.
Ein sinnvoller Weg wäre z.B.
1. Lese den Wert von PORTC ein und maskiere ihn auf sinnvolle Werte, um 
Überschreitungen von Werten zu vermeiden.
2. Benutze diesen Wert als Zeiger in eine Tabelle, um das passende 
Bitmuster für die 7-Segment Anzeige zu erhalten.
3. Schreibe den erhaltenen Tabellenwert auf den Port der Anzeige.
4. Springe zu 1.

: Bearbeitet durch User
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

michael_ schrieb:
> Es geht nicht. Wie willst du ein Bit in einem Register ablegen?
Ich lege gleich alle 8 Bits des Pin-Registers parallel im R16 ab. So wie 
das hier gemacht werden muss, weil ja anschließend dieses R16 mit einem 
8-bit Wert verglichen wird.

Marc V. schrieb:
> aber mit Dezimalpunkt
Du hast noch eine Chance. Als Tipp: das Bit 7 ist ein Eingang... ?


...wenn man statt dem DDRB das richtige Portregister konfiguriert.

: Bearbeitet durch Moderator
von michael_ (Gast)


Lesenswert?

Lothar M. schrieb:
> michael_ schrieb:
>> Es geht nicht. Wie willst du ein Bit in einem Register ablegen?
> Ich lege gleich alle 8 Bits des Pin-Registers parallel im R16 ab. So wie
> das hier gemacht werden muss, weil ja anschließend dieses R16 mit einem
> 8-bit Wert verglichen wird.

Ist ja auch richtig. Man liest das gesamte Port (PORTC)ein.

spess53 schrieb:
> Witzbold. Selbstverständlich werden die Ports über das PIN-Register
> eingelesen:
>
> PINC – The Port C Input Pins Address

Dann zeig mal den gesamten Ausdruck.
Durch Komma getrennt muß dahinter noch das konkrete Pin kommen.
   PINC,PC3      ; das 3. Beinchen am Port-C

Geht aber nicht mit dem IN-Befehl.

Lothar M. schrieb:
> michael_ schrieb:
>> erst mal an fertigen einfachen Programmen versuchen.
> Und wenn schon nicht fertig, dann wenigstens einfach. Das meinte ich mit
> der blinkenden LED. In diesem Fall wäre das simpelste
> LED-Einlesen-und-Ausgeben-Programm etwa so:.include "m328pdef.inc"
>
>   ldi r16, 0b01111111 ; PB6..0 = Ausgang
>   out DDRB, r16       ; 7-SegDisp Bit 6..0 = abcdefg
>
> loop:                 ; jetzt gehts los
>   in   r16,PINC       ; alle Pins vom PC einlesen
>   out  PORTD,r16      ; und geradeaus auf den PD ausgeben
>   jmp  loop           ; wieder von vorn beginnen

So richtig fertig ist das aber noch nicht?
Mindestens fehlt eine Zeitschleife ...

Aber "Hallo Welt" wäre erst mal eine blinkende LED.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Matthias S. schrieb:
> Ein sinnvoller Weg wäre z.B.

 Genau.
 Und hier ein Beispiel:
1
.include "m328pdef.inc"
2
3
   ldi  r16, 0b01111111 ; PB0-6 as outputs (7 segment display)
4
   out  DDRB, r16       ;
5
   clr  r10
6
  
7
loop:
8
   ldi  zl, low(Tab7seg)
9
   ldi  zh, high(Tab7Seg)
10
WertIn:
11
   in   r16, PINC       ; Eingaenge einlesen   
12
   cpi  r16, 10         ; Falschen Wert eingelesen?
13
   brlo GetWert         ; Nein, Wert aus der Tabelle lesen
14
   ldi  r16, 10         ; Ja, Minus Zeichen anzeigen
15
GetWert:
16
   add  zl, r16
17
   adc  zh, r10
18
   ld   r16, Z
19
ausgabe:
20
  out PORTB, r16      ; Segmente updaten
21
  rjmp loop           ;
22
23
Tab7seg:  .db  0b01111110, 0b00110000, 0b01101101, 0b01111001, 0b00110011
24
          .db  0b01011011, 0b01011111, 0b01110000, 0b01111111, 0b01111011
25
          .db  0b00000001  ; Fehler, Minus Zeichen (Seg_g) anzeigen
 Anstatt Tagelang über Fehler, Fähigkeiten, Kenntnisse etc. zu
 diskutieren...

: Bearbeitet durch User
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

michael_ schrieb:
> Geht aber nicht mit dem IN-Befehl.
Nein, nur mit bestimmten In Registern. Und man liest damit aber kein Bit 
ein, sondern man fragt es mit einem bedingten Sprungbefehl als SBIS oder 
SBIC ab.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Marc V. schrieb:
> Und hier ein Beispiel:

Da sieht doch cool aus. Ich hätte vermutlich, angeregt durch den 
'Hexzahl auf 7-Segment' Thread  einfach auf 0x0F maskiert und die 16 
Zustände in die Tabelle geschrieben. Aber das ist nur ein Nerd-Spässchen 
:-P

michael_ schrieb:
> Mindestens fehlt eine Zeitschleife ...

Da fehlt überhaupt keine Zeitschleife. Der MC liest das ein und gibt es 
aus. Da muss nichts gebremst werden - ist ja keine blinkende LED.

> Ist ja auch richtig. Man liest das gesamte Port (PORTC)ein.

Immer noch nicht. Man liest PINC, denn nur da finden sich die Zustände 
der Inputports.

michael_ schrieb:
> Dann zeig mal den gesamten Ausdruck.
> Durch Komma getrennt muß dahinter noch das konkrete Pin kommen.
>    PINC,PC3      ; das 3. Beinchen am Port-C
>
> Geht aber nicht mit dem IN-Befehl.

Siehe bitte endlich ein, das du die Aufgabenstellung nicht verstanden 
hast. Bitweises einlesen ist hier kontraproduktiv.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Lothar M. schrieb:
> Marc V. schrieb:
>> aber mit Dezimalpunkt
> Du hast noch eine Chance. Als Tipp: das Bit 7 ist ein Eingang... ?

 Ja, aber nur beim TO, normalerweise ergeben 7 Segmente und DP genau
 8 bit und so wird es auch verbunden, angefangen bei bit0 und Segment a.

von Wolfgang (Gast)


Lesenswert?

Matthias S. schrieb:
> Es gibt keinen Compiler bei Assembler.

Irgendwer wird aus dem ASCII-Text schon die passenden Befehle für den µC 
generieren. Von mir aus nenne das Ding Cross-Assembler.

von Wolfgang (Gast)


Lesenswert?

Matthias S. schrieb:
> 1. Lese den Wert von PORTC ein und maskiere ihn auf sinnvolle Werte, um
> Überschreitungen von Werten zu vermeiden.

Mit maskieren alleine wird das wohl nichts, wenn man hinterher für die 
Ausgabe nur den Zahlenraum von 0 bis 9 zur Verfügung hat.

von michael_ (Gast)


Lesenswert?

Lothar M. schrieb:
> michael_ schrieb:
>> Geht aber nicht mit dem IN-Befehl.
> Nein, nur mit bestimmten In Registern. Und man liest damit aber kein Bit
> ein, sondern man fragt es mit einem bedingten Sprungbefehl als SBIS oder
> SBIC ab.

Uff, fast hast du es richtig geschnallt.
Mit dem IN-Befehl kann man nur auf das Daten-Register zugreifen.
Dann gibt es noch das Direction-Register.
Und dann gibt es das PIN-Register.
Da geht dann das SBIC/SPIS und weitere.

Das sind drei verschiedene Sachen.

Matthias S. schrieb:
> Marc V. schrieb:
>> Und hier ein Beispiel:
>
> Da sieht doch cool aus. Ich hätte vermutlich, angeregt durch den
> 'Hexzahl auf 7-Segment' Thread  einfach auf 0x0F maskiert und die 16
> Zustände in die Tabelle geschrieben. Aber das ist nur ein Nerd-Spässchen
> :-P

Nur solange, wie nur ein 7-Segment.

Matthias S. schrieb:
> michael_ schrieb:
>> Mindestens fehlt eine Zeitschleife ...
>
> Da fehlt überhaupt keine Zeitschleife. Der MC liest das ein und gibt es
> aus. Da muss nichts gebremst werden - ist ja keine blinkende LED.

Naja, es ging um "Hello Welt".

Matthias S. schrieb:
> michael_ schrieb:
>> Dann zeig mal den gesamten Ausdruck.
>> Durch Komma getrennt muß dahinter noch das konkrete Pin kommen.
>>    PINC,PC3      ; das 3. Beinchen am Port-C
>>
>> Geht aber nicht mit dem IN-Befehl.
>
> Siehe bitte endlich ein, das du die Aufgabenstellung nicht verstanden
> hast. Bitweises einlesen ist hier kontraproduktiv.

Uff!
Darum geht es ja hier, bitweises einlesen geht nicht!

Matthias S. schrieb:
>> Ist ja auch richtig. Man liest das gesamte Port (PORTC)ein.
>
> Immer noch nicht. Man liest PINC, denn nur da finden sich die Zustände
> der Inputports.

Vielleicht geht das in irgend einer Hochsprache, aber in Assembler geht 
das nicht.
Da geht nur PORTC.
Hoffentlich ist das Elend bald zu Ende.

von Mario M. (thelonging)


Lesenswert?

Ein Autofahrer hört aus'm Radio: "Ein Geisterfahrer auf der A7!"
Sagt der Fahrer: "Was? Einer? Hunderte!"

von Martin V. (oldmax)


Lesenswert?

Hi Stefan
Wenn du diesen Thread noch folgst, vergißdas, was dir die 
Pseudo-Experten sagen wollen. Kurz, warum du keine Ausgabe bei denen 
Programm bekommst, legt daran, das du diesen Befehl nicht erreichst. 
Warum habe du bereits geschrieben. Das Lesen der Eingänge wäre dann dein 
zweiter Fehler gewesen, auch das ist bereits schon erwähnt worden, 
allerdings vielleicht ein wenig verkompliziert. Also,einen Port einlesen 
erfolgt nicht mit einem Ladebefehl LDI oder LDS sondern mit IN 
(Register),PINx. Auch was die Beschaltung des Controllers betrifft, bin 
ich mir nicht sicher, ob dir Pullup und pulldown Widerstände bekannt 
sind. Deswegen, wenn du wirklich lernen willst, nimm Kontakt über eine 
PN auf. Das was du hier liest, wird dich nur verwirren, weil selbst die 
fachlichen Antworten schon abgehoben und in Selbstdarstellung gehen. ( 
Wie kann man eben Anfänger schon indirekte Adressierung vorschlagen, wo 
noch nicht einmal eine ganz einfache Aufgabe gelöst werden kann????)
Also, ihr Superhelfer, schaltet Mal eine Stufe runter, wenn ihr das noch 
könnt und lässt eure kleinen Spezialitäten erst Mal ruhen. Irgendwann 
einmal ist das kleine Einmaleins verstanden und der Weg über 
Multiplikation und Division zur höheren Mathematik frei.
Gruß oldmax

von Oliver S. (oliverso)


Lesenswert?

michael_ schrieb:
> Uff, fast hast du es richtig geschnallt.
> Mit dem IN-Befehl kann man nur auf das Daten-Register zugreifen.
> Dann gibt es noch das Direction-Register.
> Und dann gibt es das PIN-Register.
> Da geht dann das SBIC/SPIS und weitere.

Du solltest wirklich mal in die Datenblätter schauen, auch doc856 von 
Atmel ist sehr zu emnpfehlen. Bis dahin halts lieber mit Dieter Nuhr. Du 
weisst schon, "..., Fresse halten".

Oliver

von spess53 (Gast)


Lesenswert?

Hi

>Also,einen Port einlesen
>erfolgt nicht mit einem Ladebefehl LDI oder LDS sondern mit IN
>(Register),PINx.

Selbstverständlich kann man LDS zum Einlesen eines Port verwenden. 
Allerdings  nicht mit der IO-Adresse sondern mit der um 0x20 größeren 
RAM-Adresse. Also

lds r16, PINC+0x20 bewirkt das gleiche wie in r16, PinC

MfG Spess

von Peter D. (peda)


Lesenswert?

spess53 schrieb:
> lds r16, PINC+0x20 bewirkt das gleiche wie in r16, PinC

Je nach AVR-Typ sind nicht mehr alle Ports und viele Hardwareregister 
nicht per IN/OUT erreichbar, d.h. es geht nur LDS/STS.
In C hat man es daher sehr einfach, da kümmert sich der Compiler 
automatisch drum.

von Sebastian S. (amateur)


Lesenswert?

@Marc V
Wenn ich mir den Ausgangscode von Sebastian so ansehe, kann ich mir 
nicht vorstellen, dass er mit Deinem Programm sehr viel anfangen kann.
Könnte gehen, auch wenn mir die Zuordnung zu den einzelnen Segmenten 
unklar ist.

von Oliver S. (oliverso)


Lesenswert?

Peter D. schrieb:
> Je nach AVR-Typ

Hier gehts aber nicht um irgendeinen Typ, sondern um einen ATmega328P.

Oliver

von Karl B. (gustav)


Lesenswert?

Peter D. schrieb:
> Je nach AVR-Typ sind nicht mehr alle Ports und viele Hardwareregister
> nicht per IN/OUT erreichbar, d.h. es geht nur LDS/STS.

Hi,
schau mal unter Stichwort:

Memory mapped

https://de.wikipedia.org/wiki/Memory_Mapped_I/O


ciao
gustav

von spess53 (Gast)


Lesenswert?

Hi

>Hier gehts aber nicht um irgendeinen Typ, sondern um einen ATmega328P.

Auch der ATMega328 hat IO-Register die mit LDS/STS und nicht mit IN/OUT 
angesprochen werden müssen.

MfG Spess

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

michael_ schrieb:
> Uff, fast hast du es richtig geschnallt.
Sei dir vollkommen sicher: mir musst du dahingehend nichts mehr 
beibringen. Ich kenne die AVR-Controller vom ersten Baustein an. Dessen 
Assembler war kommend vom 8049, 8051 und einem unsäglichen Ausflug zu 
den ersten PIC-Controllern ein leuchtender Stern am Himmel.

> Mit dem IN-Befehl kann man nur auf das Daten-Register zugreifen.
Mit dem IN Befehl kann man genauso wie mit dem OUT Befehl auf die 
unteren 64 IO Register zugreifen.

> Dann gibt es noch das Direction-Register.
Auch darauf kann man mit IN und OUT zugreifen.

> Und dann gibt es das PIN-Register.
Auch darauf kann man mit IN und sogar überraschenderweise(!) mit OUT 
zugreifen. Denn wenn eine 1 auf ein Bit eines PIN Registers geschrieben 
wird (z.B. auch mit SBI), dann toggelt per unerwarteter Fernwirkung das 
entsprechende PORT Register Bit und damit der Pegel des Ausgangs (oder 
der Pullup, wenn der Pin mit dem DDR auf Eingang geschaltet ist).

> Da geht dann das SBIC/SPIS und weitere.
Und diese bedingten Sprungbefehle SBIS bzw. SBIC können nur auf 
bestimmte Register (IO Register 0..31) zugreifen. Genauso wie SBI und 
CBI.
Mehr dazu im Datenblatt unter "Register Summary" und der "Data Memory 
Map".

EDIT: hoppla, da hatte ich wohl vergessen, auf "Senden" zu drücken... 
;-)

: Bearbeitet durch Moderator
von Oliver S. (oliverso)


Lesenswert?

Natürlich, aber die Portregister liegen bei dem im durch in/out- 
addressierbaren Bereich. Und darum ginsg ja hier die ganze Zeit.

Oliver

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> die Portregister liegen bei dem im durch in/out- addressierbaren Bereich.
Und noch besser: sie liegen sogar im bitadressierbaren Bereich, auf 
den  mit SBI, CBI, SBIS und SBIC zugegriffen werden kann.

von michael_ (Gast)


Lesenswert?

Lothar M. schrieb:
> michael_ schrieb:
>> Uff, fast hast du es richtig geschnallt.
> Sei dir vollkommen sicher: mir musst du dahingehend nichts mehr
> beibringen. Ich kenne die AVR-Controller vom ersten Baustein an. Dessen
> Assembler war kommend vom 8049, 8051 und einem unsäglichen Ausflug zu
> den ersten PIC-Controllern ein leuchtender Stern am Himmel.

Hier sind wir uns doch einig.
Zuerst der 90S1200 und dann mit dem 90S2313 konnte ich meine 
PWM-Probleme lösen können.
Froh, dass ich mich gegen die unmöglichen PIC entschieden habe.

Sollen doch alle mit dem PINC glücklich werden.
Die Erkenntnis, dass es damit nicht geht, hat mich damals vor 20 Jahren 
Stunden gekostet.
Um 8 Bit von einem I/O Register einzulesen.

spess53 schrieb:
> Selbstverständlich kann man LDS zum Einlesen eines Port verwenden.
> Allerdings  nicht mit der IO-Adresse sondern mit der um 0x20 größeren
> RAM-Adresse. Also
>
> lds r16, PINC+0x20 bewirkt das gleiche wie in r16, PinC
>
> MfG Spess

Vielleicht.
Aber warum? Braucht die 3-fache Zeit.

Bei 8MHz 125ns zu 375ns.

von spess53 (Gast)


Lesenswert?

Hi

Aber warum? Braucht die 3-fache Zeit. Braucht die 3-fache Zeit.

Fast gibt es nicht. Es ist genau die doppelte Zeit. Solltest du 
eigentlich wissen. wenn du das  Instruction Set gelesen hast

MFG Spess

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

michael_ schrieb:
> Sollen doch alle mit dem PINC glücklich werden.
> Die Erkenntnis, dass es damit nicht geht, hat mich damals vor 20 Jahren
> Stunden gekostet.
> Um 8 Bit von einem I/O Register einzulesen.

Ich zitiere jetzt nochmal aus dem Datenblatt des ATMega 48/88/168/328:
" Reading the pin value
Independent of the setting of Data Direction bit DDxn, the port pin can 
be read through the PINxn Register bit."

Das sollte jetzt klar sein. PINC ist die richtige Adresse, um den Wert 
der Port C Pins einzulesen. Ist der Pin als Eingang deklariert, liest 
man damit einen extern angelegten Logiklevel ein.

: Bearbeitet durch User
von oldmax (Gast)


Lesenswert?

Hi
Ich weiß nicht so recht, ob es vielleicht besser wäre, ihr würdet mal 
die Telefonnummern tauschen. Der TO ist schon lange raus und ihr längst 
nicht mehr beim Thema und, sorry, das ich mecker, es ist niemandem eine 
Hilfe, wenn ihr euch hier einen Kleinkrieg leistet, wer in der kürzesten 
Zeit auf welchem Weg eine Peripherie anspricht und was das Datenblatt 
aussagt. Erstellt doch diesbezüglich einen eigenen Thread, anstatt 
Anfänger mit eurem (beachtenswertem) Wissen zu frustrieren, denn die 
verstehen nur noch Bahnhof. Das ist nicht wirklich nett, wenn man bei 
jedem Post mit sicherlich gut gemeinter aber für hier die Antwort völlig 
überzogener tiefgründiger Fachkenntnis reagiert.
Gruß oldmax

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.