Forum: Compiler & IDEs _BV() funktioniert nicht richtig?


von Christian W. (clupus)


Angehängte Dateien:

Lesenswert?

Hallo allerseits,

ich hätte da ein kleies Problem, das mir Kopfschmerzen macht.
Kurz zu meiner Ausstattung: Ich verwende SuSE 10.1 mit avr-gcc und
avr-libc & Co. Alles zum Thema AVR habe ich selber übersetzt.

Jetzt hab ich eine kleine Platine, die mir den letzten Nerv raubt. Das
Problem (vgl. Code-Teil im Anhang; main() ruft nur Init_HW() auf):
In Zeile 45-58 sollte der Mega8 eigentlich angewiesen werden, einige
Eingänge mit Pull-Ups zu schalten. OK, theoretisch kein Problem, oder?
Also hab ich oben die Arrays definiert (die dann noch irgendwann in den
PGMSPACE sollen) und unten in einer for-Schleife verwendet (asm
ignorieren!). Leider werden die Pull-Ups nicht aktiviert (Spannung
bleibt bei ca. 0,3V).
Ich hab mir dann den disassembelten Code angeschaut und leider nix
wirkliches gefunden, außer einer Stelle, bei der es um die Sache mit
dem Rotieren (_BV()) geht. Das wird über Additionen gemacht. Hat mich
gewundert, aber ich hab's mal geschluckt. (Ich kann die Ausgabe von
avr-objdump gerne mal posten, wenn wer Interesse dran hat ;-) )

Nachdem ich den Fehler nicht gefunden hab, hab ich die asm-Bereiche
eingefügt, weil ich sehen wollte, was mit diesem "geshifteten"
Register (r18) los ist (Meine Vermutung war hier ein Fehler).
Eigentlich sollte nach der Schleife (deshalb auch die Endlosschleife
zum Verifizieren) die Pins 6 und 7 auf 1 und der Rest auf 0 geschalten
haben. Leider ist nur Pin 1 auf 1 und der Rest auf 0! Das heißt, dass
der Compiler Code erzeugt hat, der eigentlich shiften sollte, es aber
nicht tut (meiner Logik nach müsste er aber).

Vllt. könnt ihr mir helfen.

MfG
Christian

von Christian W. (clupus)


Angehängte Dateien:

Lesenswert?

OK, ich hab mal sicherheitshalber die Ausgebe von obj-dump
mitgeschickt.

Um nict suchen zu müssen: In Zeile 377 steht die Adresse 300, in der
PORTD=0; ausgeführt wird. Die "kritischen" Zeilen sind Adresse 322
bis 328.

MfG
Christian

von A.K. (Gast)


Lesenswert?

Keine Ahnung was dich an 31E-328 stört. Das ist eine völlig korrekte
Umsetzung von 1<<expr. Ein kurzer Blick in die Befehlsreferenz würde
dir auch verraten, dass die von dir vielleicht erwarteten Befehle LSL
und ROL nicht nur funktionell sondern auch tatsächlich identisch mit
ADD und ADC sind.

Allerdings ist _BV keine Rotation, sondern eine Schiebeoperation.

von Christian W. (clupus)


Lesenswert?

OK, dennoch verstehe ich nicht, warum das Ergebnis rauskommt.
De facto kommt nur an Pin 1 eine 1 raus. Wenn es aber geschoben,
rotiert oder was auch immer worden wäre, hätte das nicht sein dürfen.
Und das falsche Register hab ich ja auch nicht verwischt. Das sieht man
im disass. Code.

MfG
Christian

von A.K. (Gast)


Lesenswert?

"if(PORT_Knopf[nr] & N_Knopf[nr]) return 1;"

So wird das nie was.

Deine Vorliebe für type casts kann ich nicht teilen. To cast heisst
wegwerfen, und allzu oft kommt dabei genau das raus: Code zum
wegwerfen. Wären deine Zeiger noch welche, keine Integers, hätte der
Compiler dich zumindest auf einen Fehler hingewiesen.

Wenn du partout die letzten Bytes sparen musst, dann mach das nachdem
alles funktioniert. Nicht vorher.

von A.K. (Gast)


Lesenswert?

Ehrlich gesagt: Um das ganze nun Befehl für Befehl zu untersuchen, ist
mir schon dein ganzer Ansatz etwas zu schräg geraten.

- Wenn du das schon generisch halten willst, dann verwende echte
Pointer. Und da es sich um Ports handelt, dann auch dazu passende (d.h.
volatile). Schreib das so, dass du keine casts brauchst, und der
Compiler keine Warnungen ausspuckt (-wall).

