Forum: Mikrocontroller und Digitale Elektronik Lerne Assembler - brauche eure Hilfe


von Johannes N. (strangeman)


Lesenswert?

Hallo Forum,

ich lerne gerade Assembler für den AVR und komme gewissermaßen nicht 
weiter. Ich versuche einen SPI-Slave zu bauen, der von 12 
angeschlossenen ADC's Daten ausliest und diese per SPI an den Master 
übertragen kann. Da das ganze verdammt schnell gehen muss, habe ich mich 
entschieden jetzt endlich mal assembler zu lernen. Das "Rundumzeugs" ist 
kopiert, damit ich da nix falsch mache. Der Rest ist mehr oder weniger 
mein Verschulden, soll heißen: Bitte schaut mal drüber, ich schaffe es 
nämlich gerade nicht mehr, meinen Fehler zu finden.

Erstmal der Code:
1
 .NOLIST                   
2
 .INCLUDE <m48def.inc>       
3
 .LIST                      
4
5
 
6
 .DSEG
7
; ------------------------------
8
; Reserv data
9
 Data: .Byte 144
10
11
12
13
 .CSEG                      ; Flash
14
15
16
#define  rHilf r16
17
#define rChannel r17
18
#define rChannelByte r18
19
#define rBit1 r19
20
#define rBit2 r20
21
22
23
#define CHIPSEL_HIGH sbi PORTC, 0
24
#define CHIPSEL_LOW  cbi PORTC, 0
25
#define DOUT_HIGH    sbi PORTC, 1
26
#define DOUT_LOW     cbi PORTC, 1
27
#define CLOCK_HIGH   sbi PORTC, 2
28
#define CLOCK_LOW    cbi PORTC, 2
29
30
 ;------------------------------------------------------
31
 ;     Start Adresse 0000
32
 ;------------------------------------------------------
33
 RESET:
34
     rjmp INIT           ; jump to "INIT"
35
                         
36
   
37
 ;------------------------------------------------------
38
 ;     ISR VECTORS
39
 ;------------------------------------------------------
40
41
42
43
 .ORG INT_VECTORS_SIZE    
44
 INIT:  
45
 ;------------------------------------------------------
46
 ;     INITIALIZE
47
 ;------------------------------------------------------
48
     ldi r24,high(RAMEND)     ;Stack Pointer setzen 
49
     out SPH,r24              ; "RAMEND" ist in m48def.inc (s.o.) festgelegt
50
     ldi r24,low(RAMEND)      ; 
51
     out SPL,r24              ;
52
53
 ;------------------------------------------------------
54
 ;   eigene Initialisierungen
55
 ;------------------------------------------------------
56
 ;.EQU 
57
 ;....
58
 ;....
59
 ; set DDR
60
 ;-----------------------------------
61
   ; PortB
62
   ldi rHilf,0b00010000  ; only PB4 for MISO
63
  out DDRB,rHilf
64
  ; PortC
65
  ldi rHilf,0b00000111  ; only SCK, DIN, CS
66
  out DDRC,rHilf
67
  ; PortD
68
  ldi rHilf,0b00000010  ; nope
69
  out DDRD,rHilf
70
71
  ; Enable SPI
72
  ldi rHilf,(1<<SPE)
73
  out SPCR,rHilf
74
 
75
76
77
78
 ;------------------------------------------------------
79
 ;   MAIN LOOP
80
 ;------------------------------------------------------
81
 Main: 
82
  ; create some dummy data to transmit
83
84
  ldi ZL, LOW(Data)
85
  ldi ZH, HIGH(Data)
86
  ldi rHilf,123
87
  st Z, rHilf
88
89
  ; point to first byte of "Data"
90
   ldi ZL, LOW(Data)
91
  ldi ZH, HIGH(Data)
92
  ; load the byte
93
  ld rHilf, Z+
94
  ; put into buffer
95
  out SPDR, rHilf
96
97
 SPI_SlaveReceive:
98
  ; Wait for reception complete
99
  in rHilf, SPSR
100
  sbrs rHilf, SPIF ; if complete...
101
  rjmp SPI_SlaveReceive ; (else retry)
102
103
  ; ...load the next byte
104
  ld rHilf, Z+
105
  ; put into buffer
106
  out SPDR, rHilf
107
  
108
  ; if CS is high again, stop filling the buffer ...
109
  sbis PINB, 2
110
  rjmp SPI_SlaveReceive ; else put next byte into the buffer
