Forum: Mikrocontroller und Digitale Elektronik Byte seriell ausgeben mit Start- und Stopbit (ASM, Attiny2313)


von Niklas B. (niklas90)


Lesenswert?

Hallo,

ich möchte die Bits eines 8-Bit Registers nacheinander über den PORTD 
ausgeben, allerdings soll vorher ein Startbit kommen, was immer 1 ist 
und nach dem Byte ein Stopbit, was immer 0 ist (für Synchronisation).

Hier mein Code:
1
.include "tn2313def.inc"
2
3
.def temp = R16
4
.def mask = R17
5
.def input = R18
6
.def output = R19
7
.def timer = R20
8
9
.org 0x0000 RJMP reset
10
.org 0x0006 RJMP tim0_ovf
11
12
reset:
13
LDI temp, (1<<CS02)|(1<<CS00)  ;Prescale 1024
14
OUT TCCR0B, temp
15
LDI temp, (1<<TOIE0)      ;Interrupt bei Overflow
16
OUT TIMSK, temp
17
SEI                ;Interrupts erlauben
18
LDI temp, 0            ;PortB als Input
19
OUT DDRB, temp
20
LDI temp, 255          ;Interne Pull-ups anziehen
21
OUT PORTB, temp
22
LDI temp, 127          ;PortD als Output
23
OUT DDRD, temp
24
LDI mask, 1            ;Maske auf 00000001 bringen
25
CLR timer
26
CLR output
27
28
loop:
29
;IN input, PINB
30
LDI input, 1
31
OUT PORTD, output
32
RJMP loop
33
34
tim0_ovf:
35
CBI PORTD, 0          ;"Impuls" ,das nullte Bit 0 setzen
36
INC timer
37
CPI timer, 1
38
BRNE normal            ;wenn Timer = 1
39
LDI output, 1          ;Startbit immer 1
40
LDI mask, 1
41
RETI
42
normal:
43
MOV temp, mask
44
AND temp, input          ;(Maske AND Input) in temp
45
CPI temp, 1
46
LDI output, 0
47
BRCS sendNull          ;wenn (Maske AND Input) > 0 dann 1 senden, sonst 0
48
LDI output, 1
49
sendNull:
50
ROL mask            ;Maske vorbereiten zur Abfrage des nächsten Bits
51
BRCC resetNotTimer
52
CLR timer            ;Wenn alle Bits des Bytes durch sind, dann Timer = 0
53
LDI output, 0          ;Stopbit immer 0
54
resetNotTimer:
55
RETI



Zu Testzwecken - und weil ich zu faul war, Jumper zu bauen :) - 
definiere ich "input" direkt in der der Loop.

