Hallo, ich habe ein Problem mit meinem C-Code...
Kurze Beschreibung, ich möchte einen Kleinen Schalter Programmieren.
Das Programm soll den Pin PCINT0 (PB0) Überwachen und bei zwei Impulsen
innerhalb 500 Mikrosekunden den Pin PCINT4 (PB4) auf Hi (LED) Setzen,
bis wieder zwei Impulse Kurz hintereinander an PCINT0 ankommen, dann
PCINT4 LOW...
Mein Problem ist das die Impulse nicht erkennt werden und die LED nur
Blinkt.
PULL-UP ist aktiviert.
Und das Programm soll leicht zu ändern sein damit es auch zum ATmega168P
1
#include<util/delay.h>
2
#include<stdint.h>
3
#include<avr/io.h>
4
#include<avr/wdt.h>
5
#include<avr/interrupt.h>
6
#include<avr/power.h>
7
#include<avr/sleep.h>
8
9
#if defined (__AVR_ATmega168__) || defined (__AVR_ATmega168P__)
Thomas R. schrieb:> { GIMSK |= (1<<PCINT0);
Was soll denn das werden? Ein Blick ins Datenblatt täte dir gut.
Der betreffende Pin muß im PCMSK-Register aktiviert werden, der
richtige(!) Interrupt muß freigegeben werden und es muß eine ISR
vorhanden sein. Ich sehe nichts von alledem.
wäre im Beruf ein Kündigungsgrund. Es macht den Code maximal
unleserlich.
An anderen Stellen hast du #defines erfolgreich genutzt, um die
Lesbarkeit zu verbessern. Daran sehe ich, dass du es besser kannst.
Schreibe über deine if Blöcke, wozu die gut sind. Was sollen sie
bewirken?
Ist Switch ein Schalter? Wenn ja, hast du eventuell ms und µs
durcheinander gebracht?
> OCR0A = (uint8_t)(F_CPU / (64.0 * 1e-3));
Das ergibt nach meinem Taschenrechner OCR0A = 156250000
Kann nicht sein, oder?
Du könntest deinen ATmega168 mit einer Hand voll LED's bestücken, die
bei Erreichen diverser Punkte in der ISR eingeschaltet werden. So siehst
du, welche Bedingungen wann erfüllt sind.
Anscheinend möchstest du Strom sparen, vielleicht wegen Batteriebetrieb?
Dann würde ich den Timer aber nicht ständig laufen lassen. Ich würde
stattdessen den Timer erst beim ersten Switch-Impuls starten und nach
Ablauf der 500 Mikrosekunden (wirklich µS?) wieder anhalten.
Deine LED kann weiter leuchten, während der µC schläft.
#if defined (__AVR_ATtiny13A__) || defined (__AVR_ATmega168__) || defined (__AVR_ATmega168P__)
10
power_adc_disable();
11
#endif
12
13
flag0=0;
14
flag1=0;
15
flag2=0;
16
17
timer1=0;
18
timer2=0;
19
timer3=0;
20
21
Timer0_Init();
22
23
sei();// Interrupts aktivieren
24
25
for(;;)
26
{
27
set_sleep_mode(SLEEP_MODE_IDLE);
28
sleep_mode();
29
}
30
31
return0;//nie erreicht
32
}
Sorry ich fange gerade erst an mit dem Programmieren, bis jetzt habe ich
mir alles selbst erarbeitet, alles aus Büchern die ich schon zig mal
gelesen habe....
Thomas R. schrieb:> Die ISR ist doch vorhanden oder habe ich ein Totales verständnis Problem
Aber sowas von.
Das ist eine ISR. Und zwar für den Timer. Der PCINT bekommt seine
eigene.
Thomas R. schrieb:> Sorry ich fange gerade erst an mit dem Programmieren, bis jetzt habe ich> mir alles selbst erarbeitet, alles aus Büchern die ich schon zig mal> gelesen habe....
Dann mutest dir gerade zuviel zu. Spiel in Ruhe mit dem PCINT rum. Nur
mit dem PCINT. Bis es läuft und du das verstanden hast.
Thomas R. schrieb:> zwei Impulsen> innerhalb 500 Mikrosekunden
D.h. die Impulse sind nicht von mechanischen Tastern und man braucht
keine Entprellung.
Thomas R. schrieb:> uint8_t flag0;> uint8_t flag1;> uint8_t flag2;>> uint16_t timer1;> uint16_t timer2;> uint16_t timer3;
Damit bin ich raus. Nichts sagende Namen verschlechtern die Lesbarkeit
extrem (auch für Dich).
Stefan U. schrieb:> Anscheinend möchstest du Strom sparen, vielleicht wegen Batteriebetrieb?
Nach meiner Erfahrung hat es sich bewährt, zuerst nur mal die Funktion
zu implementieren und als nächsten Schritt dann das Stromsparen.
// TODO: I/O Pins konfigurieren und Pin-Change Interrupt einschalten
14
set_sleep_mode(SLEEP_MODE_IDLE);
15
16
while (1)
17
{
18
switch (status)
19
{
20
case WARTE_AUF_ERSTEN_START_IMPULS:
21
sleep();
22
if (SWITCH_ON)
23
{
24
starte_den_timer();
25
status=WARTE_AUF_ERSTEN_START_IMPULS_ENDE;
26
}
27
break;
28
29
case WARTE_AUF_ERSTEN_START_IMPULS_ENDE
30
if (SWITCH_OFF)
31
{
32
status=WARTE_AUF_ZWEITEN_START_IMPULS;
33
}
34
break;
35
36
case WARTE_AUF_ZWEITEN_START_IMPULS:
37
if (SWITCH_ON)
38
{
39
status=WARTE_AUF_ZWEITEN_START_IMPULS_ENDE;
40
}
41
if (TCNT0 > TIMEOUT_VALUE)
42
{
43
// Fange nochmal von vorne an,
44
// auf 2 Startimpulse zu warten
45
stoppe_den_timer();
46
status=WARTE_AUF_ERSTEN_START_IMPULS;
47
}
48
break;
49
50
case WARTE_AUF_ZWEITEN_START_IMPULS_ENDE:
51
if (SWITCH_OFF)
52
{
53
stoppe_den_timer();
54
status=AUSGANG_AN;
55
}
56
break;
57
58
case AUSGANG_AN:
59
OUT_ON;
60
status=WARTE_AUF_ERSTEN_STOP_IMPULS;
61
break;
62
63
case WARTE_AUF_ERSTEN_STOP_IMPULS:
64
sleep();
65
if (SWITCH_ON)
66
{
67
starte_den_timer();
68
status=WARTE_AUF_ERSTEN_STOP_IMPULS_ENDE;
69
}
70
break;
71
72
case WARTE_AUF_ERSTEN_STOP_IMPULS_ENDE
73
if (SWITCH_OFF)
74
{
75
status=WARTE_AUF_ZWEITEN_STOP_IMPULS;
76
}
77
break;
78
79
case WARTE_AUF_ZWEITEN_STOP_IMPULS:
80
if (SWITCH_ON)
81
{
82
starte_den_timer();
83
status=WARTE_AUF_ZWEITEN_STOP_IMPULS_ENDE;
84
}
85
if (TCNT0 > TIMEOUT_VALUE)
86
{
87
// Fange nochmal von vorne an,
88
// auf 2 Stopimpulse zu warten
89
stoppe_den_timer();
90
status=WARTE_AUF_ERSTEN_STOP_IMPULS;
91
}
92
break;
93
94
case WARTE_AUF_ZWEITEN_STOP_IMPULS_ENDE:
95
if (SWITCH_OFF)
96
{
97
stoppe_den_timer();
98
status=AUSGANG_AUS;
99
}
100
break;
101
102
case AUSGANG_AUS:
103
OUT_OFF;
104
status=WARTE_AUF_ERSTEN_START_IMPULS;
105
break;
106
}
107
}
108
}
Für den Pin-Change Interrupt verwendest du eine leere ISR (sie muss aber
existieren). Der Eingang wird hier stattdessen durch Polling abgefragt.
Den Interrupt brauchst du aber, um aufzuwachen.
Den Timer nutzt du ohne Interrupt. Er soll nach dem Start einfach von 0
an hoch zählen, aber langsam genug daß er nicht vor dem Erreichen des
TIMEOUT_VALUE (500us) überlauft.
Mache Dir noch gedanken über die Aufwach-Zeit. Da es Dir hier um
Mikrosekunden geht, könnte diese Zeit eine signifikante Rolle spielen.
Da musst du mal ins Datenblatt schauen, wie lange das Aufwachen dauert.
Das coole an so einem Konstrukt ist, daß du mehrere solcher Tasks quasi
parallel abarbeiten kannst. Im Prinzip beliebig viele.
Dazu packst du jeden switch/case Block in eine eigene Prozedur und rufst
sie dann in der Hauptschleife so auf:
Man kann sich den Ablauf vereinfachen, indem man eine Entprellroutine
nimmt, die fertige Drückereignisse liefert.
Und die Zeit für die Doppeldruckerkennung kann man mit einem Scheduler
machen.
Dafür gibt es auch einen Beispielcode:
Beitrag "Wartezeiten effektiv (Scheduler)"
Ist aber für einen Anfänger ne Menge Holz, beides zu verstehen. Man kann
aber jeden Teil für sich analysieren. Im Prinzip reicht es auch erstmal
aus, zu verstehen, was die einzelnen Funktionen machen und wie man sie
aufruft.
Man sieht daran auch schön, wie man ein Problem in einzelne Teilaufgaben
zerlegen kann.