• Hi,

    ich bin im Moment dabei, die hohe Kunst der C-Programmierung zu lernen. :)

    Mein Ziel, natürlich zu lernen wie ich meinen eigenen Fahrtregler programmieren kann!
    Mein Traum wäre ja, meine Bots auf Dauer "teil-Autonom" zu machen. :) Aber davon bin ich noch weeeeiiiiit entfernt.

    Um den Einstieg zu schaffen habe ich mir zwecks dessen den NiboBee von Reichelt besorgt. Ein klasse Teil! Die anfängliche programmiererei ist sehr leicht gestaltet, da alle Funktionen vorgegeben sind. Man muss diese nur noch richtig verstricken.

    Hier mein Baby. Wie man sieht, habe ich schon ein bissl daran gebastelt.
    Dazu gehöhrt:
    -ein modifiziertes "Vorderrad" (sieht man hier nicht)
    -eine Akku-Spannungsmessung
    -Helligkeitssensor zur Einstellung der Display-Beleuchtung (funzt najaaa)
    -neben den vier Tastern auf dem Grafik-Modul sind LEDs die ich durch blaue LEDs ersetzt habe.
    -und zur guter Letzt habe ich den Orange-Empfänger an die Bee gehängt


    http://imageshack.us/g/13/1032443.jpg/

    Soo, warum mache ich den Tread? Ich bin jetzt mit den Bord-Sensoren/Aktoren relativ vertraut. Nun möchte ich einen großen Schritt weiter gehen. Ich will die Biene ferngesteuert haben!!!


    Ich habe Reiner auch schon zu dem Thema befragt und wir beide denken, warum das nicht öffentlich machen? Wenn sich jemand irgendwann auch mal mit dem Thema beschäftigen möchte, so kann er diesen Tread zur Hilfe nehmen.
    Evt. hat der ein oder andere auch schon Erfahrung mit dem Thema und kann auch was dazu schreiben.


    Als Anfang poste ich hier mal das bisherige geschreibsel:


    Hi Reiner,

    Ich hab da mal eine Frage zum Fahrtregler-Programmieren:

    Ich lerne in letzter Zeit ja das C-Programmieren. Dazu habe ich diesen NIBOBee von Reichelt, ein klasse Teil!!

    Nun bin ich mit dem Tutorial dazu durch und versuche mich an eigene Projekte.
    Ich bin zwar noch ein ziemlicher Anfänger, aber ich weiss jetzt zumindest die Grundlegenden Dinge und den generellen Aufbau eines Programms.

    So, nun möchte ich den nächsten Schritt wagen und versuchen einen Empfänger an die kleine Bee zu hängen.

    Ich möchte mir das Programm zwar selbst erarbeiten, aber ich weiss noch garnicht wie man sowas überhaupt angeht.
    Ich möchte keine Komplettlösung, nur einen Anhaltspunkt. smile
    Als erstes müsste ich wissen, wie verarbeitet man das Signal überhaupt in dem Programm?

    Wär nett, wenn du mir dabei helfen könntest, im Inet habe ich dazu noch nicht so ganz viel herausgefunden.


    Hallo Andreas,


    Zum Erfassen von den Empfängersignalen:
    Du muss im Prozessor einen Timer laufen lassen. Z.B. einen 8-Bit-Timer, der dann Werte von 0 bis 255 liefert.
    Der Timer muss durch einen Prescaler so eingestellt werden, dass seine Zykluszeit das abdeckt, was denn an Pulsen so reinkommt. Heißt: Die Pulse sind maximal 2ms lang, das muss der Timer können.
    Beim PIC ist der Timer so eingestellt, dass er sich all 4ms wiederholt bzw. abgelaufen ist. Somit ergeben die 4ms einen Bereich von 255 Stufen.
    Jetzt musst Du also eine Schleife im Prozessor laufen lassen, der ständig den Pin abfragt, an dem der Empfänger hängt. Wenn der Puls "high" wird, dann merkst Du Dir den aktuellen Zählestand des Timers in einer Variablen. Dann macht der Prozessor wieder was anderes und schaut regelmäßig auf den Empfängerpin. Irgendwann ist der Puls abgelaufen, der Pin geht auf Low. Auch das kriegst Du mit und merkst Dir den zweiten Zählerstand.
    Anschließend wird gerechnet. Die Differenz zwischen den beiden gemerkten Zählerständen ergibt im Prinzip die Pulslänge. Allerdings zunächst nur in einer Zahl. Jetzt müßte man die Zahl auf eine Zeit umrechnen. Braucht man aber nicht. Denn im Endeffekt willste ja nur das PWM-Signal für den Motor errechnen oder einen Schalter auslösen. Stichwort dazu wäre "Lookup-Table". Dazu bei Bedarf gerne mehr....


    Danke für die schnelle Antwort:

    Puuh, ich hab noch null Ahnung von Timern. smile
    Sprich der erste Schritt wird wohl sein den Timer kennen zu lernen...


    Hallo Andreas,
    ja, lies mal im Datenblatt die Beschreibung von den Timern, dann wird die Sache schon klar.

    Im Prinzip gibt es mehrere Möglichkeiten, mit Timern zu arbeiten. Ich empfehle:
    - keinesfalls einen Interrupt auslösen, wenn ein Timer abgelaufen ist. => Macht nur Ärger im Programmablauf
    - Den Timer frei laufen zu lassen. Wenn er bei 255 angekommen ist, dann fängt er ganz normal wieder von vorne an.
    - Bei meiner Software ist der Timer über die Vorteiler so eingestellt, dass er eine Laufzeit von 4ms hat, bevor er wieder von vorne anfängt.
    - Darauf achten, dass bei einem Standardquarz für den Prozessor (z.B. 4MHz) der Timer gut läuft. Zum Schluss wird dann der Quarz noch auf ein Vielfaches erhöht (z.B. dann 16MHz). Der Vorteiler vom Timer muss das dann wieder so können, dass nach der Umstellung von 4MHz auf 16MHz der Timer immer noch einen Bereich von 4ms hat.

    Wenn Fragen sind, dann einfach melden. Wir können das meinetwegen auch über das Forum machen, dann haben die anderen auch etwas zum lesen. *grinsel*. Vielleicht findet sich noch jemand, der in die Prozessorprogrammierung einsteigen will.


    Da es bei dem NiboBee zu Timern, etc. nichts an Beispielen/Übungen gibt, wende ich mich an euch.
    Wie ich meinen Timer konfiguriere und den Prescaler einstelle weiss ich denke ich jetzt.
    Dazu gibt es ja die Interrups, (ich weiss, ich sollte die nicht einsetzten, aber wenigstens kennenlernen muss ich die ja mal. ;) ) in denen man ja weitere Befehle aufrufen kann etc.
    Dafür ist die Seite Microcontroller.net nicht schlecht.

    Nur weiter komme ich jetzt nicht. Und eine Idee wie du (Reiner) das meintest mit den Puls-, bzw. Pegel-Abstand-Messen hab ich mal garnicht.


    Hab meinen bisherigen Code in den Anhang gepackt. Hoffe das funzt.
    Das Programm scheint so auch zu laufen, die LED blinkt und blinkt.


    Also wie geht es weiter? Wie stell ich fest, wann der Pegel am Eingang high oder low ist??


    Vielen Dank erstmal für eure Hilfe, hoffe ich kriege das bald hin "ganz ungeduldig ist".


    Hoffe auch das hier ist jetzt nicht zu unübersichtlich geraten. Keine Ahnung wie ich das besser zusammenschreiben könnte. :)


    Gruß

    Einmal editiert, zuletzt von Replikator (15. August 2011 um 22:22)

    • Offizieller Beitrag

    Ok, im Intro-Posting von Dir ist also schon mal das "Ziel" erläutert. Mit der Bee-Software bin ich daneben. Keine Ahnung, was hier alles bei der Initialisierung (z.B. LED) so geschieht.

    Derzeit ist in dem kleinen Demo-Program von Dir der Timer anscheinend aktiviert und löst wahrscheinlich bei jedem "0" einen Interrupt aus, aber Du benutzt den Interrupt auch nicht.

    Wie o.g. bereits erwähnt, würde ich nicht mit Interrupts arbeiten. Erstens machen die bei diversen Empfangsstörungen nur Ärger und zweites weiß ich nicht, ob Du auf jeden Eingang Deines AVR (wo ein Empfängersignal draufgeht), einen Interrupt auslösen kannst.

    Bleiben wir bei Deinem kleinen Beispiel.

    In der Arbeitsschleife ( => while(1) ) steuerst Du jedesmal die LED an. Und zwar einmal "Ein" und dann wieder "Aus". Dazwischen hast Du eine Delayfunktion.
    Delays sind zum Austesten von einfachen Versuchsprogrammen ideal. Aber bei "scharfen" Programmen sind sie sehr hinterlich. Warum? In dieser Zeit tritt der Prozessor auf der Stelle und hat nichts anderes zu tun, als darauf zu warten, dass die Zeit vergeht. Ist (um es mal bildlich auszudrücken) so, als wenn in einem Restaurant der Kellner vor der Essensausgabe so lange wartet, bis Dein Essen fertig ist. Inzwischen sind aber einige andere Gäste gekommen und hätten auch gerne was bestellt oder etwas zum Trinken serviert bekommen.

    Heißt: Die Delays wirst Du jetzt mal rausschmeißen und dafür Deinen Timer einbinden. Dies ist jetzt nur ein Vor-Test, damit Du mit dem Timer das Laufen lernst. ;)

    Wie weit kannst Du den Prescaler vom Timer erhöhen? Geht der bis 255? Damit würde ein Herunterteilen des Prozessortakts an den Timer soweit funktionieren, dass Du damit die LEDs "verzögern" kannst.

    Definiere eine Variable, die Dir den Zustand der LED anzeigt. (An alle anderen C-Programmierer: Ich weiß, dass es einfacher geht, aber wir wollen hier ja eine übersichtliche Anleitung geben.... die Tricks kommen später.)

    Also im allgemeinen Teil eine globale Variable definieren:

    unsigned char vLEDStatus; // enthält den aktuellen Status der LED

    Ich habe jetzt keine Ahnung, wie Du an den Timerwert herankommst. Schau mal in die Anleitung nach, da muss es ein Register geben, das den aktuellen Zählwert (bei einem 8bit-Timer von 0 bis 255) enthält.
    Ich taufe dieses Register jetzt mal provisorisch "Timer1Count". (Heißt bei Dir natürlich anders)

    In diesem Timer1Count ist jetzt immer der aktuelle Zählwert von dem Prozessor-Timer enthalten. Den fragen wir regelmäßig ab.

    vLEDStatus=0; // vor der Arbeitsschleife unsere Statusvariable auf einen definierten Wert setzen

    while (1) // Beginn der Arbeitsschleife
    {
    ....if (Timer1Count==0) // Abfragen, ob der Timer genau in diesem Augenblick zufällig den Wert Null hat
    ....{
    ........// Ja, Glück gehabt, genau bei Null den Timer erwischt
    ........if (vLEDStatus==0) // wenn die LED ausgeschaltet ist, dann jetzt einschalten
    ........{
    ............led_set(LED_R_RD, 1); // LED einschalten
    ............vLEDStatus=1; // Den Status der Variable ändern. Warum? Beim nächsten Aufruf könnte die LED ja schon eingeschaltet sein. Dieser Teile würde dann erneut aufgerufen. =>Unnötig
    ........}
    ........// Jetzt die Gegenabfrage, um die LED beim nächsten Mal, wenn der Timer wieder Null ist, auszuschalten
    ........if (vLEDStatus==2) // Die LED ist jetzt für eine Zyklusdauer eingeschaltet gewesen
    ........{
    ............led_set(LED_R_RD, 0); // LED ausschalten
    ............vLEDStatus=3; // Den Status der Variable ändern. Warum? Beim nächsten Aufruf könnte die LED ja schon ausgeschaltet sein. Dieser Teile würde dann erneut aufgerufen. =>Unnötig
    ........}
    ....} // Ende von der Abfrage, ob der Timer jetzt Null ist

    ....else // Der Timer hat einen Wert von 1 bis 255. Also nicht "Null", um die LED ein- oder auszuschalten.
    ............// Jetzt einmalig eine/zwei Aktionen unternehmen.
    ....{
    ........if (vLEDStatus==1) // wenn im letzten Zyklus der Timer Null war und die LED eingeschaltet wurde, dann vorbereiten für das nächste Ausschalten
    ........{
    ............vLEDStatus=2; // Jetzt ist die Variable scharfgeschaltet, um beim nächsten Timer=0 die LED auszuschalten
    ........}
    .......if (vLEDStatus==3) // wenn im letzten Zyklus der Timer Null war und die LED ausgeschaltet wurde, dann vorbereiten für das nächste Einschalten
    ........{
    ............vLEDStatus=0; // Jetzt ist die Variable scharfgeschaltet, um beim nächsten Timer=0 die LED einzuschalten. Der Zyklus beginnt von vorne
    ........}
    ....} // Ende von der "else", also wenn der Timer zwischen 1 und 255 ist
    } // Ende der while-Arbeitsschleife


    Der Gag ist also, dass der Timer genau dann erwischt wird, wenn er Null ist. Dann wir einmalig was gemacht, nämlich die LED ein- oder ausgeschaltet.
    Der Clou: Angenommen, der Timer ist Null und wir haben die LED gerade eingeschaltet. Dann kommt wir beim nächsten Rundgang durch die Arbeitsschleife genau an die gleiche Stelle. Der Timer ist aber immer noch Null. Damit würden wir die Prozedur zum Einschalten der LED noch einmal aufrufen. Das braucht es nicht. (Würde in diesem einfachen Fall jetzt zwar kein Unheil anrichten, aber wir wollen ja für die "schärferen" Sachen trainieren...).
    Darum ist also die Variable vorher von 0 auf 1 gesetzt worden. Beim zweiten, nachfolgenden Durchgang der Arbeitsschleife ist der Timer immer noch Null, aber die Variable ist inzwischen 1. Also kein erneutes Einschalten der LED.
    Irgendwann ist der Timer nicht mehr 0 , sondern wurde durch den Prozessor auf 1 erhöht. Damit kommen wir in die Else-Schleife rein. Die Variable steht auf "1" und wird jetzt auf "2 gesetzt. Das ist die Vorbereitung, damit wir die LED beim nächstenmal, wenn der Timer auf Null steht, wieder ausschalten.
    Beim nächsten Timer=0 wird abgefragt, ob die Variable auf 0 steht. Tut sie nciht, sie steht auf 2. Also die LED ausschalten. Damit das Ausschalten wieder nur einmalig passiert, wird die Variable auf 3 erhöht.
    Dann das gleiche wie beim Einschalten: Erst wenn der Timer von 0 auf 1 erhöht wurde, wird die Variable wieder so umgesetzt, dass die nächste Aktion stattfinden kann. Die nächste Aktion wäre, dass bei Timer=0 die LED eingeschaltet werden kann. Das kann dann erfolgen, wenn die Variable auf "0" steht. Das tun wir dann auch in der "else", wenn sie vorher auf "3" gesetzt worden war.

    Nochmal zur Betonung: Man kann das auch viel einfacher machen. Aber dieses Beispiel soll ein bißchen darauf hinzielen, wie man mit Statusvariablen arbeitet und wie man sozusagen vermeidet, dass eine Prozedur zweimal aufgerufen wird. Also eine "Software-Entprellung". Mit "Case" etc will ich hier noch gar nicht agieren. Machen wir das mal auf "Hausfrauenmethode" mit "if" und "else". :D

    Replikator: kannst Du dieses Beispiel mal auf die Bee überführen? Interessant wäre, wie Du dem Timer in die Eingeweide greifen kannst.

    • Offizieller Beitrag

    Replikator: Andreas, schau in Deiner Anleitung bitte mal nach, wie man an die Ports von der Bee herankommt.
    Es geht im nächsten Schritt also mal darum, wie man das Empfängersignal in das Programm reinkriegt. Das erfolgt normalerweise über Porteingänge. Stichwort im Verzeichnis wäre "I/O" oder"I/O-Ports" oder "Ports". Ich tippe darauf, dass das so ähnlich läuft wie mit der Ansteuerung der LED. Nur dass wir diesmal ein Signal abzapfen, statt auszugeben.

  • Wie gesagt, der Support ist 1A, so liefern einem die Firma quasi alles dazu. Sei es ein eigenes Wiki, den kompletten Schaltplan für alles und natürlich auch die Library


    Hier ist auch die PORT-/PIN-Beschreibung. Hoffe das ist so ziemlich das, nachdem du gesucht hast.
    Find ich jetzt nicht optimal gemacht von denen aber es gibt ja noch den Schaltplan.

    Da ich auf X1 bin, heisst das also, das ich am Pin "schnell nachschaut" "PA0" und "PA1" hänge.


    Danke schonmal für deine nächste *Lehrstunde*
    Klingt alles noch sehr komplex, werd mich aber rein hängen.
    Das mit den Delays ist mir klar, regt mich auch auf, ist aber wie du schon sagtest, für meine bisherigen Noob-Programme ausreichend. :)
    Freu mich schon wenn ich auch das mit den Timern realisieren kann! :)

  • Zitat

    Wie weit kannst Du den Prescaler vom Timer erhöhen? Geht der bis 255? Damit würde ein Herunterteilen des Prozessortakts an den Timer soweit funktionieren, dass Du damit die LEDs "verzögern" kannst.

    Meine das geht bis max clk/1024. Da mein uC aber eine Taktfrequenz hat von 16MHz, habe ich doch dennoch eine Periodendauer von ~16ms bei meinem 8bit-Timer, sehe ich das richtig?? Ist das nicht dennoch viel zu fix?

    • Offizieller Beitrag
    Zitat

    Original von Replikator

    Meine das geht bis max clk/1024. Da mein uC aber eine Taktfrequenz hat von 16MHz, habe ich doch dennoch eine Periodendauer von ~16ms bei meinem 8bit-Timer, sehe ich das richtig?? Ist das nicht dennoch viel zu fix?

    Kann sein, dass das so ist. Normalerweise wird der Timer aber nicht bei jedem Prozessortakt erhöht, sondern z.B. bei jedem vierten Prozessortakt. (Müßte in der Beschreibung stehen).
    Ein Prescaler von 1024 hört sich gut an. Das reicht. Der o.g. Test mit der LED ist ja nur dafür da, um die Brücke zwischen Timer und dem Timerregister zu ergründen. Wenn die LED dann etwas nervös blinkt, dann macht das ja nichts. Notfalls mit dem Oszi beweisen, dass hier alle 16ms was passiert....
    :D

    Aber: man kann natürlich noch eine Software-Bremse in das o.g. Progrämmchen mit einbauen. :D
    Wir starten normalerweise bei "0". Dann wird auf "1" erhöht, um anschließend mit "2" die LED wieder auszuschalten.
    Was hindert uns daran, die LED bei 0 einzuschalten, aber erst bei 128 wieder auszuschalten. Bei jeder Erhöhung der Variablen muss der Timer erst mal durch den Nulldurchgang gehen. => Wenn also Bedarf besteht, dann können wir das o.g. LED-Progrämmchen noch ein bißchen auffrisieren. Probier's aber bitte zunächst mal mit der o.g. "Einfachversion".

  • Hi, also wie ich das sehe tickt der bei jedem Prozessortakt.

    In dem Datenblatt des ATmega16 steht:

    Zitat

    Internal Clock Source

    The Timer/Counter can be clocked directly by the system clock (by setting the CSn2:0 = 1). This provides the fastest operation, with a maximum Timer/Counter clock frequency equal to system clock frequency (fCLK_I/O). Alternatively, one of four taps from the prescaler can be used as a clock source. The prescaled clock has a frequency of either fCLK/8, fCLK/64, fCLK/256, or fCLK/1024.


    Zitat

    Ich habe jetzt keine Ahnung, wie Du an den Timerwert herankommst. Schau mal in die Anleitung nach, da muss es ein Register geben, das den aktuellen Zählwert (bei einem 8bit-Timer von 0 bis 255) enthält.

    Also Laut Microcontoller.net-Tread wäre das ja das TCNT0

    Zitat

    Der aktuelle Zählerstand steht in TCNT0

  • Hehe ich glaube ich habs!

    Im Anhang mein Code nach deinen Vorgaben. Hoffe/Denke das ist richtig, denn die LED Blinkt! Zwar Argh-Schnell, dass man fast nur ein flackern sieht, aber das ist ja klar gewesen!!

    Sollte das korrekt sein erstmal DANKE! Allein bei dem simplen Programmaufbau hab ich schon ne menge gelernt, wie simpel man ein kleines Programm basteln kann... ;)

    Jetzt wäre meine nächste Frage, wie kann man die LED langsamer blinken lassen. Werde Zuhause mal deinen nächsten Tipp probieren.
    Aber wie man das z.B. mit 1Hz blinken lassen kann... KA

    Nun aber erstmal wieder zurück an die Arbeit (blöde zu kurze Pausen...) :)

    2 Mal editiert, zuletzt von Replikator (16. August 2011 um 13:51)

    • Offizieller Beitrag

    Sieht doch schon mal gut aus !

    Kleiner Tipp für die zukünftige Karriere als C-Programmierer: Bei meiner Demo habe ich immer sehr ausführliche Kommentare hingeschrieben. Das diente jetzt nicht nur zur Erläuterung für die Allgemeinheit, sondern ich mache das bei mir grundsätzlich in all meinen Programmen so. Hat den Vorteil, dass man bei Änderungen nicht erst wieder "lernen" muss, was man vor ein paar Wochen gemacht bzw. "verbrochen" hat. Denn oft baut man ja ein paar Tricks ein, die man sich anhand nächtelanger Versuche mit der Hardware erst mal erarbeiten muss und die man dann eben nach einiger Zeit wieder vergessen hat. Darum: Bitte in den "schärferen" Programmen das Kommentieren nicht vergessen.

    Wichtig ist dabei: Einstellungen von Registern etc. : Welches Bit ist gesetzt und warum wurde das gemacht. Oder z.B. bei Portbelegungen, damit man weis, welcher Port in der Hardware "was" machen soll.

    • Offizieller Beitrag

    Im ersten Versuch, die LED langsamer blinken zu lassen, würde ich zur Demo eine zwar etwas umständliche Methode vorschlagen. Aber sie zeigt Dir, wie man "Ping-Pong" spielen kann.

    while(1)
    {
    ....if (TCNT0==0) // Der Timer ist exakt auf 0
    ....{
    ........if(vLEDStatus==0)
    ........{
    ............led_set(LED_L_RD, 1);
    ............vLEDStatus=1;
    ........}

    ........if(vLEDStatus==2)
    ........{
    ............vLEDStatus=3;
    ........}

    ........if(vLEDStatus==4)
    ........{
    ............led_set(LED_L_RD, 0);
    ............vLEDStatus=5;
    ........}
    ........if(vLEDStatus==6)
    ........{
    ............vLEDStatus=7;
    ........}
    ....}

    ....else // Der Timer ist hier zwischen 1 und 255
    ....{
    ........if(vLEDStatus==1)
    ........{
    ............vLEDStatus=2;
    ........}

    ........if(vLEDStatus==3)
    ........{
    ............vLEDStatus=4;
    ........}

    ........if(vLEDStatus==5)
    ........{
    ............vLEDStatus=6;
    ........}

    ........if(vLEDStatus==7)
    ........{
    ............vLEDStatus=0;
    ........}

    ....}
    }

    Die LED müßte jetzt langsamer blinken. Aber wenn sie immer noch zu schnelle ist, dann einfach ein paar "Ping-Pongs" mehr einbauen.

  • Jau, das mit den Kommentaren... Das muss ich mir noch angewöhnen. :) Dass das wichtig ist weiss ich, nur bin ich da etwas... bequem... In den schwierigeren Programmen werde ich das aber natürlich machen. :)


    Hab das Programm noch etwas erweitert. Eine Art Lauflicht. :)
    Einfacher wäre das aber wohl mit einer "for-Schleife". :)
    Aber für längere Zeiten kann man das immernoch relativ vergessen. Um sagen wir mal 1Hz zu bekommen müsste ich so ja Unmengen an Code-Zeilen schreiben. :)

    • Offizieller Beitrag
    Zitat

    Einfacher wäre das aber wohl mit einer "for-Schleife".


    Selbstverständlich.
    Ich wollte Dir jetzt nur zeigen, wie man das zum Einstieg macht. So wie Du jetzt verschiedene LEDs ansteuerst, so kann man damit auch andere Sachen machen. Z.B. bei "1" den linken Motor ansteuern, bei "2" den rechten Motor ansteuern, usw.... ;)

    Nachdem der Timer alle 16ms den Nulldurchgang hat, brauchst Du für die LEDs eine Zykluszeit von 500ms (=500ms On und 500ms Off => = 1Hz). Macht somit eine Zählerzeit von 32 Schleifendurchläufen.

    Als Erleichterung am Programmanfang eine Konstante definieren:

    #define cCountMax 0x20 // Vorbelegung des Schleifenzählers für 32 Durchläufe mit jeweils 16ms


    Also machen wir die künstliche Zeitverzögerung jetzt mit einer Zählschleife. Dazu werden noch ein paar Hilfsvariablen benötigt.

    unsigned char vLEDStatus; // Aktueller Status der LED-Zustände
    unsigned char vJitter; // Flag, um festzustellen, ob der Nulldurchgang bei der letzten Arbeitsschleife schon durchlaufen wurde. 0xFF : Was zu tun . 0x00 : nein danke, ist schon erledigt.
    unsigned char vCount; // Zähler für die Anzahl der Timer-Nulldurchgänge, bis sich der Zustand der LED ändern darf


    vLEDStatus=0;
    vJitter=0xFF; // Markieren, dass der Nulldurchgang noch nicht abgearbeitet wurde
    vCount=cCountMax; // Schleifenzähler mit dem Wert für 500 ms vorbelegen

    while(1)
    {
    ....if (TCNT0==0) // Der Timer ist exakt auf 0
    ....{
    ........if (vJitter) // Flag gesetzt: Der Nulldurchgang des Timers ist noch nicht abgearbeitet => also was tun
    ........{
    ............vCount = vCount -1; // Den Nulldurchgangszähler verringern
    ............if (!vCount) // Ist der Schleifenzähler schon Null ?
    ............{
    ................// Ja, der Schleifenzähler ist Null. Also an dem Zustand der LEDs was ändern
    ................if(vLEDStatus==0) // Die LED ist ausgeschaltet, also jetzt einschalten
    ................{
    ....................led_set(LED_L_RD, 1);
    ....................vLEDStatus=1; // Den Status setzen: LED ist jetzt an
    ................}
    ................else // Die LED ist aktuell eingeschaltet, also jetzt ausschalten
    ................{
    ....................led_set(LED_L_RD, 0);
    ....................vLEDStatus=0; // Den Status setzen: LED ist jetzt aus
    ................}
    ................vJitter=0; // Jetzt das Flag löschen, das besagt, dass bei einem Nulldurchgang des Timers etwas geprüft oder bearbeitet werden muss. (Könnte ja noch der gleiche Nulldurchgang wie eben sein)
    ................vCount=cCountMax; // Die Schleife wurde erfolgreich abgearbeitet. Jetzt den Schleifenzähler wieder mit dem Wert für die nächsten 500 ms vorbelegen
    ............} // Ende von der Schleifenzähler-Abfrage
    ........} // Ende von der Flagabfrage, ob der Nulldurchgang des Timers schon bearbeitet wurde
    ....} // Ende von der Abfrage, ob der Timer genau auf Null ist.

    ....else // Der Timer ist hier zwischen 1 und 255
    ....{
    ........vJitter=1; // Jetzt das Flag setzen, dass beim nächsten Nulldurchgang des Timers der Schleifenzähler wieder bearbeitet werden darf

    ....}
    }

    Wir könnten diesen Programmteil jetzt noch ein bißchen straffen, aber dann wird es unübersichtlicher.

    Ich nehme an, die Negierung mit (!vCount) ist geläufig? Das "!" sagt, dass hier geprüft wird, ob die Variable vCount den Wert Null enthält.

  • Hi,

    ich muss mir das heute nochmal genauer anschauen. Das Grundprinzip hab ich denke ich gerafft. :)
    Nur stimmt wohl etwas mit meiner Rechnung zur "Periodendauer" des Timers nicht. Mit dem Wert von cCountMax=0x20 blinkt die LED immernoch viiiieel zu schnell. Hab jetzt stumpf den Wert auf 0xff gesetzt. Und grob würde ich sagen hab ich immernoch ~2Hz. :/

    Quellcode wie immer im Anhang. :)


    Was mir noch nicht so ganz klar ist. Das Werkzeug "Flag" Was genau macht man mit dem, bzw. wo/wie setzt man den ein?


    Doofe Frage noch, wie kommt man/du eig. auf solche Variablen? In den Nibo-Beispielen heissen die auch immer komisch. cCount, vCount oder vJitter? Was z.B. bedeutet das c, das v oder das Jitter? :)


    Zitat

    Ich nehme an, die Negierung mit (!vCount) ist geläufig?


    Ehrlich gesagt, Nö ;) Geht das überall mit dem "!" ?

    3 Mal editiert, zuletzt von Replikator (17. August 2011 um 13:43)

  • Hab grad mal ein wenig probiert und den Nulldruchgangszähler auf -0.5 gesetzt.

    vCount = vCount -0.5;

    Hätte ja nicht gedacht das der das frisst, aber die LED blinkt nun gaaanz gemächlich. Wie kommt denn das? Ich hab jetzt ~0,16Hz (3s-3s Pulse) :?

    • Offizieller Beitrag
    Zitat

    Original von Replikator
    Hab grad mal ein wenig probiert und den Nulldruchgangszähler auf -0.5 gesetzt.

    vCount = vCount -0.5;

    Hätte ja nicht gedacht das der das frisst, aber die LED blinkt nun gaaanz gemächlich. Wie kommt denn das? Ich hab jetzt ~0,16Hz (3s-3s Pulse) :?


    8o
    Die Variable hast Du schon mit "unsigned char" definiert ? Im Prinzip hast Du ein halbes Bit weggenommen, was bei Byte-Variablen nicht geht. Wenn die Variable als Floating-Point definiert worden wäre, dann ginge das. Aber so..... *VerstehDieWeltNichtMehr*

    Zitat

    Das Werkzeug "Flag" Was genau macht man mit dem, bzw. wo/wie setzt man den ein?


    Ein Flag (kommt vom englischen mit "Flagge") ist eigentlich die Umschreibung von einer boolschen Variablen. Da gibt es nur "Ja" / "Nein" bzw. "Ein" oder "Aus".
    Im Prinzip bräuchte man von einem Byte nur ein einziges Bit, um den Zustand zu signalisieren. Aber im Programm tut man sích leichter, wenn man dafür ein ganzes Byte opfert.

    Hintergrund: Der Mikrocontroller kann in seinem Akku-Register abfragen, ob da ein Wert drin steht, der "Null" ist, oder ob da was drinnsteht, was "größer Null" ist. Das sind für den Mikrocontroller ganz einfache Operationen.
    Um im Programm also eine Entscheidung zu treffen, ob was gemacht werden muss oder nicht, schreibt man in eine Variable entweder "Null" oder "255". Natürlich würde auch "1" statt "255" funktionieren.

    Der Ausdruck

    if (vDemoFlag)

    prüft also nur ab, ob in der Variablen "Null" enthalten ist oder nicht. Wenn ein Wert größer Null enthalten ist, dann wird die if-Anweisung ausgeführt. Wenn Null enthalten ist, dann wird die if-Anweisung übersprungen.

    Dann die Invertierung von dem Ganzen:

    if (!vDemoFlag)

    Ist in der Variablen Null enthalten, dann wird die if-Anweisung ausgeführt. Sprich: "If Not Demoflag istgleich Zero, then do it". Betonung liegt jetzt auf "Not", denn das macht dieses "!"

    Zitat

    Doofe Frage noch, wie kommt man/du eig. auf solche Variablen? In den Nibo-Beispielen heissen die auch immer komisch. cCount, vCount oder vJitter? Was z.B. bedeutet das c, das v oder das Jitter?


    Ich muss natürlich einschränken, dass ich mir das C weitgehend selbst beigebracht habe. Natürlich hatte ich sporadische Unterstützung von Leuten, die ich manchmal fragen durfte. Aber der ganze Programmierstil ist weitgehend auf meinem Mist gewachsen. So hat sich halt im Laufe der Jahre (bei C) oder Jahrzehnte (bei Modula2 und Visual-Basic) ein bißchen was entwickelt, was sehr praktisch ist, um sich später im Programm zurechtzufinden.

    Das "v" bedeutet "Variable". Ist also im RAM gehalten und kann verändert werden.
    Das "c" ist eine Konstante, die am Programmanfang fest vorgegeben wird.

    In C arbeite ich fast ausnahmslos mit 1Byte-Variablen. In Ausnahmefällen mit Integer. Auch hier mache ich stellenweise einen Hinweis in den Variablennamen rein, was für ein Typ dahintersteckt. Also würde z.B. "viMotorSpeed" bedeuten, dass hier eine RAM-Variable mit 2 Byte vorliegt, die mir dann für den Motor den Pulsweitenmodulator ansteuert. Integer braucht man, wenn man z.B. einen Wert mitteln will.

    Das brauchst Du zwar erst in ein paar Tagen in Deinem Programm, aber jetzt gleich mal im Vorgriff: Wie mittelt man die eingelesenen Motorgeschwindigkeiten?

    Alle 20 ms wird von der Funke ein PWM-Signal zum Empfänger geschickt. Der Empfänger kann aber auch versehentlich mal einen Störpuls bekommen. Um den zu minimieren, liest man einfach vier empfangene Werte ein, addiert sie und teil dann durch vier. Also gemittelt.
    Nur: Der empfangene Wert hat 1 Byte. Da passt nur ein Wert rein. Also werden alle vier empfangenen Werte in einem Integer (=2Byte) addiert. Dann durch zweimaliges rechts-shiften durch vier geteilt. Und schon liegt in der Integer-Variablen ein Wert vor, der wieder in eine Byte-Variable passt.

    Ist Dir das mit dem "Rechts-Shift" zum Teilen geläufig ?

    Zitat


    Die Variablennamen sind immer eine Entscheidung persönlicher Willkür. Generell sollte man sinnvolle Namen verwenden, die einen Bezug zur Anwendung darstellen. Mit den früheren Bezeichnungen zur Commodore-Basic-Zeit wie "A1", "A2", usw weis kein Schwein, was da im Programm passiert. Aber die Bezeichnung "vRFPulsTime1" deutet doch eher darauf hin, dass hier die empfangene Pulslänge vom Funk-Empfangskanal 1 enthalten ist, oder ?
    Ich setze die Variablennamen auch gerne aus verschiedenen Begriffen zusammen, damit man weis, in welchem Programmteil hier diese Variable vorwiegend zur Anwendung kommt.

    "Jitter" dürfte aus der analogen Meßtechnik bekannt sein. Wenn ein Signal nicht sauber ansteht, sondern z.B. bei der Pulsweitenmodulation die Pulsweite selbstständig geändert wird. Merkt man bei uns, wenn z.B. in der Ant-Arena ein paar Ants plötzlich "zum Zucken" anfangen. Dann ändert sich geringfügig das empfangene PWM-Signal. Der Empfänger gibt das 1:1 weiter, was auch richtig ist. Aber die meisten Fahrtregler-Programm haben hier keine "Totzeit" drin. Bei der kleinsten Änderung wird das geänderte Signal sofort an die Motoren weitergeleitet. Bei meiner Software muss sich der Puls im Vergleich zum "Ruhewert" erst mal um einen bestimmten Betrag ändern, bevor das die Motoren mitkriegen dürfen.

    "Jitter" in o.g. Demo-Beispiel sollte nur darauf hinweisen, dass man beim Abfragen dieser Arbeitsschleife u.U. schon mehreremals in den "Timer=0"-Abfrage hineinkommt, wobei man das aber nur einmal auswerten darf.

    • Offizieller Beitrag
    Zitat

    Nur stimmt wohl etwas mit meiner Rechnung zur "Periodendauer" des Timers nicht. Mit dem Wert von cCountMax=0x20 blinkt die LED immernoch viiiieel zu schnell


    Ich habe mir den Quellcode von Deinem Timer2.zip angesehen. Ich glaube, der Fehler liegt in meinen Vorgaben. Die Variable vJitter wird zu spät gelöscht. Normalerweise müßte sie sofort gelöscht werden, wenn die Zählervariable vCount um 1 reduziert worden ist. Derzeit wird aktuell jedesmal, wenn Timer=0, ständig die Variable vCount reduziert. Also öfters und nicht nur einmalig, wenn der Timer=0. Somit wird dann als Folge bei jedem Timer=0 die LED ein bzw. ausgeschaltet. Somit ist die ganze Schleifenzählerei derzeit sinnlos.

    Setz' den "vJitter=0" mal direkt unter die Zeile, wo Du den Zähler um den Wert 1 verringerst.

  • Hi,

    hab grad mal alles in Ruhe gelesen und den von dir genannten Tipp im Programm geändert.

    Fazit:

    Hab wieder viel gelernt und meine LED Leuchtet exakt so wie sie soll. Mit dem Wert...

    cCountMax 0x20 hab ich genau 1Hz
    cCountMax 0x40 hab ich genau 0,5Hz
    etc.


    DANKE!

    Die Bezeichnungen mit den "v" und "c" werde ich dir mal stibitzen, macht auf alle Fälle Sinn!


    Zitat

    Ist Dir das mit dem "Rechts-Shift" zum Teilen geläufig ?

    Nö... :/ Mit Rechnen über uC hab ich bislang noch rein garnichts am Hut gehabt...

    • Offizieller Beitrag
    Zitat

    Die Bezeichnungen mit den "v" und "c" werde ich dir mal stibitzen, macht auf alle Fälle Sinn!


    Das befürworte ich auf alle Fälle ! Manche professionellen C-Entwickler mögen sich bei meinem Code vielleicht arrogant abwenden, aber das Zeugs funktioniert und ich kann auch noch nach Jahren meinen Code lesen und verstehen.


    Bezüglich "Links-Shift" oder "Rechts-Shift":

    Es rächt sich, wenn man statt mit Assembler-Programmierung gleich mit einer Hochsprache auf so einen armen Prozessor losgeht. In Maschinensprache, wenn man wirklich mit den zur Verfügung stehenden Prozessorbefehlen arbeitet, weiß man genau, was der kleine Kerl will und wie man es ihm am einfachsten beibringt. In C kannst Du dafür sorgen, dass über Gleitpunkt-Formate die Berechnung von "1 plus 1" mit jeweils 4 Byte erfolgt und das Ergbenis mit 8 Stellen Genauigkeit hinter dem Komma ausgegeben wird. :rolleyes:

    Also zum Shiften:
    Du weißt was ein Byte ist. :D Dabei hat jedes Bit eine bestimmte Bedeutung:
    Bit 0 = Wert 1
    Bit 1 = Wert 2
    Bit 2 = Wert 4
    Bit 3 = Wert 8
    Bit 4 = Wert 16
    Bit 5 = Wert 32
    Bit 6 = Wert 64
    Bit 7 = Wert 128

    Je nachdem, welches dieser Bits gesetzt ist, werden die einzelnen Werte dieser Bits zusammengezählt.

    ...B7..B6..B5..B4..B3..B2..B1..B0
    ...X....X....X....X....X....X....X....X

    Um z.B. die Zahl 6 (Dezimal) darzustellen, werden die beiden Bits 1 und 2 gesetzt. Der Rest bleibt gelöscht.

    ...B7..B6..B5..B4..B3..B2..B1..B0
    ..._...._...._...._...._....1....1...._


    Bei den Prozessoren gibt es den Befehl, das Akku-Register (bei den PIC-Prozessoren heißt es "Work-Register") um ein Bit nach rechts oder links zu schieben. Das ist "shiften". Zur Betrachtungsweise: "Rechts" ist dabei so, dass das minderwertigste Bit ( LSB =Bit0) rechts liegt, während das höherwertigste Bit (MSB = Bit7) links lliegt.
    Bei einem "Rechts-Shift" wird also Bit 0 erbarmungslos nach rechts hinausgeschoben und ist somit verloren. Das Bit1 wird zum Bit0, das Bit2 wird zum Bit1 usw.

    Wenn also das o.g. Beispiel mit dem Wert 6 um eine Stelle rechts geshiftet wird, dann ergibt sich folgende Bitveränderung:


    ...B7..B6..B5..B4..B3..B2..B1..B0
    ..._...._...._...._...._...._....1....1

    Also vom Wert her "3". Die Zahl 6 wurde zur Zahl 3. Somit genau die Hälfte bzw. "Geteilt durch zwei". Eigentlich logisch, denn die Bitstrukturierung ist ja genau so aufgebaut, dass jedes höhere Bit genau den doppelten Wertinhalt hat.

    Jetzt mal ein Beispiel aus dem Fahrtregler: Wir wollen unsere Messwerte "glätten". Ich muss hier jetzt einfachere bzw. kleinere Werte nehmen, sonst reicht mir ein einziges Byte nicht mehr aus....

    Erster eingelesener Wert vom Empfänger: 20 (Dezimal)
    Zweiter eingelesener Wert vom Empfänger: 18 (Dezimal)

    Sieht Binär also so aus:
    20:....B7..B6..B5..B4..B3..B2..B1..B0
    .........._...._...._....1...._....1...._...._

    18:....B7..B6..B5..B4..B3..B2..B1..B0
    .........._...._...._....1...._...._....1...._

    Um den Wert zu mitteln werden zunächst die beiden Zahlen zusammengezält (addiert). Addieren kann ein Microcontroller im Standard-Befehlssatz ohne Probleme.
    20 + 18 ergibt zu Fuß nachgerechnet 38:

    38:....B7..B6..B5..B4..B3..B2..B1..B0
    .........._...._....1...._...._....1....1...._

    Anschließend wird der zusammengezählte Wert (aus den zwei Messwerten) wieder durch zwei geteilt. Wie ich o.g. erklärt habe, erfolgt das durch einen Rechts-Shift. Es muss dabei 19 herauskommen.

    19:....B7..B6..B5..B4..B3..B2..B1..B0
    .........._...._...._....1...._...._....1....1

    ? Die o.g. Darstellung mit den Bit's und den 1sen ist verständlich ?

    Vergleich dann einfach mal die binäre Darstellung von 38 mit den 19 und Dir ist das Shiften somit klar.

    Eine Ungenauigkeit gibt es natürlich, wenn vor dem Shiften das Bit0 gesetzt ist. Denn ein halbes Bit gibt es nicht. Also wird somit abgerundet.


    @All: Frage in die Runde aus reinem Interesse: Liest hier sonst noch jemand mit, oder ist das ein Privat-Info-Austausch zwischen Replikator und mir ?

    Replikator: Wenn das soweit verständlich rübergekommen ist, dann würde ich im nächsten Run gerne mal Deine Bee per Fernsteuerung zum Laufen kriegen. Allerdings noch nicht über PWM, sondern einfach "Digial" die Motoren ansteuern. Kannst Du zwischenzeitlich mal die Register heraussuchen, die man braucht, um die beiden Motoren in Bewegung zu setzen (habe keine Ahnung, wie die Ansteuerung der Motoren ist. Ist da jeweils eine H-Brücke zum Ansteuern? Oder ist ein Motoren-IC da, bei dem zwei Bits (einmal "Fahren" und einmal "Fahrtrichtung") für die Ansteuerung genügt?