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


von Rush .. (rush)


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

von Detlev T. (detlevt)


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

von Guido (Gast)


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

von Rush .. (rush)


Lesenswert?

Vielleicht sollte ich den code einfach anhängen ;-)
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <avr/interrupt.h>
4
5
volatile int i = 0;
6
volatile uint8_t  PWM_SPEED = 10;
7
volatile uint16_t  PWM_BUTTOM;
8
volatile uint16_t  PWM_TOP;
9
#define  PWM_OCR  OCR1A      //Pin Ausgang
10
#define  LED    PB3
11
#define  DDR_IN    DDRD
12
#define  PULLUP    PORTD
13
#define  PIN_IN    PIND
14
#define  MODE    PD0
15
#define  UP      PD1
16
#define  DOWN    PD2
17
#define  PORT_IN  PORTD
18
#define  DDR_OUT  DDRB
19
#define  PORT_OUT  PORTB
20
21
#define  PWM_STEP  10        //Helligkeitserhöhung
22
#define  SPEED_STEP 5        // Geschwindigkeitsstufen
23
24
25
//------------ Taster entprellen ----------------------------
26
#define ALL_KEYS        (1<<MODE | 1<<UP | 1<<DOWN)
27
#define REPEAT_MASK     (1<<MODE | 1<<UP | 1<<DOWN)       // repeat: key1, key2
28
#define REPEAT_START    300                              // after 500ms
29
#define REPEAT_NEXT     5                                 // every 200ms
30
           // every 200ms
31
32
volatile uint8_t key_state;                                // debounced and inverted key state:
33
                                                           // bit = 1: key pressed
34
volatile uint8_t key_press;                                // key press detect
35
 
36
volatile uint8_t key_rpt;                                  // key long press and repeat
37
 
38
 
39
ISR( TIMER0_OVF_vect )                                   // every 10ms
40
{
41
  static uint8_t ct0, ct1, rpt;
42
  uint8_t i;
43
 
44
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
45
 
46
  i = key_state ^ ~PIN_IN;                        // key changed ?
47
  ct0 = ~( ct0 & i );                             // reset or count ct0
48
  ct1 = ct0 ^ (ct1 & i);                          // reset or count ct1
49
  i &= ct0 & ct1;                                 // count until roll over ?
50
  key_state ^= i;                                 // then toggle debounced state
51
  key_press |= key_state & i;                     // 0->1: key press detect
52
 
53
  if( (key_state & REPEAT_MASK) == 0 )            // check repeat function
54
     rpt = REPEAT_START;                          // start delay
55
  if( --rpt == 0 ){
56
    rpt = REPEAT_NEXT;                            // repeat delay
57
    key_rpt |= key_state & REPEAT_MASK;
58
  }
59
}
60
 
61
///////////////////////////////////////////////////////////////////
62
//
63
// check if a key has been pressed. Each pressed key is reported
64
// only once
65
//
66
uint8_t get_key_press( uint8_t key_mask )
67
{
68
  cli();                                          // read and clear atomic !
69
  key_mask &= key_press;                          // read key(s)
70
  key_press ^= key_mask;                          // clear key(s)
71
  sei();
72
  return key_mask;
73
}
74
 
75
///////////////////////////////////////////////////////////////////
76
//
77
// check if a key has been pressed long enough such that the
78
// key repeat functionality kicks in. After a small setup delay
79
// the key is reported beeing pressed in subsequent calls
80
// to this function. This simulates the user repeatedly
81
// pressing and releasing the key.
82
//
83
uint8_t get_key_rpt( uint8_t key_mask )
84
{
85
  cli();                                          // read and clear atomic !
86
  key_mask &= key_rpt;                            // read key(s)
87
  key_rpt ^= key_mask;                            // clear key(s)
88
  sei();
89
  return key_mask;
90
}
91
 
92
//-------------------------------------------------
93
uint8_t get_key_short( uint8_t key_mask )
94
{
95
  cli();                                          // read key state and key press atomic !
96
  return get_key_press( ~key_state & key_mask );
97
}
98
 
