PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : NIBOBee und Timer- für Anfänger



Rabenauge
12.03.2010, 16:36
Hallo zusammen.
Seit einigen Tagen versuche ich, einen Timer an meiner Biene zum laufen zu bekommen und ich weiss nun, dass ich keine Ahnung von habe.

Tutorials habe ich jede Menge gefunden, die sich mit dem Thema auseinandersetzen, aber mehr als das gundsätzliche Wissen, wie sowas gehen müsste, kam dabei nicht heraus.

Kurz gesagt: ich möchte einen Timer programmieren, der mir für den Anfang einfach in bestimmten Zeitabständen eine LED blinken lässt, währen die Biene irgendwas anderes tut.
Ich denke, wenn ich das hinbekomme, komme ich soweit zurecht- es fehlen mir schlichtweg die Grundlagen, wie man sowas programmiertechnisch angeht.

Leider bin ich nicht wirklich fündig geworden, Tutorials, wie man mit nem ATMega16 eine Uhr programmiert, sind viel zu verwirrend, und ich denke, da bin ich nicht der Einzige, dem das so geht.
Ich will keine Uhr, die aus nem ATMega16 und _aller möglichen anderen Hardware_ besteht, sondern nur wissen, wie ich einen Timer im NIBOBee einrichte, der mir von Zeit zu Zeit einen Interrupt liefert.

Wäre nett, wenn man daraus vielleicht eine Art Workshop machen könnte, denn wie gesagt: ich bin mir sicher, dass das nicht nur mir so geht.
Es wäre wirklich schön, das ganze (und wichtig: an genau diesem Beispiel) mal richtig einsteigerfreundlich erklärt zu bekommen, auch das was, warum und wie, denn irgendwelche Codes abtippen bringt es nicht.

Tuxi-Halle
13.03.2010, 02:54
Hm. So richtig weiß ich ehrlich gesagt nicht, wie man das besser als hier http://www.rn-wissen.de/index.php/Timer/Counter_%28Avr%29
erklären könnte. Hast Du soetwas schon probiert?

Rabenauge
13.03.2010, 20:49
Ja.
Funktioniert auch, wenn ich standhaft bin und die Bibliotheken der Biene nicht benutze. Leider finde ich die aber nunmal ganz praktisch.

Es geht nicht darum, erklärt zu bekommen, wie so ein Timer _grundsätzlich_ funktioniert (googlen, lesen und auch ein einigermassen verstehen bekomme ich durchaus hin), sondern darum, dass ich in der NIBOBee-Doku beispielsweise kein Wort über die von den Bibliotheken bereits verwendeten Timer finden kann.
Somit kann ich zwar herumprobieren, darf aber jedesmal, wenn ich irgendeine Funktion der Bibliothek benutzen will (wie z.B. den Liniensensor, oder einfach delay) mit Überraschungen rechnen.

oberallgeier
14.03.2010, 00:05
... googlen, lesen und auch ein einigermassen verstehen bekomme ich durchaus hin ...Stimmt, ich weiß, daß DU das kannst, aber das kriegt nicht jeder hin ; - )


... in der NIBOBee-Doku beispielsweise kein Wort über die von den Bibliotheken bereits verwendeten Timer finden kann ...So etwas habe ich bisher auch nicht gefunden. BTW: ich nehme an, dass Du die Dokumentation der C Bibliothek für den NIBObee Roboter kennst. Die liegt vermutlich bei Dir auf der C: , möglicherweise auf Deiner Festplatte C:\Programme\NIBObeeLib\doc\html\index.html. Wenn nicht, dann müsstest Du Dir "die Bibliothek" installieren. Im NIBO-Wi ki gibts auch etwas, aber vermutlich nicht wirklich das, was Du suchst : http://www.nibo-roboter.de/wiki/Bibliothek , aber das habe ich (noch) nicht durchgängig getestet. Leider hatte bei Abgabe meiner Testberichte an nicai.systems nur festgestellt, dass die saubere, umfangreiche Auflistung der Routinen, Funktionen, Variablennamen etc. etwas sehr versteckt ist. Das Fehlen einer Tabelle aller benutzten Interrupts, Ti mer u.ä. war mir damals noch nicht aufgefallen.

Was ich vermisse ist etwas Ähnliches, wie das, was ich für meinen R2D03/Dottie und Nachfolger gemacht habe, siehe Attachment.

Rabenauge
14.03.2010, 00:41
Ja, klar kenne ich die Doku zur Bibliothek.
Ohne die wäre das Ganze -für mich- unlösbar. ;)
Sie ist im Grunde das einzige Nachschlagewerk, was mich wirklich weiter bringt, da sie eben auf den NIBOBee passt. Leider eben ist nicht alles drin, was man (Anfänger- angeblich ist der Roboter für solche doch?) braucht.

Für mich kommt vieles zusammen: erstens habe ich, da ich einige Jährchen mit der eingeschränkten Funktionalität von NQC zu leben gelernt habe (dort hast du kein Problem mit Timern, du kannst ganz einfach zehn getrennte Tasks zugleich laufen lassen, um alles andere kümmert sich die Firmware alleine) habe, nun eine schier unübersehbare Masse an Möglichkeiten. ;)
Das ist Klasse, das macht Spass, vorausgesetzt, man kommt damit klar, wenigstens kleinere Erfolge sind immer ganz nett.
Wenns aber _gar_ nicht weitergeht -wieviele Bienen liegen schon wieder, einstaubend herum? Wills gar nicht so genau wissen, meine wird das nicht tun.

Nun hatte ich vorher auch nie einen Grund, auch nur in ein Datenblatt zu sehen- wozu auch? Und ganz ehrlich: das meiste, was ich da sehe, ist -noch- kryptisches Zeugs. Ich werd das nötige lernen, das ist sicher, nur wann? Kleinere Erfolge, von Zeit zu Zeit wären eben...sagte ich ja schon. ;)

