Assembler Byte über UART einlesen

JimKnopf

Neues Mitglied
08. Sep. 2013
7
0
0
Sprachen
Hallo zusammen,

vor kurzem bin ich auf die Idee gekommen, mich ein wenig mit Mikrocontrollern zu beschäftigen, d.h. ich bin noch absolut in den Anfängen.

Mein Problem ist es im Moment, ein Byte seriell über den USART einzulesen, das über die USB-Schnittstelle des PC ausgegeben wird.
Das Ganze würde ich gerne in Assembler programmieren.

Hardware :
ATmega 328P (mit 16MHZ)
Schnittstellenbaustein FT232

Der erste Schritt war die Konfiguration des USART ;
Code:
//Einstellen der Baudrate über die Register UBRR0H / UBRR0L
//Formel für Berechnung UBRR = (CPU_Freq/(16*Baud))-1
//Ergibt bei 16000000Hz und 9600Baud = 103,17 bzw 0x0067

//Register mit berechnetem Wert UBRR laden
ldi r16,0x00
sts UBRR0H,r16
ldi r16,0x67
sts UBRR0L,r16

//aktivieren von receiver and transmitter
ldi r16,(1<<RXEN0)|(1<<TXEN0)
sts UCSR0B,r16

//datenformat setzen: 8data, 1stop bit
ldi r16, (1<<USBS0)|(3<<UCSZ00)
sts UCSR0C,r16

Dann sollte erst einmal ein Byte seriell ausgegeben werden :
Code:
//Zeichen Senden
StartZeichenSenden :
ldi r16,UCSR0A	//r16 mit USART Control and Status Register (UCSR0A) laden
andi r16, 0x20  //prüfen, ob Bit 5 – UDRE0 gesetzt ist (1=transmitter buffer is ready for new data)
tst r16
brne transmBuffNotReady //wenn Bit 5 – UDRE0 = false ist, dann kein neues Zeichen senden
ldi r16,0x55
sts UDR0,r16 
transmBuffNotReady :

//Zeitverzögerung
ldi r23,255
Verzoegerung3 :
ldi r24,LOW(0xFF)
ldi r25,HIGH(0xFF)
LoopVerzoegerung3 :
sbiw r25:r24,1
brne LoopVerzoegerung3
sbci r23,1
brne Verzoegerung3

rjmp StartZeichenSenden

Das funktioniert auch soweit, im Terminalprogramm kann die Übertragung des Bytes verfolgt werden.


Leider funktioniert der nächste Schritt, nämlich das Lesen eines Bytes, nicht.
(für den Test ist der Programmteil, der für das Senden zuständig ist, auskommentiert. Die Konfiguration des USART ist noch die Selbe)
Code:
//Zeichen einlesen
UartEinlesenStart :
ldi YH, HIGH(AnfangDatArray)	//Y-Pointer wieder auf den Anfang vom Datenarray setzen
ldi YL, LOW(AnfangDatArray)
//Zeichen Empfangen
ldi r16,UCSR0A	//r16 mit USART Control and Status Register (UCSR0A) laden
andi r16, 0x80  //prüfen, ob Bit 7 – RXC0 (USART Receive Complete) gesetzt ist (1=new data)
tst r16
brne transmBuffNoNewData //wenn Bit 7 – RXC0 gesetzt ist, dann ist ein ungelesenes Zeichen im Buffer
ldi r16,UDR0
st y,r16 
transmBuffNoNewData :

Der Code wird in einer Endlosschleife wiederholt.
Eigentlich möchte ich in jedem Durchlauf prüfen, ob ein Zeichen im Empfangsregister ist und falls ja, das Zeichen auslesen und in den Speicher schreiben.


Wenn ich den Teil des Auslesen des Empfangsspeichers umgehe, dann landet das (testweise "geschickte") Byte auch im Speicher
Code:
//Zeichen einlesen
UartEinlesenStart :
ldi YH, HIGH(AnfangDatArray)	//Y-Pointer wieder auf den Anfang vom Datenarray setzen
ldi YL, LOW(AnfangDatArray)
//Zeichen Empfangen
ldi r16,UCSR0A	//r16 mit USART Control and Status Register (UCSR0A) laden
andi r16, 0x80  //prüfen, ob Bit 7 – RXC0 (USART Receive Complete) gesetzt ist (1=new data)
tst r16
[COLOR="#FF0000"]//[/COLOR]brne transmBuffNoNewData //wenn Bit 7 – RXC0 gesetzt ist, dann ist ein ungelesenes Zeichen im Buffer
ldi r16,[COLOR="#FF0000"]100//UDR0[/COLOR]
st y,r16 
transmBuffNoNewData :

Ich hoffe, es kann mir einer von euch einen Tip geben, was ich falsch mache.