111
112
    rjmp Main ; ... and go back to wait for next transmission
113
114
  ; da kommt noch der Code zum auslesen eines Sensorwertes, aber der ist erstmal wurscht weil bisher ungenutzt.

Soweit sogut. Was ich da denke zu tun:
Initialisieren der SPI-Schnittstelle als Slave. Erzeugen eines 
Dummywertes (123), diesen ablegen an erster Stelle von "Data". Das erste 
byte von "Data" in den Puffer legen und warten, dass es übertragen wird. 
Dann solange die nächsten Bytes nachlegen, bis CS wieder high wird. Wird 
CS high, zurückspringen zu Main.

Was nicht geht: Naja, es wird nix übertragen. Und ich hab keine Ahnung 
wieso...

Schaut mal drüber, wenn ihr Lust und Zeit habt, mir würde es wahnsinnig 
helfen! Danke,
Johannes

von Matthias L. (Gast)


Lesenswert?

Du willst also von abgesetzten ADCs per SPI die Daten an deinen AVR 
holen?

Dann müsstest du den Atmel aber bei SPI im MasterMode betreiben. Denn er 
ist ja der Master, der die SPI-Slaves (ADCs) anspricht...

von SPI-Fan (Gast)


Lesenswert?

Entweder habe ich was falsch verstanden oder Du hast einen
Denkfehler. Ein Slave kann NIEMALS eine Aktion ausführen,
das ist Sache des Masters!
Und ADC sind immer Slaves!

Ich würde das so lösen:
Den µC als MASTER definieren, jedem der ADCs eine Chip-Select
Leitung verpassen.
Bei dem ADC den ich auslesen will die CS-Leitung auf L, damit
ist der ADC angesprochen, und die Daten per SPI abholen/reinschreiben.
Wenn fertig: CS-Leitung wieder auf H, und
den nächsten ADC ansprechen.

Dann kann man auch die SPI-Schnittstelle des ATMEGA verwenden.
Man spart sich einigees an Code, wenn man die HW-SPI-Schnittstelle
einsetzt. (Datenblatt)

von Johannes N. (strangeman)


Lesenswert?

Sorry, habe mich unklar ausgedrück. Ich habe 12 ADCs dranhängen, die ich 
auslesen möchte. Da ich alle Werte möglichst exakt parallel brauche, 
habe ich das SPI zu den ADCs per Software gelöst. Dabei teilen sich alle 
ADCs MOSI, SCK und CS. Die MISO-Leitungen sind von jedem ADC einzeln an 
Portpins geführt. Somit kann ich alle 12 parallel auslesen. Wie gesagt, 
das geht über Software-SPI.
Das ist aber momentan noch gar nicht das Problem.

Die Daten sollen dann nämlich von einem anderen Board (STM32 mit Midibox 
firmware drauf) ausgelesen werden. Dazu muss mein Atmega48 im 
Slave-Modus sein. Wenn ich 96 mal 12 bit übertragen will, und das auch 
noch mit 1000Hz, dann brauche ich das Hardware-SPI des AVR zur 
Kommunikation mit dem STM32-Board. Und das versuche ich gerade zum 
laufen zu bekommen.

Dazu generiere ich einfach irgendwelche Daten, die ich mir dann vom 
STM32 abholen lassen will. Nur genau das geht noch nicht.

Lassen wir die ADCs erstmal außen vor: Ich bekomme einfach diese 
Testdaten (123) nicht vom Slave (dieser AVR) zum Master (STM32).


Johannes

PS: Damit es nicht zu Verwirrungen führt: Ich initialisiere teile von 
PORTC als Ausgang. Im Kommentar steht was von SCK usw. Das sind die 
Leitungen an denen die ADC's hängen. Die sind momentan noch irrelevant.

von Steffen H. (avrsteffen)


Angehängte Dateien:

Lesenswert?

Schau mal hier:
1
   ; PortB
2
   ldi rHilf,0b00010000  ; only PB4 for MISO
3
  out DDRB,rHilf
Du hast den SS-Pin auf Ausgang gesetzt! Damit fungiert dein HW-SPI als 
Master!

von Matthias L. (Gast)


Lesenswert?

>Die Daten sollen dann nämlich von einem anderen Board (STM32 mit Midibox
>firmware drauf) ausgelesen werden. Dazu muss mein Atmega48 im
>Slave-Modus sein. Wenn ich 96 mal 12 bit übertragen will, und das auch
>noch mit 1000Hz, dann brauche ich das Hardware-SPI des AVR zur
>Kommunikation mit dem STM32-Board. Und das versuche ich gerade zum
>laufen zu bekommen.