Deine Tabelle ist genial, so ungefähr stelle ich mir das vor, wenn man in die Doku der Bienenbibliothek schaut, macht es richtig Spass wenn da ein Link ist (ich glaub, der ist auf jeder Seite, immer mit dem gleichen, etwas...demotivierenden Ergebnis) "mehr Informationen".
Dort steht dann exakt das Gleiche wie _vor_ dem Link auch, kein Buchstabe mehr, auch keiner weniger. Tooooll

Vielleicht könnte man so eine Tabelle auch mal fürs Bienchen zusammentragen, ich bin sicher, dass hier einige Füchse die schon haben, oder? ;)
Soweit meine, bisher eher bescheidenen, Kenntnisse helfen können (ich bin hochmotiviert, seit ich allmählich zu begreifen beginne, was in dem kleinen Ding _wirklich_ steckt), wäre ich da `türlich auch dabei.
Achja: ich bin, glaub ich, zu doof in Word (oder was auch immer man dafür hernimmt) eine Tabelle zu erstellen, denk ich, kann man die vielleicht in nem 3D-Programm wie Blender...? :D

Wie gesagt, es macht mir wirklich Spass, und ich denke, man kann die Biene (der Aufbau war ja im Grunde echt ein Kinderspiel) durchaus auch Anfängern guten Gewissens empfehlen, aber es fehlt hat noch was.
Keine Ahnung, was Nicai noch so in der Hinterhand hat (irgendwo las ich auch mal einen mehr oder weniger versteckten Hinweis auf Erweiterungen), aber im Moment tut sich scheinbar nichts- und wenn, merkts keiner. Schon schade, zumal ja die Bibliothek auch noch nicht fertig ist, wie aus der Doku _auch_ hervorgeht.

So, morgen schaue ich mal mit den Timern weiter, mein heutiger Test (ich hatte den Timer0 "verbogen") hat....merkwürdige Ergebnisse gebracht, das schau ich mir morgen mal an-aber das Programm war auch noch nich wirklich fertig.

oberallgeier
14.03.2010, 09:42
... einige Jährchen mit der eingeschränkten Funktionalität von NQC zu leben gelernt habe ...Gleich JAHRE lang ! ? Ohh boy, this is Not Quite Cool.


... Datenblatt zu sehen- wozu auch? Und ganz ehrlich: das meiste, was ich da sehe, ist -noch- kryptisches Zeugs ...Klar, ist wie beim Auto. Wenn Du Autofahren aus dem Buch lernst - und nie vorher damit Kontakt hattest, kannst Du Dir auch nicht richtig vorstellen, wozu das Kupplungspedal ist. In der Fahrschule - einmal zu schlapp getreten - und alles ist klar.


... Deine Tabelle ... so ungefähr stelle ich mir das vor ... Vielleicht könnte man so eine ... fürs Bienchen zusammentragen ... Word ... eine Tabelle zu erstellen ....Ich habe die mit Excel gemacht. Excel ist zwar nix für Textverarbeitung (ausser in Gates own country) - aber für solche Tabellen sehr gut zu nutzen. Und ich brauchte das natürlich, da ich sowohl R2D03 (https://www.roboternetz.de/phpBB2/viewtopic.php?p=390196#390196) und R3D01, den kleineren Bruder mit 0,15 Liter, selbst geplant hatte. Ohne solche Pläne hätte ich ja den Überblick total verloren. Daher bin ich ganz sicher, dass die Hersteller so ein Blättchen irgendwo rumliegen haben, warum das nicht bereitgestellt wird . . . ? Ok, dann ... bin hochmotiviert ... wäre ich da `türlich auch dabei ... ... dann weißt Du ja, was Du in ruhigen Stunden tun darfst. Schon mal danke im Voraus *hihi*. Schönen Sonntag - und grüße ins OstE - manchmal bin ich in Auerbach/V.

Ein Tipp für die laufende Arbeit. Ich übersetze meine C(äh)-Codes mit AVRStudio. In der subdirectory ../default steht nicht nur die *hex, sondern auch eine Datei namens *.lls. Schau mal da rein, öffnen z.B. mit notepad. Da drin findest Du jedenfalls alle aktuell für Deine *.hex verwendeten Routinen namentlich aufgelistet. Und Timerroutinen sind ja meist irgendwo hinter "ISR(TIM..." versteckt.

Rabenauge
14.03.2010, 12:47
Ja, mein RCX dürfte inzwischen bestimmt zehn Jährchen aufm Buckel (okok, bei LEGO heisst das Noppen) haben. Ist noch der 1.5er.
Zu dem bin ich mehr durch günstigen Zufall gekommen, ich hab damals das Komplettset für DM 150 bekommen. ;)
Und wie weit man mit der LEGO-Programmierumgebung kommt, war mir ziemlich schnell klar. Ist ja ganz nett, um schnell was zu erleben, aber das wars dann auch.
Aber meine Philosophie ist halt, dass LEGO eben LEGO bleibt, und drum wollte ich auch die originale Firmware (da allerdings schon die letzte Version) laufen haben. Und mit Fremdteilen aufrüsten kam schonmal gar nicht in Frage- deswegen auch der Wechsel.
Den RCX halte ich trotzdem in Ehren, er kann bestimmt (so doof, wie man denkt, wenn man LEGO hört, ist das Ding keineswegs, der kann _richtig_ was) mal mit der Biene zusammen spielen, da bin ich sicher.
Ist halt irgendwie schick, auch die Programmierung per IR-Schnittstelle, kein Kabelgedöns, nix, hinstellen, Button klicken, und gegebenenfalls gleich noch per PC starten.

Excel also für die Tabelle, das ist genial, denke ich: ich weiss, dass es so ein Programm gibt, ich habe es sogar aber noch nie _irgendwas_ damit anfangen können. ;)
Dann lieber noch Word. :D

