Byte seriell ausgeben mit Start- und Stopbit (ASM, Attiny2313)

OP #2320843
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.
#2320903
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
#2320921
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
#2320950
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.
OP #2320966
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.
OP #2321056
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:
Gast #2321078
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
#2321086
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.
#2321088
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)
#2321096
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:
OP #2321366
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
Gast #2321543
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

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 15 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.