- Speichere in N_Knopf nicht die Bitnummer, sondern die Bitmaske. Und
schon verschieben sich die ganzen aufwendigen _BV(expr) von Laufzeit
(Code) weg in den Compiler.

von Christian W. (clupus)


Lesenswert?

@A.K.:
Dieser Teil des Codes ist nur eine vor-vor-vor-Version. Der steht in
der main.c und gehört eigentlich nicht zum Problem. Abgesenen davon, wo
liegt das Problem bei dem kleinen, zitierten Code-Stück? Da sind auch
keine Casts drinne, also warum der Kommentar wegen vieler Casts?

Zudem was ist mit dieser Integer-/Zeiger-Geschichte? Das sind Zeiger,
die ich in einem eigenen Code (#include "init_leds.c") (dehalb nicht
gepostet, da nur Definitionen als Array abgelegt) abgelegt sind. Die
werden da zu Integern gecastet, weil sie dann angenehmer zu handhaben
sind. (V.a. für später) In meinem kleinen Code werden nur die Cast
zurückgewandelt.

MfG
Christian

von Christian W. (clupus)


Lesenswert?

so, also das hat's auch nicht gebracht (mit dem Einsetzten der
Bit-Muster in Array-Definition). Jetzt wird jeder Port auf 1 gesetzt.
(auch bei der vereinfahcten Version im Kommentar "out 0x12, r18")

MfG
Christian

von A.K. (Gast)


Lesenswert?

"Da sind auch keine Casts drinne"

Nö, aber im dort benutzten Array. Da vergewaltigst du uint8_t ja als
"Zeiger auf Port". Das hat Folgen.

Die Bits der Portadresse statt des Ports zu testen, bringt nicht viel
(linke Seite, da hätte der Compiler dich drauf gestossen, hättest du
ihm eine Chance gegeben). Diese Adresse mit Bitnummer statt Bitmaske zu
maskieren auch nicht (rechte Seite).

Inwiefern sind diese Pseudo-Zeiger angenehmer zu handhaben?

von Christian W. (clupus)


Lesenswert?

Zum einen, weil sie kleiner sind. (=> 8bit Artimetik; lässt sich in ASM
leichter lesen bei Fehlern.) Zudem lassen sie sich dann auch leichter
mal "nebenbei" zum debug ausgeben (alle Pins belegt. Ich kann also
über UART & Co. nicht gehen)

Eine weitere Sache (Hauptgrund) war die: Ich wollte die Zeiger in einem
Array zusammenfassen. Dadurch kann ich die LEDs einzeln steuern, wie ich
will und muss nicht für jede manuell prüfen. Das Array besteht aus einer
Strukur (? struct halt), in der PORT, DDR, Pin-Nr und weiter Infos
untergebracht sind. Wie du selber gesagt hast, bringt es mir nix, die
PORT/DDR/PIN-Werte anzuspeichern, ich muss die Adresse der
PORTs/DDRs/PINs abspeichern.
Und das weiß ich nicht, wie es geht. Muss ich dann eine Struktur so
machen?:

struct LED {
uint8_t PIN-Nr; // Ist ja fix
uint8_t* PORT;
uint8_t* DDR;
// Weitere Werte
};

Könnte ja noch klappen. Kann ich dann auch irgendwie ein Array von
Zeigern (kein Zeiger auf ein Array) erzeugen? Mit

uint8_t * PORT_Knopf[2] = {...};

gibt's ja nur den Zeiger auf das Array, oder?

MfG
Christian

von A.K. (Gast)


Angehängte Dateien:

Lesenswert?

Ich habe mir mal angesehen, was bei dieser Version rauskommt. Sieht
eigentlich ganz gut aus.

von A.K. (Gast)


Lesenswert?

int *array_of_ptrs[]; // *(array_of_ptrs[])
int (*ptr_to_array)[];

Ich weiss, dass man sich an diese Logik erst gewöhnen muss. Tip: Solche
Zeilen analysiert man, ausgehend von der Variablen, den Prioritäten
folgend von innen nach aussen.

von Christian W. (clupus)


Lesenswert?

Nee, tut mir leid, aber bei mir gibt's da ne Dicke Fehlermeldung:

hardware.h:50: error: assignment of read-only location

Ich hab allerdings die Arrays als const definiert. Dürfte aber kein
Problem machen. Denn auf die Arrays soll ja nicht mehr geschrieben
werden. Lasse ich die consts weg, macht er es ohne Probleme.

