Forum: Compiler & IDEs Merkwürdiges Verhalten von PORTB PINB4


von Frank L. (franklink)


Angehängte Dateien:

Lesenswert?

Hallo Zusammen,
ich benötige ein wenig Hilfestellung bei der im Anhang befindlichen 
Software.

Die Software selber war ursprünglich für einen MEGA32 und einen 4 x 4 x 
4 LED Cube entwickelt. Ich habe die Software für einen ATMEGA8 und einen 
3 x 3 x 3 LED Cube angepaßt.

Grundsätzlich funktioniert die Software bis auf zwei Macken einwandfrei.

Die für mich im Augenblick wichtigste Macke, ist die Tatsache, das PINB4 
(MISO)  nicht dass tut was er soll.

Den Effekt, den ich bei laufenden Würfel festgestellt habe, ist das 
PINB4 nicht schaltet bzw. sich sehr mekrwürdig verhält und damit die 
Spalte die an diesen Port angschlossen ist ausgeschaltet bleibt.

Ich habe das ganze dann mal im AVRStudio simuliert und konnte den Fehler 
nachvollziehen. Nur ist mir leider die Ursache dieses Fehlers nicht 
wirklich klar.

Einen Hardwarefehler kann ich ausschliessen, ich habe einen kleines 
Testprogramm, dass mir alle Zeilen und Spalten durchschaltet. Hier 
funktioniert alles einwandfrei.

Könnte dies mal jemand simulieren und mir noch mit ein paar Ideen unter 
die Arme greifen.

[EDIT] ich habe die Software soweit reduziert, dass die zweite Macke ein 
Überschreiten der Array-Grenze und damit hängen des Controllers nicht 
vorkommen kann[EDIT]

Entwicklungsumgebung:
Eclipse
AVR-Studio 4.16 (zum simulieren)
WinAVR-20090313
ATMEGA8

Gruß
Frank

von Frank L. (franklink)


Lesenswert?

Problem erkannt und beseitigt, zum einen wahr es nicht PINB4 der 
gesponnen hat sondern PINB3.

Für den, den es interessiert.

In der Dokumentation zum ATMEGA8 Seite 58, Abschnitt "Alternate 
Functions of Port B" findet man zu PINB3 die folgende Aussage:
1
PB3 -> MOSI (SPI Bus Master Output/Slave Input), OC2 (Timer/Counter2 
2
       Output Compare Match Output)

Damit war klar, dass durch die Verwendung des ISR(TIMER2_COMP_vect) der 
PINB3 zur Verwendung als Output wegfallen oder ein alternativer Ansatz 
gesucht werden muss.
1
  TCNT2 = 0x00;
2
  TIMSK |= (1 << OCIE2);
3
4
  OCR2 = 15;
5
  TCCR2  =   (0 << WGM21) | (1 << WGM20)  // CTC-Mode
6
           | (1 << COM21) | (0 << COM20)  // Toggle OC2 on Compare Match
7
           | (1 << CS21)  | (1 << CS20);  // Prescaler: 5

Ich habe mich dafür entschieden, einen alternativen Ansatz zu wählen, da 
ich keine Lust habe mmeine Hardware zu ändern.

Ich habe das ganze jetzt mit einem ISR(TIMER0_OVF_vect) gelöst und 
betreibe diesen mit 100Hz.

ATMEGA8 mit einem 4 MHz Quartz unter Verwendung von Timer0 (8 Bit).

ISR - Initialisierung
1
  TCCR0 = ( 1 << CS02 ) | ( 1 << CS00 );  // Teiler 1024
2
  TIMSK = ( 1 << TOIE0 );                 // Timer/Counter0 Overflow Interrupt Enable

ISR - Vector
1
ISR(TIMER0_OVF_vect)
2
{
3
  TCNT0 = 217;  // 4000000 / 1024 / 100Hz = 39,0625 = 39
4
                // 256 - 39 = 217
5
.
6
.
7
.
8
}

Warum nicht gleich so werden einige Fragen, dazu muss ich sagen, dass 
die Software ursprünglich von Christian Moen für einen MEGA16 
geschrieben wurde. Andreas verwendet den TIMER2_COMP_vect um den Würfel 
zu aktualisieren.

Gruß
Frank

von STK500-Besitzer (Gast)


Lesenswert?

>Damit war klar, dass durch die Verwendung des ISR(TIMER2_COMP_vect) der
>PINB3 zur Verwendung als Output wegfallen oder ein alternativer Ansatz
>gesucht werden muss.

Das wäre für nicht klar, da man beim AVR eigentlich immer die alternate 
Portunction explizit ein- und ausschalten kann.

