Forum: Mikrocontroller und Digitale Elektronik Befehlszusammenfassung in C


von Steffen K. (steffenkeller)


Lesenswert?

Hallo,

ich habe es jetzt geschafft meinen Wecker zum Laufen zu bringen, ohne 
ihn nach jedem Auslösen neu programmieren zu müssen
(http://www.youtube.com/watch?v=2AaPri_VT-c&feature=plcp).
Allerdings ist ein kleines Manko geblieben:

Wenn der Wecker auslöst, soll natürlich ein Hupton ertönen. Durch 
ausprobieren habe ich herausgefunden, das die Lautstärke akzeptabel ist, 
wenn ich
1
PORTB^=0x01;
2
waitMs(1);
3
PORTB^=0x01;
4
waitMs(1);
5
PORTB^=0x01;
6
waitMs(1);
7
PORTB^=0x01;
8
waitMs(1);
ungefähr 195 Mal wiederhole. Spätestens an diesem Punkt kann man 
feststellen, welche Ausmaße folglich das „Testprogramm“ hatte =)
Wenn ich aber nur einmal den Befehl setze, hört man nur ein sekündliches 
Knacken.
Die sekündliche Abfrage kommt denke ich vom Zähler der Uhr.

Nun meine Frage: kann man die Wiederholung eines Befehls zu einem Befehl 
zusammenfassen?

von ElTio (Gast)


Lesenswert?

Du möchtest eine Schleife?

von guest (Gast)


Lesenswert?

schonmal was von Kontrollstrukturen gehört?

Stichwort for, while,...

von Ingo L. (Gast)


Lesenswert?

Ganz einfach:
1
for (unsigned char i=0;i<195;i++{
2
   PORTB ^= (1<<PB1);
3
   _delay_ms(1);
4
}


Ingo

von Jason (Gast)


Lesenswert?

(1) Du kannst es über einen Timer laufen lassen, der gestartet und nach 
einer entsprechenden Zeit wieder gestoppt wird.
(2) Oder du schaltest für eine bestimmte Zeit einen PWM ein und 
schaltest ihn dann wieder aus. Wenn du keinen HardwarePWM hast, den du 
entsprechend umbiegen kannst, dann kannst du es über Software mit einem 
Timer lösen, wie bei 1 schon erwähnt

von Peter (Gast)


Lesenswert?

Vielleicht wäre da auch ein PWM Ausgang gut geeignet...

von Holger74 (Gast)


Lesenswert?

Weshalb gibts Du den Hupton nicht mittels Timer PWM aus?

von iocc (Gast)


Lesenswert?

do { int i; for(i=195;i--;waitMs(1))PORTB^=0x01; } while(0)

von Jason (Gast)


Lesenswert?

Der Vorteil von Timer/HW-PWM liegt darin, dass es Interruptgesteuert ist 
und deswegen die main(), in der es anscheinend steht (Ohne kompletten 
Quelltext kann man sowas natürlich nicht sagen...), nicht zum Stillstand 
bringt.

delay und wait ist zwar manchmal ganz schön, wie aber schon oft an 
anderer Stelle gesagt nicht DIE Lösung, sondern meistens eher der 
Fehler...

von P. M. (o-o)


Lesenswert?

Sag doch ein paar Worte über den verwendeten Code und über deine 
C-Kenntnisse. So werden Sie besser geholfen ;-)

von ... (Gast)


Lesenswert?

P. M. schrieb:
> Sag doch ein paar Worte über den verwendeten Code
so ein blödsinn! code und schaltungen beschreibt man nicht, sondern man 
zeigt sie her!

von Steffen K. (steffenkeller)


Lesenswert?

Also,
um auf möglichst alle Antworten zu reagieren:
ich hbe noch nicht viel Erfahrung im Programmieren. Habe aber relativ 
gute Elektronik-Kenntnisse und wenn man mir etwas langsam und geduldig 
erklärt, raff ichs.