Das ist doch Unsinn.

Wozu willst du die ADCs per SPI durch einen Atmel auslesen, um diese 
Daten dann per SPI an einen anderen µC weiterzuleiten?

Warum liest der andere µC die Daten nicht direkt von den ADC per SPI 
aus?

von Johannes N. (strangeman)


Lesenswert?

Steffen H. schrieb:
> Schau mal hier:   ; PortB
>    ldi rHilf,0b00010000  ; only PB4 for MISO
>   out DDRB,rHilf
> Du hast den SS-Pin auf Ausgang gesetzt! Damit fungiert dein HW-SPI als
> Master!

Hä? Steh ich auf dem Schlach? Ich setzte nur PB4 als Ausgang. Und das 
ist am ATMega48 ein MISO Pin. PB2 wäre SS, aber der ist doch als Input 
eingestellt.

Matthias Lipinsky schrieb:
> Das ist doch Unsinn.
>
> Wozu willst du die ADCs per SPI durch einen Atmel auslesen, um diese
> Daten dann per SPI an einen anderen µC weiterzuleiten?
>
> Warum liest der andere µC die Daten nicht direkt von den ADC per SPI
> aus?

Nunja, ich verwende hier zwei Boards, die als solche schon vorhanden 
waren und nun einem neuen Zweck dienen sollen. Würde ich es so machen, 
wie du vorschlägst, so müsste ich die SMD-ADCs wieder auslöten und ein 
neues Board ätzen usw. usw. Daher wollte ich es erstmal so versuchen. 
Ist im Endeffekt auch nicht mehr Aufwand.

Danke schonmal,
Johannes

von Johannes N. (strangeman)


Lesenswert?

Johannes Neumann schrieb:
> Steffen H. schrieb:
>> Schau mal hier:   ; PortB
>>    ldi rHilf,0b00010000  ; only PB4 for MISO
>>   out DDRB,rHilf
>> Du hast den SS-Pin auf Ausgang gesetzt! Damit fungiert dein HW-SPI als
>> Master!
>
> Hä? Steh ich auf dem Schlach? Ich setzte nur PB4 als Ausgang. Und das
> ist am ATMega48 ein MISO Pin. PB2 wäre SS, aber der ist doch als Input
> eingestellt.

Ganz abgesehen davon sollte der PIN laut Datenblatt automatisch ein 
Input sein. Ich habe lediglich Einfluss auf das Verhalten von MISO.

Ich habe inzwischen noch etwas herumprobiert. Clock und Phase sollte 
beides 1 sein, das hatte ich bisher in der Initialisierung vergessen. 
Auch dachte ich, kann es nicht schaden, SPDR nach jedem 
gesendeten/empfangenen Byte auch mal zu lesen: Habe im Datenblatt keinen 
expliziten Hinweis darauf gefunden, ob man das machen muss, aber es kann 
ja nicht schaden.
Ich habe außerdem alle Kabel überprüft.
Ich habe außerdem herausgefunden, dass die Übertragung geht, wenn ich 
den Master SS invertiert ausgeben lasse, sprich: SS high schalten, 
übertragen, SS Low schalten. Das sollte ja eigentlich andersherum 
sein...

Johannes

von der alte Hanns (Gast)


Lesenswert?

Da ohne Interrupt gearbeitet wird, muss SPDR gelesen werden, sonst 
wird SPIF nicht zurückgesetzt, die Übertragung bliebe also nach dem 
ersten Byte stehen; siehe Datenblatt unter SPSR.SPIF .

von der alte Hanns (Gast)


Lesenswert?

Sorry, das war schlecht ausgedrückt - der Mega48 liefe unaufhaltsam 
durch die Schleife, da SPIF ständig gesetzt wäre.

von der alte Hanns (Gast)


Lesenswert?

Und noch ein Tipp: zumindest in der Testphase empfiehlt sich die Abfrage 
von SPSR.WCOL mit entsprechender Kontrollausgabe.

von Johannes N. (strangeman)


Lesenswert?

der alte Hanns schrieb:
> Da ohne Interrupt gearbeitet wird, muss SPDR gelesen werden, sonst
> wird SPIF nicht zurückgesetzt, die Übertragung bliebe also nach dem
> ersten Byte stehen; siehe Datenblatt unter SPSR.SPIF .

Ok, das hatte ich irgendwie nicht gesehen. Danke