Naja, wird schon. Im Moment taue ich erstmal auf (ich hatte das Vergnügen, vier Stunden im Freien arbeiten zu dürfen heute) und dann schau ich mal, was die Biene sich über Nacht so überlegt hat.

radbruch
14.03.2010, 14:54
Hallo

Die Library der bee ist durch ihre vielen Einzeldateien für Anfänger nicht einfach zu durchschauen. Was den Einstieg zudem erschwert ist die Verwendung von Makros (in iodefs.h definiert) für den Zugriff auf die Kontrollerregister. Um die von der Lib belegten Timer zu finden bleibt letzlich nur der beschwerliche Weg jeweils die Init-Funktionen die Teildateien zu untersuchen. Dann wird man in motpwm.c fündig: Hier wird in motpwm_init() der Timer 1 zur PWM-Ansteuerung der Motoren parametriert.

Ein Timer/Counter besteht aus einem Zählregister TCNTx, einem Vergleichsregister OCRx und einem Kontrollregister TCCRx (x steht hierbei für die Nummer des Timers)

Das Zählregister wird mit dem Timertakt von der Hardware unabhängig vom Programm weitergezählt. Neben anderen Möglichkeiten kann man den Timertakt auch aus dem Kontrollertakt erzeugen, Mithilfe des im Kontrollregister einstellbaren Prescaler (=Vorteiler) kann man den Timertakt verlangsamen.

Bei bestimmten "Ereignissen" kann der Timer eine Interruptanforderung erzeugen. Diese wird vom Kontroller erkannt und bearbeitet, wenn der entsprechende Interrupt im TIMSK-Register (TIMer interrupt maSKe) aktiviert ist und die Interrupts generell erlaubt sind (sei()).

Ein mögliches Ereigniss ist der Überlauf des Zählregisters. Wenn dies auftritt, wird ein Overflow-Interrupt signalisiert und die zugehörige Interruptserviceroutine ISR(TIMERx_OVF_vect) ausgeführt. Ein weiteres Ereigniss ist eine Übereinstimmung des Zählregisters mit dem Inhalt des Vergleichsregisters. Dieses Ereigniss erzeugt einen Match-Compare-Interrupt ISR(TIMERx_COMP_vect) (treffender Vergleich)

Die Timer kennen verschiedene Modi. Beim normalen Mode wird das Zählregister stur durchgezählt. Beim CTC-Mode wird nur bis zum Wert im Vergleichsregister gezählt, dann ein Comp-Interrupt ausgelöst und das Zählregister wieder auf null gesetzt. In der Fast-PWM-Betriebsart wird ebenfalls aufwärts gezählt, allerdings können hier bei 16-bit-Countern die Anzahl der Bits des Zählregisters verändert werden. Letztlich gibt es noch den Phase-Correct-PWM-Mode. Dieser zählt aufwärts bis zum Maxwert des Zählregisters und zählt dann rückwärts (!) wieder auf null. Bei null wird dann ein Overflow ausgelöst, Comp tritt bei Gleichheit mit dem Vergleichsregister in beiden Zählrichtungen auf!

Jetzt wagen wir uns mal an die Praxis. Grundkenntnisse in C sollten vorhanden sein, Zugriff auf Register des Kontrollers sollte beherrscht werden, das Datenblatt sollte bereit liegen und der Schaltplan der bee könnte auch hilfreich sein. Die gestellte Aufgabe: Eine Led im Sekundentakt blinken lassen. Ich wähle LED3 an Port B3. Da Timer1 schon belegt ist, verwenden wir Timer0.

Bevor wir nun losprogrammieren müssen wir etwas rechnen. Die bee läuft mit 15MHz Kontrollertakt. Wenn wir den Takt mit 1024 vorteilen kommen wir auf knapp 15000 Zähltakte pro Sekunde, bei 256 Zählschritten bis zum Überlauf ergibt das ca. 57 ISR-Aufrufe pro Sekunde:


// LED3 ansteuern mit Timer 0 14.3.2010 mic

#include <nibobee/iodefs.h>
#include <avr/interrupt.h>

int main(void)
{
TCCR0 = (1<<CS02) | (1<<CS00); // Normal Mode, kein OC0-Pin, prescaler /1024
TIMSK |= (1<<TOIE0); // Timer0 Overflow-Interrupt erlauben

enable_interrupts();

DDRB |= (1<<PB3); // LED an einem Ausgang
PORTB &= ~(1<<PB3); // LED aus

while(1)
{
// nix zu tun
}

}
ISR(TIMER0_OVF_vect)
{
static uint8_t count=0;

count++;
if(count == 57) PORTB |= (1<<PB3); // LED an
if(count > 2*57)
{
PORTB &= ~(1<<PB3); // LED aus
count=0;
}
}

Der eigentliche Trick ist natürlich eine günstige Kombination aus Prescaler und Betriebsart des Timers zu finden. Das Beispiel dient nur zum Einstieg...

Gruß

mic

Rabenauge
14.03.2010, 15:10
Dann frage ich mal quer rein (sorry, genau da hing ich gestern Abend fest):


#include <nibobee/iodefs.h>
#include <nibobee/led.h>
#include <nibobee/delay.h>

int main(void)
{
// Timer 0 konfigurieren
TCCR0 = (1<<CS00);
TCCR0 = (1<<CS02); // Prescaler 1024

// Overflow Interrupt erlauben
TIMSK |= (1<<TOIE0);

enable_interrupts(); // Global Interrupts aktivieren

while(1==1) //mach was;
{ //hier nur ein Lauflicht mit drei LED`s


int ledNr;
led_set(ledNr,0);
delay(700);
for (ledNr=0;ledNr<3;ledNr++)
{
led_set(ledNr,1);
delay(350);
}
delay(350);
for (ledNr=3;ledNr>0;ledNr--)
{
led_set(ledNr,0);
delay(350);
}

}
}