Hier der relevante Teil des Quellcodes:
1
volatile int tc; 
2
char hr=22, min=13, sec=0;
3
unsigned char h=0, m=0;
4
5
char str[2];
6
7
ISR(TIMER1_OVF_vect)
8
{
9
  TCNT1 = 51136; tc++;
10
}
11
12
int main (void)
13
{
14
  TIMSK |= _BV(TOIE1);   
15
  TCCR1B |=(1<<CS12);
16
  TCNT1 = 0x0000;
17
  sei();
18
  
19
  lcdInit();
20
  void lcdClear();
21
  lcdGoto(1,4);
22
  lcdWrite("Uhr: 22:13:00");
23
        
24
  while(1)
25
  {  
26
    sec=tc;
27
    
28
    if(sec==60)
29
    
30
    {tc=0; sec=0; min++;
31
    
32
    if(min==60) {min=0; hr++; if(hr==24) hr=0;}
33
    
34
    sprintf(str,"%02d",min); lcdGoto(1, 12); lcdWrite(str);
35
    sprintf(str,"%02d",hr); lcdGoto(1, 9); lcdWrite(str);
36
    
37
    }
38
    
39
    sprintf(str,"%02d",sec); lcdGoto(1, 15); lcdWrite(str);
40
    
41
    while(sec==tc);
42
43
44
      DDRB = 0xff;  
45
      DDRC = 0x00;  
46
      PORTC = 0xff;
47
      
48
      lcdGoto(2,1);
49
      lcdWrite("Wecker:   :  ");
Dazwischen die Funktion zum Weckerstellen,
die Weckerauslösung mit dem bereits erwähnten Hupton:
1
if ((h==hr)&&(m==min))  
2
{
3
PORTB^=0x01;
4
waitMs(1);
5
...
6
}
,und die Funktion für die LCD-Darstellung

Ich betreibe die Weckeruhr mit dem ATmega8.
Wie funktioniert das denn mit dem PWM-Ausgang?
Werden dort die Impulsdauern über Variablen festgelegt?

von Peter II (Gast)


Lesenswert?

> char str[2];

hier passen 2 zeichen rein.

Du schreibst aber

sprintf(str,"%02d",min);

3 zeichen rein, 2mal eine ziffer und dann eine 0byte für das ende. Damit 
überschreibst du dir den speicher, vermutlich geht irgendetwas aus 
diesem grund nicht.

von Steffen K. (steffenkeller)


Lesenswert?

Peter II schrieb:
> 3 zeichen rein, 2mal eine ziffer und dann eine 0byte für das ende. Damit
> überschreibst du dir den speicher

Heißt das dann, dass ich das durch
1
char str [3]
 ersetzen sollte?

Peter II schrieb:
> vermutlich geht irgendetwas aus
> diesem grund nicht.

Ja gut, aber das ist ja nur ein Ausdruck zum Darstellen auf dem LCD, das 
hat doch dann keinen Einfluss auf andere Routinen, oder?

von Stefan (Gast)


Lesenswert?

Für Deine Anwendung ist die while() Schleife sicher prima geeignet. PWM 
ist eine Alternative, die technisch eleganter erscheinen mag, allerdings 
auch anspruchsvoller zu verstehen und zu programmieren ist.

PWM ist eine Hardwarefunktion, im Datenblatt beschrieben. Dabei wird ein 
Timer verwendet, um den Ton zu erzeugen. Du sagst dem Timer, welche 
Frequenz er erzeugen soll. Die Anzahl der Schwingungen zählst Du in 
einer Interrupt-Routine, so dass Du den Timer nach einer gewissen Anzahl 
von Schwingungen stoppen kannst.

Aber dazu müsstest Du dich mit PWM und Timer und Interrupts 
beschäftigen.

von Peter II (Gast)


Lesenswert?

Steffen Keller schrieb:
> Ja gut, aber das ist ja nur ein Ausdruck zum Darstellen auf dem LCD, das
> hat doch dann keinen Einfluss auf andere Routinen, oder?

doch weil du damit wild im speicher rumschreibst, damit kann alles 
mögliche passieren.

> Heißt das dann, dass ich das durch
> char str [3]
> ersetzen sollte?
ja

von Steffen K. (steffenkeller)


Lesenswert?

*Nachtrag:

egal ob eine 2 oder eine 3 in den Klammern steht, das Ergebnis ist das 
Gleiche.

von Steffen K. (steffenkeller)


Lesenswert?

@ Stefan:

Soweit habe ich das jetzt mit dem PWM verstanden:

Es wird eine Variable, sagen wir "i" definiert.

Nun zählt ein Timer i aufwärts. An einem bestimmten Zählerstand von i 
wird dann ein Interupt ausgelöst. Die Zählzeit bis dahin ist abhängig 
vom Quarz und vom maximalen Zählerstand.

In der Interupt-Routine wird dann ein Ausgang auf 1 oder 0 gesetzt.
*Frage von mir: kann da mit einem Flankenwechsel PORTB^=0x05 oder so 
gearbeitet werden?

Nach der Routine wird wieder ins Hauptprogramm gesprungen und der Zähler 
zählt weiter.

von Karl H. (kbuchegg)


Lesenswert?

Steffen Keller schrieb:
> Also,
> um auf möglichst alle Antworten zu reagieren:
> ich hbe noch nicht viel Erfahrung im Programmieren.

Merkt man.
Wo auch immer du den Code geklaut hast, der hatte auch nicht viel 
Ahnung.

AN deinem Wecker wirst du nicht viel Freude haben, weil der ziemlich 
schnell falsch gehen wird.

Erstes Hauptproblem:
Um eine einigermassen genaue Uhr zu bekommen, kommst du am CTC Modus des 
Timers nicht vorbei.
Timer Overflow mit Vorladen des Timers ist Unsinn, ausserdem kriegst du 
nicht die notwendige Langzeit-Genauigkeit. Bei Uhren, die monatelang 
laufen sollen, spielt es eine Rolle, dass der Uhr-Zählmechanismus 
möglichst auf den Takt genau angestossen wird. Andernfalls summieren 
sich die Abweichungen ganz schnell auf. D.h. vom Prinzip her muss der 
Timer ganz alleine und ohne äussere Einflussnahme auf das Zählregister 
den Takt machen. Es gibt keine Garantie, dass die Anzahl der Takte vom 
Auftreten des Overflows bis zum tatsächlichen Neusetzen des 
Zählregisters im Overflow-Interrupt immer gleich ist. Je nachdem, 
welcher Befehl in der Hauptschleife gerade in Arbeit ist, dauert das 
eine unterschiedliche Anzahl Takte. Spielt bei kurzen Programmen keine 
Rolle, wenn ein Programm aber über Monate hinweg läuft, dann summiert 
sich diese Abweichung - die Uhr geht falsch.
Der CTC Modus vermeidet dieses Problem, weil hier der Timer ganz 
alleine, ohne Unterstützung durch einen Interrupt eine genau bestimmte 
Anzahl an Takten abzählt. Ob dann die Zeit selbst ein paar Takte später 
hochgezählt wird oder nicht, spielt keine große Rolle. Wichtig ist, dass 
das 'Pendel' (das ist der Timer im CTC Modus) exakt regelmässig 
schwingt. Wenn die 'Zeiger' mal ein bischen mehr, mal ein bischen 
weniger lang diesem Schwingen hinterherhinken, ist dahingehend ziemlich 
egal. Dauert es mal ein weniger länger, dann kriegt der 'Zeiger' den 
nächsten Vorrückpuls eben ein bischen früher. Die Genauigkeit des 
'Pendels' ist bei einer Uhr der entscheidende Punkt, nicht die der 
Zeiger.

Das zweite ist, dass dein Quarz eben nicht exakt mit der aufgedruckten 
Frequenz schwingt. Auch wenn da 8Mhz drauf steht, sind es eben nicht 
exakt 8000000 Schwingungen in der Sekunden sondern das können auch 100 
weniger oder 100 mehr sein (oder irgendein Wert dazwischen). Wieder: bei 
kurz laufenden Programmen spielt das keine Rolle. Läuft aber ein 
Programm ein paar Monate dann summiert sich dieser Fehler. Und das geht 
ganz schnell! Es ist nicht ungewöhnlich, dass eine Uhr im CTC-Modus, die 
mit exakt der am Quarz aufgedruckten Frequenz durchgerechnet wurde, am 
nächsten Tag schon ein paar Sekunden falsch geht. Am Ende des Monats 
sind es dann schon Minuten und in einem halben Jahr weckt dich dein 
Wecker dann nicht mehr wie eingestellt um 6, sondern um halb 7 
(Realzeit).


Dann:
Das Erhöhen und weiterschalten der Uhrzeit nimmst du mit in den 
Timer-Interrupt hinein. Und zwar vollständig!
Nicht so wie jetzt, dass die die Weiterschaltung der Zeit in der 
Hauptschleife passiert. Deine Uhr muss auch dann weiterlaufen, wenn die 
Hauptschleife mit etwas völlig anderem beschäftigt ist, wie zb der 
Behandlung der Benutzereingabe, der sich gerade die Alarmzeit ansieht. 
Wenn dein Benutzer sich die Alarmzeit 3 Minuten lang ansieht und 
einstellt, dann darf deswegen die eigentliche Uhr nicht plötzlich 
nachgehen, nur weil der Benutzer so lange gebraucht hat! Also: Die 
komplette Abhandlung der Uhr gehört in den Interrupt. Das ist nicht viel 
Code und verlängert die ISR Abarbeitung nicht wesentlich - aber: deine 
Uhr 'verliert' dadurch die Zeit nicht mehr.

von Steffen K. (steffenkeller)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wo auch immer du den Code geklaut hast

Ok, den Teil mit dem Timer und der Uhr hatte ich aus einem Buch. Und für 
Anfänger ist es meiner Meinung nach wichtig, Gehilfen zu haben. Und 
dafür werden solche Bücher ja geschrieben. Ich würde also nicht von 
"klauen" reden :/ Der Wecker war aber ein eigener Versuch, der sich nun 
weiterentwickelt (sonst wäre ich ja nicht hier).

Karl Heinz Buchegger schrieb:
> Auch wenn da 8Mhz drauf steht

Die Toleranz ist klar. Hängt ja auch von äußeren Umständen ab. Sind die 
Abweichungen bei kleineren Frequenzen genauso hoch (ich habe 3,686Mhz 
drin)?

Zu deiner Aussage, den Zählmechanismus in die ISR reinzustecken:

Sieht das dann so aus:
1
ISR(TIMER1_OVF_vect)
2
{
3
  TCNT1 = 51136; tc++;
4
  sec=tc;
5
    
6
    if(sec==60)
7
    
8
    {tc=0; sec=0; min++;
9
    
10
    if(min==60) {min=0; hr++; if(hr==24) hr=0;}
11
}
?

von Karl H. (kbuchegg)


Lesenswert?

Steffen Keller schrieb:
> Karl Heinz Buchegger schrieb:
>> Wo auch immer du den Code geklaut hast
>
> Ok, den Teil mit dem Timer und der Uhr hatte ich aus einem Buch. Und für
> Anfänger ist es meiner Meinung nach wichtig, Gehilfen zu haben. Und
> dafür werden solche Bücher ja geschrieben. Ich würde also nicht von
> "klauen" reden :/ Der Wecker war aber ein eigener Versuch, der sich nun
> weiterentwickelt (sonst wäre ich ja nicht hier).

Oops.
Bücher sind gut und wichtig. Kein Einwand.
Allerdings gibt es mir zu denken, wenn der Autor des Buches so einen 
Quatsch macht.

> Zu deiner Aussage, den Zählmechanismus in die ISR reinzustecken:
>
> Sieht das dann so aus:
>
1
> ISR(TIMER1_OVF_vect)
2
> {
3
>   TCNT1 = 51136; tc++;
4
>   sec=tc;
5
> 
6
>     if(sec==60)
7
> 
8
>     {tc=0; sec=0; min++;
9
> 
10
>     if(min==60) {min=0; hr++; if(hr==24) hr=0;}
11
> }
12
>
> ?

Genau.
Aber arbeite ein wenig an deiner äusseren Form! Interessanterweise haben 
nämnlich diejenigen mit den scheuslichsten und unübersichtlichsten 
Programmen immer die tollsten und blödesten Fehler. Bei dem Code gehts 
noch, weil er nicht sehr umfangreich ist. Aber das wird sich im Laufe 
der Zeit ändern

(die Variable tc brauchst du nicht mehr.
1
ISR(TIMER1_OVF_vect)
2
{
3
  TCNT1 = 51136;
4
5
  sec++;
6
  if( sec == 60 )
7
  {
8
    sec = 0;
9
    min++;
10
11
    if( min == 60 )
12
    {
13
      min = 0;
14
      hr++;
15
16
      if( hr == 24 )
17
        hr = 0;
18
    }
19
  }
20
}

Pro Zeile ein Befehl. Achte darauf, dass { und zugehörige } 
untereinanderstehen (es gibt da auch noch andere Formatiersysteme). Bei 
jeder { kommen der nächste Befehl in eine neue Zeile. Befehle, die in 
einem { } Block stehen, werden konsistent um 2 (das ist mein System, es 
gibt da auch andere Einrücktiefen) Leerzeichen eingerückt.

So sieht man auf einen Blick, welche Befehle zusammengehören und wie die 
Schachtelung der Programmblöcke ist. Welche Programmteile hängen wovon 
ab? Diese Frage muss einfach mit einem Blick klärbar sein.

Wie gesagt: Bei deinem kleinen Programm mag dir das noch als unwichtig 
erscheinen. Wenn deine Programme aber mal größer werden, sind solche 
formalen Richtlinien essentiell um möglichst viele Fehler schon im 
Vorfeld auszuschliessen.

von Steffen K. (steffenkeller)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Aber arbeite ein wenig an deiner äusseren Form!

Alles klar =) Darauf werde ich in Zukunft achten.

Dann verändere ich gleich mal den QC :D

von Karl H. (kbuchegg)


Lesenswert?

Wenn du den Code so umändern würdest, dass die ISR alle 1/1000 Sekunde 
aufgerufen wird (du brauchst dann klarerweise eine Variable die bis 1000 
zählt um die Sekunden weiterzuschalten), dann könntest du deinen Summer 
auch in der ISR jeweils umschalten.
Vorteil: Du brauchst dich in der Hauptschleife nicht mehr darum kümmern 
und du hast kein _delay_ms in der Hauptschleife.

Denn das, einen _delay_ms, den willst du nach Möglichkeit vermeiden, 
wenn du kannst. _delay_ms ist selten die Lösung für ein Problem. Aber 
_delay_ms wird oft selbst zu einem Problem.

1
#include <avr/io.h>
2
3
#define SUMMER_DDR   DDRB
4
#define SUMMER_PORT  PORTB
5
#define SUMMER_PIN   PB0
6
7
volatile uint8_t soundOn;
8
volatile uint8_t hr = 22, min = 13, sec = 0;
9
10
uint8_t  alarmHr = 22;
11
uint8_t  alarmMin = 14;
12
13
char str[10];
14
15
ISR(TIMER1_OVF_vect)
16
{
17
  static uint8_t milliSec;
18
19
  TCNT1 = .....;   // musst du, zusammen mit dem Vorteiler bei der
20
                   // Timereinstellung, für 1/1000 Sekunde neu berechnen
21
22
  //
23
  // Um den Summer kümmern, wenn er eingeschaltet ist
24
  //
25
  if( soundOn )
26
    SUMMER_PORT ^= _BV( SUMMER_PIN );
27
28
  //
29
  // Die eigentliche Uhrzeit weiterschalten
30
  //
31
  milliSec++;
32
  if( milliSec == 1000 )
33
  {
34
    milliSec = 0;
35
    sec++;
36
37
    if( sec == 60 )
38
    {
39
      sec = 0;
40
      min++;
41
42
      if( min == 60 )
43
      {
44
        min = 0;
45
        hr++;
46
47
        if( hr == 24 )
48
          hr = 0;
49
      }
50
    }
51
  }
52
}
53
54
int main (void)
55
{
56
  SUMMER_DDR |= _BV( SUMMER_PIN );
57
58
  TIMSK |= _BV(TOIE1);   
59
  TCCR1B |=(1<<CS12);
60
  TCNT1 = 0x0000;
61
  sei();
62
  
63
  lcdInit();
64
  lcdClear();
65
66
  lcdGoto( 1, 4 );
67
  lcdWrite( "Uhr" );
68
69
  while(1)
70
  {  
71
    sprintf( str, "%02d:%02d:%02d", hr, min, sec);
72
    lcdGoto( 1, 9 );
73
    lcdWrite( str );
74
75
    if( hr == alarmHr && min == alarmMin )
76
      soundOn = 1;
77
    else
78
      soundOn = 0;
79
  }
80
}

Wenn der Summer tröten soll, setzt du in der Hauptschleife einfach die 
Variable 'soundOn' auf 1 und der Tröterich gibt Laut. Soll er wieder 
ruhig sein ... Variable auf 0 setzen und der Spuk ist vorbei.

(Um den CTC Modus kümmern wir uns später)

von Steffen K. (steffenkeller)


Lesenswert?

Ich habe jetzt den QC folgendermaßen geändert:
1
char hr=12, min=3, sec=0;
2
unsigned char h=0, m=0;
3
4
char str[2];
5
6
ISR(TIMER1_OVF_vect)
7
{
8
  TCNT1 = 51136;
9
    
10
    sec++;
11
  if( sec == 60 )
12
  {
13
    sec = 0;
14
    min++;
15
16
    if( min == 60 )
17
    {
18
      min = 0;
19
      hr++;
20
21
      if( hr == 24 )
22
              hr = 0;
23
    }
24
  }
25
  sprintf(str,"%02d",min); lcdGoto(1, 12); lcdWrite(str);
26
  sprintf(str,"%02d",hr); lcdGoto(1, 9); lcdWrite(str);
27
  sprintf(str,"%02d",sec); lcdGoto(1, 15); lcdWrite(str);
28
}
29
30
int main (void)
31
{
32
  TIMSK |= _BV(TOIE1);
33
  TCCR1B |=(1<<CS12);
34
  TCNT1 = 0x0000;
35
  sei();
36
  
37
  lcdInit();
38
  void lcdClear();
39
  lcdGoto(1,4);
40
  lcdWrite("Uhr: 12:03:00");
41
  
42
43
  DDRB = 0xff;  //PORTB als Ausgang für Speaker
44
          //und LED
45
  DDRC = 0x00;  //PORTC als Eingang für Taster zum 
46
          //Stellen der Weckzeit
47
  PORTC = 0xff;  //PullUp-Widerstände an PORTC ein
48
  
49
  lcdGoto(2,1);
50
  lcdWrite("Wecker:   :  ");
51
  
52
  stellen();
53
54
  if ((h==hr)&&(m==min))  
55
  {
56
    lcdGoto(1, 1); lcdWrite("Einsatz");
57
    lcdGoto(2, 1); lcdWrite ("7/83-1");
58
    
59
    PORTB=0xFF;
60
    
61
    alarm();
62
  }
63
  
64
  else
65
  {
66
    PORTB=0x00;
67
    lcdGoto(1, 1); lcdWrite ("   Uhr:");
68
    lcdGoto(2, 1); lcdWrite("Wecker");
69
  }
70
  
71
  
72
  sprintf(str,"%02d",h); lcdGoto(2,9); lcdWrite(str);
73
  sprintf(str,"%02d",m); lcdGoto(2,12); lcdWrite(str);
74
      
75
  return 0;  
76
}
Jetzt läuft aber die Uhr nicht mehr...

von Karl H. (kbuchegg)


Lesenswert?

Steffen Keller schrieb:

> Jetzt läuft aber die Uhr nicht mehr...


Du hast keine Hauptschleife mehr :-)

Scroll ein wenig hoch, den Post vor deinem.
Ich hab dir da mal was zusammengestellt.
Den Timer müsstest du neu auf 1/1000 Sekunde ausrechnen, aber abgesehen 
davon, ist der Code vollständig

von Karl H. (kbuchegg)


Lesenswert?

Aber prinzipiell
1
ISR(TIMER1_OVF_vect)
2
{
3
  TCNT1 = 51136;
4
    
5
    sec++;
6
  if( sec == 60 )
7
  {
8
    sec = 0;
9
    min++;
10
11
    if( min == 60 )
12
    {
13
      min = 0;
14
      hr++;
15
16
      if( hr == 24 )
17
              hr = 0;
18
    }
19
  }
20
  sprintf(str,"%02d",min); lcdGoto(1, 12); lcdWrite(str);
21
  sprintf(str,"%02d",hr); lcdGoto(1, 9); lcdWrite(str);
22
  sprintf(str,"%02d",sec); lcdGoto(1, 15); lcdWrite(str);
23
}

Nein!
Davon, dass die Ausgabe aufs LCD ebenfalls in die ISR soll, war nicht 
die Rede. Die Weiterschaltung der Zeit kommt in die ISR, die Ausgabe 
bleibt in der Hauptschleife!

von Cyblord -. (cyblord)


Lesenswert?

Wie nennt sich das hier eigentlich? Online-Programming? Wir entwicklen 
ein Programm on-the-fly im Forum? Früher hat man mal selbst 
programmiert, wenns nicht ging hat man selbst debugt. Und solange 
probiert bis es ging. Heute schmeißt man jeden einzelnen Schritt ins 
Forum? Ist doch irgendwie abartig.

gruß cyblord

von Steffen K. (steffenkeller)


Lesenswert?

Ok, ich brauch kurz Zeit, um deinen Vorschlag auszuproieren...

von Dietrich L. (dietrichl)


Lesenswert?

Karl Heinz Buchegger schrieb:
> static uint8_t milliSec;

Sollte das nicht uint16_t heißen? Er soll ja bis 1000 zählen :-)

von Karl H. (kbuchegg)


Lesenswert?

cyblord ---- schrieb:
> Wie nennt sich das hier eigentlich? Online-Programming? Wir entwicklen
> ein Programm on-the-fly im Forum? Früher hat man mal selbst
> programmiert, wenns nicht ging hat man selbst debugt. Und solange
> probiert bis es ging. Heute schmeißt man jeden einzelnen Schritt ins
> Forum? Ist doch irgendwie abartig.

Irgendwann muss man dem Nachwuchs auch die richtigen Techniken zeigen, 
wie man Programmier-Probleme angeht.

Da gabs schon schlimmeres. Er hatte ja prinzipiell schon was, man muss 
ihn jetzt in die richtige Richtung lenken. Und sein Buch scheint da 
nicht so der Bringer zu sein.

von Karl H. (kbuchegg)


Lesenswert?

Dietrich L. schrieb:
> Karl Heinz Buchegger schrieb:
>> static uint8_t milliSec;
>
> Sollte das nicht uint16_t heißen? Er soll ja bis 1000 zählen :-)

Oops. Danke für den Catch.
Das hatte ich beim Online-Programmieren übersehen!

Danke.

von Christopher C. (Gast)


Lesenswert?

Das ist nur ein Vorschlag!
Wie wärs wenn du einfach noch mal gescheit C auf dem Computer lernst und 
dich dann weiter mit dem µC beschäftigst. Und vor allem die GCC-AVR 
Tutorials durcharbeitest, da lernst du dann z.B., dass sowas wie die LCD 
Ansteuerung in einer ISR nichts zu suchen hat.
Weil man einfach sieht, dass dir noch ein bisschen was fehlt. Allerdings 
scheinst du C schon mal recht gut begriffen zu haben, aber C ist nun mal 
nicht Fehlertolerant und haut einem sofort auf die Finger! Das sind 
schon solche kleinen Dinge wie, dass ein String immer mit '\0' endet!

Wie gesagt, das ist nur ein Vorschlag, aber du wirst sehen, dass das 
Programmieren ansonsten sehr frustrierend sein wird.

von Steffen K. (steffenkeller)


Lesenswert?

Beim Overflow-Ausrechnen habe ich noch einige Hindernisse:

Den Wert fürs Register, wenn ich Millisekunden will, berechne ich dann 
so:

(2^16 - (Quarzfrequenz / Prescaler)) / 1000

Und da muss dann ein ganzzahliger Wert rauskommen, oder?

Ich bekomme aber entweder Zahlen mit drei Dezimalstellen oder einen 
negativen Wert mit drei Dezimalstellen.

von spess53 (Gast)


Lesenswert?

Hi

>Den Wert fürs Register, wenn ich Millisekunden will, berechne ich dann
>so:

Nimm die Formel aus dem Datenblatt

Timer->Modes of Operation->CTC

und stelle sie um. Deine ist falsch.

MfG Spess

von Steffen K. (steffenkeller)


Lesenswert?

Die Formel aus dem Datenblatt müsste demnach diese sein:

f(OCR1A) = f(clk_I/O) / 2*N*(1+OCR1A)

N ist der Prescaler
f(clk_I/O) ist die Quarzfrequenz
f(OCR1A) ist die Frequenz der Overflows

Hab ich das so richtig verstanden?

von Karl H. (kbuchegg)


Lesenswert?

Sorry, musst kurzfristig mal weg.

Meiner Meinung nach ist es am Vernünftigsten, wenn man versteht, wie 
diese Formel zu Stande kommt als einfach nur Werte in irgendeine Formel 
einzusetzen.

Hier
FAQ: Timer
findet sich ein Ansatz, mit dem man versteht was man wie rechnet. Timer 
sind eigentlich nicht schwer und auch die Bestimmung der Zahlenwerte ist 
nicht wirklich schwer, wenn man sich einmal überlegt hat, was da 
eigentlich passiert.

(Und im Endeffekt landet man natürlich wieder bei den Formel. Nur mit 
einem Unterschied: plötzlich versteht man, warum die Formel genauso 
aussieht wie sie aussieht, und wenn man mal die Formel nicht parat hat, 
dann leitet man sich die Zahlenwerte einfach so auf die Schnelle her)

von R. M. (rmax)


Lesenswert?

Karl Heinz Buchegger schrieb:

>   //
>   // Um den Summer kümmern, wenn er eingeschaltet ist
>   //
>   if( soundOn )
>     SUMMER_PORT ^= _BV( SUMMER_PIN );
> [...]
> Wenn der Summer tröten soll, setzt du in der Hauptschleife einfach die
> Variable 'soundOn' auf 1 und der Tröterich gibt Laut. Soll er wieder
> ruhig sein ... Variable auf 0 setzen und der Spuk ist vorbei.

Je nach Art und Anschaltung des Tröterichs bietet es sich evtl. an, den 
Pin im Aus-Zustand definiert auf Low zu schalten, statt ihn auf dem 
letzten Zustand zu belassen.

von Steffen K. (steffenkeller)


Lesenswert?

Danke für den Link, werde jetzt mal viel lesen.

von Karl H. (kbuchegg)


Lesenswert?

Steffen Keller schrieb:

> Ich bekomme aber entweder Zahlen mit drei Dezimalstellen oder einen
> negativen Wert mit drei Dezimalstellen.


Hinweis:
Die 1000 stammen ja daraus, dass du den Summerport alle 1ms umschalten 
willst. Das muss aber nicht unbedingt so sein.
Wenn sich die Zahlen mit 1002 oder 998 besser ausgehen, dann kannst du 
natürlich auch die nehmen. Für die Uhr ist es egal. Ob da mit 1000 oder 
mit 998 für 1 volle Sekunde verglichen wird, spielt keine Rolle.

Und ob die Tröte jetzt genau 500Hz von sich gibt, oder 449Hz oder 551Hz 
oder 505Hz, wird praktisch gesehen auch keine Rolle spielen.

Schlussfolgerung: Es gibt keinen Grund, warum du an den 1000 kleben 
musst. Rechne die Werte für 1000 durch, schneide die Kommastellen ab und 
rechne daraus dann zurück, welchen Sekundenteilungsfaktor (tolles Wort 
:-); das was ursprünglich die 1000 waren) du dadurch erhältst. Und der, 
bei dem du am nächsten an den 1000 bist, den nimmst du (sofern dein Wert 
zum Vorsetzen des Timers noch ins 16 Bit Register passt und nicht 
negativ ist, selbstverständlich). Noch beim '1000-er' Vergleich für die 
Sekundenweiterschaltung den errechneten Wert eintragen, dann läuft auch 
die Uhr wieder richtig.

von walTTer (Gast)


Angehängte Dateien:

Lesenswert?

Karl Heinz Buchegger schrieb:
> Da gabs schon schlimmeres. Er hatte ja prinzipiell schon was, man muss
> ihn jetzt in die richtige Richtung lenken. Und sein Buch scheint da
> nicht so der Bringer zu sein.

Das Buch heißt "AVR-Mikrocontroller in C programmieren" von Dr. Günter 
Spanner.
Das Buch habe ich auch und ratet mal was ich gerade versuche zu 
programmieren- richtig eine Uhr ;-) genauer eine Fussballuhr (siehe 
Bild)
Als Betreuer einer Fussballmanschaft bin ich für die Zeitnahme 
zuständig. Wenn jetzt z.B. in der 13.Min 2.Halbzeit ein Tor fällt muss 
ich 45-min plus 13-min rechnen und wenn ein Spieler fragt "walTTer wie 
lange noch?" muss ich 45 minus 13 rechnen, was meine geistigen 
Fähigkeiten als fast 60 jährigen auf eine harte Probe stellt.
Deswegen meine Fussballuhr:
Tastendruck: 1.Halbzeit 00-45 min / Restspielzeit
Tastendruck: 2.Halbzeit 45-90 min / Restspielzeit
Nach 95 min legt sich die Uhr schlafen und wird durch einen Tastendruck 
wieder aufgeweckt. (nach einer Woche beim nächsten Spiel)

