Forum: Mikrocontroller und Digitale Elektronik AVR - ATmega168PA - Assembler - Problem beim Wechsel von Ouput zu Input


von Andreas (Gast)


Lesenswert?

Hallo!
Bei meinem aktuellen Projekt kommuniziert der ATmega168PA über EINEN 
digitalen Pin mit einer Gegenstelle.
Konkret wird ein Polling-Signal vom ATmega (Pin ist OUTPUT) gesendet und 
danach antwortet die Gegenstellen mit den Daten (Pin ist INPUT). Beide 
Kommunikations-Richtungen erfolgen über den selben PIN.


Das Polling-Signal wird bereits richtig erzeugt. Wenn der Pin 
anschließend als Input konfiguriert wird, funktioniert keine weitere 
Ausgabe (Polling-Signal) mehr. Dies lässt sich am Logic Analyzer gut 
beobachten -> nach einem Reset des ATmega kommt das Polling-Signal 
einmal und dann nie mehr.

Hier ein Auszug aus dem Assembler Code (Kommunikation über PB0):
1
LDI R16,0b00100001
2
OUT DDRB,R16        ; define PB 0 and 5 as output
3
4
SBI PORTB,0      ; initalize HIGH
5
6
; generate polling-signal here
7
8
9
LDI R16,0b00100000
10
OUT DDRB,R16      ; define PB 0 as input for Response --> Ab dieser Zeile wird kein Polling-Signal mehr ausgegeben.


Mein erster Verdacht war, dass ich beim Wechseln eine Zwischenschritt 
benötigte wie in der Doku beschrieben:

[Doku]
Switching Between Input and Output
When switching between tri-state ({DDxn, PORTxn} = 0b00) and output high 
({DDxn, PORTxn}
= 0b11), an intermediate state with either pull-up enabled {DDxn, 
PORTxn} = 0b01) or output
low ({DDxn, PORTxn} = 0b10) must occur. Normally, the pull-up enabled 
state is fully acceptable,
as a high-impedance environment will not notice the difference between a 
strong high
driver and a pull-up. If this is not the case, the PUD bit in the MCUCR 
Register can be set to disable
all pull-ups in all ports.
Switching between input with pull-up and output low generates the same 
problem. The user
must use either the tri-state ({DDxn, PORTxn} = 0b00) or the output high 
state ({DDxn, PORTxn}
= 0b11) as an intermediate step.
[/Doku]

Daran scheint es aber nicht zu liegen. Hab die genannten 
Zwischenschritte testweise implementiert gehabt.

Was mache ich hier falsch?

Danke schon vorab!

von c-hater (Gast)


Lesenswert?

Andreas schrieb:

> Was mache ich hier falsch?

Du hast vergessen, zuerst mal ein ein brauchbares Protokoll zu designen 
und statt dessen einfach sinnlos und planlos losprogrammiert.