>ISR(TIMER0_OVF_vect)
>{
>  TCNT0 = 217;  // 4000000  1024  100Hz = 39,0625 = 39
>                // 256 - 39 = 217

Na, ob das auch wirklich stimmt?
Möglicherweise sichert der Compiler noch ein paar Register...
Selbstnachladende Timer-Modi sind immer die bessere Wahl, wenn man ein 
genaues Timing einhalten will.

von Frank L. (franklink)


Lesenswert?

Da es nicht wirklich auf die ms ankommt. Braucht es auch nicht so genau 
zu sein.

Mittlerweile habe ich die Framerate sogar auf 1KHz erhöht. Weil ich 
einfach mal sehen wollte was der ATMEGA8 so leistet. Und das Teil rennt 
ohne Ende.

Mittlerweile ist der Speicher mit 98,5% belegt und das Teil rennt jetzt 
seit 48 Stunden ununterbrochen.

Wenn alles  fertig ist, stelle ich das ganze Projekt in die Codesammlung 
und werde einen Artikel dazu verfassen.

Wenn ich es wirklich auf die 100stel Sekunde genau bräuchte, dann 
natürlich CTC oder ähnliches. Mein Problem war wie oben beschreiben, 
dass der PINB3 durch den CTC belegt war! Ich hatte also keine andere 
Wahl wenn ich die Hardware nicht ändern wollte.

Im übrigen, wenn Du mir zeigst, wie man die alternate Portfunction beim 
ATMEGA8 abschaltet, bist Du für dieses Wochenende mein absoluter Held! 
Ich habe es im Handbuch nicht gefunden, was nicht heisst das es nicht 
dort steht...

Gruß
Frank

von Oliver (Gast)


Lesenswert?

>Im übrigen, wenn Du mir zeigst, wie man die alternate Portfunction beim
>ATMEGA8 abschaltet,

Die sind defaultmäßig immer abgeschaltet, aber du schaltest die ja 
selber ein (vermutlich, ohne zu wissen, was du da tust). Mit Interrupts 
o.ä. hat das nichts zu tun.
1
(1 << COM21) | (0 << COM20)

Die Rumschieberei der Nullen trägt hier nichts zur besseren Übersicht 
bei.

Oliver

von Bernd O. (bitshifter)


Lesenswert?

Oliver schrieb:
>
>
1
(1 << COM21) | (0 << COM20)
>
> Die Rumschieberei der Nullen trägt hier nichts zur besseren Übersicht
> bei.
Aber definitiv tut es das. Nimm' beispielsweise ein Register wie "TCCR0" 
mit den Bits "CS02" "CS01" "CS00".

Wenn im Quelltext folgende Passage auftaucht:
1
(1 << CS02) | (0 << CS01) | (1 << CS00)

Dann sehe ich direkt "aha Clock Select steht auf 101". Ein Blick ins 
Datenblatt mit "101" im Hinterkopf und ich sehe "101 => Vorteiler 1024".

Nicht ohne Grund ordnet Atmel in den Datenblättern einzelne Bits, die 
eine Funktionalität definieren in eine Tabelle ein (selbst wenn sie 
registerübergreifend sind wie teilweise die WGMxx-Bits).

Der einzige Grund warum jemand überhaupt
1
REG = (1 << XXYY) | (0 << ABzz) ...
verwendet und nicht gleich
1
REG = 0x23
schreibt liegt darin, dem nachfolgenden Programmierer (und sich selbst 
in 2-3 Monaten ;-) die Arbeit zu erleichtern. Für Codegröße und Laufzeit 
spielt es keine Rolle, da ein Compiler, der tatsächlich anfängt Literale 
zur Laufzeit und nicht zur Compilezeit zu shiften ein ganz andeses 
Problem hat. Code wird nicht geschrieben um dem Compiler die Arbeit zu 
erleichtern, sondern in erster Linie um klar auszudrücken, was man haben 
will.

Ist das gleiche Problem wie bei Klammerung in C. Eigentlich sind die 
meisten Klammern in arithmetischen Ausdrücken redundant, da die Operator 
Precendence in C klar definiert ist. Ich habe beruflich oft mit 
Quelltext von Programmierern zu tun, die relativ frisch aus dem 
Hochschulumfeld kommen und glauben, sie wären besonders klug, wenn sie 
Ausdrücke mit möglichst wenigen Klammern schreiben. Einige davon haben's 
auch tatsächlich drauf und alles funktioniert wie es soll. Das Problem 
kommt spätestens dann, wenn ein Kollege den Quellcode bekommt. Der 
blickt's dann nicht und schwupps ist ein Bug drin ("aber ich habe doch 
nur einen weiteren Summanden hinzugefügt..."). Mittlerweile kommt mir 
kein Code mehr durch's Review, der mehr als zwei unterschiedliche 
Operatoren in einem Ausdruck hat und keine Klammern verwendet - selbst 
wenn er tut was er soll.


Gruß,
Bernd

von Frank L. (franklink)


Lesenswert?

Hallo,
alles klar, wer die Dokumente richtig liest und nicht nur überfliegt, 
kommt auch auf die richtigen Einstellungen.

Seite 118, Tabelle 43 Compare Output Mode, Non-PWM Mode
1
    COM21    COM20 Description
2
      0        0    Normal port operation, OC2 disconnected.
3
      0        1    Toggle OC2 on Compare Match
4
      1        0    Clear OC2 on Compare Match
5
      1        1    Set OC2 on Compare Match

Also, wenn ich es richtig verstanden habe, ist die korrekt Einstellung:

Das bedeutet, will ich eine Bildwiederholfrequenz von ca. 100 FPS 
erreichen, muss ich die folgende Werte zu Grunde legen.

(ich benutze jetzt bewusst die von Oliver angemäkelte schreibweise).

Brechnung:
1
     Quartz / Vorteil / OCR2 / Anzahl Layer = FrameRate
2
3
     4.000.000 HZ / 256 / 52 sind ca. 300 Hz
4
5
     3 Ebenen müssen aktualisiert werden
6
7
     4.000.000 / 256 / 52 / 3 sind ca. 100 FPS

Code:
1
TCNT2 = 0x00;
2
  TIMSK |= (1 << OCIE2);
3
4
  OCR2 = 52;
5
  TCCR2  =   ( 0 << WGM21 ) | ( 1 << WGM20 )  // CTC-Mode
6
           | ( 0 << COM21 ) | ( 0 << COM20 )  // Normal port operation, OC2 disconnected
7
           | ( 1 <<  CS22 ) | ( 1 <<  CS21 ) 
8
           | ( 0 <<  CS20 );                 // Prescaler: 256


Da das für meinen Anwendungsfall Näherungswerte vollkommen ausreichen, 
sind, sind die Wert hinreichend genau.

Danke für die Hinweise und die großen Zaunpfähle :-))) haben echt 
geholfen.