Ich vermute halt, dass der Compiler "sieht":
DDR_Knopf[0] ist der Zeiger auf ein Array-Eintrag.
Damit wäre *DDR_Knopf[0] der Eintrag selber und damit die Adresse des
DDRs.
Das heißt eigentlich müsste ich ja mit **DDR_Knopf[0] kommen, oder?

MfG
Christian

von Christian W. (clupus)


Lesenswert?

» int *array_of_ptrs[]; // *(array_of_ptrs[])
» int (*ptr_to_array)[];

??
Ich denke, dass ich ein Zeiger auf ein Array immer nur auf das erste
Element zeigt. Laut dem 2. Bsp. (ptr_to_array) wäre aber doch für jeden
Eintrag ein Zeiger vorhanden, oder?

MfG
Christian

von A.K. (Gast)


Lesenswert?

Zur Abwechslung mal was zum Inhalt, nicht nur der Form:

Dein Drilling
  DDR &= ~mask;
  PORT |= mask;
  DDR  |= mask;
ergibt nicht sonderlich viel Sinn. Das Endergebnis ist das gleiche wie
  DDR  |= mask
  PORT |= mask;
nämlich ein auf Ausgang programmierter Portpin, der 5V liefert.

Wenn da ein Taster dranhängt, der runterzieht, schliesst du den Port
kurz. Wenn du Pech hast, ist der Pin anschliessend hinüber.

Was du wohl erreichen willst (mit DDR=0 als Ausgangszustand):
  PORT |= mask;
Genau und nur so kriegst du die schwachen Pullups eines auf Eingang
programmierten Pins aktiviert.

von Christian W. (clupus)


Angehängte Dateien:

Lesenswert?

Jep, auch das war ein Debug-Test, weil ich bisher noch nicht weiß, warum
das nicht klappt.

Ich hab mal eine aktualisierte Version hoahcgestellt, die etwas
übersichtlicher sein sollte.

Das Problem bleibt aber, dass die Pull-Ups nicht einschalten und (so
glaube ich mal) auch die DDRs nicht auf Eingang gehen würden, wenn das
nicht der Standard wäre. Ich glaube fest, da liegt ein Fehler im
System.

MfG
Christian

von Christian W. (clupus)


Angehängte Dateien:

Lesenswert?

Uups, falsche Datei.

von A.K. (Gast)


Lesenswert?

Mal ein Beispiel zu C Typen:

  volatile uint8_t * const array[2];

liest sich als

  array                 Variable "array" ist ein
  []                    Array
  *const                bestehend aus konstanten Zeigern
  volatile uint8_t      auf volatile Bytes.

während

  int (*ptr)[];

darauf raus läuft:

  ptr                   Variable "ptr" ist ein
  *                     Zeiger
  []                    auf ein Array
  int                   aus Integers

Wird "array" nun im Programm verwendet, ist es ein Ausdruck vom Typ
   volatile uint8_t const 
also ein Zeiger auf einen konstanten Zeiger. Der Wert davon ist die
Adresse des ersten Elements.

Habe ich dich nun gänzlich verwirrt?

Ein C Handbuch könnte hilfreich sein.

von A.K. (Gast)


Lesenswert?

Und dein
  const volatile uint8_t * DDR_Knopf[2]
enthält variable Zeiger auf konstante Bytes. Da es sich bei diesen
Bytes um die Ports handelt, konntest du den Compiler wohl kaum dazu
überreden, da was reinzuschreiben.

Und wenn du Pech hast, sind deine Portpins schon abgeraucht.

"Ich glaube fest, da liegt ein Fehler im System."

Wenn du mit System den Compiler meinst: Viel Spass mit dieser These.
Solche Thesen werden hier recht oft aufgestellt. Indes gilt die Regel,
dass es sich zu 99,9% um Fehler oder Unverständnis seitens des
Programmierers handelt. Mancher wirft in seiner Verzweiflung auch dem
Prozessor einen Fehler vor. Sind dann noch einige Neuner mehr.

von Christian W. (clupus)


Lesenswert?

OK, OK, das mit der Leserei ist ein bisschen strange. (Im wahrsten
Sinne des Wortes.) Muss ich mir mal bei Zeit zu Gemüte führen.

Mein Schalter hat aber immer noch keine PullUps. Und ohne die klappt
die Sache zwar prinzipiell schon, allerdings weiß ich dann nicht, warum
es nicht geht. (=> Wenn es dann mal richtig komplex ist, wird's echt
unangenehm, wenn so ein Fehler aufkreuzt.)