/*
Der Overflow Interrupt Handler
wird aufgerufen, wenn TCNT0 von
255 auf 0 wechselt (256 Schritte),
d.h. ca. alle 2 ms <<<Werte stimmen nicht, da Prescaler auf 1024 statt auf 8 und
der ATMega16 auf 16MHz läuft
*/
ISR (TIMER0_OVF_vect)
{
/* Interrupt Aktion alle
(1500000/1024)/256 Hz = 57,22045898 Hz
*/
}


Der von mir gepostete Code stammt, mit kleinen Änderungen aus einem der Tutorials betreffs Timer.
Da ich noch nix habe, was auf den Interrupt hin passieren sollte, passiert auch nix, ist soweit klar.
Ergänzt habe ich den Code (einfach, um zu sehen, das was passiert) mit nem kleinen LED-Lauflicht auf LED 1-3.
Soweit funktioniert das Programm auch wie gewünscht, ABER: die LED`s glimmen nur, sie leuchten nicht ordentlich.
Ich hab den Code übrigens einfach aus nem Lauflicht herauskopiert, was durchaus bestens funktioniert (dort leuchten sie mit voller Helligkeit, wie das soll).
What`s this? ](*,)
Kann, wenn es weiterhilft, auch das richtig funktionierende Programm posten (ich schätze, das ist nichts als eine Erweiterung des LED-Programmes aus dem Tutorial der Biene gewesen).

Hardwareprobleme kann ich ausschliessen, das ursprüngliche Programm läuft nach wie vor tadellos.
Mögliche Fehler bei den Spielereien mit dem Timer auch, da man auch die betreffenden Zeilen auskommentieren kann, und trotzdem glimmen die LED`s nur.
Kurz gesagt: HILFE... ;)


So, nun schau ich mir dein Beispiel mal an...und bin gespannt.

radbruch
14.03.2010, 15:40
Hallo

Dann liege ich mit meinem Beispiel ja schon ganz richtig. Bei deinem Programm steckt hier ein Fehler:

TCCR0 = (1<<CS02); // Prescaler 1024

So werden alle Bits des TCCR0-Registers geändert, bis auf CS02 werden alle, auch CS00, auf null gesetzt (=gelöscht). Um die anderen Bits nicht zu verändern muss man den alten Inhalt des TCCR0 erst auslesen und dann gezielt das gewünschte Bit setzen:

TCCR0 = TCCR0 | (1<<CS02);

oder kurz:

TCCR0 |= (1<<CS02);

Die LEDs glimmen nur, weil du led_init(); vergessen hast. Damit werden die Pins der LEDs auf Ausgang gesetzt.



Da ich noch nix habe, was auf den Interrupt hin passieren sollte,...
Ein schlichtes
ISR (TIMER0_OVF_vect)
{
led_set(3,1);
} würde schon ausreichen um zu erkennen, ob die ISR überhaupt aufgerufen wird :)

Gruß

mic

Rabenauge
14.03.2010, 16:35
led_init();

Alles klar. Hat mich schier zur Verzweiflung gebracht. Ich habe nämlich _dein_ Progrämmchen auch dahingehend geändert, dass ich die LED über die Bibliotheksfunktion led_set aufrufe, und es glimmte auch nur.
Ich hatte absichtlich noch nichts reingeschrieben, was der Interupt auslösen soll, ich finde es günstiger, ein Problem nach dem anderen anzugehen, je komplizierter ein Programm ist, umso schwerer findet man Fehler.
Dass ich beim Prescaler was falsch gemacht habe, hatte ich befürchtet, und an deinem Beispiel habe ich auch gesehen, wie das richtig aussehen muss.

Was _ich_ aber nun noch nicht weiss, ist z.b. woher der "Zeittakt" für beispeilsweise delay() kommt, deswegen hab ich mich auch erst nicht an die Timer herangetraut.
Wenn ich an einem bereits benutzten Timer einfach den Prescaler ändere (wenn mans weiss, kann man es ja umgehen), dann ändere ich ja gleich mehrere Dinge.
Werden nicht die Liniensensoren auch getaktet?
Odometrie auch, oder?
Da _muss_ doch einer der Timer (ausser dem 01, der ja die PWM für die Motoren bereitstellt) noch mitspielen, oder geht das alles über ebendiesen?

Nuja, eben habe ich die fehlende Zeile (in dem Code von dir, wie gesagt, ich spreche die LED mit led_set() an, ergänzt: funktioniert.
Werd nun da mal was in die Hauptschleife schreiben, auch, um zu testen, ob sich nun andere Dinge geändert haben.
Naja, und zum üben halt..;)

radbruch
14.03.2010, 16:51
Hallo


woher der "Zeittakt" für beispeilsweise delay() kommt, ......weiß ich selbst nicht genau. Wenn ich mich recht erinnere macht AVRGCC daraus eine Zählschleife mit NOPs. Aber bei mir hat das nie richtig funktioniert. Die Ausführungszeiten schwanken enorm, deshalb verwende ich meist einen Timer (zusätzlich zu seiner eigentlichen Funktion) als eigene Zeitbasis.

Die Liniensensoren verwenden die Analog-Lib um an die Werte der Sensoren zu kommen. Die Analog-Lib liest zyklisch alle ADC-Kanäle ein, die Liniensensoren sogar zweimal, je einmal unbeleuchtet und einmal beleuchtet. Dazu wird der ADC-Interrupt eingesetzt.

Die Odometrie funktioniert mit den externen Interruptquellen INT0 und INT1.

Gruß

mic

Rabenauge
14.03.2010, 17:16
Ich denke, die Odometriesensoren und die Liniensensoren arbeiten Frequenzmoduliert?
Dann muss man ja auch dafür irgendwo einen Takt hernehmen...

delay() beeinflussen die Einstellungen des Timers0 übrigens offenbar nicht, habs gerade mal mit nem anderen Prescaler-Wert probiert:


// LED3 ansteuern mit Timer 0 14.3.2010 mic

#include <nibobee/iodefs.h>
#include <nibobee/led.h>
#include <nibobee/delay.h>
//#include <avr/interrupt.h>

int main(void)
{
TCCR0 = (1<<CS02)| (1<<CS00); // Normal Mode, kein OC0-Pin, prescaler /1024
TIMSK |= (1<<TOIE0); // Timer0 Overflow-Interrupt erlauben

enable_interrupts();
led_init();



while(1)
{ // LED 2 u.3 treiben ihr eigenes Spiel
int ledNr;
led_set(ledNr,0);
delay(100);
for (ledNr=1;ledNr<3;ledNr++)
{
led_set(ledNr,1);
delay(50);
}
delay(50);
for (ledNr=2;ledNr>1;ledNr--)
{
led_set(ledNr,0);
delay(50);
}
}
}
ISR(TIMER0_OVF_vect)
{
static uint8_t count=0;

count++;
if(count == 57)
{
led_set(LED_R_YE,1); // LED an
led_set(LED_L_YE,1);
}
if(count > 2*57)
{
led_set(LED_R_YE,0); // LED aus
led_set(LED_L_YE,0);
count=0;
}
}

oberallgeier
14.03.2010, 17:48
... delay() beeinflussen die Einstellungen des Timers0 übrigens offenbar nicht ...Wenn ich das richtig im Kopf habe, dann wird über die Nibobee-lib durch
#include <nibobee/delay.h> die Standard-AVRGCC-Bibliotheksroutine eingebunden. Bei mir liegt die lokal auf
C:\Programme\WinAVR-20090313\avr\include\util

Diese Routine dreht nur eine Warteschleife, sprich: sie zählt etliche ticks des Controllers und benutzt keinen Interrupt. Die Millisekunde wird durch die Anzahl der gezählten ticks (die wird aus der Controllerfrequenz errechnet) und die Anzahl der Millisekunden durch den aufrufenden Parameter festgelegt. Läuft also ohne jegliche Interrupt-Initialisierung und beeinflusst keinen Ti mer.
Nachteil 1: der Controller ist während der Wartezeit blockiert.
Nachteil 2: InterruptServiceRoutinen können die Laufzeit verlängern, sodass die Verzögerungszeit nicht unbedingt genau und auch nicht in allen Fällen reproduzierbar gleich ist.

Rabenauge
15.03.2010, 12:24
Gut, wenn man von den beiden Nachteilen weiss, ich halte eh nix von Warteschleifen, in denen "nichts" getan wird. Meine Güte, wir haben Prozessoren, die einige Millionen Befehle pro Sekunde (wann immer ich mir _das_ vor Augen führe, erstarre ich eine halbe Sekunde vor Ehrfurcht ;) ) abarbeiten können, und uns fällt nix besseres ein, als sie mit Warteschleifen auszubremsen..

Als nächstes (um Timer1 mache ich erst einmal vorsichtshalber einen Bogen) werde ich mal mit dem Timer2 spielen, der kann ja wieder ein paar _andere_ Dinge, mal gucken, wozu`s gut ist.

