mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik ATTiny2313 Timer "aufteilen" (ein Timer mit zwei Funktionen?)


Autor: Rush ... (rush)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tag alle zusammen...

Ich habe eine kleine Verständnisfrage bezüglich des/der Timer eines 
Tiny2313.
Undzwar habe ich den ersten Timer für die Tastenentprellung genommen und 
den zweiten lasse ich im PWM-Modus laufen um LEDs zu dimmen.

Nun ist es ja so, wenn ich z.b. die LED von sagen wir mal 10%-50% hin 
und her dimme, und ich sage mal der OCRx-Wert jede 10 ms hoch- bzw. 
runtergezählt wird, dimmen die LED im groben und ganzen langsamer als 
wenn ich sie von sagen wir mal 10%-30% dimmen würde.

So, bis jetzt habe ich die Möglichkeit, die Geschwindigkeit vorzugeben, 
über Tasteneingaben realisiert und die Dimmroutine läuft mit Zuhilfename 
der delay()-Funktion. Sie funktioniert zwar, ja, aber wenn ich, während 
der Dimmroutine, die Geschwindigkeit über die Tasten erhöhen oder 
verringern möchte, muss ich immer erstmal warten bis meine Dimmroutine 
durch ist. Verstänlich, wegen dem delay().

Hat jemand eine Idee wie ich einen von meinen beiden Timern 
zwischenzeitlich für die Angabe von meiner Geschwindigkeit, also des 
Intervalls zwischen dem hoch- bzw. runterzählen des OCRx-Wertes 
verwenden könnte ohne das die restliche Funktionalität des Programms 
darunter leidet?

MfG Rush

Autor: Detlev T. (detlevt)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Rush,

so ganz durchschaue ich nicht, was du genau willst. Aber mit der 
Entprellroutine hast du ja schon etwas, was in regelmäßigen Abständen 
aufgerufen wird. Überlasse der doch das Inkrementieren und 
Dekrementieren des Wertes und die Ausgabe in das OCR-Register. Die 
Hauptroutine stellt dann nur noch die Geschwindigkeit in einer globalen 
Variablen ein.

Gruß, DetlevT

Autor: Guido (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Rush,

schau dir doch bitte mal an, in welchen Modies ein Timer betrieben 
werden kann, dann ist deine Frage auch schon beantwortet. Einfach mal 
das Datenblatt des µC nehmen und unter Timer lesen.
Ansonsten wäre ein Beispielprogramm sehr hilfreich. Du kennst ja den 
Spruch:"Ich habe meine Kugel gerade verlegt".

Gruß Guido

Autor: Rush ... (rush)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielleicht sollte ich den code einfach anhängen ;-)
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

volatile int i = 0;
volatile uint8_t  PWM_SPEED = 10;
volatile uint16_t  PWM_BUTTOM;
volatile uint16_t  PWM_TOP;
#define  PWM_OCR  OCR1A      //Pin Ausgang
#define  LED    PB3
#define  DDR_IN    DDRD
#define  PULLUP    PORTD
#define  PIN_IN    PIND
#define  MODE    PD0
#define  UP      PD1
#define  DOWN    PD2
#define  PORT_IN  PORTD
#define  DDR_OUT  DDRB
#define  PORT_OUT  PORTB

#define  PWM_STEP  10        //Helligkeitserhöhung
#define  SPEED_STEP 5        // Geschwindigkeitsstufen


//------------ Taster entprellen ----------------------------
#define ALL_KEYS        (1<<MODE | 1<<UP | 1<<DOWN)
#define REPEAT_MASK     (1<<MODE | 1<<UP | 1<<DOWN)       // repeat: key1, key2
#define REPEAT_START    300                              // after 500ms
#define REPEAT_NEXT     5                                 // every 200ms
           // every 200ms

volatile uint8_t key_state;                                // debounced and inverted key state:
                                                           // bit = 1: key pressed
volatile uint8_t key_press;                                // key press detect
 
volatile uint8_t key_rpt;                                  // key long press and repeat
 
 
ISR( TIMER0_OVF_vect )                                   // every 10ms
{
  static uint8_t ct0, ct1, rpt;
  uint8_t i;
 
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
 
  i = key_state ^ ~PIN_IN;                        // key changed ?
  ct0 = ~( ct0 & i );                             // reset or count ct0
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
  i &= ct0 & ct1;                                 // count until roll over ?
  key_state ^= i;                                 // then toggle debounced state
  key_press |= key_state & i;                     // 0->1: key press detect
 
  if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
     rpt = REPEAT_START;                          // start delay
  if( --rpt == 0 ){
    rpt = REPEAT_NEXT;                            // repeat delay
    key_rpt |= key_state & REPEAT_MASK;
  }
}
 
