Forum: Mikrocontroller und Digitale Elektronik [AVR]Fragen zur C Programmierung (Timer / multiplexing)


von Daniel H. (danielh)


Lesenswert?

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

von Anderas B. (andibc)


Lesenswert?

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?

von Daniel H. (danielh)


Lesenswert?

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.

von Anderas B. (andibc)


Lesenswert?

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.

von Daniel H. (danielh)


Lesenswert?

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

von Ja mann (Gast)


Lesenswert?

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.

von Daniel H. (danielh)


Lesenswert?

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.

von Anderas B. (andibc)


Lesenswert?

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.

von Johannes M. (johnny-m)


Lesenswert?

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?

von Daniel H. (danielh)


Lesenswert?

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?

von Johannes M. (johnny-m)


Lesenswert?

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.

von Daniel H. (danielh)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@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

von Anderas B. (andibc)


Lesenswert?

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.

von Daniel H. (danielh)


Lesenswert?

Ö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?

von Peter D. (peda)


Lesenswert?

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

von Daniel H. (danielh)


Lesenswert?

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 ;)

von Falk B. (falk)


Lesenswert?

@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

von Daniel H. (danielh)


Lesenswert?

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!

von Johannes M. (johnny-m)


Lesenswert?

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.

von Daniel H. (danielh)


Lesenswert?

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 ?

von Johannes M. (johnny-m)


Lesenswert?

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?

von Daniel H. (danielh)


Lesenswert?

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?

von Johannes M. (johnny-m)


Lesenswert?

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.

von Daniel H. (danielh)


Lesenswert?

Also das hier:
1
taster_neu = PINB;
2
taster_flags = taster_alt & ~taster_neu;
3
taster_alt = taster_neu;

In einen Timer-Interrupt?

von Johannes M. (johnny-m)


Lesenswert?

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.

von Johannes M. (johnny-m)


Lesenswert?

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!

von Peter D. (peda)


Lesenswert?


von Daniel H. (danielh)


Lesenswert?

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
Noch kein Account? Hier anmelden.