Ein Eindraht-Bus (auch wenn's nur P2P ist) ist so ziemlich das 
anspruchsvollste für ein Protokolldesign, was man sich vorstellen kann, 
jedenfalls wenn man Wert darauf legt, dass die Kommunikation dauerhaft 
zuverlässig läuft.

Das fängt bereits bei der Wahl geeigneter Hardware an. Zwei (zumindest 
potentiell) gegeneinander arbeitende PushPull-Ausgänge sind absolut 
ungeeignet dafür. Deswegen benutzen Eindraht-Busse praktisch immer 
Konstrukte mit OpenDrain-Ausgängen und Pullup-Widerständen.

von Andreas (Gast)


Lesenswert?

Danke für deine Antwort!
Das Protokoll musste ich nicht designen. Konkret möchte folgendes 
Projekt (PIC) auf einen ATmega realisieren: 
http://www.pieter-jan.com/node/10

Derzeit habe ich das generierte Signal nur mit einem Logic-Analyzer 
geprüft. Mir ist weiterhin unklar, warum das Polling-Signal nur einmal 
ausgegeben wird? Dies scheint ein Problem in der Programmierung zu sein.


Parallel ergibt sich mir aus deiner Antwort eine weitere Frage:
Der PIC hat doch auch nur einen Push-Pull Ausgang? Entgeht mir hier ein 
wichtiges Detail?

von S. Landolt (Gast)


Lesenswert?

Andreas schrieb:
> Dies scheint ein Problem in der Programmierung zu sein.
Scheint mir auch so.
Mit einer eingehenderen Analyse anhand dieses fünf-Zeilen-Schnipsels bin 
aber nicht nur ich, sondern offenbar sogar Fähigere überfordert.

von S. Landolt (Gast)


Lesenswert?

> Derzeit habe ich das generierte Signal nur mit einem
> Logic-Analyzer geprüft. Mir ist weiterhin unklar,
> warum das Polling-Signal nur einmal ausgegeben wird?

" nur "? Könnte es sein, dass der ATmega auf eine Antwort wartet, ein 
Logic-Analyzer diese aber nicht liefert?

von c-hater (Gast)


Lesenswert?

Andreas schrieb:

> Das Protokoll musste ich nicht designen. Konkret möchte folgendes
> Projekt (PIC) auf einen ATmega realisieren:
> http://www.pieter-jan.com/node/10

Da war jemand am Werk, der ungefähr genausowenig Ahnung hat wie du 
selber. Zum Glück waren allerdings wenigstens die Entwickler des N64 
richtige Ingenieure und haben deswegen auch alles richtig gemacht...

> Derzeit habe ich das generierte Signal nur mit einem Logic-Analyzer
> geprüft. Mir ist weiterhin unklar, warum das Polling-Signal nur einmal
> ausgegeben wird? Dies scheint ein Problem in der Programmierung zu sein.

Natürlich. Aber das ist nur ein Folgeproblem...

> Parallel ergibt sich mir aus deiner Antwort eine weitere Frage:
> Der PIC hat doch auch nur einen Push-Pull Ausgang? Entgeht mir hier ein
> wichtiges Detail?

Ja, natürlich. Sowohl beim PIC als auch beim Atmel kann man die Ausgänge 
auch als Open-Drain benutzen und sogar ggf. interne Pullups hinzu 
schalten. Wobei es je nach Länge der Signalleitung u.U. sinnvoller ist, 
nicht die internen Pullupszu benutzen, sondern einen externen, 
wesentlich niederohmigeren.

von Andreas (Gast)


Lesenswert?

Vielen Dank für eure Antworten!

> Mit einer eingehenderen Analyse anhand dieses fünf-Zeilen-Schnipsels bin
> aber nicht nur ich, sondern offenbar sogar Fähigere überfordert.

Ok, hier der gesamte Code:
1
.DEF mp = R16
2
.DEF lifetick = R28
3
.DEF numbZeros = R29
4
.DEF numbOnes = R30
5
.DEF numbCycles = R31
6
7
RJMP main
8
9
main:
10
; ###### send polling signal #######
11
12
  SBI PORTB,0        ; initalize HIGH
13
  LDI  mp,0b00100001
14
  OUT  DDRB,mp        ; define PB 0 as output  
15
  
16
  LDI numbZeros, 7
17
  LDI numbOnes, 2
18
19
  zero:
20
    CBI PORTB,0        ;16,1
21
    LDI numbCycles,15    ;2
22
    
23
    zeroFalseLoop:          ; 14 * 3 ICs + 1 * 2 IC = 44
24
      DEC numbCycles    ;3
25
      BRNE zeroFalseLoop  ;4,(5)
26
27
    NOP        
28
    SBI PORTB,0        ;LAST,1
29
    LDI numbCycles,3    ;2
30
31
    zeroTrueLoop:          ; 2 * 3 ICs + 1 * 2 IC = 8
32
      DEC numbCycles    ;3
33
      BRNE zeroTrueLoop  ;4,(5)
34
35
    NOP
36
    NOP  
37
      
38
    DEC numbZeros      ;2
39
    BRNE zero
40
41
42
43
  one:
44
    LDI numbCycles,5    ;2
45
    CBI PORTB,0
46
47
    oneFalseLoop:          ; 4 * 3 ICs + 1 * 2 IC = 14
48
      DEC numbCycles    ;3
49
      BRNE oneFalseLoop  ;4,(5)
50
      
51
    SBI PORTB,0
52
    LDI numbCycles,14
53
54
    oneTrueLoop:          ; 13 * 3 ICs + 1 * 2 IC = 41
55
      DEC numbCycles    ;3
56
      BRNE oneTrueLoop  ;4,(5)
57
58
    DEC numbOnes
59
    BRNE one
60
61
62
; ########## receive controller signal #########
63
64
  ;CBI PORTB,0
65
  LDI  mp,0b00100000
66
  OUT  DDRB,mp        ; define PB 0 as input and PB 5 as output for LED
67
68
  ;CBI PORTB,0
69
70
; ########## Wait for data - approx. 3 sec #############
71
72
  LDI R18,255
73
  delayOutOuter:
74
    LDI R17,255
75
    delayOuter:
76
      LDI numbCycles,255
77
      delayInner:
78
        DEC numbCycles
79
        BRNE delayInner
80
      DEC R17
81
      BRNE delayOuter
82
    DEC R18
83
    BRNE delayOutOuter
84
85
; ######### lifetick - toggle onboard LED #########
86
87
  SBI PINB, 5


> " nur "? Könnte es sein, dass der ATmega auf eine Antwort wartet, ein
> Logic-Analyzer diese aber nicht liefert?

Das kann ausgeschlossen werden, da am Beginn des Main Programmes immer 
(zyklisch) das Polling Signal gesendet wird.

> Natürlich. Aber das ist nur ein Folgeproblem...
Wie meinst du das? Sobald ich nachfolgenden Zeilen im Code 
auskommentiere, ist am Ausgang wieder das zyklische Polling-Signal 
messbar:

>
1
> LDI R16,0b00100000
2
> OUT DDRB,R16      ; define PB 0 as input for Response --> Ab dieser Zeile wird kein Polling-Signal mehr ausgegeben.
3
>


> Ja, natürlich. Sowohl beim PIC als auch beim Atmel kann man die Ausgänge
> auch als Open-Drain benutzen und sogar ggf. interne Pullups hinzu
> schalten. Wobei es je nach Länge der Signalleitung u.U. sinnvoller ist,
> nicht die internen Pullups zu benutzen, sondern einen externen,
> wesentlich niederohmigeren.

Wenn ich dich richtig verstehe, wäre bei Verwendung von Open-Drain 
(Ausgang) und Pullups (Eingang) (elektrisch) alles sauber?

von S. Landolt (Gast)


Lesenswert?

Andreas schrieb:
> Ok, hier der gesamte Code:

Sicher? Am Ende, also nach "SBI PINB, 5", fällt das Programm doch ins 
Leere.

von Andreas (Gast)


Lesenswert?

Ja, nach SBI PINB, 5 kommt kein Code mehr.
Wieso ins Leere? Danach beginnt die Abarbeitung der Befehle wieder von 
vorne.

von Oliver S. (oliverso)


Lesenswert?

Wenn das Programm danach wieder von vorne beginnen soll, dann solltest 
du das auch hinschreiben.

Zur Zeit ist das Verhalten nach deinem letzten Befehl offiziell 
undefiniert. Im der Praxis gehts zwar tatsächlich irgendwann von vorne 
los, was allerdings zwischenzeitlich passiert, ist offen.

von S. Landolt (Gast)


Lesenswert?

Sind Sie sicher, dass das kein Messfehler ist?
Also wenn ich noch vor dieses "Wait for data - approx. 3 sec" ein "rjmp 
0" setze, dann sehe ich mit meinem alten Analogoszilloskop auf PB0 eine 
hübsche Impulskette.

von Andreas (Gast)


Lesenswert?

Danke für Eure Antworten und Tipps!
Ich habe am Ende des Codes nun noch die Zeile "RJMP main" ergänzt und 
nun funktioniert es!

Komischerweise funktionierte der zyklische Aufruf OHNE "RJMP main" 
solange, bis die Data Direction des Pins (mit DDRB Register) einmal 
geändert wurde.

Danke für Eure Unterstützung!

von S. Landolt (Gast)


Lesenswert?

Bootloader?

von Andreas (Gast)


Lesenswert?

Ja, der ATmega168PA hat einen Bootloader drauf. Macht das einen 
Unterschied?

von S. Landolt (Gast)


Lesenswert?

(Dann hatten Sie uns also doch nicht den gesamten Code gezeigt)

Nach dem "SBI PINB, 5" holpert das Programm weiter, bis es auf den 
Bootloader trifft, und dieser reagiert offenbar verschieden auf die 
unterschiedlichen Port-Konfigurationen.

Ohne Bootloader funktioniert es nämlich ohne das "rjmp main", auch wenn 
es schlechter Programmierstil ist, ich hatte es ausprobiert.

von S. Landolt (Gast)


Lesenswert?

Ergänzung: es muss nicht mal der Bootloader selbst sein, da können ja 
noch irgendwelche Reste im Programmspeicher stehen; im Gegensatz zum 
direkten Brennen, bei welchem zuerst komplett gelöscht wird.

von Axel S. (a-za-z0-9)


Lesenswert?

Andreas schrieb:
> Ja, nach SBI PINB, 5 kommt kein Code mehr. Wieso ins Leere?
> Danach beginnt die Abarbeitung der Befehle wieder von vorne.

<facepalm/> Wie kommst du auf dieses schmale Brett?

Andreas schrieb:
> Ich habe am Ende des Codes nun noch die Zeile "RJMP main" ergänzt und
> nun funktioniert es!

Unglaublich! Jetzt wirst du sicher gleich noch behaupten, daß es nachts
dunkel wird.

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.