walTTer

von Sam P. (Gast)


Lesenswert?

Mal ein klein wenig Off-Topic:

Christopher C. schrieb:
> Und vor allem die GCC-AVR
> Tutorials durcharbeitest

Solche Ratschläge lese ich öfter. Das ist schonmal etwas konstruktiver 
als "lern erstmal C" oder "nimm nochmal dein C-Buch zur Hand", es ist 
gut gemeint, und richtig und wichtig, aber... kann man sowas nicht mal 
(bitte :) direkt verlinken?

Ich kann C schon lange, und darum kenne ich auch keine gescheiten 
Tutorials mehr (hab mal mit CTUTOR.DOC gelernt, falls das noch wer 
kennt. Damals, als .DOC noch nix mit MS Word zu tun hatte). Es gibt ja 
doch viel Ramsch, was sich Tutorial schimpft. Selber-Googlen führt da 
auch mal zu dem Driss, und wie soll der Anfänger Spreu von Weizen 
trennen? Gibt es eine Wiki-Seite mit solchen Ressourcen? Die wäre ja 
banal einfach zu verlinken.

Wenn öfter mal was gutes direkt verlinkt wird, hilft es dem Fragesteller 
beim Lernen, dem Forum beim weniger-Anfängerfragen-beantworten-müssen 
und auch so jemandem wie mir beim Tipps Weitergeben.