Gruß
JK
 
Hallo JK,

Willkommen im AVR-PRAXiS Forum :)


Ich habe deinen Sourcecode mal überflogen (lässt sich gut lesen!).

Ich denke, hier stimmt der bedingte Sprung nicht:

Code:
andi r16, 0x80  //prüfen, ob Bit 7 – RXC0 (USART Receive Complete) gesetzt ist (1=new data)
tst r16
[COLOR=#b22222]brne transmBuffNoNewData //wenn Bit 7 – RXC0 gesetzt ist, dann ist ein ungelesenes Zeichen im Buffer[/COLOR]
ldi r16,UDR0
st y,r16 
transmBuffNoNewData :

Du müsstest hier bei equal (also Null) springen. Also ...

Code:
breq transmBuffNoNewData //wenn Bit 7 – RXC0 gesetzt ist, dann ist ein ungelesenes Zeichen im Buffer

Probiere dies erst mal aus.

Dirk :ciao:
 
Hallo Dirk,

vielen Dank für das Willkommen und deine Hilfe.

Der Code sieht jetzt also so aus :
Code:
//Zeichen einlesen
UartEinlesenStart :
ldi YH, HIGH(AnfangDatArray)	//Y-Pointer wieder auf den Anfang vom Datenarray setzen
ldi YL, LOW(AnfangDatArray)
//Zeichen Empfangen
ldi r16,UCSR0A	//r16 mit USART Control and Status Register (UCSR0A) laden
andi r16, 0x80  //prüfen, ob Bit 7 – RXC0 (USART Receive Complete) gesetzt ist (1=new data)
tst r16
//brne transmBuffNoNewData //wenn Bit 7 – RXC0 gesetzt ist, dann ist ein ungelesenes Zeichen im Buffer
breq transmBuffNoNewData //wenn Bit 7 – RXC0 gesetzt ist, dann ist ein ungelesenes Zeichen im Buffer
ldi r16,UDR0
st y,r16 
transmBuffNoNewData :

Leider funktioniert das so auch nicht, es werden immer noch keine sinnvollen Werte eingelesen.


Meine Idee war :
-Einlesen des Statusregisters in R16
Code:
andi r16, 0x80  //prüfen, ob Bit 7 – RXC0 (USART Receive Complete) gesetzt ist (1=new data)
Maskieren des Bit 7
Code:
tst r16
Prüfen, ob das Ergebnis 0 ist (also kein Byte empfangen wurde) das Z-Flag wird dann gesetzt
Code:
brne transmBuffNoNewData //wenn Bit 7 – RXC0 gesetzt ist, dann ist ein ungelesenes Zeichen im Buffer
Wenn das Z-Flag gesetzt ist, dann das Auslesen des Empfangsregisters überspringen.

Aber du hast natürlich recht, bei brne passiert ja genau das Gegenteil, es wird verzweigt, wenn Z nicht gesetzt ist.

Trotzdem kommen (scheinbar) immer noch keine sinnvollen Werte im Controller (bzw. im SRAM) an.

Hast du noch andere Ideen ?

Gruß
JK
 
Hallo JK,
Hast du noch andere Ideen ?

ich habe noch einen Fehler entdeckt, du setzt bei der Initialisierung das Bit USBS0. Dadurch stellst du den USART0 auf 2 Stopp Bits ein.

Nach Reset des Mikrocontrollers sind die Register übrigends bereits für 8N1 initialisiert.

Wenns immer noch nicht geht, kann ich später noch einmal genauer drüberschauen, ich habe gerade nicht so die Zeit.

Dirk :ciao:
 
Ach ich habe gerade noch etwas entdeckt:

Code:
ldi r16,UCSR0A    //r16 mit USART Control and Status Register (UCSR0A) laden

richtig wäre

Code:
lds r16,UCSR0A    //r16 mit USART Control and Status Register (UCSR0A) laden

Dirk :ciao:
 
Hallo Dirk,

danke für Deine Tips, ich werde das Programm entsprechend ändern.

(außerdem muss ich mich doch wohl noch mal ganz in Ruhe mit den Assemblerbefehlen beschäftigen, ehrlich gesagt kenne ich (noch) nicht den Unterschied zwischen ldi und lds. :flute:)

Gruß
JK
 
Hallo zusammen,

noch mal kurz zu ldi und lds :
ldi wird eigentlich nur genutzt um eine konstante ins Register zu laden ?
lds lädt ein Byte aus dem SRAM ins Register ?

Eigentlich komisch, dass die Zeile
Code:
ldi r16,UCSR0A	//r16 mit USART Control and Status Register (UCSR0A) laden
Nicht schon bei der Übersetzung einen Fehler verursacht hat.

Gruß
JK
 
Ich habe noch 2 kosmetische Hinweise:
-statt 0x80 kann die Konstante "RXC0" verwendet werden (also "ANDI R16, RXC0")
-das TST ist überflüssig - was machst Du?
Code:
LDI R16, UCSR0A
UCSR0A einlesen, klar
Code:
ANDI R16, 0x80
in R16 alle Bits außer dem MSB wegmaskieren (=0), also 0b10000000 bei Data, 0b00000000 bei no Data.
Code:
TST R16
verANDet R16 mit sich selbst, Z=1 bei no Data, Z=0 bei Data.
Code:
BRNE noData
BRNE springt bei Z=0, also bei Data - darauf hatte Dirk aber schon hingewiesen.

Was ich sagen will ist daß Du hier also ((R16 and 0x80) and (R16 and 0x80)) berechnest, und das Z dann auswertest. Oder anders gesagt: das ANDI manipuliert das Z bereits, den Schritt mit dem TST kannst Du weglassen. Hat aber nichts mit den falsch initialisierten Registern zu tun...

...Eigentlich komisch, dass die Zeile
Code:
ldi r16,UCSR0A	//r16 mit USART Control and Status Register (UCSR0A) laden
Nicht schon bei der Übersetzung einen Fehler verursacht hat...
wiso? UCSR0A ist doch 'ne Konstante, nämlich 0xC0 - hier soll aber nicht 0xC0 geladen werden (LDI), sonder der Wert, der sich hinter der Speicheradresse verbirgt (LDS)
 
Hi Dirk,

dank deiner Hilfe geht's jetzt :

Code:
//Zeichen einlesen
UartEinlesenStart :
ldi YH, HIGH(AnfangDatArray)	//Y-Pointer wieder auf den Anfang vom Datenarray setzen
ldi YL, LOW(AnfangDatArray)
//Zeichen Empfangen
lds r16,UCSR0A	//r16 mit USART Control and Status Register (UCSR0A) laden
andi r16, 0x80  //prüfen, ob Bit 7 – RXC0 (USART Receive Complete) gesetzt ist (1=new data)
tst r16
breq transmBuffNoNewData //wenn Bit 7 – RXC0 gesetzt ist, dann ist ein ungelesenes Zeichen im Buffer
lds r16,UDR0
st y,r16 
transmBuffNoNewData :

Das gesendete Byte landet jetzt im SRAM.

Vielen Dank !

Gruß
JK
 
Hallo JK,
Nicht schon bei der Übersetzung einen Fehler verursacht hat.

es gibt kein Fehler, da du ja so die Adresse des Registers in r16 laden kannst.

So ähnlich, wie du es bei der indirekten Adressierung machst, um dir die Startadresse des Arrays im SRAM zu holen:

Code:
ldi YH, HIGH(AnfangDatArray)    //Y-Pointer wieder auf den Anfang vom Datenarray setzen
ldi YL, LOW(AnfangDatArray)

Dirk :ciao:


EDIT: LotadaC war etwas schneller ;-)