So, ich habe jetzt folgendes am Laufen: SPI geht mit richtiger SS 
Polarität. Ich generiere für die ersten 6 Bytes die Daten 1,2,3,4,5,6 
und lasse sie mir am Master auf einem LCD darstellen. Beim ersten Mal 
lesen bekomme ich: 255,1,2,3,4,5 danach immer 0,1,2,3,4,5. Da erscheint 
irgendwie ein Murks-Byte in der Übertragung, nur woher? Meines Erachtens 
sollte es das eigentlich nicht: Ich lade ja das erste Byte (Wert: 1) in 
SPDR und ware danach, bis es übertragen wurde. Erst danach schiebe ich 
die Bytes mit 2,3,4,5,6 raus.

Mein Code sieht jetzt so aus:
1
Main: 
2
  ; create some dummy data to transmit
3
4
  ldi ZL, LOW(Data)
5
  ldi ZH, HIGH(Data)
6
  ldi rHilf,1
7
  st Z+, rHilf
8
  ldi rHilf,2
9
  st Z+, rHilf
10
  ldi rHilf,3
11
  st Z+, rHilf
12
  ldi rHilf,4
13
  st Z+, rHilf
14
  ldi rHilf,5
15
  st Z+, rHilf
16
  ldi rHilf,6
17
  st Z+, rHilf
18
    
19
20
   ; put first byte into send buffer
21
  
22
  ; point to first byte of "Data"
23
   ldi ZL, LOW(Data)
24
  ldi ZH, HIGH(Data)
25
  ; load the byte
26
  ld rHilf, Z+
27
  ; put into buffer
28
  out SPDR, rHilf
29
30
 SPI_SlaveReceive:
31
32
  ; if CS is high (for whatever reason) ...
33
  sbic PINB, 2
34
  rjmp Main ; ... jump back to main
35
36
  ; Wait for reception complete
37
  in rHilf, SPSR
38
  sbrs rHilf, SPIF ; if not complete...
39
  rjmp SPI_SlaveReceive ; ... check again
40
41
42
  ; if complete, load the next byte
43
  ld rHilf, Z+
44
  ; put into buffer
45
  out SPDR, rHilf
46
  ; dummy read data
47
  in rHilf,SPDR
48
  
49
  ; if CS is still low ...
50
  sbis PINB, 2
51
  rjmp SPI_SlaveReceive ; ... put next byte into the buffer
52
53
    rjmp Main ; else go back to readout and wait for next transmission

Danke für eure Hilfe,
Johannes

von spess53 (Gast)


Lesenswert?

Hi

Dein erstes Byte muss schon im SPDR vom Slave liegen bevor der Master 
anfängt zu Lesen. Das erste Byte ist sonst der zufälligen Wert, der 
gerade im SPDR ist.

MfG Spess

von Johannes N. (strangeman)


Lesenswert?

spess53 schrieb:
> Hi
>
> Dein erstes Byte muss schon im SPDR vom Slave liegen bevor der Master
> anfängt zu Lesen. Das erste Byte ist sonst der zufälligen Wert, der
> gerade im SPDR ist.
>
> MfG Spess

Das mach ich doch - denke ich. Siehe hier:
1
 ; put first byte into send buffer
2
  
3
  ; point to first byte of "Data"
4
   ldi ZL, LOW(Data)
5
  ldi ZH, HIGH(Data)
6
  ; load the byte
7
  ld rHilf, Z+
8
  ; put into buffer      => Diese stelle meine ich. Das Byte landet in SPDR bevor der Master zu empfangen veruscht
9
  out SPDR, rHilf
10
11
 SPI_SlaveReceive:
12
13
  ; if CS is high (for whatever reason) ...
14
  sbic PINB, 2
15
  rjmp Main ; ... jump back to main
16
17
  ; Wait for reception complete
18
  in rHilf, SPSR
19
  sbrs rHilf, SPIF ; if not complete...
20
  rjmp SPI_SlaveReceive ; ... check again

Johannes

von der alte Hanns (Gast)


Lesenswert?

Startet der Master vor dem Slave ?

von Johannes N. (strangeman)


Lesenswert?

Der Master braucht mehrere Sekunden zum booten, erst danach startet er 
die Kommuniktaion. Der Slave sollte da deutlich schneller sein. Ich 
hoffe, das war, was du meintest.

von der alte Hanns (Gast)


Lesenswert?

Das Slave-Programm scheint okay, ich würde mir das Master-Programm näher 
anschauen.

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.