Gruß
Frank

P.S. ich hoffe ich hab mich nicht verrechnet.
P.P.S @STK500-Besitzer: für heute bist Du mein Held...

von Oliver (Gast)


Lesenswert?

Das mit den Nullen ist Geschmacksache.

Wenn im Quelltext folgende Passage auftaucht:
1
(1 << CS02) | (0 << CS01) | (1 << CS00)

Dann sehe ich direkt "aha Clock Select steht auf 111", denn warum sonst 
sollte da jemand an CS01 rumfummeln?

Oliver

von Justus S. (jussa)


Lesenswert?

Bernd O. schrieb:
> Oliver schrieb:

> Aber definitiv tut es das. Nimm' beispielsweise ein Register wie "TCCR0"
> mit den Bits "CS02" "CS01" "CS00".
>
> Wenn im Quelltext folgende Passage auftaucht:
>
1
(1 << CS02) | (0 << CS01) | (1 << CS00)
>
> Dann sehe ich direkt "aha Clock Select steht auf 101". Ein Blick ins
> Datenblatt mit "101" im Hinterkopf und ich sehe "101 => Vorteiler 1024".

und was ist, wenn CS01 vorher auf 1 stand?

von Simon K. (simon) Benutzerseite


Lesenswert?

Dann ist es jetzt auf 0. Vorausgesetzt man benutzt den 
Zuweisungsoperator.

von Bernd O. (bitshifter)


Lesenswert?

Justus Skorps schrieb:
> Bernd O. schrieb:
>> Oliver schrieb:
>
>> Aber definitiv tut es das. Nimm' beispielsweise ein Register wie "TCCR0"
>> mit den Bits "CS02" "CS01" "CS00".
>>
>> Wenn im Quelltext folgende Passage auftaucht:
>>
1
(1 << CS02) | (0 << CS01) | (1 << CS00)
>>
>> Dann sehe ich direkt "aha Clock Select steht auf 101". Ein Blick ins
>> Datenblatt mit "101" im Hinterkopf und ich sehe "101 => Vorteiler 1024".
>
> und was ist, wenn CS01 vorher auf 1 stand?
Was soll dann sein?

Dann wird es von:
1
REG = (1 << CS02) | (0 << CS01) | (1 << CS00)
genauso auf 0 gesetzt wie von:
1
REG = (1 << CS02) | (1 << CS00)

Im ersten Fall eben offensichtlich und im zweiten Fall implizit (und 
eventuell vom Programmierer nicht beabsichtigt - zumindest hat der 
Programmierer nicht dokumentiert, dass er das entsprechende Bit löschen 
wollte).

Bei "|=" sind Nullshifts dementsprechend zu unterlassen, da man beim 
überfliegen des Codes geneigt ist, davon auszugehen, dass das erwähnte 
Bit gelöscht würde.

Zusammenfassend ist es also so:
Bei "=" werden bei fehlendem "Nullshift" Bits implizit gelöscht, die 
nicht im Befehl stehen.
Bei "!=" wird bei vorhandenem "Nullshift" suggeriert, dass mit dem 
Befehl Bits gelöscht werden, die nicht gelöscht werden.

Beides also schlecht. Zwar nicht falsch, aber fehlerträchtig.

Gegen Programmierfehler hilft ohnehin weder Nullen zu shiften noch sie 
nicht zu shiften. Wenn man sich aber an die Regel hält, bei "=" alle 
Bits aufzuführen und bei "|=" nur die zu setzenden, senkt man die 
Fehlerwahrscheinlichkeit.

Gruß,
Bernd

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.