von Michael (Gast)


Lesenswert?

Steffen Keller schrieb:
> Die Toleranz ist klar. Hängt ja auch von äußeren Umständen ab. Sind die
> Abweichungen bei kleineren Frequenzen genauso hoch (ich habe 3,686Mhz
> drin)?

Beim Quarz als Zeitbasis für eine Uhr gibt es drei Problemstellen.

1. Die Nennfrequenz des Quarzes ist mit einem einfachen Teiler nicht 
exakt auf einen brauchbaren Basistakt (z.B. 1 ms) herunterteilbar.

2. Der Quarz weicht von der Nennfrequenz ab

3. Der Quarz ändert seine Frequenz mit der Temperatur/Alterung

Zu 1. kann man sich evtl. einen Quarz mit passender Frequenz 
heraussuchen. Das nützt aber wegen (2) alleine nichts, um auf eine 
Genauigkeit´zu kommen, wie sie für eine Uhr erforderlich ist. Da hilft 
nur Abgleichen in Form von Hardware (Trimmkondensator), was man in Uhren 
ohne µC macht, sofern die passenden Meßtechnik verfügbar ist, oder per 
Software durch Taktausblendung.
Beitrag "Die genaue Sekunde / RTC"
Gegen 3. hilft nur Voralterung und eine flache Temperaturkennlinie 
und/oder geringe Temperaturschwankungen. Temperatureffekte kann man 
durch passenden Quarzschnitt klein halten, so dass der Quarz im 
Wendepunkt bzw. Max/Min der f(T)-Kennlinie arbeitet (-> Datenblatt).

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.