MfG
Christian

von Christian W. (clupus)


Lesenswert?

Also die Pins sind noch in Ordnung.

Wenn ich explizit mit
*PORT_Knopf[1] |= N_Knopf[1];
die PullUps manuell einschalte, funktioniert es. Das ist es ja, was
mich wundert.
Prinzipiell könnte ich die 2 auch manuell einschalten, aber es will mir
nicht runter, dass das nicht auch mit der Schleife gehen soll. (Brauche
ich halt für später, dann ist es aber deutlich komplizierter. Deshalb
lieber jetzt den Fehler lösen.)

MfG
Christian

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Zum einen, weil sie kleiner sind. (=> 8bit Artimetik; lässt sich in
> ASM leichter lesen bei Fehlern.) Zudem lassen sie sich dann auch
> leichter mal "nebenbei" zum debug ausgeben (alle Pins belegt. Ich
> kann also über UART & Co. nicht gehen)

Du weißt aber, dass es auch CPUs mit IO-Ports >= 0x100 gibt, ja?

OK, die klassischen PORTx/PINx/DDRx werden wohl aus
Effektivitätsgründen immer ,,ganz unten'' untergebracht, für die
trifft das nicht zu.  Ich wollte dich nur daran erinnert haben.

von Christian W. (clupus)


Lesenswert?

Keiner mehr eine Idee?
Bitte korrigiert mich, wenn ich jetzt falsch liege, aber bisher hab ich
so verstanden:

Eine Schleife
» for(i=0;i<2;i++){
»   *DDR_Knopf[i] &= ~N_Knopf[i];
»   *PORT_Knopf[i] |= N_Knopf[i];
» }
macht das selbe wie diese Befehle (halt noch mit Bedingungen, die lasse
ich der Übersicht wegen weg):

» i=0;
» <Bed auswerten, nicht rausspringen>
» *DDR_Knopf[0] &= ~N_Knopf[0];
» *PORT_Knopf[0] |= N_Knopf[0];
» i++;        // i=1
» <Bed auswerten, nicht rausspringen>
» *DDR_Knopf[1] &= ~N_Knopf[1];
» *PORT_Knopf[1] |= N_Knopf[1];
» i++;        // i=2
» <Bed auswerten, rausspringen>

Wenn das so ist, warum wird dann in der Schleife der Befehl nicht
richtig ausgeführt, wenn ich aber manuell z.B.
» *PORT_Knopf[1] |= N_Knopf[1];
eingebe, funktioniert es?

MfG
Christian

von Christian W. (clupus)


Lesenswert?

Mir ist gerade aufgefallen, dass der GCC unterschiedliche
Adressierungstechniken für den Zugriff auf die I/O-Register verwendet:
In der Schleife verwendet er die indirektke Adressierung mit X-, Y- und
Z-Register. Das funktioniert nicht.
Bei der manuellen Eingabe nimmt er die Befehle in/out/sbi/cbi. Die
tun.

Kann es damit was zu tun haben, dass die I/O-Register nicht indirekt
adressiert werden dürfen/können? (Ich meine zwar, im Datenblsatt stehe
was anderes, aber bin mir nicht mehr sicher.)

MfG
Christian

von Peter D. (peda)


Lesenswert?

Wenn es nur darum geht, Portpins universell anzusprechen, schau Dir mal
diese Macros hier an:

http://www.mikrocontroller.net/forum/read-2-418759.html#420075


Zumindest ist es sehr übersichtlich und effizient, da sie jeweils nur
zu einem Portsetz- oder Löschbefehl assembliert werden.


Peter

von Peter D. (peda)


Lesenswert?

Und bitte keinen Code in ein *.h-File !!!

Das gehört sich nicht nur einfach nicht, sondern macht Dir bei größeren
Projekten unweigerlich Probleme.


Peter

von A.K. (Gast)


Lesenswert?

Natürlich dürften IO-Ports indirekt adressiert werden. Nur muss die
Adresse stimmen. Ist der gleiche Effekt wie bei Ports von neueren AVRs,
die nicht mit IN/OUT sondern nur mit LDS/STS angesprochen werden können.
Und GCC macht das auch richtig.

von Christian W. (clupus)


Lesenswert?

Es scheint, dass das Problem nicht am _BV() liegt.
Weil das Problem also nix mehr mit dem Thema/der Überschrift zu tun
hat, schreibe ich in einen neuen Thread.

MfG
Christian

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.