Wenn ich 255 oder 0 als Input nehme, dann scheint es auch zu 
funktionieren, aber bei allen anderen Werten (als Bsp 1) macht der nur 
Murks, die LED, die ich an PD0 angeschlossen habe, blinkt nur apathisch 
:(

Was mache ich falsch? Ich hoffe, jemand kann mir helfen.

von Karl H. (kbuchegg)


Lesenswert?

CPI temp, 1
LDI output, 0
BRCS sendNull


Wieso BRCS? Du willst doch hier einen Vergleich auf Gleichheit. Das wird 
vom Zero-Flag angezeigt.


Ansonsten ist das schon sehr konfus. In der ISR das auszugebende Bit zu 
bestimmen, welches dann in der Hauptschleife tatsächlich ausgegeben 
wird. Und die Sache mit der Mask ist auch ein wenig konfus. Wenn du 
sowieso schon ein Hilfsregister brauchst, warum lädst du dir nicht dort 
den auszugebenden Wert hinein und shiftest den bei jedem ISR Aufruf eine 
Stelle nach Rechts. Dazu noch ein Counter, der die ISR Aufrufe mitzählt.

Counter = 0 ?  1 Bit ausgeben, fertig

Counter = 9 ?  0 Bit ausgeben, fertig

ansonsten      Das jeweils 0. Bit ausgeben und 1 mal rechts shiften

von Julian R. (tuefftler)


Lesenswert?

>ich möchte die Bits eines 8-Bit Registers nacheinander über den PORTD
>ausgeben, allerdings soll vorher ein Startbit kommen, was immer 1 ist
>und nach dem Byte ein Stopbit, was immer 0 ist (für Synchronisation).

Das klingt doch sehr nach UART(Bis auf: start:0 stop:1), wofür brauchst 
du das eigentlich?

julian

von spess53 (Gast)


Lesenswert?


von (prx) A. K. (prx)


Lesenswert?

Niklas Beuster schrieb:

> ich möchte die Bits eines 8-Bit Registers nacheinander über den PORTD
> ausgeben, allerdings soll vorher ein Startbit kommen, was immer 1 ist
> und nach dem Byte ein Stopbit, was immer 0 ist (für Synchronisation).

UART verwenden, Inverter dahinter und Daten vor dem Schreiben ins 
Transferregister invertieren.

Software-UART auf dem fast einzigen Tiny mit Hardware-UART ist schon ein 
wenig pervers. Es sei denn die Hardware-UART ist anderweitig belegt.

von Niklas B. (niklas90)


Lesenswert?

Hm, danke erst mal.

vom UART hör ich zum ersten mal, habe gerade die Funktionsweise 
überflogen, dass scheint ja genau das zu sein, was ich machen will^^.

Ansonsten,

> CPI temp, 1
> LDI output, 0
> BRCS sendNull
>
>
> Wieso BRCS? Du willst doch hier einen Vergleich auf Gleichheit. Das wird
> vom Zero-Flag angezeigt.

Ich bin immer davon ausgegangen, dass sowas nicht geht, dass man erst 
eine Rechenoperation á la CPI macht, also CPI temp, 1 heißt ja temp - 1 
und wenn temp = 0 ist, dann gibt's einen Carry, wie soll dass anders 
gehen?

> Und die Sache mit der Mask ist auch ein wenig konfus. Wenn du
> sowieso schon ein Hilfsregister brauchst, warum lädst du dir nicht dort
> den auszugebenden Wert hinein und shiftest den bei jedem ISR Aufruf eine
> Stelle nach Rechts.

Das verstehe ich jetzt gar nicht, ich will doch nur 1 oder 0 an PD0 
ausgeben, wie soll ich dass anders machen als über eine Maske, die sich 
die Bits rauspickt, die ich im Inputregister habe, also genau so, wie 
ich das gemacht habe. Also beispielsweise:

Input:  00000110
Maske: 00000001

mit AND würde das 00000000 werden.

Input:  00000110
Maske: 00000010

mit AND würde das 00000010 werden.

ich frage anschließend halt ab, ob die Verknüpfung 0 oder größer ist.


Danke nochmal, ich werde mir das mit UART in Ruhe durchlesen.

von spess53 (Gast)


Lesenswert?

Hi

Noch ein kleiner Tip: Ohne initialisierten Stack wird das nichts.

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

> also CPI temp, 1 heißt ja temp - 1
> und wenn temp = 0 ist, dann gibt's einen Carry,

Zero!   das Zwischenergebnis ist 0, daher Zero!
Einen Carry gibts bei Über- oder Unterlauf.


Ich empfehle ganz dringend einen Blick ins AVR-Tutorial

von Niklas B. (niklas90)


Lesenswert?

Auch ein Blick dort hinein bestätig, das 0 - 1 nicht 0 ist^^. 
http://www.mikrocontroller.net/articles/AVR-Tutorial:_Arithmetik8#Carry
Bei dieser Operation dürfte das Carryflag 1 werden?!

Geht das hier vielleicht? CPI temp, 0 ? und dann auf Zeroflag 
überprüfen?

Also dann würde:

LDI temp, 0
CPI temp, 0
BREQ null
null:

dasselbe bewirken wie:

LDI temp, 0
CPI temp, 1
BRCS null
null:

von Niklas B. (niklas90)


Lesenswert?

Karl Heinz Buchegger schrieb:
> warum lädst du dir nicht dort
> den auszugebenden Wert hinein und shiftest den bei jedem ISR Aufruf eine
> Stelle nach Rechts.

Ach, jetzt verstehe ich, was du meinst, ok.

von spess53 (Gast)


Lesenswert?

Hi

Ich habe mal, das was Karl Heinz vorgeschlagen hat, für dich umgesetzt:
1
.include "tn2313def.inc"
2
3
.def temp = R16
4
.def mask = R17
5
.def input = R18
6
.def output = R19
7
.def timer = R20
8
9
.org 0x0000 RJMP reset
10
.org 0x0006 RJMP tim0_ovf
11
12
reset:
13
14
     ldi r16,Low(RAMEND)
15
     out SPL,r16
16
17
18
19
     LDI temp, (1<<CS02)|(1<<CS00)  ;Prescale 1024
20
     OUT TCCR0B, temp
21
     LDI temp, (1<<TOIE0)      ;Interrupt bei Overflow
22
     OUT TIMSK, temp
23
     SEI                ;Interrupts erlauben
24
     LDI temp, 0            ;PortB als Input
25
     OUT DDRB, temp
26
     LDI temp, 255          ;Interne Pull-ups anziehen
27
     OUT PORTB, temp
28
     LDI temp, 127          ;PortD als Output
29
     OUT DDRD, temp
30
     LDI mask, 1            ;Maske auf 00000001 bringen
31
     
32
     ldi timer,10           ; Start+8Bit+Stop
33
     ldi output,$AA         ; auszugebendes Byte 
34
   ;  CLR timer
35
   ;  CLR output
36
37
loop:       tst timer
38
            breq loop10
39
40
            RJMP loop
41
42
43
;           neu laden
44
loop10:     ldi timer,10           ; Start+8Bit+Stop
45
            ldi output,$AA         ; auszugebendes Byte 
46
47
48
tim0_ovf:   push r16
49
            in r16,SREG
50
            push r16 
51
52
            dec timer
53
            breq zero_bit         ; wenn Stoppbit
54
            cpi timer,9
55
            breq one_bit          ; wenn Startbit
56
           
57
            lsr output
58
            brcs one_bit
59
           
60
zero_bit:   cbi PortD,0
61
            rjmp tim0_ovf10
62
63
one_bit:    sbi PortD,0
64
65
tim0_ovf10: pop r16
66
            out SREG,r16
67
            pop r16
68
            reti

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

Niklas Beuster schrieb:

>
> Also dann würde:
>
> LDI temp, 0
> CPI temp, 0
> BREQ null
> null:
>
> dasselbe bewirken wie:
>
> LDI temp, 0
> CPI temp, 1
> BRCS null
> null:

Nein.
Bei deinen spezifischen Zahlen vielleicht (hab ich nicht durchdacht), 
aber im allgemeinen nicht.


Zero     Bei Vergleichen auf gleich/ungleich
Carry    Bei Vergleichen auf größer/kleiner


"nicht gleich" ist aber nicht dasselbe wie "kleiner"

-1 ist zwar kleiner als 0, aber +1 ist größer als 0 und trotzdem ist +1 
nicht dasselbe wie 0

Im Tutorial ist im Kapitel über Verzweigungen eine schöne 
Zusammenfassung, welche Branches es gibt und wann sie eingesetzt werden. 
Aufgeschlüsselt je nachdem, ob man es mit vorzeichenlosen oder 
vorzeichenbehafteten Zahlen zu tun hat.

Das Carry Flag ost zwar wichtig, aber nicht alles wird über das Carry 
Flag geregelt. In deinem Fall gibt es nur 2 Möglichkeiten: entweder das 
Teil ist nach der Maskierung 0, oder es ist nicht 0. Daher Branch if 
equal bzw. Branch if not equal.

von Ralf G. (ralg)


Lesenswert?

Niklas Beuster schrieb:
> Also dann würde:
>
> LDI temp, 0
> CPI temp, 0
> BREQ null
> null:
>
> dasselbe bewirken wie:
>
> LDI temp, 0
> CPI temp, 1
> BRCS null
> null:

Ja. Aber dem Programmverlauf entsprechend anpassen!
Frage 1: 'ist temp gleich 0?'
Frage 2: 'tritt ein Überlauf ein?' (das ist hier eigentlich nicht die 
Frage, und verwirrt etwas)

Für die Frage: 'Ist temp gelöscht?' kann man auch 'tst temp' nehmen. 
Würde hier genauso gehen. Führt alles zum gleichen Ergebnis. ('tst' geht 
sogar ab R0)

von Karl H. (kbuchegg)


Lesenswert?

Und nein. Du willst hier nicht

AND temp, input          ;(Maske AND Input) in temp
CPI temp, 1
LDI output, 0
BRCS sendNull          ;wenn (Maske AND Input) > 0 dann 1 senden, sonst 
0


mit 1 vergleichen, weil je nachdem wie deine Maske gerade steht, da eben 
nicht 0 oder 1 rauskommt, sondern 0 oder 1 bzw. 0 oder 2 bzw. 0 oder 4 
bzw. 0 oder 8 etc....
Dein Kommentar ist nämlich irreführend. Da hast nämlich nicht mit 0 
verglichen, sondern mit 1.

Man könnte zb auch ausnutzen, dass der AND das Zero Flag schon ganz von 
alleine setzt und der ldi die Flags überhaupt nicht beeinflusst

  LDI  output, 0
  AND  temp, input
  BREQ sendNull
  LDI  output, 1

sendNull:

: Wiederhergestellt durch User
von (prx) A. K. (prx)


Lesenswert?

Niklas Beuster schrieb:

> vom UART hör ich zum ersten mal, habe gerade die Funktionsweise
> überflogen, dass scheint ja genau das zu sein, was ich machen will^^.

Fast. Da ist das Startbit 0, das Stopbit 1 und die Leitung ist im 
Ruhezustand auf 1.

von Niklas B. (niklas90)


Lesenswert?

Danke an alle!

Ich habe nochmal komplett neu angefangen und nun funktioniert's :)
1
.include "tn2313def.inc"
2
3
.def temp = R16
4
.def output = R17
5
.def timer = R18
6
7
.org 0x0000 RJMP reset
8
.org 0x0006 RJMP tim0_ovf
9
10
reset:
11
ldi temp, low(RAMEND)      ;irgendwas wichtiges mit Stack
12
out SPL, temp
13
ldi temp, (1<<CS02)|(1<<CS00)  ;Prescale 1024
14
out TCCR0B, temp
15
ldi temp, (1<<TOIE0)      ;Interrupt bei Overflow
16
out TIMSK, temp
17
sei                ;Interrupts erlauben
18
ldi temp, 0            ;PortB als Input
19
out DDRB, temp
20
ldi temp, 255          ;interne Pull-ups anziehen
21
out PORTB, temp
22
ldi temp, 127          ;PortD als Output
23
out DDRD, temp
24
ldi timer, 10
25
26
loop:
27
RJMP loop
28
29
tim0_ovf:
30
;CBI PORTD, 0          ;"0-Impuls", für Funkübertragung
31
32
dec timer
33
brne notStopbit
34
ldi timer, 10
35
cbi PORTD, 0          ;Stopbit immer 0
36
reti
37
notStopbit:
38
39
cpi timer, 9
40
brne notStartbit
41
sbi PORTD, 0          ;Startbit immer 1
42
;in output, PINB          ;Wert holen, der gesendet werden soll
43
ldi output, 2
44
reti
45
notStartbit:
46
47
ldi temp, 1            ;sende 1
48
lsr output
49
brcs sendOne
50
ldi temp, 0            ;sende 0
51
sendOne:
52
53
out PORTD, temp
54
reti

von spess53 (Gast)


Lesenswert?

Hi

>Ich habe nochmal komplett neu angefangen und nun funktioniert's :)

Aber nur, weil dein Programm weiter nichts macht. Gewöhne dir, in deinem 
Interesse, an, in Interruptroutinen SREG und alle Register, die nicht 
verändert werden sollen, zu sichern. Sowie dein Programm etwas größer 
wird hat die Nichtbeachtung dieser Regel ganz lustige Effekte (außer für 
dich) zur Folge.

MfG Spess

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.