99
//-------------------------------------------------
100
uint8_t get_key_long( uint8_t key_mask )
101
{
102
  return get_key_press( get_key_rpt( key_mask ));
103
}
104
///////////////////////////////////////////////////////////////////////////////////7
105
106
107
void pwm_init(void)
108
{
109
  //fast-pwm; clear on compare match, set at top
110
  TCCR1A |= (1<<WGM10) | (1<<WGM11) | (1<<WGM12) | (1<<COM1A1) | (1<<COM1A0);
111
  TCCR1B |= (1<<CS10); //Clock 1:1
112
}
113
114
void debounce_init(void)
115
{
116
  TCCR0B = (1<<CS02)|(1<<CS00);      // divide by 1024; timer for decouncing
117
  TIMSK = 1<<TOIE0;
118
}
119
120
//----------------------------------------------------
121
void init_io(void)
122
{
123
  DDR_OUT |= (1<<LED);
124
  PORT_OUT |= (1<<LED);
125
  DDR_IN &=~ (1<<MODE) | (1<<UP) | (1<<DOWN);
126
  PULLUP |= (1<<MODE) | (1<<UP) | (1<<DOWN); 
127
}
128
129
130
void wait( int ms )
131
{
132
  int a;
133
134
  for( a = 0; a < ms; ++a )
135
    _delay_ms( 1 );
136
}
137
138
//-------------------------------------------------------------------
139
void dimm(void)
140
{
141
 int i = PWM_BUTTOM;
142
 int a = PWM_TOP;
143
 int change;
144
145
 if (PWM_TOP > PWM_BUTTOM)
146
  {
147
    PWM_OCR = PWM_TOP;
148
149
    for (i; i < a; i++)          // dimming down
150
      {
151
      PWM_OCR--;
152
      wait(PWM_SPEED);
153
      }
154
155
    for (i; i > PWM_BUTTOM; i--)    // dimming up
156
    {
157
      PWM_OCR++;
158
      wait(PWM_SPEED);
159
    }
160
  }
161
162
 if (PWM_TOP < PWM_BUTTOM)        //switch variables to have high value in PWM_TOP and low in PWM_BUTTOM
163
   {
164
     change = PWM_TOP;
165
   PWM_TOP =  PWM_BUTTOM;
166
   PWM_BUTTOM = change;
167
   }
168
 
169
170
}
171
//-----------------------------------------------------------------------
172
void main(void)
173
 {
174
   char chrMODE=0;
175
   init_io();
176
   debounce_init();
177
   pwm_init();
178
   sei();
179
   PWM_OCR = 0;
180
   while(1)
181
   {
182
      if( get_key_press( 1<<MODE ) || get_key_rpt(1 <<MODE))
183
     {
184
       chrMODE++;
185
     if(chrMODE == 3) { chrMODE = 0;}
186
     }
187
188
   switch (chrMODE)
189
     {
190
       case 0:            //set top in PWM_OCR
191
         PWM_OCR = PWM_TOP;
192
           if( get_key_press( 1<<UP ) || get_key_rpt( 1<<UP ))
193
             {
194
             PWM_OCR = PWM_OCR + PWM_STEP;
195
           if (PWM_OCR >=1023) {PWM_OCR = 0;}
196
          PWM_TOP = PWM_OCR;
197
             }
198
        
199
        if( get_key_press( 1<<DOWN ) || get_key_rpt( 1<<DOWN ))
200
             {
201
             PWM_OCR = PWM_OCR - PWM_STEP;
202
           if (PWM_OCR < 0 ) {PWM_OCR = 1023;}
203
          PWM_TOP = PWM_OCR;
204
             }break;
205
    
206
     case 1:            //set buttom PWM_OCR
207
        PWM_OCR = PWM_BUTTOM;
208
           if( get_key_press( 1<<UP ) || get_key_rpt( 1<<UP ))
209
             {
210
             PWM_OCR = PWM_OCR + PWM_STEP;
211
           if (PWM_OCR >=1023) {PWM_OCR = 0;}
212
          PWM_BUTTOM = PWM_OCR;
213
             }
214
        
215
        if( get_key_press( 1<<DOWN ) || get_key_rpt( 1<<DOWN ))
216
             {
217
             PWM_OCR = PWM_OCR - PWM_STEP;
218
           if (PWM_OCR < 0 ) {PWM_OCR = 1023;}
219
          PWM_BUTTOM = PWM_OCR;
220
             }
221
         break;
222
    
223
     case 2:            //flush between PWM_BUTTOM and PWM_TOP
224
         if( get_key_press( 1<<UP ) || get_key_rpt( 1<<UP ))
225
          {
226
            PWM_SPEED = PWM_SPEED + SPEED_STEP;
227
          if(PWM_SPEED > 255) {PWM_SPEED = 10;}
228
          }
229
230
        if( get_key_press( 1<<DOWN) || get_key_rpt( 1<<DOWN ))
231
          {
232
            PWM_SPEED = PWM_SPEED - SPEED_STEP;
233
          if(PWM_SPEED < 10) {PWM_SPEED = 10;}
234
          } dimm(); break;
235
    }
236
237
   }
238
}

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?

von Gast-ritits (Gast)


Lesenswert?

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

von Rush .. (rush)


Lesenswert?

Und ich glaube deine dummen sind reine Zeitverschwendung, geh Schnee 
schieben!!

von Detlev T. (detlevt)


Lesenswert?

Hallo Rush,

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

Gruß, DetlevT

von Guido (Gast)


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

von Rush .. (rush)


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 ?

von Guido (Gast)


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

von Karl H. (kbuchegg)


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.

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.