Hallo, ich habe zwei kurze Fragen zur Programmierung von meinem AVR Atmega8. Ich habe vor, ihn in C zu programmieren. Daher nun zwei Fragen: 1.) Ist es Möglich, den Zählerstand eines Timers abzufragen? Der Timer wird inkrementiert durch ein externes Signal, und ich würde diesen Wert gerne multiplexen auf 7-segment Anzeigen. Sprich: kann ich den Zählerstand in eine Variable einlesen? 2.) Kennt ihr ein gutes "Tutorial" für's muxen in C? Hier auf der Seite ist ja ein umfangreiches Tutorial für ASM, würde aber gerne in C schreiben ... Möchte es zwar selbst schreiben, jedoch wenigstens vorher vergleichen, bevor ich den nachgeschalteten 4511 zerschieße... Danke schonmal und Gruß Daniel
Hallo Daniel, vorab eine Frage zur Klärung: Möchtest Du den Timer als Zähler nutzen oder verwendest Du den Timer als als Timer dessen Reload Wert durch einen externen Impuls inkrementiert wird?
Hallo, ich möchte den Timer ganz simpel als Zähler nutzen. Es wird nicht höher als maximal 99 gezählt, sodass der 8-bit Timer ausreicht. Er soll eben auf den externen Takt am Pin reagieren, dann um 1 inkrementieren. Diesen "Zählerstand" würde ich gerne auslesen.
Zuerst mußt du den Timer als Zähler definieren. Hierzu sind einige Bits in Statusregistern zu setzen. Welche genau mußt du dir im Datneblatt ansehen. Der Zähler hat ein oder zwei Zählerregister. Je nach dem ob es ein 8Bit oder 16Bit Zähler ist. Wenn du bis 99 Zählst reichen dir 8Bit aus. Also mußt du das Low Byte Register auslesen. Wenn du ein Include File hast in dem die SFR deines Controller definiert sind dann machst du einfach eine Zuweisung von diesem Register an eine Variable. unsigned char meineVariable; meineVariable = cntLow; cntLow ist das deklarierte SFR deines Zählers. Ich weiß nicht wie es beim AVR richtig heist. Im Datenbaltt und in der Include Datei nachsehen.
Setzten muss ich ja eigentlich jeweils nur CS12, CS11, CS10 sowie CS02, CS01, CS00, welche die Quelle für den Counter angeben. Mir fällt gerade auf, dass dies auch nebenbei im Tutorial dieser Seite erwähnt wird ... TCNT0 sowie TCNT1L ... oder hab ich jetzt was übersehen? Danke dir schonmal ;) @all: bleibt noch Frage zwei nach dem Multiplexen in C
Dazu brauchst du keinen Timer, sondern nur eine einfache Variable. Immer wenn du einen Flankenwechsel feststellst, zählst du selbige um 1 hoch. Den Timer brauchst du bestenfalls um eine Entprellung für Deinen Eingang zu realisieren. Im einfachsten Fall nimmst du einen interruptfähigen Pin, und in der passenden ISR zählst du Deine Variable hoch.
Es gibt am AtMega8 nur zwei Interruptfähige Pins, welche ich allerdings beide schon belegt habe. Daher wollte ich es einfach mit einem Timer lösen, vor allem, weil die nicht anderweitig verwendet werden.
Es geht natürlich vieles. TCNT0 sowie TCNT1L hört sich gut. Das L steht für das Low Register im Zähler 1. Zähler 0 scheint ein reiner 8Bit Zähler zu sein.
Ja mann wrote: > Dazu brauchst du keinen Timer, sondern nur eine einfache Variable. Immer > wenn du einen Flankenwechsel feststellst, zählst du selbige um 1 hoch. Die Dinger heißen nicht umsonst Timer/Counter! Variable ist umständlich, wenn man nen freien Timer zur Verfügung hat. Warum sich in Software einen abbrechen, wenn Hardware dafür vorhanden ist, die das ganz allein machen und nebenbei bei Erreichen bestimmter Werte auch noch nen Interrupt auslösen kann? > Den Timer brauchst du bestenfalls um eine Entprellung für Deinen Eingang > zu realisieren. Wenn das Signal nicht prellt, braucht's auch keine Entprellung! > Im einfachsten Fall nimmst du einen interruptfähigen > Pin, und in der passenden ISR zählst du Deine Variable hoch. Und das soll einfach sein?
Anderas Baehne wrote: > Es geht natürlich vieles. > TCNT0 sowie TCNT1L hört sich gut. Das L steht für das Low Register im > Zähler 1. Zähler 0 scheint ein reiner 8Bit Zähler zu sein. Richtig. Zähler 0 und 1 können eben durch einen externen Takt gesteuert werden. Timer 2 hat eben nur BufferOverflow und nen CompareRegister, daher nehme ich den fürs muxen ... => hat dazu jemand ne ausführliche Anleitung? Will nicht nur CopyPaste machen, sondern das Multiplexen auch wirklich verstehen. (Das Prinzip verstehe ich, nur die Umsetzung kann ich mir noch nicht genau vorstellen) Danke und Gruß //Nochmal kurz zum Entprellen: Die einige Maßname, die ich angesetzt habe, um den Timer zu inkrementieren, ist ein 10kOhm Pulldown Widerstand. Sollte ich da noch mit nem Kondensator oder gar einem Schmitt-Trigger arbeiten?
Daniel Herrmann wrote: > //Nochmal kurz zum Entprellen: Die einige Maßname, die ich angesetzt > habe, um den Timer zu inkrementieren, ist ein 10kOhm Pulldown > Widerstand. Sollte ich da noch mit nem Kondensator oder gar einem > Schmitt-Trigger arbeiten? Erstens: Woher kommt das Signal, mit dem der Timer inkrementiert wird? Zweitens: Wenn es ein Signal von einem mechanischen Kontakt ist, dann wird das mit dem Timer nur mit einigem Zusatzaufwand in Hardware etwas (z.B. RC-Glied); in dem Fall wäre die bessere Lösung das zyklische Abfragen des/der betreffenden Portpin(s) (siehe auch Entprellung) Drittens: Die AVRs haben bereits Schmitt-Trigger-Eingänge.
Das Signal kommt von einem Taster. Ich würde das gerne auch dabei belassen, sprich Taster, entprellen und an den Counter-Eingang. Also dann doch besser mit RC-Glied? Würde es reichen, es wie hier (http://www.loetstelle.net/praxis/entprellen/entprellen.php) beschrieben zu machen? Ohne Schmitt-Trigger. Dann habe ich halt ne fallende Flanke, ist aber egal, kann man beim AVR ja einstellen.
@Daniel Herrmann (danielh) >Das Signal kommt von einem Taster. Dann kann man das spielend in Software entprellen und zählen. Siehe Entprellung. >Ich würde das gerne auch dabei belassen, sprich Taster, entprellen und >an den Counter-Eingang. Wozu? Um zu lernen, wie man es NICHT macht? >(http://www.loetstelle.net/praxis/entprellen/entprellen.php) beschrieben Schlecht, de taster schliesst den Kondensator kurz. Das kann auf Dauer die Kontakte abbrennen lassen. MFG Falk
Ja genau, so könntest du das machen. R0 begrenzt den Entladestrom vom Kondensator. Da sollten wenige Ohm reichen. Er verlangsamt aber auch die Entladung und somit die Zeit bis der Eingang einen Low-Pegel hat. Schwellwert ist ca. 0,6V. Ein Schmitt Trigger wäre gut wenn dein Eingang auf Flanke reakiert. Wenn er Zustandsgesteuert ist sollte es auch ohne gehen. Mit ST ist aber sauber.
Öh ... Wat nun? ;) Erstmal danke für eure beiden Antworten, auch wenn sie ein wenig kontrovers sind. Mir wäre es lieber, wenn ich es mit den Countern machen könnte und nicht per Software. Das ist Flankengesteuert, also sollte doch der Schmitt-Trigger eingesetzt werden? So habe ich auch teilweise die Taster für nen 4017 etc entprellt, sollte auch klappen, oder ? @Falk: Was meinst du damit, dass die Kontakte abbrennen könnten? Welche denn?
Daniel Herrmann wrote: > Mir wäre es lieber, wenn ich es mit den Countern machen > könnte und nicht per Software. Warum? Als Weltmeister im Schnelltippen schaffst Du vielleicht 100ms, als normaler Mensch 300ms Drückrate. 100ms sind für nen MC aber ne halbe Ewigkeit, der langweilt sich tierisch. Du hast also keinerlei Vorteil durch den HW-Counter, sondern nur den Nachteil, daß er nicht entprellen kann. Peter
Hi Peter, Ja, stimmt soweit. Außerdem werden die werden nicht so "schnell" sein, zwischen der einen Flanke und der nächsten können u.U. auch schonmal mehrere Minuten vergehen. Allerdings habe ich erstens Erfahrung mit Countern, das ist mir irgendwie lieber, Software, insbesondere auch C, sind relatives Neuland für mich. Ich würde es sicherlich hinbekommen, aber das bewährte, die Hardwareentprellung ist irgendwie ein wenig vertrauter, zumal auch auf der Platine kein Platzmangel herrscht.
1 | |
2 | #define TASTERPORT PINC
|
3 | #define TASTERBIT PINC1
|
4 | |
5 | char taster(void) |
6 | {
|
7 | static unsigned char zustand; |
8 | char rw = 0; |
9 | |
10 | if(zustand == 0 && !(TASTERPORT & (1<<TASTERBIT))) //Taster wird gedrueckt (steigende Flanke) |
11 | {
|
12 | zustand = 1; |
13 | rw = 1; |
14 | }
|
15 | else if (zustand == 1 && !(TASTERPORT & (1<<TASTERBIT))) //Taster wird gehalten |
16 | {
|
17 | zustand = 2; |
18 | rw = 0; |
19 | }
|
20 | else if (zustand == 2 && (TASTERPORT & (1<<TASTERBIT))) //Taster wird losgelassen (fallende Flanke) |
21 | {
|
22 | zustand = 3; |
23 | rw = 0; |
24 | }
|
25 | else if (zustand == 3 && (TASTERPORT & (1<<TASTERBIT))) //Taster losgelassen |
26 | {
|
27 | zustand = 0; |
28 | rw = 0; |
29 | }
|
30 | |
31 | return rw; |
32 | }
|
Außerdem müsste ich diesen Code ja für jeden Taster den ich habe einfügen, das sind immerhin 4. (Zwei an den Countern, und zwei an den interrupt-eingängen) Würde ziemlich lang werden das ganze, oder? Und ich wollte eigentlich auch keine Diskussion auslösen, ob Hard- oder Software entprellung, aber gut ... ;) PS.: Frage II (multiplexen in C) steht noch aus (siehe erster Post). Nochmal danke an alle bisherigen Helfer, ich bin echt angenehm überrascht, wie schnell das hier geht ;)
@Daniel Herrmann (danielh) >kontrovers sind. Mir wäre es lieber, wenn ich es mit den Countern machen >könnte und nicht per Software. Machs per Software! >Das ist Flankengesteuert, also sollte doch der Schmitt-Trigger >eingesetzt werden? Ja, der ist aber schon im AVR drin. Steht auch im Artikel ;-) >@Falk: Was meinst du damit, dass die Kontakte abbrennen könnten? Ja, so in etwa. > Welche denn? Na die im Taster! >Allerdings habe ich erstens Erfahrung mit Countern, das ist mir >irgendwie lieber, Software, insbesondere auch C, sind relatives Neuland >für mich. Dann musst du es lernen. Wenn du schon bei sowas einfachem die mädchenhaft anstellst kannst du gleich aufhören zu programmieren. >Außerdem müsste ich diesen Code ja für jeden Taster den ich habe >einfügen, das sind immerhin 4. (Zwei an den Countern, und zwei an den >interrupt-eingängen) Der Code von Perter ist ziemlich kompakt. Und es ist ja auch SEHR SINNVOLL, für ier popelige Taster wertvolle Ressourcen wie Timer und Interrupteingänge zu verbraten . . . >Würde ziemlich lang werden das ganze, oder? Nö. >Und ich wollte eigentlich auch keine Diskussion auslösen, ob Hard- oder >Software entprellung, aber gut ... ;) Da gibt es auch gar keine Diskussion. Für Taster ist Software die klar bessere Methode. ;-) MFG Falk
Falk Brunner wrote: >>Allerdings habe ich erstens Erfahrung mit Countern, das ist mir >>irgendwie lieber, Software, insbesondere auch C, sind relatives Neuland >>für mich. > > Dann musst du es lernen. Wenn du schon bei sowas einfachem die > mädchenhaft anstellst kannst du gleich aufhören zu programmieren. > Einfach nur, weil es mir einfacher erscheint, das Hardwaretechnisch zu lösen. Dass das einfach ist, weiß ich selbst. Ne Variable zu inkrementieren nach nem Funktionsaufruf dürfte ich gerade noch hinbekommen. >>Außerdem müsste ich diesen Code ja für jeden Taster den ich habe >>einfügen, das sind immerhin 4. (Zwei an den Countern, und zwei an den >>interrupt-eingängen) > > Der Code von Perter ist ziemlich kompakt. Und es ist ja auch SEHR > SINNVOLL, für ier popelige Taster wertvolle Ressourcen wie Timer und > Interrupteingänge zu verbraten . . . Da sie anderweitig nicht gebraucht werden, dachte ich eben, ich kann sie dafür verwenden. Aber gut, da ihr sicherlich mehr Erfahrung habt als ich, werde ich mich dem beugen ;) Wie muss der Taster denn dann angeschlossen werden? Einfach VCC -> Taster -> PIN? Oder noch nen Pulldown hinter den Taster? Außerdem: Welches Verfahren zur Entprellung würdet ihr empfehlen? Am Besten so wie hier (http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#.28Tasten-.29Entprellung) beschrieben? Ist ja relativ simpel. Gruß Daniel //edit: ich sehe grade, der AVR hat interne Pull-Ups ... Also einfach die verwenden, dann einfach GND -> Taster -> Pin Dann kann ich auch die vorgeschlagene Funktion 1zu1 verwenden. Ich danke bisher dafür, hat den Hardwareaufwand nun doch deutlich verringert ;) Bleibt immer noch die Urspürngliche Frage, wie eine gute Software fürs Multiplexen aussieht. Danke nochmals an alle!
Daniel Herrmann wrote: > Wie muss der Taster denn dann angeschlossen werden? Einfach VCC -> > Taster -> PIN? Oder noch nen Pulldown hinter den Taster? Die AVRs haben eingebaute Pull-Ups. Warum willst Du also einen zusätzlichen Pull-Down einbauen? Taster gegen GND und fallende Flanken auswerten. Geht im einfachsten Falle, indem man sich im Programm jeweils den letzten Zustand der Taster merkt und dann mit einer einfachen logischen Verknüpfung alle paar zig Millisekunden abfragt, ob der Zustand der Taster sich von High auf Low geändert hat. Z.B. Taster an Port B:
1 | uint8_t taster_flags, taster_neu, taster_alt; |
2 | //...
|
3 | taster_neu = PINB; |
4 | taster_flags = taster_alt & ~taster_neu; |
5 | taster_alt = taster_neu; |
Jedes Bit in taster_flags entspricht einem Pin des Ports. Nur wenn ein Taster seinen Pegel zwischen zwei Abfragen von High auf Low ändert, wird das dazugehörige Bit gesetzt, alle anderen Zustände und Zustandsänderungen werden ignoriert. Das reicht normalerweise auch zur Entprellung völlig aus.
Jupp, hab ich auch gemerkt, siehe edit. http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#.28Tasten-.29Entprellung Steht ja da alles drin, mit konkretem Beispiel. Muss man da echt 2* 50ms warten? Hängt das noch von was anderem außer der Qualität der Taster ab ?
Daniel Herrmann wrote: > Muss man da echt 2* 50ms warten? Hängt das noch von was anderem außer > der Qualität der Taster ab ? Mit einigermaßen gescheiten Tastern sind einmal 50 ms völlig ausreichend. Ich arbeite seit Jahren mit den (allerdings schon nicht ganz billigen) ITT-Rechtecktastern mit 20 ms zwischen zwei Abfragen und hatte nie Probleme mit Prellen. Ich würde es aber auch nicht mit einem delay machen, da der µC dann nicht mehr viel anderes machen kann (außer Interrupts). Du magst die Timer doch so sehr, warum konfigurierst Du nicht einfach einen Timer so, dass er alle 50 ms einen Interrupt auslöst (Compare oder Overflow) und in dem dazugehörigen Interrupt Handler machst Du einfach die o.g. Abfrage?
Falls du meinst, dass ich diese Funktion:
1 | inline uint8_t debounce(volatile uint8_t *port, uint8_t pin) |
2 | {
|
3 | if ( ! (*port & (1 << pin)) ) |
4 | {
|
5 | /* Pin wurde auf Masse gezogen, 100ms warten */
|
6 | _delay_ms(50); // max. 262.1 ms / F_CPU in MHz |
7 | _delay_ms(50); |
8 | if ( *port & (1 << pin) ) |
9 | {
|
10 | /* Anwender Zeit zum Loslassen des Tasters geben */
|
11 | _delay_ms(50); |
12 | _delay_ms(50); |
13 | return 1; |
14 | }
|
15 | }
|
16 | return 0; |
17 | }
|
nehme, und dann im Timer-Interrupt abfrage, dann hatte ich das sowiso so vor ;) Sehr qualitativ hochwertig sind meine Taster nicht, deshalb bleibe ich dabei. Wenn ich allerdings da 4 Taster abfrage, dann sind das immerhin 800ms, also fast eine Sekunde, die nur fürs warten drauf gehen. Oder sehe ich da was falsch?
Daniel Herrmann wrote: > Falls du meinst, dass ich diese Funktion: > > [...] > > nehme, und dann im Timer-Interrupt abfrage, dann hatte ich das sowiso so > vor ;) Nein, das meinte ich nicht! Erstens haben delays in einem Interrupt-Handler nichts verloren und zweitens meinte ich mein obiges Beispiel (Beitrag "Re: [AVR]Fragen zur C Programmierung (Timer / multiplexing)"). > Wenn ich allerdings da 4 Taster abfrage, dann sind das immerhin 800ms, > also fast eine Sekunde, die nur fürs warten drauf gehen. Oder sehe ich > da was falsch? Allerdings. Deshalb werden in meinem Beispiel ja auch alle Taster an dem betreffenden Port gleichzeitig abgefragt.
Also das hier:
1 | taster_neu = PINB; |
2 | taster_flags = taster_alt & ~taster_neu; |
3 | taster_alt = taster_neu; |
In einen Timer-Interrupt?
Daniel Herrmann wrote: > Also das hier: > > [...] > > In einen Timer-Interrupt? Dann am besten mit hohem Vorteiler, oder? Es > muss ja dann schon eine gewisse Zeit vergangen sein... sonst nützt es ja > auch nichts gegen das Prellen. Nicht einfach 1:1 übernehmen, sondern auch denken und v.a. das lesen, was dabei steht! Wenn ich schreibe "alle paar zig Millisekunden", dann ist damit gemeint, dass der Timer alle paar zig Millisekunden diese Abfrage-Routine ausführen soll. Wie Du den Timer so konfigurierst, dass er alle ca. 50 ms einen Interrupt auslöst, setze ich mal als bekannt voraus... Und zumindest die Variablen taster_alt und taster_flags müssen global sein (taster_alt darf auch lokal static sein). taster_neu darf einfach lokal sein.
Daniel Herrmann wrote: > Also das hier: > >
1 | > taster_neu = PINB; |
2 | > taster_flags = taster_alt & ~taster_neu; |
3 | > taster_alt = taster_neu; |
4 | >
|
> > In einen Timer-Interrupt? Schon besser!
Entprellung + Flankenerkennung: http://www.mikrocontroller.net/articles/Entprellung#Komfortroutine_.28C_f.C3.BCr_AVR.29 Peter
Gut, dann danke ich dir erstmal für deine Geduld. Ich weiß, die musstest du haben ;) Wie man einen Timer konfiguriert, ist mir klar, ich hatte nur oben dein Post nicht vollständig gelesen, ist mir erst später aufgefallen, daher auch nochmal editiert. Sorry!
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.