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
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
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
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?
oh je... ich glaub die bunten holzklötzchen wären mehr deine welt
Und ich glaube deine dummen sind reine Zeitverschwendung, geh Schnee schieben!!
Hallo Rush, du kannst natürlich auch die Auswertung der Tastendrücke in die Funktion wait() verschieben. Gruß, DetlevT
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
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 ?
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.