• Hi,

    um dieses Thema mal wieder aufleben zu lassen:

    Ich beschäftige mich in der letzten Zeit wieder vermehrt mit der C-Programmiererei.

    Hab in den letzten Wochen auch gute Fortschritte mit den analogen Eingängen und der Motoransteuerung mittels PWM gemacht.
    Vlt. hat der eine oder andere ja mal meinen Nibo in Aktion gesehen. (Hab mit Marien bei den Mechaknights ein kleines "rennen" gemacht).


    Nun soll es aber endlich weiter-, bzw. "ins Eingemachte" gehen. Ich will endlich meinen Nibo ferngesteuert haben!

    Reiner unterstützt mich dabei auch extrem, Danke nochmal!

    Bei deinem Code hab ich bislang noch Schwierigkeiten, mich durch die über 1600 Codezeilen durchzuwurschteln und die für mich wichtigen Zeilen zu finden. Besonders, weil die PIC-Befehle zum Teil auch anders sind.

    Somit fange ich erst einmal an, anhand der Informationen, die du mir letzte Woche gegeben hast, ein bisschen zu probieren.


    Die Gute Nachricht, zumindest tut sich etwas, wenn ich den Knüppel an der Funke bewege. Zwar was falsches, aber immerhin. ;)

    Somit die Frage an dich, bzw. natürlich auch an all die anderen, die sich damit auskennen, ob ich generell auf dem richtigen Weg bin und ob ihr noch ein paar Tipps habt?


    Anbei mein kleines Test-Programm

    • Offizieller Beitrag

    Hab' mir Dein C-Demo-Programm mal angesehen. Im Prinzip sieht das vom System so aus, dass es eigentlich funktionieren müßte, wobei Du von den "Reproduzierbarkeiten" der empfangenen Funkensignale noch sehr viel Gottvertrauen drin hast. :D Mir fehlt hier ein bißchen die Hysterese, damit auch bei einer kleinen Verschiebung des Nullpunkts die Fahr-Quadranten (vorwärts/rückwärts) noch berücksichtigt werden.

    Aber jetzt mal ein paar Sachen im Detail:

    Du verwendest von den Timern her ein anderes System als ich. Das funktioniert hier in diesem Demo-Fall, weil Du nur einen einzigen Motor berücksichtigst. Aber wenn Du zwei Motoren verwenden willst, dann klappt das nicht.
    Und zwar löscht Du den Timercounter bei jeder steigenden Flanke des Empfängersignals. Und diesen gelöschten Wert (=0) schreibst Du dann in das Zählerregister, wo Du Dir den Timerwert für die steigende Flange merkst. Ich würde also empfehlen, das Löschen des Timers beim Status "steigende Flanke empfangen" zu eleminieren. Lass den Timer fröhlich munter im Kreis zählen, also zwischen 0 und 255. Nach 255 beginnt er automatisch wieder bei 0. Und wenn Du einen Wechsel des Emfpängersignals von 0 auf 1 feststellst, dann merkst Du Dir den aktuellen Zählerwert, so wie Du das jetzt auch schon tust.

    Einen Fehler wird die Empfänger-Pulserkennung machen. Und zwar wenn Du die Software startest und genau in diesem Augenblick der Empfänger schon auf "1" ist. In dem Zählerstatus 0 reagierst Du nämlich darauf, dass eine 1 am Empfängersignal vorhanden ist, aber nicht ohne die Vorbedingung, dass vorher eine 0 war. Das klappt erst nach der ersten Runde mit einem Zählerpuls. Sobald einmal ein Zählerpuls erkannt worden ist, dann stimmt die Arbeitsschleife.
    Meine Empfehlung daher:
    Mach' einen Status mehr in Deine Status-Maschinerie. Und zwar:
    Status=0: Auf ein 0-Signal vom Empfänger warten. Wenn erhalten: Status wird auf 1 geseetzt
    Status=1: Auf das 1-Signal warten. Wenn erhalten: Timerwert merken, Status wird 2
    Status=2: Auf das 0-Signal warten. Wenn erhalten: Timerwert merken . Status wird 3
    Status =3: Prüfen, ob die Timerwerte plausibel sind. Wenn ja, dann eine separate Auswertung für die Motoren aufrufen.
    Egal ob das Empfängersignal nun was brauchbares (0.8ms bis 2,2ms) geliefert hat, der Status wird jetzt wieder auf 0 zurückgesetzt.

    Du prüfst also im Prinzip zweimal ab, ob das Signal auf 0 geht. Im Status 2 um aktiv zu erkennen, ob das Empfängersignal jetzt genau in diesem Augenblick von 1 auf 0 gefallen ist, und dann anschließend wieder im Status 0, um ja ganz sicher zu sein, dass hier nicht irgendeine blöde Vorbedingung zu einem Fehl-Signal geführt hat. Beim erstmaligen Aufruf der Routine (=Start des Fahrtreglers) kommt also keine Fehlauslösung des Motors oder der Waffe zustande)

    Soweit mal zur Systematik der Empfänger-Signal-Erkennung. Wichtig ist, siehe oben, dass Du das Rücksetzen des Timers eleminierst, sonst kannst Du das zweite Empfängersignal nicht erkennen bzw. erfassen.

    Was Du mal versuchen solltest: Blockweise Ausgliederung der Funktionalitäten. Also ein paar Subroutinen bilden, die dann von der Main-Schleife aus aufgerufen werden.

    Meine Empfehlung:
    - Die Initialisierung der Ports etc. in eine "Sub Init()" legen. Diese Sub dann von der Hauptroutine aus einmal anspringen. (=> Müßte auch in meinem Demo-Programm für Dich so gemacht sein)
    - In der Hauptschleife "While(1)" dann die Abfrage der Empfängersignale legen. Also die ganzen Zeilen der Empfängersignale zunächst mal in eine Sub legen und dann von der Hauptschleife aus einmalig abarbeiten
    - Die Ansteuerung der Motoren würde ich nicht in die Auswerteroutine von dem Empfängersignal legen, sondern separieren. Heißt: In der Hauptschleife aus kommt der Aufruf von dem Empfängersignal (s.o.). Danach rufst Du eine zweite Subroutine auf, die Dir die Motoren ansteuert. Z.B. "Sub SetMot1()". In dieser Routine beginnst Du mit einer if-Anweisung damit, den Status von dem Empfängersignal abzufragen. Wenn der (laut meiner obigen Empfehlung) aktuell gerade auf Statuswert=3 liegt, dann heisst das, dass aktuell gerade eine steigende und fallende Empfängersignalflanke eingetroffen ist. Also ist das was da, was ausgewertet und verarbeitet gehört. Also dürfen jetzt die Motoren entsprechend der Timerwerte angesteuert werden. Wenn der Empfängersignal-Status anders als 3 ist, dann gibts hier in dieser Subroutine nichts zu tun. Somit Rücksprung zur Main-Schleife und weiter auf die Empfängersignale warten.
    Als Gag: In dieser Motoren-Auswerteroutine kannst Du jetzt den Status von 3 zurück auf 0 setzen. Praktisch als Quittierung für die Empfänger-Signalroutine, dass jetzt die beiden Timerwerte überprüft und ausgewertet wurden und ab sofort wieder mit dem Einlesen der Empfängersignale begonnen werden darf.

    Soweit verständlich, wie ich das meine? Wenn nicht, dann einfach nachfragen, bin da nicht beleidigt oder genervt, wenn ich es mit anderen Worten noch einmal mache.


    Zitat

    Bei deinem Code hab ich bislang noch Schwierigkeiten, mich durch die über 1600 Codezeilen durchzuwurschteln und die für mich wichtigen Zeilen zu finden. Besonders, weil die PIC-Befehle zum Teil auch anders sind.


    Ja, verständlich. Ich habe zwar sehr viel in Blöcken und Subroutinen programmiert und meinen Dokumentation macht auch sehr viel aus, aber es sind halt im Laufe der Zeit sehr viele Änderungen und Verbesserungen mit eingeflossen. Geräde die Gägs mit der Parametrierung per PC machen die Sache halt u.U. schwieriger zu Erfassen, weil ich verschiedene Betriebsmoden berücksichtigen muss.

  • Uiuiui, da hab ich morgen ja wieder ordendlich was zu tun. :)


    Wie passend, das ich mir gerade heute Nachmittag noch diese Subroutinen von meinem Bruder erklären lies, hehe. :D

    Jetzt macht der ganze Aufbau deines "Testcodes" für mich erst richtig Sinn!! X( ;) Wieder was gelernt! :D


    Das mit dem "Timer auf Null stellen" eliminieren und dem vierten Status hat leider auch noch nichts gebessert.


    Also was mein Nibo macht:

    Mein Code ist glaub ich verständlich oder? Also wann welche der vier LEDs angehen soll?

    So, nun ist beim Einschalten die LED3 eingeschaltet.
    Wenn ich dann den Hebel leicht nach vorne bewege geht diese aus. Mehr nicht. Beim Hebel nach hinten ändert sich nichts.


    Ich werde mich morgen erstmal an die Subroutinen wagen. Dann kommen wahrscheinlich die Fragen. ;)

    • Offizieller Beitrag
    Zitat

    Wenn ich dann den Hebel leicht nach vorne bewege geht diese aus. Mehr nicht. Beim Hebel nach hinten ändert sich nichts.


    Kannst Du bei Deinem Entwicklungssystem die Sache debuggen?
    Dann setz' mal einen Breakpoint auf "elseif", wo Du die Auswertung machst. Also dann, wenn sowohl eine steigende als auch fallende Flanke erkannt wurde. Dann den Wert von den beiden Timern abfragen. Liegt die Differenz in dem erwarteten Bereich, wo Du derzeit Deine (festen) Grenzen für die Vorwärtsfahrt, Neutralstellung und Rückwärtsfahrt gelegt hast?

    Möglich Fehlerursachen:
    - Der Timer läuft nicht zyklisch bzw. hört dann bei 255 auf (=> Initialisierungssache)
    - Der Timer ist ein 16bit- und nicht ein 8bit-Timer
    - Die Zeitdefinition mit den Prescalern ist falsch und die Zyklusschleife von dem Timer ist nicht 4ms
    - Die Differenz von der Start- und Stopzeit des Timers passt nicht zu den definierten Grenzen für die Erkennung der Vorwärts- Neutralstellung und Rückwärtsfahrt.

    Hier bei diesen Grenzen ist noch ein Systemfehler, weil Du keinen "Neutralbereich", sondern eine feste Grenze hast, wo die Neutralstellung ist. Da wirst Du höchstwahrscheinlich nie genau die Neutralstellung treffen. Der Motor wird immer entweder vorwärts oder rückwärts fahren. Aber egal, laut Deinem o.g. Fehlerbild ist da noch was anderes faul.... Lies mit dem Debugger einfach mal die Werte aus, die sich für die Start-/Stop-Differenz ergeben, wenn die Funke auf Neutralstellung , auf Vollgas vorwärts und auf Vollgas rückwärts ist. Ich denke, da kannst Du das dann schon herausfinden, wo der Bug im Programm ist.

    Wegen Subroutinen:
    Sorry, ich dachte, das Prinzip wäre bekannt. Aber umso besser, wenn Dir das anderweitig schon jemand erklärt hat.

  • Oh Mann, dachte ich komme an dem Debugger vorbei... ;)

    Verwende den "AVR-Studio 6.0"
    Ich verstehe davon aber mal garnichts... Ich kann das Programm ja Zeile für Zeile Durchtakern, aber wenn das Programm z.B. irgend einen Eingang abfragen will, was dann oO

    Hab auch nur den "AVR ISP MKII"-Programmer. Mit dem kann man ja leider nur Proggen und nicht Debuggen...

    Dann werde ich mich wohl noch irgendwie darein arbeiten müssen :.(


    Wenn du schon die Zykluszeit ansprichst. Bin mir da selber noch recht unsicher:

    Ich hab ja den Atmega16A. Der läuft im Nibo an einem 16Mhz Quarz. Gehe also mal davon aus, das die Fuses stimmen und der auch seine 16MHz hat.

    Ich benutze den Timer0, der laut Datenblatt ja ein 8bit Timer ist, also bis 255 zählt.

    Ich hab den Prescaler clk/256 drinne. Also läuft der Timer ja mit 62500Hz.

    Nun zählt der ja bis 255. Somit kommt der Overflow theoretisch ja mit 245Hz. Also zählt er in 4,082ms von 0 bis 255.

    Hab ich das richtig? Oder denke ich wieder total falsch?


    Zitat

    Wegen Subroutinen: Sorry, ich dachte, das Prinzip wäre bekannt. Aber umso besser, wenn Dir das anderweitig schon jemand erklärt hat.

    Wie gesagt, bin halt noch ein ziemlicher N00b. Mein Bruder ist zwar Programmierer. Allerdings macht er Computersoftware und hat keinen Schimmer von uC's X(

    Wenn er am WE dann mal da ist, frag ich ihn dann halt solche grundsätzlichen Dinge. Will dich ja nicht zu Tode fragen! ;)

    • Offizieller Beitrag
    Zitat

    Will dich ja nicht zu Tode fragen!


    :D
    Wäre kein Problem. Hauptsache es geht von dem know-how etwas an die Jugend weiter.... ;)

    Die C++ - Programmierer am PC können natürlich aus dem Vollen schöpfen, was Speicherplatz und RAM angeht. Da muss Du Deinen Bruder u.U. ein bißchen bremsen, wenn er das Programm umstricken will.

    Also: Frag ruhig !

    Mit dem AVR kann ich Dir leider nicht helfen, ich habe bisher erfolgreich einen Bogen um diese Prozessoren machen können. Aber Alex und Gizmo sind da recht fit, vielleicht können sie Dir ein paar Empfehlungen geben, wie Du den Debugger einen Tritt geben kannst.
    Ohne Debugger kannst Du eine Softwareentwicklung vergessen. Früher habe ich das auch mit Try-Error-Methode machen müssen, weil ich keinen Debugger hatte. (EProm geschossen, in die Schaltung eingesetzt, ausprobiert, entnommen, EProm gelöscht, nächster Versuch....)

    Mit dem Takt von den Timer kannst Du einen ganz primitiven Test machen: Mach ein kleines Demoprogramm, das immer im Kreis läuft. Wenn der Timer exakt auf Null steht, dann schalte eine LED ein. Wenn der Timer auf 128 steht, dann schalte die LED wieder ab. Mit dem Oszi dann die LED abfragen und schon hast Du die echte Timer-Taktfrequenz. ;)

  • 1) Hallo, ein Debugger ist beim Entwickeln natürlich sehr von Vorteil. Am besten wäre ein JTAG ICE mkII, leider nicht ganz billig. Hier sind einige JTAG Debugger für den AVR aufgelistet. Eine günstige Alternative wäre der AVR Dragon, siehe auch hier.

    2) Eine Abhilfe wäre, über die serielle Schnittstelle (UART) Debug Informationen (wie zB dem TCNT0 Wert) an einen PC zu senden und dort via Terminalprogramm (z.B HTERM) zu empfangen. Falls Interesse vorhanden, kann ich ein getestes C Modul für AVR hochladen.

    3) Das explizite Löschen von Bits wie in
    >> TCCR0 &= ~(1<<CS00);
    ist nicht notwendig, da die Register beim Reset sowieso mit 0 initialisiert werden. Der Initialwert steht im Datenblatt bei der Registerbeschreibung dabei.

    4) Gesetzt den Fall, dass die Fuses stimmen und der AVR Takt 16 MHz beträgt, dann stimmen deine Berechnungen und der Timer 0 benötigt für 256 Schritte 4.096 ms. Insofern sollte das Empfängersignal detektiert werden.

    • Offizieller Beitrag
    Zitat

    Ich werde mich morgen erstmal an die Subroutinen wagen. Dann kommen wahrscheinlich die Fragen.


    Ja, gib' dann bescheid.
    Mit dem System, wie man den Timerwert auf die PWM des Motors umsetzt, da werde ich wohl noch ein paar Worte und eine Zeichnung dazu verlieren.
    Gib' einfach bescheid, wenn Du wieder "aufnahmebereit" dafür bist. Aber wenn die Timererfassung noch nicht klappt (s.o.), dann bringt der nächste Schritt noch nichts.

  • Hi,

    erstmal wieder Danke für Eure Hilfe! Also Reiner deine Idee mit dem Testprogramm ist genial! Habs gleich mal getestet. läuft tadelos. Mit meinem Uralt-oszi hab ich ein wunderbares Signal. 2ms Low, 2ms high. Perfekt! Also stimmt wohl doch noch etwas nicht ganz mit meinem Programm... Ich werde mich dem erst einmal annehmen, bevor ich weiter gehe.

    Mal schauen, ob/wann ich es gebacken kriege.

    Ich hab schon Schwierigkeiten mit dem normalen Proggen, dann schon mit dem PC verknüpfen... Lieber nicht :D

    Das löschen der Bits hab ich nur so drin, weil ich die Bits so ganz leicht/ganz schnell umschreiben kann und es so sehr übersichtlich ist. :)

    Joa, ein Debugger ist schon ziemlich genial, aber ich als Laie fange erst einmal klein an, der AVR-ISP-MKII liegt bei Reichelt auch bei um die 50€
    Wobei der Nibo in der Form ja eh per USB mittels eigener Schnittstelle zu programmieren ist. Es gibt dort also keine herausgeführten ISP-Anschlüsse.


    @Reiner, mein Bruder programmiert mit (glaube) C Sharp, also eine ziemlich moderne Sprache. Er hatte das normale C auch nur kurz in seinem Studium gehabt. (Sind die Sprachen von Assembler über C bis nach Java und C-Sharp durchgegangen)

    • Offizieller Beitrag

    (1) Flüchtig drüber gehuscht sehe ich erstmal keinen "Bock". Genauere Analyse wäre notwendig...

    (2) Embedded Programmieren ohne Debugger ist böse. Nach der Trial & Error Methode hab ich das früher auch gemacht. Mittlerweile hab ich aber sogar 2 JTAG mkII...

    (3) Ausser den angesprochenen Debuggern von Atmel gibt es auch noch die für die Bastler die dann besonders günstig sind wenn man löten kann.
    http://www.usbprog.org/index.php/Hauptseite
    http://gandalf.arubi.uni-kl.de/avr_projects/evertool/index.html
    http://www.olimex.com/dev/avr-usb-jtag.html
    http://www.miklobit.com/JTAG_TWICE.530+B6Jkw9Mw__.0.html
    ...

    Bei den meisten wird ein JTAG mkI emuliert. Zugegeben, das ist schon etwas angegraut, aber zum günstigen Einstieg in das Debugging reicht es in der Regel und kompatibel zum aktuellen AVRstudio müssten auch noch alle sein...

    (4) Behalte das mit den Bits löschen und setzen ruhig bei, lieber zuviel als zuwenig, denn das ist guter Stil wenn es nicht auf jedes Byte Flash ankommt. Das Gleiche gilt für lokale Variablen. Da sollte man sich angewöhnen die immer mit einem definierten Zustand zu initialisieren...

    (5) Genau wie bei den Bits sollte man sich auch angewöhnen Konstanten als DEFINES auszulagern. Das macht es übersichtlicher und man muss nur an einer zentralen Stelle den Parameter ändern...

    (6) Den Port würde ich wie die Timer über die Bit Defs laden. Also in etwa so:

    Code
    PORTB ^= (1 << PB4) | (1 << PB2) | (1 << PB0);


    (7) PWM würde ich wie hier schon gemacht IMMER über die Hardware Einheiten machen, denn so läuft das auch sauber im Hintergrund weiter wenn man im Vordergrund was anderes machen muss. Eine stabile PWM in Software nachbilden die frei von Jitter und den ganzen Dreckeffekten ist, wird eh böse. Falls die OCs nicht reichen lieber 1,24 EUR in einen dickeren AVR investieren...

    [8] Periodische Signale würde ich über die ICPs einlesen. Ist wie bei (7) das Gleiche bloß quasi anders rum. Mit etwas Geschick und Hardware Hacking kann man übrigens auch mit einer ICP ALLE Kanäle eines Empfängers auswerten, egal wie viele das sind. Entweder man greift am Receiver das Summensignal per Hack ab, hat Glück und der Receiver gibt es von sich aus heraus oder aber mit Aussenbeschaltung am AVR...

    (9) Das ist zwar unschön, aber manchmal kann es sich lohnen ein Programm im AVR Simulator zu testen...

  • Hi,

    nach langem testen, probieren und was weiss ich was noch, muss ich mich doch wieder bei Euch melden...

    Also ich hab das Programm jetzt nach Reiners Vorgaben umgestrickt. Also erstmal alles in einzelne Funktionen gesteckt und diese werden dann nach und nach abgearbeitet.

    Muss sagen, so ist das Programm echt übersichtlicher und vor allem sieht das jetzt richtig professionell aus! hehe ;)

    Ich habe ja schon mit dem Mini-LED-Programm die korrekte Arbeitsweise des Timers festgestellt.
    Dann hab ich mich ein bisschen mit dem debugger auseinandergesetzt und habe festgestellt: Das Programm tackert eigendlich korrekt vor sich hin!!

    Das einzige was ich noch nicht hinbekommen habe, ist das "anschauen" des Timers. Meine Variablen z.B. lassen sich in eine Liste stecken und man kann so direkt schauen, was da momentan drin steht.
    Man kann den Variablen auch ganz leicht einen belibigen Wert verpassen.

    So konnte ich z.B. schon genau testen, was passiert, wenn etwas in den Vaiabeln für den Timerwert steht. Der rechnet tatsächlich korrekt den Wert für den vCount_Result aus und springt in den dazugehörigen Part, in dem die LEDs gesetzt werden...

    Dann setzt der die Variablen brav zurück und fängt von Vorne an. Goil!

    Das Problem ist halt nur, das der den Part, in dem er den Wert des TCNT0 in die Variable (vCount_1 oder vCount_0) packt nicht macht! Ich muss den Variablen immer manuell den Wert verpassen, damit der weiter macht.


    Darum meine Primäre Frage:

    Ist der Befehl " vCount_1 = TCNT0; " bzw.
    " vCount_0 = TCNT0; " wirklich korrekt??


    Ich meine, das kann am Simulator liegen, das ,kA der "virtuelle Timer", oder was auch immer, das nicht gebacken kriegt. Aber andererseits funktioniert das Programm im Simulator bis auf diese Sache ja korrekt und dennoch zeigt mir mein Nibo nur eiskalt die LED3...


    Ich hoffe ihr könnt mir helfen und seht, was ich nicht sehe...


    EDIT: Anbei natürlich mein aktuelles Programm

    Einmal editiert, zuletzt von Replikator (16. April 2012 um 13:09)

    • Offizieller Beitrag

    Ich hab' jetzt das Programm noch nicht angesehen. Aber "trocken" eine Anmerkung dazu: Wenn Du im Debuggen bist, dann läuft das Empfängersignal eiskalt weiter. Das heißt, dass Du durch das "Ansehen" der Variablen inzwischen Zeiten bekommst, die jenseits der Brauchbarkeit sind. => Dies also mal beachten.

    Nach meiner Ansicht ist der C-Ausdruck korrekt. (also keine zwei "=="....)

    Wie gesagt, hab' Deinen Code jetzt nicht angesehen. Aber kann es u.U. sein, dass das "Setzen" des Timers ein paar Zyklen verlangt und Du zu schnell bist?

    Dass man das Timerregister nicht auslesen kann, das glaube ich nicht. Scheidet als Ursache aus.
    Dass der Timer "im Kreis" läuft und nicht bei Endanschlag nach 4ms aufhört, das ist auch gewährleistet, oder?

    Tja,... dann gehen mir die Ideen aus.

  • Ahh *Kreisch*

    Ich bin auch ein Horst manchmal... Ich habe das Problem gefunden und behoben X(

    Und zwar hab ich meine LEDs so gesetzt:

    PORTB = (1<<PB0); // LED_L_YE / LED0
    PORTB &= ~(1<<PB1); // LED_L_RT / LED1
    PORTB &= ~(1<<PB2); // LED_R_RT / LED2
    PORTB = (1<<PB3); // LED_L_YE / LED3

    Nun muss es aber ja heissen:

    PORTB |= (1<<PB0); // LED_L_YE / LED0
    PORTB &= ~(1<<PB1); // LED_L_RT / LED1
    PORTB &= ~(1<<PB2); // LED_R_RT / LED2
    PORTB |= (1<<PB3); // LED_L_YE / LED3


    Also diese "|" verknüpfungen hab ich vergessen. Ist glaub ich ne UND-Verknüpfung oder?

    Weil ohne der Verknüpfung setzt der ja letztendlich nur den letzten Befehl. Und welcher ist das? LED3, die immer die einzige LED war, die eingeschaltet war... X(

    *Kopfauftischhaut*


    Das Beweisvideo wird noch nachgereicht. ;)


    So, endlich kann ich weiter machen! :D Lockup-Tabelle und Motoransteuerung stehen nun auf dem Plan... *Angst*

    Mal schaun wie lange das dauern wird, hehe ;)


    Aber Danke erstmal für Eure Hilfe bislang!

    • Offizieller Beitrag
    Zitat

    Also diese "|" verknüpfungen hab ich vergessen. Ist glaub ich ne UND-Verknüpfung oder?


    Nein, müßte eine ODER-Verknüpfung sein.

    UND wäre das "&".

    Etwas verwirrt bin ich über das Setzen des Ports. Ok, ich kenne den Atmel jetzt nicht. Aber wenn die LED z.B. am Port B0 dranhängt, kannst Du nicht einfach sagen

    PB0 = 1;

    Dann wird das Bit 0 vom Port B gesetzt und die LED müsste leuchten. Entsprechend dann

    PB0 = 0;

    zum Ausschalten.

    Das mit der UND/ODER-Verknüpfung und der Negierung von Deinem Bitsetz-Kommando checke ich ehrlich gesagt nicht. (Liegt vielleicht auch daran, weil ich mir den C-Käse weitgehend selbst erarbeitet habe und ich von der Denke her aus Modula2 und VisualBasic komme. :rolleyes: )

  • Also das setzten der Bits hab ich von
    Mikrocontroller.net

    Zitat

    Einzelne Bits setzt und löscht man "Standard-C-konform" mittels logischer (Bit-) Operationen.

    x |= (1 << Bitnummer); // Hiermit wird ein Bit in x gesetzt
    x &= ~(1 << Bitnummer); // Hiermit wird ein Bit in x geloescht

    Einmal editiert, zuletzt von Replikator (17. April 2012 um 16:13)

    • Offizieller Beitrag
    Zitat

    "Standard-C-konform"


    Ok, das kann sein. Ich bin mit den PIC-Prozessoren halt ein bißchen "betriebsblind" und mache es so, dass ich es verstehe und es funktioniert.

    Aber jetzt checke ich das mit dem "1 << Bitnummer", warum das so gemacht ist. Danke für den Hinweis. (Wieder was gelernt). Aber trotzdem finde ich es eigenartig, dass man hier über den Shift-Befehlt geht, wenn man eigentlich schon weis, welches Bit man gerne gesetzt bzw. gelöscht hätte. Hm...

    Replikator: Du bist bei dem komischen Shift-Befehl dabei, was der macht und warum das so kompliziert ist? (=> würde ansonsten gerne meine Erkenntnis hier zum Besten geben... :D )

    • Offizieller Beitrag

    Das mit den Bits manipulieren ist richtig so gemacht. Das hat den grandiosen Vorteil dass man sich nicht jedesmal überlegen muss wie der HEX, OKT oder BIN Wert für den Port aussehen muss. Ausserdem muss man sich so keine Gedanken machen was mit den anderen Bits am Port ist, denn die werden so nicht beinflusst. Genau deswegen toggelt man ein Bit z.B. mit einem XOR auch so: PORTB ^= (1 << PB5); Da der Shift Befehl mit statischen Werten wie hier vom Präprozessor gleich in die richtigen Zahlen gewandelt wird, kostet das nachher auf der CPU keinen einzigen Taktzyklus. Ist also sehr elegant diese Arbeitsweise...

    "|" ist ein bitweises ODER. Ein großer Unterschied zum logischen ODER "||" oder gar zu einem UND "&" oder analog "&&"!

    Je nachdem ob deine LEDs HI oder LO aktiv angeschlossen sind musst du halt eine der beiden Varianten wählen. 50/50 Chance... :]

  • Das du nochmal was von mir lernst hätte ich ja im Leben nicht gedacht... ;)

    Und ich bin für jede Erkenntnis dankbar! Bin in dem Thema noch nicht so fit. ;)


    Zitat

    Das hat den grandiosen Vorteil dass man sich nicht jedesmal überlegen muss wie der HEX, OKT oder BIN Wert für den Port aussehen muss. Ausserdem muss man sich so keine Gedanken machen was mit den anderen Bits am Port ist, denn die werden so nicht beinflusst

    Genau darum hab ich das in der Art übernommen, hehe :) So ist man auf der sicheren Seite! :D


    Ich nutze diese Schreibweise:

    PORTx = 0xf1

    nur einmal beim initialisieren der Ports.

    • Offizieller Beitrag

    Naja, ich möchte hier keine Grundsatzdiskussion anfangen. Aber wenn ich schon weis, dass ich den Portausgang vom Bit 5 setzen oder löschen muss, dann kann ich ja gleich das Bit hinschreiben und muss nicht erst das Bit0 setzen/löschen und 5 mal shiften, bis es an der richtigen Position ist.
    Wahrscheinlich bin ich von den PIC-Prozessoren auch zu sehr verwöhnt, weil da für jedes Portbit ein separater Befehl zur Verfügung steht, um dieses Bit an Ort und Stelle sofort zu setzen/löschen, ohne sich um die anderen Portbits kümmern zu müssen.

    Zitat

    Das du nochmal was von mir lernst hätte ich ja im Leben nicht gedacht... ;)


    Lernen kann man immer was.... ;)
    Zu Arroganz oder Hochnäsigkeit, weil man ein paar Jahre mehr Berufserfahrung hat, gibt es eigentlich nie einen Anlass. Jeder "säuft" im Laufe seines Lebens mal ab, so dass ihn die Anderen im know-how überholen.