///////////////////////////////////////////////////////////////////
//
// check if a key has been pressed. Each pressed key is reported
// only once
//
uint8_t get_key_press( uint8_t key_mask )
{
  cli();                                          // read and clear atomic !
  key_mask &= key_press;                          // read key(s)
  key_press ^= key_mask;                          // clear key(s)
  sei();
  return key_mask;
}
 
///////////////////////////////////////////////////////////////////
//
// check if a key has been pressed long enough such that the
// key repeat functionality kicks in. After a small setup delay
// the key is reported beeing pressed in subsequent calls
// to this function. This simulates the user repeatedly
// pressing and releasing the key.
//
uint8_t get_key_rpt( uint8_t key_mask )
{
  cli();                                          // read and clear atomic !
  key_mask &= key_rpt;                            // read key(s)
  key_rpt ^= key_mask;                            // clear key(s)
  sei();
  return key_mask;
}
 
//-------------------------------------------------
uint8_t get_key_short( uint8_t key_mask )
{
  cli();                                          // read key state and key press atomic !
  return get_key_press( ~key_state & key_mask );
}
 
//-------------------------------------------------
uint8_t get_key_long( uint8_t key_mask )
{
  return get_key_press( get_key_rpt( key_mask ));
}
///////////////////////////////////////////////////////////////////////////////////7


void pwm_init(void)
{
  //fast-pwm; clear on compare match, set at top
  TCCR1A |= (1<<WGM10) | (1<<WGM11) | (1<<WGM12) | (1<<COM1A1) | (1<<COM1A0);
  TCCR1B |= (1<<CS10); //Clock 1:1
}

void debounce_init(void)
{
  TCCR0B = (1<<CS02)|(1<<CS00);      // divide by 1024; timer for decouncing
  TIMSK = 1<<TOIE0;
}

//----------------------------------------------------
void init_io(void)
{
  DDR_OUT |= (1<<LED);
  PORT_OUT |= (1<<LED);
  DDR_IN &=~ (1<<MODE) | (1<<UP) | (1<<DOWN);
  PULLUP |= (1<<MODE) | (1<<UP) | (1<<DOWN); 
}


void wait( int ms )
{
  int a;

  for( a = 0; a < ms; ++a )
    _delay_ms( 1 );
}

//-------------------------------------------------------------------
void dimm(void)
{
 int i = PWM_BUTTOM;
 int a = PWM_TOP;
 int change;

 if (PWM_TOP > PWM_BUTTOM)
  {
    PWM_OCR = PWM_TOP;

    for (i; i < a; i++)          // dimming down
      {
      PWM_OCR--;
      wait(PWM_SPEED);
      }

    for (i; i > PWM_BUTTOM; i--)    // dimming up
    {
      PWM_OCR++;
      wait(PWM_SPEED);
    }
  }

 if (PWM_TOP < PWM_BUTTOM)        //switch variables to have high value in PWM_TOP and low in PWM_BUTTOM
   {
     change = PWM_TOP;
   PWM_TOP =  PWM_BUTTOM;
   PWM_BUTTOM = change;
   }
 

}
//-----------------------------------------------------------------------
void main(void)
 {
   char chrMODE=0;
   init_io();
   debounce_init();
   pwm_init();
   sei();
   PWM_OCR = 0;
   while(1)
   {
      if( get_key_press( 1<<MODE ) || get_key_rpt(1 <<MODE))
     {
       chrMODE++;
     if(chrMODE == 3) { chrMODE = 0;}
     }

   switch (chrMODE)
     {
       case 0:            //set top in PWM_OCR
         PWM_OCR = PWM_TOP;
           if( get_key_press( 1<<UP ) || get_key_rpt( 1<<UP ))
             {
             PWM_OCR = PWM_OCR + PWM_STEP;
           if (PWM_OCR >=1023) {PWM_OCR = 0;}
          PWM_TOP = PWM_OCR;
             }
        
        if( get_key_press( 1<<DOWN ) || get_key_rpt( 1<<DOWN ))
             {
             PWM_OCR = PWM_OCR - PWM_STEP;
           if (PWM_OCR < 0 ) {PWM_OCR = 1023;}
          PWM_TOP = PWM_OCR;
             }break;
    
     case 1:            //set buttom PWM_OCR
        PWM_OCR = PWM_BUTTOM;
           if( get_key_press( 1<<UP ) || get_key_rpt( 1<<UP ))
             {
             PWM_OCR = PWM_OCR + PWM_STEP;
           if (PWM_OCR >=1023) {PWM_OCR = 0;}
          PWM_BUTTOM = PWM_OCR;
             }
        
        if( get_key_press( 1<<DOWN ) || get_key_rpt( 1<<DOWN ))
             {
             PWM_OCR = PWM_OCR - PWM_STEP;
           if (PWM_OCR < 0 ) {PWM_OCR = 1023;}
          PWM_BUTTOM = PWM_OCR;
             }
         break;
    
     case 2:            //flush between PWM_BUTTOM and PWM_TOP
         if( get_key_press( 1<<UP ) || get_key_rpt( 1<<UP ))
          {
            PWM_SPEED = PWM_SPEED + SPEED_STEP;
          if(PWM_SPEED > 255) {PWM_SPEED = 10;}
          }

        if( get_key_press( 1<<DOWN) || get_key_rpt( 1<<DOWN ))
          {
            PWM_SPEED = PWM_SPEED - SPEED_STEP;
          if(PWM_SPEED < 10) {PWM_SPEED = 10;}
          } dimm(); break;
    }

   }
}

