Guten Tag,
ich komme mit meinem Programm nicht mehr weiter.
Es soll ein 8 Kanal Laufzeitmesser werden der über RS232 die Laufzeit an
den PC sendet. Genauichkeit +/-5 Minuten abweichung am Tag sind
tolerierbar.
Testweise habe ich derzeit nur 2 Kanäle im Programm.
Die Funktion ist simpel.
Es wird ein Starttaster gedrückt um die Messung zu starten.
Eine Status LED zeigt an ob die Messung läuft.
Wenn am Eingang die Spannung von 5V ausfällt wird die Zeit über RS232 an
den PC gesendet.
Das Funktioniert auch "fast" alles wunderbar.
Das Problem ist das der Timer nicht sofort aufhört sondern noch die 60
Sekunden nachzählt bis er sich auf 0 zurücksetzt.
Ein Auszug aus dem Terminal Programm:
#1 Laufzeit_1 0:00:01:44 Spannung am Eingang fällt auf 0V, Ausgabe
korrekt
#2 Laufzeit_1 0:00:00:51 hier müsste der Timer wieder mit 0 anfangen.
#3 Laufzeit_1 0:00:00:58 aber er zählt die Sekunden von Zeile 1 mit
"44"weiter
#4 Laufzeit_1 0:00:00:01 die Minute wird hingegen nicht mehr
hochgezählt.
Er hat nur zum Teil die Zeit richtig zurückgesetzt nur die Sekunden
zählen nach.
Die Programmierung ist etwas "durchwachsen" und unübersichtlich dies
bitte ich zu entschulden, da ich mich erst vor einigen Montaten mit "C"
und µC von Atmel beschäftige.
Ich bin für jede Hilfe dankbar. :-)
if (PINB &(1<<PINB2)) // Kanal 1 5V fallen aus
{
Kanal_1=0;
tage=0, stunden=0, minuten=0, sekunden=0;
}
if (PINB &(1<<PINB3)) // Kanal 2 5V fallen aus
{
Kanal_2=0;
tage_1=0, stunden_1=0, minuten_1=0, sekunden_1=0;
}
Ich denke du musst nicht nur den Sekundenzähler zurücksetzen sondern
auch die variable timer die du in der ISR verwendest.
if (sekunden!=timer)
{
sekunden=timer;
Da beschreibst du die Variable sekunden nämlich wieder mit dem Wert, der
noch in timer steht. und das ist nicht Null.
if (PINB &(1<<PINB0)) // Kanal 1 5V fallen aus
bezieht sich auf die Ausgabe auf RS232
if (PINB &(1<<PINB2)) // ist der Starttaster
Sorry, Kommentar falsch im Programmcode.
Christian schrieb:> if (PINB &(1<<PINB2)) // Kanal 1 5V fallen aus> {> Kanal_1=0;> tage=0, stunden=0, minuten=0, sekunden=0;> }> if (PINB &(1<<PINB3)) // Kanal 2 5V fallen aus> {> Kanal_2=0;> tage_1=0, stunden_1=0, minuten_1=0, sekunden_1=0;>> }>> Ich denke du musst nicht nur den Sekundenzähler zurücksetzen sondern> auch die variable timer die du in der ISR verwendest.
1
if(PINB&(1<<PINB2))// Kanal 1 Starttaster
2
{
3
Kanal_1=0;
4
tage=0,stunden=0,minuten=0,sekunden=0;
5
timer=0;
6
}
7
if(PINB&(1<<PINB3))// Kanal 2 Starttaster
8
{
9
Kanal_2=0;
10
tage_1=0,stunden_1=0,minuten_1=0,sekunden_1=0;
11
timer=0;
12
13
}
Das habe ich jetzt damit gemacht.
Neues Problem, es werden alle Timer an allen Eingängen zurückgesetzt
sobald ein Eingang auf 0V fällt.
Mit meinem Wissenstand kann ich das Problem nur mit Acht 16 Bit Timern
Timer_1 Timer_2 Timer_n usw lösen, diese hat der Atmega8 natürlich
nicht.
Man müsste den Timer Kanalunabhänig machen.
Dann mach es doch kanal-unabhängig.
Wie wäre so:
Es gibt nur eine Uhrzeit im System, die immer tage, stunden, min ...
hochzählt.
Start eines Kanals speichert diese Zeit auf der Variablen für diesen
Kanal ab.
Stop des Kanals berechnet die Differenz zwischen aktueller Uhrzeit und
Startzeit.
Geht mit beliebig viel Kanälen.
So wie du das machst ist das ja auch extrem umständlich und
verschwenderisch.
Mach es doch folgenddrmaßen:
Du nimmst einen uint32 zähler als variable. Der zähler zählt immer
weiter über den hw-timer.
Werden jetzt an einem kanal 5v angelegt setzt du den einen uint32 als
startwert = zähler
Fällt die spannung auf 0v setzt du den stopwert auf den aktuellen
zähler.
Die verstichene zeit ist dann stopwert-startwert in was immer der zähler
als zeitintervall zählt.
Du gibst die zeit aus und fertig ist die laube.
Vorteile: nur ein hw-timer und nur zwei variablen pro kanal zu
verwalten.
Nachteile: uint32 sind nicht gerade schnell auf dem avr jedoch sollte es
schnell genug sein.
Danke nochmal für die Hilfe,
das Programm habe ich jetzt auf 4 Kanäle erweitert.
Funktioniert super. :-)
Jetzt müsste ich das ganze was kleiner bekommen.
Bin schon bei 6734 bytes.
Einige uint32_t muss ich noch was verkleinern und einige if else
Anweisung in der Sekunden-> Std:Min:sek Zeitumrechnung sind überflüssig.
Noch weitere Ideen wie man es kleiner bekommen könnte?
Geplant sind insgesammt 8 Kanäle.
Hi
Alternative... Zeitzähler in Controller. Wenn du die Frequenz kennst,
also den Zeitwert, dann kannst du einfach die Zählerdifferenz akt.
Zähler - Startwert an den PC senden. Der kann auch rechnen und du
belastest nicht den Controller mit dieser Aufgabe. Denkbar ist auch die
Differenz im PC zu berechnen.
Also,
Startwert bei Ereignis X mit Zählerwert setzen
Endwert bei Ereignis Y mit Zählerwert setzen
-> Senden: Kanal, Startwert, Endwert..
Im PC berechnen Endwert - Startwert. Ist Ergebnis negativ, dann max.
Zählerwert dazu addieren.
Beispiel:
Deine "Counter"-Variable zählst du im Sekundentakt hoch. Bei 16 Bit wäre
die größte Zahl 65 535, dann beginnt die Zählerei von vorn. Das
Zeitfenster ist somit 18 Stunden, 12 Minuten und 12 Sekunden. Wenn du
nur 10 Sekunden Genauigkeit brauchst, dann sind das schon über 180
Stunden...
OK, angenommen Startwert ist 12301, Endwert ist 46122. Demnach ist
Zeitwert Endwert-Startwert also 32821 Sekunden.
Es kann aber auch genau umgekehrt sein, das bei der Zeitzählung ein
Überlauf stattgefunden hat.
Startwert 43113, Endwert 30251. Hier ergibt sich ein Wert von -12882. Da
das Ergebnis negativ ist addierst du nun den max. Zählwert der Variable
hinzu, also + 65535 und erhälst 52653
Somit brauchst du nicht unbedingt 32 Bit zum Zählen und belastet auch
sonst nicht deinen µC. Für den PC ist das auch nix besonderes.
Gruß oldmax
Roman schrieb:> Jetzt müsste ich das ganze was kleiner bekommen.
Funktionen sind schon erfunden wurden.
Wenn du für jeden Kanal einfach den kompletten Code erneut duplizierst,
ist klar, dass dir das alles axplodiert.
> Einige uint32_t muss ich noch was verkleinern und einige if else> Anweisung in der Sekunden-> Std:Min:sek Zeitumrechnung sind überflüssig.
Ist sinnvoll.
ABer dein Hauptproblem ist, dass du exzessive Codeduplizierung betreibst
anstatt dir eine Funktion zu schreiben, der man die Werte für 1 Kanal
übergibt! Ist doch immer die gleiche Berechnung, nur die Variablen sind
andere.
kann man in eine Funktion stecken und anders rechnen
1
voidprintf_time(uint32time)
2
{
3
uint8_tsekunde;
4
uint8_tminute;
5
uint16_thour;
6
7
sekunde=time%60;
8
time=time/60;
9
minute=time%60;
10
hour=time/60;
11
12
printf("%04d:%02d:%02d",hour,minute,sekunde);
13
}
und dann ruft man eben die Funktion mit wechselnden Aufrufparametern
auf, wenn man sie braucht:
1
intmain()
2
{
3
....
4
// KANAL 1
5
if(PIND&(1<<PIND4))// Kanal 1 5V fallen aus
6
{
7
counter_1_end=timer_1-counter_1_start;
8
9
if(Kanal_1==0)
10
{
11
printf("Laufzeit Kanal 1: ");
12
PORTC^=(1<<PC0);// Status LED ausschalten
13
14
printf_time(counter1_end);
15
}
16
}
17
18
// KANAL 2
19
if(PIND&(1<<PIND5))// Kanal 2 5V fallen aus
20
{
21
counter_2_end=timer_2-counter_2_start;
22
23
if(Kanal_2==0)
24
{
25
printf("Laufzeit Kanal 2: ");
26
PORTC^=(1<<PC1);// Status LED ausschalten
27
Kanal_2=1;// Zum testen auf "0"
28
29
printf_time(counter_2_end);
30
}
31
}
32
33
// KANAL 3
34
if(PIND&(1<<PIND6))// Kanal 3 5V fallen aus
35
{
36
counter_3_end=timer_3-counter_3_start;
37
38
if(Kanal_3==0)
39
{
40
printf("Laufzeit Kanal 3: ");
41
PORTC^=(1<<PC2);// Status LED ausschalten
42
Kanal_3=1;// Zum testen auf "0"
43
44
printf_time(counter_3_end);
45
}
46
}
47
48
// KANAL 4
49
if(PIND&(1<<PIND7))// Kanal 4 5V fallen aus
50
{
51
counter_4_end=timer_4-counter_4_start;
52
if(Kanal_4==0)
53
{
54
printf("Laufzeit Kanal 4: ");
55
PORTC^=(1<<PC3);// Status LED ausschalten
56
Kanal_4=1;// Zum testen auf "0"
57
58
printf_time(counter_4_end);
59
}
60
}
61
....
und schon ist dein Code nicht mehr 25 Seiten lang, sondern nur noch 2.
Was ja dann auch der Übersicht im Code zu gute kommt. Und selbst das
könnte man noch eindampfen, wenn man nicht 2 Millionen einzelne
Variablen macht, sondern die in Arrays (noch besser wären Strukturen und
ein Array von Strukturen) zusammenfasst.
Wie gesagt: wer etwas bauen will, muss sein Werkzeug kennen und damit
umgehen können. Dein Werkzeug ist eine Programmiersprache. Ich sehe aber
nicht, das du damit umgehen könntest.
1
structchannel
2
{
3
uint8_tshow;
4
uint32_tticks;
5
uint32_tend;
6
uint32_tstart;
7
uint8_tstatus_led;
8
uint8_teingang;
9
};
10
11
#define ANZAHL_KANAL 4
12
structchannelkanal[ANZAHL_KANAL]=
13
{
14
{1,0,0,0,1<<PC0,1<<PIND4},
15
{0,0,0,0,1<<PC1,1<<PIND5},
16
{0,0,0,0,1<<PC2,1<<PIND6},
17
{0,0,0,0,1<<PC3,1<<PIND7}
18
};
19
20
21
intmain(void)
22
{
23
uint8_ti;
24
25
TIMSK|=_BV(TOIE1);//aktivieren des Überlaufinterrupts von Timer1
26
TCCR1B|=(1<<CS12)|(1<<CS10);// Berechnet mit http://evolutec.publicmsg.de/index.php?menu=software&content=prescalertools
27
TCNT1=0xFFFF;//Zählregister vorladen mit FFFF zum Sofortstart
28
sei();//Interrupts aktivieren
29
30
DDRC|=(1<<PC0)|(1<<PC1)|(1<<PC2)|(1<<PC3)|(0<<PC4)|(0<<PC5);// LED 0-3 Stauts LED 4+5 Taster Kanal 1+2
31
DDRC=0b00001111;//Eingang Taster 1+2 = Eingang 5V PD1+2 bereits mit RXD TXD belegt
32
DDRD=0x00;// Eingang Taster 3 = PIND 2 bis PIND 3 Start PIND4-PIND7 Eingang 5V
33
34
35
initUART();
36
37
stdout=&mystdout;
38
39
do
40
{
41
if(1)// (sekunden!=timer)
42
{
43
for(i=0;i<ANZAHL_KANAL;i++)
44
{
45
kanal[i].ticks=timer+1;
46
}
47
}
48
49
// Starttaster
50
51
if(PINC&(1<<PINC4))// Kanal 1 Starttaster
52
{
53
kanal[0].start=kanal[0].ticks;
54
kanal[0].show=1;
55
PORTC|=kanal[0].status_led;
56
}
57
58
if(PINC&(1<<PINC5))// Kanal 2 Starttaster
59
{
60
kanal[1].start=kanal[1].ticks;
61
kanal[1].show=1;
62
PORTC|=kanal[1].status_led;
63
}
64
65
if(PIND&(1<<PIND2))// Kanal 3 Starttaster
66
{
67
kanal[2].start=kanal[2].ticks;
68
kanal[2].show=1;
69
PORTC|=kanal[2].status_led;
70
}
71
72
if(PIND&(1<<PIND3))// Kanal 4 Starttaster
73
{
74
kanal[3].start=kanal[3].ticks;
75
kanal[3].show=1;
76
PORTC|=kanal[3].status_led;
77
}
78
79
for(i=0;i<ANZAHL_KANAL;i++)
80
{
81
if(PIND&kanal[i].eingang)
82
{
83
kanal[i].end=kanal[i].ticks-kanal[i].start;
84
85
if(kanal[i].show)
86
{
87
printf("Laufzeit Kanal %d: ",i+1);
88
printf_time(kanal[i].end);
89
printf("\n");
90
91
PORTC^=kanal[i].status_led;// Status LED ausschalten
92
}
93
}
94
}
95
}
96
}
und wenn du deine Starttaster noch ein wenig anders anordnen würdest,
dann könnte man auch die noch einfach mit ins Array reinnehmen und den
ganzen Programmteil rund um die Starttaster ebenfalls auf 1 Schleife
eindampfen. Was dann wieder die Möglichkeit eröffnet, alle 3 SChleifen
zu lediglich einer einzigen for-Schleife zusammenzufassen.
Dann ist dein Code in main() gerade mal 1/2 Bildschirmseite lang :-)
Roman schrieb:> Jetzt müsste ich das ganze was kleiner bekommen.> Bin schon bei 6734 bytes.
Wozu sparsam programmieren, wenn andere µCs mit viel mehr Speicher
daherkommen? Optimieren ist doch nur etwas für Streber :-)
Ein pinkompatibler ATmega328 mit seinen 32kB Flash erlaubt es Dir,
sämtliche Schleifen 'auseinander zu rollen'.
M. N. schrieb:> Optimieren ist doch nur etwas für Streber :-)
Nö, sondern für die Leute, die noch nach ein paar Monaten im Code
durchsehen wollen. Oder die leicht Änderungen/Erweiterungen durchführen
wollen.
Dieser ganze Wust läßt sich für 8 Zeitmesser auf eine Schleife i = 0..7
reduzieren, die nur eine Funktion aufruft.
Ein Array enthält dann die 8 Zeitspeicher und ein Array die Bitmasken
für die 16 Eingangspins.
Peter Dannegger schrieb:> Ein Array enthält
Wie, soll er noch ein Array kaufen?
Bei A..i gibt es momentan Keramikmeseer. Vielleicht kann man die ja zu
einem Laufzeitmesser umbauen?
Karl Heinz Buchegger schrieb:> und wenn du deine Starttaster noch ein wenig anders anordnen würdest,> dann könnte man auch die noch einfach mit ins Array reinnehmen und den> ganzen Programmteil rund um die Starttaster ebenfalls auf 1 Schleife> eindampfen. Was dann wieder die Möglichkeit eröffnet, alle 3 SChleifen> zu lediglich einer einzigen for-Schleife zusammenzufassen.>> Dann ist dein Code in main() gerade mal 1/2 Bildschirmseite lang :-)
Gesagt getan.
1
structchannel
2
{
3
uint8_tshow;// Startsperre
4
uint32_tticks;// Systemlaufzeit
5
uint32_tend;// Gemessene Zeit
6
uint32_tstart;// Startzeit der Messung
7
uint8_tstatus_led;// Status der Messung
8
uint8_teingang;// Start Taster
9
uint8_tspannung;// Eingangsspannung
10
};
11
12
#define ANZAHL_KANAL 4
13
structchannelkanal[ANZAHL_KANAL]=
14
{
15
{1,0,0,0,1<<PC0,1<<PIND4,1<<PINB0},
16
{0,0,0,0,1<<PC1,1<<PIND5,1<<PINB1},
17
{0,0,0,0,1<<PC2,1<<PIND6,1<<PINB2},
18
{0,0,0,0,1<<PC3,1<<PIND7,1<<PINB3}
19
};
20
21
22
intmain(void)
23
{
24
uint8_ti;
25
26
TIMSK|=_BV(TOIE1);//aktivieren des Überlaufinterrupts von Timer1
27
TCCR1B|=(1<<CS12)|(1<<CS10);// Berechnet mit http://evolutec.publicmsg.de/index.php?menu=software&content=prescalertools
28
TCNT1=0xFFFF;//Zählregister vorladen mit FFFF zum Sofortstart
29
sei();//Interrupts aktivieren
30
31
DDRC|=(1<<PC0)|(1<<PC1)|(1<<PC2)|(1<<PC3);// LED 0-3 Stauts LED
32
DDRB=0x00;// Eingangsspannung
33
DDRD=0x00;// Messung starten PIND4-PIND7 Ue=5V
34
35
36
initUART();
37
38
stdout=&mystdout;
39
40
do
41
{
42
for(i=0;i<ANZAHL_KANAL;i++)
43
{
44
{
45
kanal[i].ticks=timer+1;
46
}
47
48
if(PINB&kanal[i].spannung)
49
{
50
kanal[i].start=kanal[i].ticks;
51
kanal[i].show=1;
52
PORTC|=kanal[i].status_led;// Status LED an
53
}
54
55
if(PIND&kanal[i].eingang)
56
{
57
58
kanal[i].end=kanal[i].ticks-kanal[i].start;
59
60
if(kanal[i].show)
61
{
62
printf("Laufzeit Kanal %d: ",i+1);
63
printf_time(kanal[i].end);
64
printf("\n");
65
66
PORTC^=kanal[i].status_led;// Status LED aus
67
kanal[i].show=0;// 5V Eingang sperren
68
}
69
}
70
}
71
}
72
while(1);
73
}
Danke für die großartige Hilfe und Kritiken.
Diese Version ist viel übersichtlicher und mit 2320 Bytes auch
schlanker, die Erweiterbarkeit ist auch gegeben.
Und wieder etwas dazu gelernt.