Schön, dass es nun geht.
 
Das mit dem Puffer ist mir auch noch nicht ganz klar. Du speicherst jedes empfangene Byte (indirekt über das Y-Register) nach "AnfangDatArray", oder?
Entweder Du reservierst das Y-Register nur für diesen Zweck (und initialisiert es vorher einmal), oder Du lädst jedesmal die neue Aktuelle Position, speicherst dort, und inkrementierst Y. dann muß das Ziel halt zurückgespeichert werden (also die Adresse). Das inkrementieren des Pointers kann mit "ST Y+, Register" erledigt werden, in jedem Fall ist auf die Obergrenze des Puffers zu prüfen.
 
Hallo zusammen.

Tja, auf der Suche nach dem Fehler ldi<->lds, hätte ich alleine sicherlich noch einige Stunden zugebracht.

Der Compiler ersetzt beim Compilieren UCSR0A gegen einfach gegen 0xC0.
D.h. bei "ldi" lädt man dann die Konstante "0xC0" in das Register und bei "lds" lädt man den Inhalt der Speicherzelle mit der Adresse 0xC0 in das Register ?

@LotadaC
Das der Befehl "andi" alleine schon das Z-Flag verändert ist mir entgangen, deswegen dachte ich, dass man anschließend noch "tst" braucht.

Nochmal Danke für eure Hinweise

Gruß
JK
 
Hallo LotadaC,

Das mit dem Puffer ist mir auch noch nicht ganz klar..........


Ich spiele ein wenig mit LEDs, die über einen Treiber WS2811 angesteuert werden.
Später soll es ein Array geben, in dem für jede LED drei Bytes gespeichert werden. Ein kleines PC-Tool weist dann jeder LED eine Farbe zu und überträgt die entsprechenden Bytes zum AVR.
Der AVR gibt dann die Bytes an die LED-Treiber weiter.

