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
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
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.
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
"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.
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.
@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
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
"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?
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
Ich habe mir mal angesehen, was bei dieser Version rauskommt. Sieht eigentlich ganz gut aus.
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.
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
» 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
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.
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
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.
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.
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
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
> 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.
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
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
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
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
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.