workwind
15.03.2010, 16:10
Ich habe im NIBObee-Wiki eine kleine Tabelle zu den verwendeten Resourcen erstellt:
http://www.nibo-roboter.de/wiki/NIBObee/Lib/Resourcen

oberallgeier
15.03.2010, 16:22
JAAaah - schön, sehr hilfreich. Das findet man sogar mit "suche [Interrupt]". Nur nicht über "suche [Odometrie]" - das wird ja im restlichen Wi ki immer mit "..ie" geschrieben. Sorry für die Pingeligkeit.

Rabenauge
15.03.2010, 21:41
Ja, super. Danke.

BirgerT
19.03.2010, 18:52
Hallo Leute;
mache auch die ersten Experimente mit der Biene und den vorhandenen Libs.
So wie Fühler nach vorne, Drehzahl vorwärts hoch bis max; Fühler nach hinten, verzögern bis null, danach rückwärts bis max. Drehzahl;
Damit man das bedienen kann war auch erst ein delay(200) in der mainloop.
Um dann aber die Leds im Takt der Odometer blinken zu lassen, funktioniert das mit delay(200) nimmer, je schneller die Räder drehen, desto langsamer wird das Blinken.

In den Sourcen zur NiboBee gibt es auch eine clock.c und .h, aber ..?

Im Tutorial "Hinderniserkennung" wird aber auch eine Lösung geboten: counter_ms.

Also sieht's bei mir jetzt erstmal so aus:


int main()
{
uint8_t timer_1 = 0; // ein Timer von x beliebigen
uint8_t LedFlag = 0; // Demo Blinker
while(1==1)
{
delay(1); // Schleife ca. alle ms durchlaufen
if (timer_1 != 0) timer_1--; //Timer pro ms Zyklus runterzählen

if (timer_1 == 0) // Wartezeit abgelaufen
{
timer_1 = 200; // neuen Verzögerungswert laden

// hier dann der Code der nur alle 200ms ausgeführt werden soll
// Sensorabfrage, Beschleunigen und Verzögern

LedFlag ~≃ LedFlag;
led_set(LED_L_YE,LedFlag);
}
// und hier wieder weiter im ms Zyklus
}
return(0);
}

Ich versuche rauszufinden, ob und wie man die Drehzahl mit Bordmitteln regeln könnte.
Auch das Beschleunigen sollte etwas sanfter gestaltet werden, mein Bienchen hat hinten Mignonakkus drauf, und das Anfahren vorwärts sieht immer wie ein Kickstart aus..

Rabenauge
20.03.2010, 23:13
Ich glaube, du hast etwas falsch verstanden.
Wir benutzen einen der Hardware-Timer des ATMega.
Der ist fest eingebaut, man muss ihn nur aktivieren.:)
Wie das geht, steht weiter oben.