Der Anfang sieht im Moment so aus :
Code:
.equ AnzahlLeds = 0x0100		//Speicherplatz für die Anzahl vorhandener LEDs
.equ AnfangDatArray = 0x0110	//Beginn des Datenarrays, dass die RGB-Daten der LEDs aufnimmt

//PortB, Pin 0 als Ausgang definieren
ldi R16,DDRB	
ori R16,0x01					//PortB0 als Ausgang definieren, Rest unverändert
out DDRB,R16

//Y-Zeiger verwaltet den Zugriff auf das Datenarray
ldi YH, HIGH(AnfangDatArray)
ldi YL, LOW(AnfangDatArray)

//Festlegen der Anzahl der vorhandenen LEDs und im SRAM speichern
ldi r19,1
sts AnzahlLeds, r19

Dann kommt der Mittelteil mit der seriellen Kommunikation mit dem PC (der ja noch nicht ganz fertig ist ;-) )

Und dann kommt der Teil mit der Ausgabe an die LED-Treiber
Code:
//***Anfang Datenausgabe WS2811************************************************

start :
cbi PORTB,0						//PORTB0 aus

ldi r16,8						//Bitzähler auf 8 rücksetzen
lds r17,AnzahlLeds				//Anzahl vorhandener LEDs laden 
ldi r19,3						//3 Bytes / LED

ldi YH, HIGH(AnfangDatArray)	//Y-Pointer wieder auf den Anfang vom Datenarray setzen
ldi YL, LOW(AnfangDatArray)


//Verzögerung (sicherer Reset der WS2811)
//Dauer 
Verzoegerung1 :
ldi r24,LOW(0xFF)
ldi r25,HIGH(0xFF)
LoopVerzoegerung1 :
sbiw r25:r24,1
brne LoopVerzoegerung1

NxtLed :
ldi r19,3						//3 Bytes / LED
NxtByte :
ldi r16,8						//Bitzähler auf 8 rücksetzen
ld r18,y+						//aktuelles Datenbyte ins Register laden

//Anfang Ausgabe RGB
AusgabeRGB :
lsl r18			//(0,125µs)
brcc Ws28111_Low1 //(0,125µs/0,25µs)

Ws28111_High1 :	// (0,6µs high + 0,65µs low)
sbi PORTB,0		//PORTB0 ein (0,125µs)
nop				//(0,125µs) 8MHZ
nop				//(0,125µs) 8MHZ
nop				//(0,125µs) 8MHZ
nop				//(0,125µs) 8MHZ
nop				//(0,125µs) 8MHZ

nop				//(0,125µs) 16MHZ
nop				//(0,125µs) 16MHZ
nop				//(0,125µs) 16MHZ
nop				//(0,125µs) 16MHZ
nop				//(0,125µs) 16MHZ

cbi PORTB,0		//PORTB0 aus (0,125µs)
//nop				//(0,125µs)
//nop				//(0,125µs)
//nop				//(0,125µs)
//nop				//(0,125µs)
rjmp NxtBit

Ws28111_Low1 :	// (0,25µs high + 1,0µs low) 
sbi PORTB,0		//PORTB0 ein (0,125µs)
nop				//(0,125µs) 8MHZ

nop				//(0,125µs) 16MHZ
nop				//(0,125µs) 16MHZ

cbi PORTB,0		//PORTB0 aus (0,125µs)
//nop				//(0,125µs)
//nop				//(0,125µs)
//nop				//(0,125µs)
//nop				//(0,125µs)
//nop				//(0,125µs)
//nop				//(0,125µs)
//nop				//(0,125µs)

NxtBit :
dec r16
cpi r16,0
brne AusgabeRGB

dec r19			//Anzahl Bytes je LED runterzählen
cpi r19,0		//prüfen, ob alle Bytes der aktuellen LED übertragen wurden
brne NxtByte	//falls noch nicht alle Bytes der aktuellen LED übertragen wurden, nächstes Byte übertragen
dec r17
cpi r17,0
brne NxtLed


EndeRGB :
//Ende Ausgabe RGB


Das es natürlich schon fertige Lösungen dafür gibt ist mir schon klar.
Aber mir geht's ja darum ein wenig mit dem AVR zu "spielen".

Gruß
JK
 
Achso, das reinitialisieren des Y-Registers hattest Du nur testweise dort. OK.

Ich hatte übrigens mal ewig nach dem Fehler suchen müssen, als ich auf eines der unteren 32 I/O-Register mit STS zugreifen wollte - aufgrund des remapping landet man da auf den Rechenregistern...
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)