Hallo allerseits,
ich verwende den Timer1 meines ATmega88PA mit CTC, um eine LED mit 180
bpm (3 Hz) blinken zu lassen. Ohne die Taster zu bedienen dachte ich,
dass folgendes passieren müsste: Die LED an PB0 ist am Anfang an und
wechselt dann eben mit 3 Hz zwischen an und aus, weil der Timer immer
bis 5207 (Inhalt von OCR1A) zählt. Nun geht sie aber nach ner
Drittelsekunde aus (soweit also richtig), danach wechselt sie aber nur
noch ungefähr alle vier Sekunden. Zurückgerechnet wäre eine Erklärung
dafür, dass der Timer einmal bis 5207 zählt, danach aber bis
65535=0xFFFF, also OCR1A ignoriert oder zurücksetzt.
Was hab ich da falsch verstanden bzw. müsste ich anders machen, damit
der Timer immer zum gleichen Wert zählt?
1 | #define F_CPU 1000000UL // 1 MHz
|
2 |
|
3 | #include <avr/io.h>
|
4 | #include <avr/interrupt.h>
|
5 | #include <util/delay.h>
|
6 |
|
7 | #define LED (1 << PB0)
|
8 | #define D_LED DDRB
|
9 | #define P_LED PORTB
|
10 | #define TASTER1_AUS (PINC & (1 << PC5))
|
11 | #define TASTER2_AUS (PINC & (1 << PC4))
|
12 |
|
13 | #define PRESCALER_LOG 6
|
14 | #define PRESCALER_BITS ((1 << CS11) | (1 << CS10))
|
15 |
|
16 | volatile int8_t blinken = 1;
|
17 |
|
18 | ISR (TIMER1_COMPA_vect) {
|
19 |
|
20 | if (blinken) {
|
21 | P_LED ^= LED;
|
22 | } else {
|
23 | P_LED &= ~LED;
|
24 | }
|
25 |
|
26 | }
|
27 |
|
28 | // muss das int sein?
|
29 | int main(void) {
|
30 |
|
31 | // Datenrichtungsregister setzen, LED an
|
32 | D_LED |= LED;
|
33 | P_LED |= LED;
|
34 |
|
35 | // Timer 1:
|
36 | // CTC (TOP = OCR1A), Prescaler setzen
|
37 | TCCR1A |= (1 << WGM12);
|
38 | TCCR1B |= PRESCALER_BITS;
|
39 |
|
40 | uint16_t bpm = 180;
|
41 |
|
42 | // Rundungsfehler beim Shift, wenn F_CPU nicht durch den Prescaler teilbar ist:
|
43 | // PRESCALER_LOG = 8 => 6*10^-5
|
44 | // PRESCALER_LOG = 10 => 6*10^-4
|
45 | // -1 am Ende, weil ja von 0 gezählt wird (?, TODO: nochmal nachlesen)
|
46 | OCR1A = 60 * (F_CPU >> PRESCALER_LOG) / bpm - 1;
|
47 |
|
48 | // Compare-Interrupt erlauben
|
49 | TIMSK1 |= (1 << OCIE1A);
|
50 | // Global Interrupts aktivieren
|
51 | sei();
|
52 |
|
53 | int8_t entpr_taster1 = 0;
|
54 | int8_t entpr_taster2 = 0;
|
55 |
|
56 | while (1) {
|
57 |
|
58 | _delay_ms(10);
|
59 |
|
60 | // TASTER1
|
61 | if (TASTER1_AUS) {
|
62 | entpr_taster1 = 0;
|
63 | } else if (entpr_taster1 < 4) {
|
64 | entpr_taster1++;
|
65 | } else if (entpr_taster1 == 4) {
|
66 | entpr_taster1++;
|
67 | // AKTION:
|
68 | blinken = !blinken;
|
69 | }
|
70 |
|
71 | // TASTER2
|
72 | if (TASTER2_AUS) {
|
73 | entpr_taster2 = 0;
|
74 | } else if (entpr_taster2 < 4) {
|
75 | entpr_taster2++;
|
76 | } else if (entpr_taster2 == 4) {
|
77 | entpr_taster2++;
|
78 | // AKTION:
|
79 | bpm += 10;
|
80 | cli(); // TODO: nötig? Denke schon wg. 16bit.
|
81 | OCR1A = 60 * (F_CPU >> PRESCALER_LOG) / bpm - 1;
|
82 | sei();
|
83 | }
|
84 |
|
85 | }
|
86 |
|
87 | return 0;
|
88 |
|
89 | }
|