Du könntest den durchaus auch benutzen, um sanfter zu beschleunigen, z.B. (oder baust, wie ich, um auf nen leichten LiPo-Akku ) dann hast du erstmal genug Strom an Bord.

Timer, die auf delay() basieren, sind immer ungenau.
Probier mal den Code, den ich gepostet hatte, er ist eigentlich unspektakulär, aber nach einer Weile wirst du sehen, dass die gelben LED`s im Gegesatz zu den roten (letztere blinken per delay() schrecklich genau funktionieren.

BirgerT
21.03.2010, 21:22
Ach, ich dachte der Thread Titel lautet "NiboBee und Timer für Anfänger";

und ob eine LED 60x oder 58x / Sekunde blinkt ist für jemanden, der mit Netzfrequenz getakteten Radioweckern aufgewachsen ist eigentlich wurscht.

Aber Rabenauge hat recht, zu einem delay(x) addiert sich noch die Ausführungszeit der Hauptschleife, und je nach dem was sie gerade zu tun hat ist diese unterschiedlich lang.

Aber wie sollte man 4 Leds mit 2 Timern unabhängig von einander unterschiedlich blinken lassen?

In diesem Beispiel Programm wird also die Hauptschleife abgearbeitet, während im Hintergrund eine Zeit tickt. Vor dem nächsten Durchlauf der Hauptschleife wird gewartet, bis die volle Millisekunde um ist. Die Hauptschleife wird also alle ms 1x durchlaufen. Somit habe ich überall in der Schleife einen festen Zeitbezug. Allerdings darf man die Funktion delay() dann nicht mehr verwenden. Wenn die Zeit von 1 ms nicht mehr reicht, verstellt man die Zykluszeit eben auf 2, 4, 5 oder 10 ms. Die Zykluszeit ist dann auch die minimale Zeitbasis (bei 10ms kann man dann auch nur in 10ms Schritten verzögern).

Also ich denke, dass mit den Kommentaren auch Anfänger zurechtkommen sollten.
Beim Kompilieren gibt es 6 Warnungen; das liegt daran, dass in main() uninitialisierte Variablen abgefragt werden. Diese Variablen gehören eigentlich oberhalb von main() deklariert (ist aber alles Demo).

Und jedesmal, wenn eine timer Variable gesetzt wird, sollte es "Timersollwert"/Zykluszeit heissen - wenn also die Zykluszeit erhöht werden muss, würde der Zeitwert noch stimmen. 2000 / Zykluszeit blieben 2 Sekunden, egal ob Zykluszeit 1ms oder 5ms oder 10ms ist.


/*! @file nibobee_timer.c

* @brief Timer Routinen f&uuml;r Verz&ouml;gerungen

* @author Birger Töpelmann

* @date 2010-03-21

*/



/************************************************** ***********



N I B O B E E T I M E R



************************************************** ***********/



#include <nibobee/iodefs.h>

//#include <nibobee/motpwm.h>

#include <nibobee/delay.h>

#include <nibobee/sens.h>

//#include <nibobee/odometry.h>

#include <nibobee/led.h>





/************************************************** ***********



Timer 0 des ATmega16 soll einen periodischen Interrupt auslösen.



Innnerhalb der Interruptserviceroutine (Interrupthandler) wird

ein Softwaretimer jede ms inkrementiert. Aus diesem Softtimer

kann ein feste Zykluszeit (1, 2, 5, 10ms..) für die Hauptschleife

abgeleitet werden. In der Hauptschleife inkrementierte oder

dekrementierte Variablen können dann als weitere Timer mit

Basis Zykluszeit verwendet werden,

--------------------------------------------------------------

Auswahl eines Teilers für das TimerCounterControlRegister:

Systemtakt 15 000 000/s; Überlauf Timer nach 256 Takten (OV);



Teiler /1: 15000000/s / 256 = 58593,75 /s = 58,6 /ms

Teiler /8; 15000000/s / 1024 = 14648,4375 /s = 14,6 /ms



Eine Quarzgenaue "ms" ist so nicht zu erreichen,



Auswahl des Zählerwertes für den Softtimer in der ISR

58: Abweichung = (58 - 58,6) / 58 = -1,03% (eilt vor)

59: Abweichung = (59 - 58,6) / 59 = +0,68% (hinkt nach)

14: Abweichung = (14 - 14,6) / 14 = -4,29%

15: Abweichung = (15 - 14,6) / 15 = +2,67%



************************************************** ***********/

#define TEILER_T0 0b001 // TCCR0: Systemtakt /1

#define ZAEHLR_T0 59 // ISR: Zusatzteiler s.o.



void timer0_init_mega16() // timer 0 eine ATmega 16 initialisieren

{

TCCR0 |= TEILER_T0; // TimerControlRegister = Teiler /8

TIMSK |= (1 << TOIE0); // Enable Overflow Interrupt

}





volatile uint8_t timer0_ms; // der ms Systemtimer Taktzähler



// volatile == die Variable für jeden Vergleich neu einlesen,

// weil sie in einer anderen Routine (Interrupt) geändert wird

/*

ohne volatile: --> Compiler Optimierung

lade register mit variable

warten:

if register < grenzwert goto warten --> Endlosschleife



mit volatile; --> Compiler soll hier nicht optimieren

warten:

lade register mit variable

if register < grenzwert goto warten



*/

/************************************************** **********/



// Timer 0 Overflow Interrupt Service Routine



volatile uint8_t tmr0_tick;



// alte Bezeichnung ISR (TIMER0_OVF_vect)

SIGNAL (SIG_OVERFLOW0)

{

if (tmr0_tick == 0)

{

tmr0_tick = ZAEHLR_T0;

timer0_ms++;

}

tmr0_tick--;

}

/************************************************** **********/



uint8_t timer0_wait(uint8_t ticks)

{

uint8_t merker; // Zwischenspeicher



while(timer0_ms < ticks); // warten auf Systemzeit ms Takte

merker = timer0_ms; // tatsächliche Takte merken

timer0_ms = 0; // Systemtimer Reset

return(merker); // tatsächliche Zykluszeit zurückliefern

}



/******************************************* V A R I A B L E N */

/* ...auch Konstanten sind variabel... ;-) */



// Hier gehören die im Hauptprogramm definierten Variablen rein,

// dann gibt's auch keine 8 Warnungen mehr vom Compiler !!!



uint16_t timer_LED1; // uint16 weil > 255 Definition aber Warnung

uint8_t timer_Sens; // uint8 für Zeiten < 255 ms





/******************************************* F U N C T I O N S */



void led_mask(uint8_t muster);

void led_aus(uint8_t muster);

void led_ein(uint8_t muster);



/************************************************** *** M A I N */



int main()

{

timer0_init_mega16(); // Timer0 für periodischen Interrupt initialisieren



sens_init(); // Fühler Taster Initialisieren

led_init(); // LEDs initialisieren



while((sens_getLeft()==0) && (sens_getRight()==0)); // Warten auf Startsignal am Fühler



while(1==1) /* - - - - - - - - - - - M A I N L O O P */

{

enable_interrupts(); // Interrupts freigeben



#define Zykluszeit 1 // Hauptschleife Zykluszeit in ms



uint8_t tmr0_watch = timer0_wait(Zykluszeit); // Zykluszeit abwarten



if(tmr0_watch > Zykluszeit) // Zykluszeit überwachen

{

while(1==1) // Fehlerfalle Zykluszeit überschritten

{

if (tmr0_watch > 10) led_ein(15); // Zykluszeit > 10 alle LED ein

else led_ein(tmr0_watch); // Zykluszeit Binär ausgeben



timer0_wait(250); // Klassische Verzögerung LED Ein Zeit

led_aus(15); // LED aus für Blinklicht

timer0_wait(150); // Klassische Verzögerung LED Aus Zeit

}

}



// Ab hier wird die Hauptschleife in einer festen Zykluszeit abgearbeitet

// Da der Intervall feststeht, können beliebig viele Timer abgeleitet werden



// uint16_t timer_LED1; // uint16 weil > 255 Definition aber Warnung



if(timer_LED1-- == 0) timer_LED1 = 1000; // Zyklischer Timer 1000ms



if(timer_LED1 > 700) led_set(LED_L_YE,1); // LED Blinklicht

else led_set(LED_L_YE,0);





// uint8_t timer_Sens; // uint8 für Zeiten < 255 ms



int8_t SensLeft; // Sensor Status



if(timer_Sens != 0) timer_Sens--;

else

{

timer_Sens = 200 / Zykluszeit; // alle 200ms

SensLeft = sens_getLeft(); // Sensorabfrage

}



// Hier wieder normaler Zyklus



/*

solange der linke Fühler nach hinten gedrückt wird, leuchtet die rote LED links

wird der linke Fühler nach vorne gedrückt sollte die LED blinken, weil aber das

delay() verwendet wird, landet das Programm in der Zyklusüberwachung.

*/

switch(SensLeft)

{

case -1: led_set(LED_L_RD,1); break;

case +1: led_set(LED_L_RD,1);

delay(200);

// dieses delay darf in der Hauptschleife nicht mehr verwendet werden

led_set(LED_L_RD,0);

delay(200);

// dieses delay darf in der Hauptschleife nicht mehr verwendet werden

break;

default: led_set(LED_L_RD,0); break;

}



/* Rechts funktioniert die o.a. Funktion */



int8_t SensRight; // Sensor Status

// timer_Sens auf 1 abfragen, er wird oben bearbeitet und bei Zählerstand 0 neu geladen

if(timer_Sens == 1) SensRight = sens_getRight(); // alle 200ms Sensorabfrage



uint16_t timer_LED_R;

if(timer_LED_R-- == 0) timer_LED_R = 400; // Zyklischer Timer 400ms



switch(SensRight)

{

case -1: led_set(LED_R_RD,1); break;

case +1: if(timer_LED_R > 200) led_set(LED_R_RD,1); // LED Blinklicht

else led_set(LED_R_RD,0);

break;

default: led_set(LED_R_RD,0); break;

}







/*

Ein- und Ausschaltverzögerungen:

Wird der rechte Fühler dauernd betätigt, schaltet nach 2 Sek Verzögerung die rechte gelbe LED ein;

wird der rechte Fühler losgelassen, leuchtet die LED noch 5 Sekunden nach.

*/



uint8_t Flanke_rechts;

int16_t timer_Verz_Ein;

int16_t timer_Verz_Aus;



if((Flanke_rechts == 0) && (SensRight != 0)) timer_Verz_Ein = 2000; // Timer setzen bei

if((Flanke_rechts != 0) && (SensRight == 0)) timer_Verz_Aus = 5000; // Flankenerkennung

Flanke_rechts = SensRight;



led_set(LED_R_YE,0);

if((SensRight != 0) && (timer_Verz_Ein == 0)) led_set(LED_R_YE,1); // Einschaltverzögerung

if((SensRight == 0) && (timer_Verz_Aus != 0)) led_set(LED_R_YE,1); // Ausschaltverzöferung



if(timer_Verz_Ein > 0) timer_Verz_Ein--; // Die Timer sollten immer als Countdown arbeiten,

if(timer_Verz_Aus > 0) timer_Verz_Aus--; // und bei 0 stehen bleiben





} /* - - - - - - - - - - - M A I N L O O P */

return(0);

}



/************************************************** ***************



Routinen zum direkten Setzen aller 4 Leds des NiboBee



LED_L_YE = 1, LED_L_RD = 2 ,LED_R_RD = 4, LED_R_YE = 8



led_mask(9); Schaltet gelbe LEDs ein und rote LEDs aus

led_ein(6); Schaltet rote LEDs ein, gelbe LEDs bleiben (Ein o. Aus)

led_aus(6); Schaltet rote LEDs aus, gelbe LEDs bleiben (Ein o. Aus)



************************************************** ***************/



void led_mask(uint8_t muster)

{

for(uint8_t c = 0; c < 4; c++)

{

if (((muster >> c) & 1) == 1) led_set(c,1); else led_set(c,0);

}

}



void led_aus(uint8_t muster)

{

for(uint8_t c = 0; c < 4; c++)

{

if (((muster >> c) & 1) == 1) led_set(c,0);

}

}



void led_ein(uint8_t muster)

{

for(uint8_t c = 0; c < 4; c++)

{

if (((muster >> c) & 1) == 1) led_set(c,1);

}

}


Viel Spaß und Erfolg bei eigenen Projekten...

radbruch
21.03.2010, 22:23
Hallo

In der Hauptschleife auf die ISR zu warten ist nicht so optimal. Basierend auf dem Code von oben würde ich das etwa so angehen:

// Vier LEDs ansteuern mit Timer 0 21.3.2010 mic

#include <nibobee/iodefs.h>
#include <avr/interrupt.h>

volatile uint8_t p=0; // Auf diese Variable greift die ISR und das Programm zu!

int main(void)
{
TCCR0 = (1<<CS02) | (1<<CS00); // Normal Mode, kein OC0-Pin, prescaler /1024
TIMSK |= (1<<TOIE0); // Timer0 Overflow-Interrupt erlauben

DDRB |= 0x0f; // alle Leds sind Ausgang
PORTB &= ~0x0f; // und low
enable_interrupts();

while(1)
{
if(!p)
{
p=4*57; // alle 4 Sek. (atomar weil byte!)
PORTB ^= 0x0f; // alle LEDs umschalten
}
// nix zu tun
}

}
ISR(TIMER0_OVF_vect)
{
static uint8_t led0=0, led1=0, led2=0, led3=0;

if(led0) led0--; else { PORTB ^= 1; led0=57; } // eine Sek.
if(led1) led1--; else { PORTB ^= 2; led1=57/2; } // 1/2 Sek.
if(led2) led2--; else { PORTB ^= 4; led2=57/3; } // 1/3 Sek.
if(led3) led3--; else { PORTB ^= 8; led3=57/4; } // 1/4 Sek.
if(p) p--; // Usertimer :)
}
Das ist natürlich auch nur eine Möglichkeit von vielen...

Gruß

mic

BirgerT
25.03.2010, 23:41
Bei sourceforge gibt es neue Dateien von Nils Springob für die NiboBeeLib = Revision 25 vom 16.3.2010:

Der Timer 2 isses:

Weil der mit einer festen Taktfrequenz zählt, und die PWM über Compare Match erzeugt wird, wird mit dem Timer 2 Overflow auch gleich für die NiboBee Software Clock zum Ticken gebracht.

Die Odometrie geht auf eine Regelung der Motordrehzahl, sogar Streckenvorgabe ist möglich. Probiert habe ich noch nicht, nur mal angeschaut. Ich weiß allerdings nicht sicher, ob die Uhr weitertickt, wenn die Motoren gestoppt werden. Das sollte aber so sein, weil in clock.c auch die Sekunden seit dem Einschalten des Nibobee gezählt werden.

Um im Simulator zu sehen, was die Bibliotheken mit den Registern machen, muss man die entsprechenden Header und Source Dateien direkt in das Projekt mit einbinden und gemeinsam mit der eigen main() compilieren. Beobachten, welches Bit in welchem Register gesetzt wird, geht einfacher, als all die defines durchzudenken.

Rabenauge
26.03.2010, 00:10
Klingt interessant.
Leider (nunja, soo leider auch nicht) muss ich die Timer-Geschichte einige Tage zurückstellen, da ich mir fürs Bienchen was gebastelt habe, und es funktioniert sogar. :D
Werde aber demnächst (muss den Basteltüdel erst wieder hübsch machen) unbedingt reinschauen.

bantyy
26.03.2010, 11:07
Hallo Leute,

oh nein, und ich habe mir in den letzten Wochen selber eine Regelung zusammengebaut - sie kann regeln, Strecke fahren und definierte Kurven fahren. Und was les ich hier? Nicai hat es selber der Bibliothek zugefügt.

Allerdings fahre ich einen ganz anderen Ansatz. Zum einen ist meins kein echter PID-Regler und zum anderen ist mein Regelalgorithmus langsamer und verbraucht deshalb viel weniger CPU-Resourcen.

Hintergrund: Ich habe die Biblitothek von Grund auf neu geschrieben. Bin allerdings noch mittendrin. Herzstück ist ein Timer-System, dass durchaus ähnlich den Tasks von NXC / NXQ ist. Man kann damit Funktionen zu einem zukünftigen Zeitpunkt ausführen, Auflösung ist 1 ms und maximale Verzögerung ist 60 s.

Bisher funktioniert das Timer-System, Tastenentprellung (Fühler), Motorsteuerung und die Motorregelung. Die LED-Steuerung hab ich noch von Nicai - die will ich aber auch noch überarbeiten. Das Thema Liniensensoren hab ich noch gar nicht in Angriff genommen...

Als nächstes habe ich eine ganz andere Geschichte auf dem Programm: Ich habe an den Port mit der USART einen IR-Empfänger angeschlossen und will da erstmal einen RC5-Receiver zusammenschustern. Dann kann man die Biene schön mit einer FB steuern :-)

Ich habe auch geplant, das ganze auf sourceforge zu veröffentlichen, will mich vorher noch mit Nicai abstimmen. Nicht, dass Herr Springob noch sauer wird...

Ciao bantyy