Naja, meine LED dimmt z.b. so.

5%
6%
7%
usw. bis 50% und dann wieder runter
49%
48%
47%
usw. bis wieder auf 5%

Und das ganze sozusagen "im Kreis" und da diese Funktion ( dimm() ) mit 
delays und mit einer Funktion und nicht mit Interrupt realisiert wurde, 
wird mein Tastendruck erst erkannt nachdem die zwei Schleifen in der 
Funktion durchlaufen sind.

Bedeutet, wenn meine zwei Schleifen angenommen 20 sekunden brauchen bis 
sie durch sind, wird mein Tastendruck den ich sage ich mal bei 10 
getätig habe erst nach 20 sekunden überhaupt erkannt.
Ich will aber dass er sofort erkannt und der neue "Zeitwert" übernommen 
wird.

Dich habe ich leider nur Ansatzweise verstanden. Gut, meine 
Tastenrouting prüft auch in gewissen zeitabständen. und so wie ich dich 
verstanden habe würde sich dann die intervallzeit der tastenentprellung 
gleich der zeitintervalle für die incrementierung von OCx setzen oder 
nicht?

Autor: Gast-ritits (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
oh je... ich glaub die bunten holzklötzchen wären mehr deine welt

Autor: Rush ... (rush)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und ich glaube deine dummen sind reine Zeitverschwendung, geh Schnee 
schieben!!

Autor: Detlev T. (detlevt)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Rush,

du kannst natürlich auch die Auswertung der Tastendrücke in die Funktion 
wait() verschieben.

Gruß, DetlevT

Autor: Guido (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe versucht dein Programm zu lesen. Leider bin ich nicht sehr weit 
gekommen. Wenigstens die Basics solltest du beachten.
Im Datenblatt ist Alles gut beschrieben.

Rush ... schrieb:
> void pwm_init(void)
> {
>   //fast-pwm; clear on compare match, set at top
>   TCCR1A |= (1<<WGM10) | (1<<WGM11) | (1<<WGM12) | (1<<COM1A1) | (1<<COM1A0);
>   TCCR1B |= (1<<CS10); //Clock 1:1
> }

Z.B. ist mir aufgefallen, dass du WGM12 in TCCR1A gesetzt hast. Das ist 
falsch, WGM12 gehört in TCCR1B.

Ich weiss, dass das nicht die Antwort auf deine Frage ist. Doch die 
grundlegenden Sachen sollten stimmen, sonst schleppen sich die Fehler 
immer weiter. Dann solltest du dir überlegen, für deine Taster einen 
Interrupt zu benutzen. Es ist am Anfang etwas schwierig dies zu lernen, 
aber es macht sich bezahlt.
Natürlich kannst du auch während der Pausen (wait) die Tasten abfragen.

Guido

Autor: Rush ... (rush)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
oh hast recht. danke. aber das ist ja auch blöd im datasheet 
beschrieben. die bit-beschreibung steht unter TCCR1A mit drin, bzw. in 
der tabelle unter TCCR1A.

Taster abfragen meinst du über nen timerinterrupt ?
hast du da ein grundlegendes beispiel für micht ?

Autor: Guido (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nein ich habe kein Beispiel. Es ist besser für dich, wenn du selbst 
darauf kommst. Ich gebe nur Tipps.
Ich meine natürlich keinen Timer für die Taster. Normalerweise schliesst 
man die Taster so an, dass sie gegen Masse schalten. Wenn du die Taster 
(zwei hast du?) an die externen Interrupts anschliesst, dann kannst du 
diese Interrupts benutzen, um die Abfrage der Tasten auszulösen.
Das Prinzip geht auch mit mehreren Tastern, sogar auch eine Tastenmatrix 
kann so ausgewertet werden. Hier werden die Taster an beliebige Ports 
angeschlossen und über Dioden gelangt das Signal an den 
Interruptanschluss.

Nehmen wir mal den INT0. An diesen schließt du einen Taster gegen Masse 
an. Einen Pull-Up-Widerstand solltest du nicht vergessen. Später kann 
dieser entfallen und der interne Pull-Up wird benutzt.
Zuerst must du deinen INT0 initialisieren. Natürlich solltest du die 
fallende Flanke nehmen, damit der Int beim Drücken und nicht beim 
Loslassen des Tasters ausgelöst wird.
In der Interruproutine fragst du den Taster nach einer gewissen Zeit 
nochmal ab, ob er auch stabil gedrückt ist. Das musst du machen, damit 
du das Prellen der Taste ausschliessen kannst. Diese Abfrage kann auch 
mehrmals erfolgen Danach kannst du das machen, was der Taster auslösen 
soll, den Wert einer Variablen hoch- oder runterzählen.

Gruß
Guido

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rush ... schrieb:

> Taster abfragen meinst du über nen timerinterrupt ?
> hast du da ein grundlegendes beispiel für micht ?

Hast du doch schon.
Die ganze Tastenabfragerei findet in der
ISR( TIMER0_OVF_vect )
statt.

Die wird alle 10ms aufgerufen.

Damit hast du einen 10ms Takt, in dem du auch noch andere Dinge machen 
kannst. Zb. den OCR Wert für die PWM um einen Schritt auf den Zielwert 
nachführen.

ISR( TIMER0_OVF_vect )
{
  ....

  if( PWM_OCR < PwmZiel )
    PWM_OCR++;

  else if( PWM_OCR > PwmZiel )
    PWM_OCR--;
}

Du gist deine Dimmstufe über PwmZiel vor (mittels Tasten). Alle 10ms 
wird überprüft, ob die tatsächliche PWM Einstellung kleiner/größer als 
dieser Zielwert ist. Ist sie ungleich wird die tatsächliche PWM Stufe um 
einen Schritt in Richtung dieses Zielwertes nachgestellt. Nach genügend 
ISR Aufrufen( die alle 10ms erfolgen) hat daher die tatsächliche PWM 
Einstellung diese Zielvorgabe erreicht und das nachstellen hört auf: Die 
PWM arbeitet mit der Einstellung, die als Vorgabe in PwmZiel vorhanden 
ist.

-> du kannst jederzeit PwmZiel verändern. PWM_OCR wird langsam an diese 
Zielvorgabe nachgeführt. Zu jeder Zeit. Auch dann wenn die PWM 
eigentlich gerade hochdimmt, kannst du PwmZiel jederzeit auf 0 setzen 
und die Dimmrichtung dreht sich sofort um.

   while(1)
   {
     if( get_key_press( 1<<UP ) || get_key_rpt( 1<<UP ))
     {
       PwmZiel += PWM_STEP;
       if (PwmZiel >= 1023 )
         PwmZiel = 1023;
     }

     if( get_key_press( 1<<DOWN ) || get_key_rpt( 1<<DOWN ))
     {
       PwmZiel -= PWM_STEP;
       if (PwmZiel < 0 )
         PwmZiel = 0;
     }
   }

und den ganzen Hokuspokus mit dimm brauchst du nicht mehr :-)

Die Denkweise: Ich mache jetzt das und das ist bei µC programmierung 
meistens nicht so schlau.
Besser ist die Denkweise: Ich habe ein Programm. Alle paar ms schaue ich 
mir die Situation an und entscheide was zu tun ist.
Durch dieses 'Alle paar ms' entsteht die Dynamik. Das machst du im 
realen Leben auch. Wenn du dir ein Bad einlässt bleibst du auch nicht 
daneben sitzen und wartest bis die Wanne voll ist. Du gehst in 
regelmässigen Abständen ins Bad und siehst nach, wie die Situation 
aussieht. Ist zu wenig Wasser in der Wanne, lässt du das Wasser weiter 
laufen und gehst wieder zurück zum Kühlschrank. Nach ein paar Minuten 
ghehst du wieder ins Bad und siehst dir die Situation erneut an und 
entscheidest was zu tun ist. Solange du nur regelmässig ins Bad gehst, 
wird dir die Wanne nicht überlaufen.

Und genau so (in etwa) funktioniert auch obiges. Die ISR wird alle 10ms 
aufgerufen. In einem Aufruf wird entschieden: PWM erhöhen oder PWM 
verringern oder nichts tun.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.