Forum: Mikrocontroller und Digitale Elektronik Probleme bei selbstgebauter Uhr (DEM 16217 | STK500)


von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Hallo,

ich versuche gerade mit meinem STK500 und dem Display DEM 16217 SYH eine 
einstellbare Uhr zu bauen. Für das Timing nutze ich die util/delay.h und 
den 8-Bit Timer 0, für das Ansprechen des Displays nutze ich die 
lcd-routines von 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung 
.

Das Programm zählt die Sekunden hoch und nach den Regeln die unsere 
Uhren nun mal haben auch die Minuten und Stunden.
Zusätzlich lassen sich auch die Minuten und Stunden einstellen. Die 
Tasten wurden dafür entprellt.
Der Code stammt aus dem Wiki hier: 
http://www.mikrocontroller.net/articles/Entprellung .

Hier erstmal mein Sourcecode:
1
/*
2
 ============================================================================
3
 Name        : LCD_TestProgramm.c
4
 Author      : Fabian Hoemcke
5
 Version     :
6
 Copyright   : actually no copyright
7
 Description : currently a playground
8
 ============================================================================
9
 */
10
11
#include <avr/io.h>
12
#include <avr/interrupt.h>
13
#include <stdio.h>
14
#ifndef F_CPU
15
#define F_CPU 3686400
16
#endif
17
#include <util/delay.h>
18
#include "lcd-routines.h"
19
20
volatile unsigned char c = 0,
21
             Hours = 0,
22
             Minutes = 0,
23
             Secundes = 0;
24
char String[3] = "";
25
26
SIGNAL(TIMER0_OVF_vect)
27
{
28
  TCNT0 = 30;
29
  if(c++ == 16)
30
  {
31
    c = 0;
32
    if(Secundes++ == 59)
33
    {
34
      Secundes = 0;
35
      if(Minutes++ == 59)
36
      {
37
        Minutes = 0;
38
        if(Hours++ == 23) Hours = 0;
39
      }
40
    }
41
42
    lcd_setcursor(0, 1);
43
    sprintf(String, "%02i", Hours);
44
    lcd_string(String);
45
    lcd_string(":");
46
    sprintf(String, "%02i", Minutes);
47
    lcd_string(String);
48
    lcd_string(":");
49
    sprintf(String, "%02i", Secundes);
50
    lcd_string(String);
51
  }
52
  return;
53
}
54
55
void entprellung( volatile uint8_t *port, uint8_t maske ) {
56
  uint8_t   port_puffer;
57
  uint8_t   entprellungs_puffer;
58
59
  for( entprellungs_puffer=0 ; entprellungs_puffer!=0xff ; ) {
60
    entprellungs_puffer<<=1;
61
    port_puffer = *port;
62
    _delay_us(150);
63
    if( (*port & maske) == (port_puffer & maske) )
64
      entprellungs_puffer |= 0x01;
65
  }
66
}
67
68
int main(void)
69
{
70
  char Buttons;
71
  DDRA = 0x00;  // Buttons at port A
72
  PORTA = 0xFF;  // enable pull ups | buttons default high
73
  TCCR0 = 0x05;  // 0000|0101 | pre-scaler Clck/1024
74
  TCNT0 = 30;
75
  TIMSK |= 0x02;  // 0000|0010 | enable Timer Counter Overflow Interrupt
76
  
77
  lcd_init();    // initialize LCD module
78
  sei();      // enable interrupts 
79
  
80
  while(1)
81
  {
82
    while(PINA==255);
83
    entprellung( &PINA, 0xFF);
84
    Buttons = ~PINA;
85
    switch(Buttons)
86
    {
87
    case 1 : Minutes++; break;
88
    case 2 : Hours++; break;
89
    default: break;
90
    }
91
    while(!(PINA==255));
92
    entprellung( &PINA, 0xFF);
93
  }
94
95
  return 0;
96
}

Ich weiß, man muss nicht unbedingt den ganzen Sourcecode posten sondern 
nur den relevanten Teil. Aber ich war mir nicht ganz sicher, auf was ich 
hier verzichten sollte. Ich hoffe es ist im wesentlichen gut kommentiert 
oder selbsterklärend.

Nun zu meinen drei Problemen:

Timing
Soweit funktioniert das Programm auch wie erwartet. Nur gibt es einen 
extremen Tineshift.
Ich habe das Programm mal über Nacht laufen lassen um einen möglichst 
(unter den Umständen) guten Messwert zu bekommen.
Nach 10 Stunden ging meine Uhr eine 3/4 Stunde nach.
Das heißt umgerechnet, aller 13 Sekunden geht mein Projekt 1 Sekunde 
nach.
Das finde ich schon gehörig viel. Es wird sogar schwierig das mit dem 
DCF77 Signal konstant zu halten, was ich später noch vorhabe.
Natürlich fordern der Code in der Main und der Aufruf der LCD Routinen 
ihren Tribut. Da gehen bestimmt ein paar Overflows verloren.
Jedoch wenn ich das Programm stark abspecke und lediglich jede Sekunde 
eine LED an und ausgehen lasse, fällt es auch schon stark auf.
Als vergleich dient meine Wanduhr, deren mechanischer Sekundenzeiger gut 
zu hören ist.
Mir ist durchaus bewusst, dass, wenn auch ein Quarz in ihr schlägt, ich 
hier nicht mit einer besonders hohen Genauigkeit zu tun habe.
Die Mechanik des Zeigers, bis ich es höre und der Umstand dass ich kein 
Messinstrument bin verhindert das natürlich.
Jedoch, wenn beide einigermaßen synchron laufen, kann es nicht sein, 
dass ich bereits nach wenigen Sekunden einen deutlichen Shift wahrnehme.
Bei einer Differenz von 1/13 aber auch kein Wunder. Bereits nach etwa 8 
Sekunden sind sie aus dem Takt und nach weiteren 8 Sekunden wieder im 
Takt.
Das ist wie ich finde gehörig viel.

Was kann die Ursache der Ungenauigkeit sein?

Der Jumper von OSCSEL ist auf Pin 1-2. Das ist die Standardeinstellung. 
So wie ich es im Handbuch las, liegt dort ein Takt von 3.686.400Hz an.
Dieser Takt wird per Software generiert und ist die Standardeinstellung. 
Diese habe ich natürlich auch nicht verändert.

Mir ist natürlich klar, dass ein solcher Takt nie so genau ist, wie der 
eines Quarzes. Aber kann die Ungenauigkeit denn wirklich so erheblich 
sein?
So nebenbei, ich habe nirgends gefunden welcher Takt beim OSCSEL an Pin 
3 an liegt. Da liegt einer an, erheblich langsamer als die 3,7MHz. Aber 
er ist da. Nur was für einer das ist, konnte ich bisher noch nicht raus 
finden.

Vielleicht liegt auch das Problem bei der Verwendung der util/delay.h. 
Ich habe mir die delay.h und die delay_basic.h genau angeschaut, aber 
keinen Hinweis darauf gefunden, ob und welchen Timer sie verwenden.

Volatile
Was ist die Eigenschaft des Specifiers volatile?
Ein Link würde reichen.
Der Wikipedia-Artikel http://de.wikipedia.org/wiki/Volatile_(Informatik) 
half mir nicht wirklich weiter.
Ich verstehe vor allem nicht den Unterschied zu static und warum ich 
auf diese globalen Variablen von der Main aus nicht darauf zu greifen 
kann.
Weshalb ich diese Variablen als volatile spezifizierte.
Ich habe mir sagen lassen, bei einem PC landen diese Variablen im Level1 
Cache und sind somit schneller zu verarbeiten. Was einleuchtet. So lange 
der Level1 Cache nicht voll ist. Aber ein Level1 Cache bei einem 
Kontroller? (ATmega8515)

Wenn also jemand einen Link weiß, wo es für Idioten beschrieben ist, 
wäre ich sehr dankbar.

Switch Register

Warum scheitert der Versuch den Inhalt einer Registers als 
Switch-Bedingung zu nutzen?
1
switch(~PINA)
2
    {
3
    case 1 : Minutes++; break;
4
    case 2 : Hours++; break;
5
    default: /* Show content of PINA on Display */ break;
6
    }
oder auch:
1
switch(PINA)
2
    {
3
    case 254 : Minutes++; break;
4
    case 253 : Hours++; break;
5
    default: /* Show content of PINA on Display */ break;
6
    }
????
Es landet immer im Default. Obwohl der Inhalt des Registers den Case 
Bedingungen entspricht.
Weshalb ich die Variable Buttons zusätzlich verwenden muss.


Vielen Dank erstmal schon fürs Durchlesen.
Das ist ja schon eine ganze Menge.

Gruß
BrEin



_____________________________________________________________________
P.S.
Ich lese des öfteren hier im Forum wie einige sich nicht an hiesige 
Gepflogenheiten halten und deshalb gemaßregelt werden.
Ich habe versucht mich an diese zu halten.
Sollte ich mich dennoch vertan haben, so bitte ich um Rücksicht und 
einen freundlichen Hinweis.
Sollte es durch diesen riesigen Artikel so wirken, als würde ich alle 
meine Probleme auf euch Abladen, möchte ich mich dafür entschuldigen.

von clocker (Gast)


Lesenswert?

Erste Massnahme sollte sein, die LCD-Ausgabe aus der Interrupt-Routine 
ins Hauotprogramm zu verlagern.

Zweite Massnahme: wenn man schon dabie ist, sollte man auch das 
Berechnen der Uhrzeit ins Hauptprogramm verlagern, so dass in der 
interruptroutine nur noch ein Merker gesetzt wird, dass eine Sekunde 
vergangen ist.

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

clocker schrieb:
> Erste Massnahme sollte sein, die LCD-Ausgabe aus der Interrupt-Routine
> ins Hauotprogramm zu verlagern.
>
> Zweite Massnahme: wenn man schon dabie ist, sollte man auch das
> Berechnen der Uhrzeit ins Hauptprogramm verlagern, so dass in der
> interruptroutine nur noch ein Merker gesetzt wird, dass eine Sekunde
> vergangen ist.

Vielen Dank für deine schnelle Antwort.
Aber ich glaube, ich habe ein generelles Problem.
Denn selbst bei einem einfachen/kurzen Programm entsteht der Shift.

Beispiel:
1
/*
2
 * LightRunner_AVR.c
3
 *
4
 *  Created on: Oct 13, 2010
5
 *      Author: fabian
6
 */
7
8
#include <avr/io.h>
9
#include<avr/interrupt.h>
10
11
volatile char c = 0;
12
13
SIGNAL(TIMER0_OVF_vect)
14
{
15
  TCNT0 = 30;
16
  if(c++ == 16)
17
        {
18
          c = 0;
19
          if(PORTB == 0x7F) { PORTB = 0xFE; return;}
20
    PORTB = ~(~PORTB << 1) | 0x01;
21
        }
22
}
23
24
int main (void)
25
{
26
  DDRB = 0xFF;
27
  PORTB = 0xFE;
28
  TCCR0 = 0x05;  // 0000|0101
29
  TCNT0 = 30;
30
  TIMSK |= 0x02;  // 0000|0010
31
  sei();
32
  while (1)
33
  {
34
  }
35
}

Oder ist auch hier die Berechnung in der SIGNAL das Problem? Soll auch 
hier ich es auslagern?

Danke

von clocker (Gast)


Lesenswert?

ja

von Karl H. (kbuchegg)


Lesenswert?

Übertreib mal nicht.

Die LCD Aufrufe aus dem Interrup raus ist gut und richtig.
Den Rest kannst du (und sollst du sogar) drinnen lassen, damit die Uhr 
für sich alleine vollständig tickt.

Auch wenn die Empfehlung lautet: ISR so kurz wie möglich, muss man auch 
nicht übertreiben. In einer ISR soll nur das notwendigste passieren, 
aber man muss auch nicht auf Biegen und Brechen die ISR auf reines Flag 
setzen reduzieren.

von Karl H. (kbuchegg)


Lesenswert?

> Jedoch, wenn beide einigermaßen synchron laufen, kann es nicht sein,
> dass ich bereits nach wenigen Sekunden einen deutlichen Shift
> wahrnehme. Bei einer Differenz von 1/13 aber auch kein Wunder.
> Bereits nach etwa 8 Sekunden sind sie aus dem Takt und nach weiteren
> 8 Sekunden wieder im Takt.
>
> Das ist wie ich finde gehörig viel.
>
> Was kann die Ursache der Ungenauigkeit sein?


Was erwartest du

3686400 / 1024 = 3600

3600 / 236 = 15.25zerquetschte

Interrupts pro Sekunde. Du hast keinen ganzzahligen Teiler, so dass du 
deine Taktfrequenz auf eine ganzzahlige Anzahl Interrupt Aufrufe pro 
Sekunde herunterteilen würdest.

Wenn du daher erst alle 16 Interrupts 1 Sekunde weiterschaltest, dann 
geht deine Uhr zu langsam. Und zwar um 0.75/16 Sekunden pro Sekunde. Und 
das ist schon ziemlich viel und summiert sich sehr schnell.

von Karl H. (kbuchegg)


Lesenswert?

Fabian Hoemcke schrieb:

> Ich habe versucht mich an diese zu halten.
> Sollte ich mich dennoch vertan haben, so bitte ich um Rücksicht und
> einen freundlichen Hinweis.

Dein Posting ist fein.
Es sollte mehr Postings in diesem Stil geben.
Einzig: Wenn du uns noch die Hardware dazu nennst, wäre alles 
vollständig, denn das hier

> Der Jumper von OSCSEL ist auf Pin 1-2. Das ist die
> Standardeinstellung. So wie ich es im Handbuch las, liegt dort ein
> Takt von 3.686.400Hz an. Dieser Takt wird per Software generiert und
> ist die Standardeinstellung.

lässt sich nur verstehen, wenn man die konkrete Schaltung kennt.

von Lehrmann M. (ubimbo)


Lesenswert?

Zitat:
[...]
In C und C++ wird durch diesen Typqualifikator spezifiziert, dass sich 
der Wert der Variablen außerhalb des aktuellen Programmkontextes ändern 
kann, beispielsweise durch externe Hardware. Bei der Generierung des 
Maschinen-Codes aus einem in C oder C++ geschriebenen Programm 
verhindert die Kennzeichnung einer Variablen als volatile eine 
übermäßige Optimierung, so dass das Programm immer auf den tatsächlich 
in der Hardware vorhandenen Wert zugreift
[...]

Quelle: Der vom Threadsteller gelinkte Artikel wikipedia.


Ich verstehe noch nicht ganz was man an diesem Artikel missverstehen 
kann.
Mal ganz salop gesagt: Nehmen wir du veränderst in der Software den Wert 
dieser Variable nie dann könnte der Compiler "denken": 'Ha um 
Speicherplatz zu sparen kann ich die ja umwandeln oder ihr nur weniger 
Speicherplatz zugestehen. Die wird ja sowieso nicht verändert'
Sagst du den Compiler, dass sie volatile ist so weiß der Compiler - oha 
auch externe Hardware (PORT, ADC, etc.) können die Variable "ohne mein 
Wissen" verändern. Darum nicht optimieren.

von Knut (Gast)


Lesenswert?

Nur leider verschenkst du eine Sekunde wenn du Sekunden und Minuten bei 
59 schon wieder auf 0 setzt ;-)



Gruß Knut

von Karl H. (kbuchegg)


Lesenswert?

Knut schrieb:
> Nur leider verschenkst du eine Sekunde wenn du Sekunden und Minuten bei
> 59 schon wieder auf 0 setzt ;-)

:-)
Seine Schreibweise ist nicht sehr glücklich. Tatsächlich stimmt die 
Logik durch den Postincrement schon.

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Karl heinz Buchegger schrieb:
>> Jedoch, wenn beide einigermaßen synchron laufen, kann es nicht sein,
>> dass ich bereits nach wenigen Sekunden einen deutlichen Shift
>> wahrnehme. Bei einer Differenz von 1/13 aber auch kein Wunder.
>> Bereits nach etwa 8 Sekunden sind sie aus dem Takt und nach weiteren
>> 8 Sekunden wieder im Takt.
>>
>> Das ist wie ich finde gehörig viel.
>>
>> Was kann die Ursache der Ungenauigkeit sein?
>
>
> Was erwartest du
>
> 3686400 / 1024 = 3600
>
> 3600 / 236 = 15.zerquetschte
>
> Interrupts pro Sekunde. Du hast keinen ganzzahligen Teiler, so dass du
> deine Taktfrequenz auf eine ganzzahlige Anzahl Interrupt Aufrufe pro
> Sekunde herunterteilen würdest.

Stimmt! Danke! TCNT0 muss den Wert 31 erhalten!
Ich probiere es gleich mal aus, ob das der Teufel im Detail war!

Karl heinz Buchegger schrieb:
> Fabian Hoemcke schrieb:
>
>> Ich habe versucht mich an diese zu halten.
>> Sollte ich mich dennoch vertan haben, so bitte ich um Rücksicht und
>> einen freundlichen Hinweis.
>
> Dein Posting ist fein.
> Es sollte mehr Postings in diesem Stil geben.
> Einzig: Wenn du uns noch die Hardware dazu nennst, wäre alles
> vollständig, denn das hier
>
>> Der Jumper von OSCSEL ist auf Pin 1-2. Das ist die
>> Standardeinstellung. So wie ich es im Handbuch las, liegt dort ein
>> Takt von 3.686.400Hz an. Dieser Takt wird per Software generiert und
>> ist die Standardeinstellung.
>
> lässt sich nur verstehen, wenn man die konkrete Schaltung kennt.

STK500 ist das bekannte Evauationbord von Atmel. Bestückt mit einem 
ATmega8515.

Handbuch: http://www.atmel.com/atmel/acrobat/doc1925.pdf

Lehrmann Michael schrieb:
> Zitat:
> [...]
> In C und C++ wird durch diesen Typqualifikator spezifiziert, dass sich
> der Wert der Variablen außerhalb des aktuellen Programmkontextes ändern
> kann, beispielsweise durch externe Hardware. Bei der Generierung des
> Maschinen-Codes aus einem in C oder C++ geschriebenen Programm
> verhindert die Kennzeichnung einer Variablen als volatile eine
> übermäßige Optimierung, so dass das Programm immer auf den tatsächlich
> in der Hardware vorhandenen Wert zugreift
> [...]
>
> Quelle: Der vom Threadsteller gelinkte Artikel wikipedia.
>
>
> Ich verstehe noch nicht ganz was man an diesem Artikel missverstehen
> kann.
> Mal ganz salop gesagt: Nehmen wir du veränderst in der Software den Wert
> dieser Variable nie dann könnte der Compiler "denken": 'Ha um
> Speicherplatz zu sparen kann ich die ja umwandeln oder ihr nur weniger
> Speicherplatz zugestehen. Die wird ja sowieso nicht verändert'
> Sagst du den Compiler, dass sie volatile ist so weiß der Compiler - oha
> auch externe Hardware (PORT, ADC, etc.) können die Variable "ohne mein
> Wissen" verändern. Darum nicht optimieren.

Jedoch geht daraus nicht hervor, weshalb ich eine global deklarierten 
Variablen innerhalb meines Programms ( main() ) nicht verwenden kann.


Danke.

Bin jetzt wieder ein Stück weiter.

von Karl H. (kbuchegg)


Lesenswert?

Karl heinz Buchegger schrieb:

> lässt sich nur verstehen, wenn man die konkrete Schaltung kennt.

Und selbst das nehm ich zurück. Ich hab nicht gut genug gelesen.

von Karl H. (kbuchegg)


Lesenswert?

Fabian Hoemcke schrieb:

>> Was erwartest du
>>
>> 3686400 / 1024 = 3600
>>
>> 3600 / 236 = 15.zerquetschte
>>
>> Interrupts pro Sekunde. Du hast keinen ganzzahligen Teiler, so dass du
>> deine Taktfrequenz auf eine ganzzahlige Anzahl Interrupt Aufrufe pro
>> Sekunde herunterteilen würdest.
>
> Stimmt! Danke! TCNT0 muss den Wert 31 erhalten!
> Ich probiere es gleich mal aus, ob das der Teufel im Detail war!

Nimm dir einen Taschenrechner und rechne es durch. Auch mit 235 geht es 
sich nicht aus.


> Jedoch geht daraus nicht hervor, weshalb ich eine global deklarierten
> Variablen innerhalb meines Programms ( main() ) nicht verwenden kann.

Weil Wikipedia auf die speziellen Belange von µC (wie die meiste 
C-Literatur) kaum Rücksicht nimmt.

In deiner Hauptschleife steht zb

   hour = 0

   while( hour == 0 )
     mach irgendwas in dem hour nicht vorkommt

Für den Compiler, der sich nur diese Schleife ansiehst, sieht es so aus, 
als ob hour keine Chance hätte, innerhalb dieser Schleife seinen Wert zu 
verändern. Dass hour in der ISR verändert wird, erkennt der Compiler 
nicht, da es keinen direkten Aufruf der Funktion gibt. ISR werden von 
der Hardware aufgerufen, ohne dass es dazu im Programm einen expliziten 
Aufruf gäbe.
Der Compiler geht daher von der Annahme aus, dass innerhalb dieser 
Schleife hour immer den Wert 0 haben wird und optimiert in dem Fall zb 
die Überprüfung ob hour gleich 0 ist einfach weg. Denn: Wenn hour 
sowieso nur 0 sein kann, dann muss man das auch nicht ständig abtesten.

Durch das volatile sagst du dem Compiler:
Diese Variable kann sich auch auf Wegen ändern, die du nicht erkennen 
kannst, wenn du Codeanalyse betreibst. Du musst jeden Zugriff auf diese 
Variable, jede Überprüfung, jedes Auslesen auch tatsächlich so machen, 
wie ich das hingeschrieben habe. Deine Datenflussanalyse wird nämlich 
sonst zu falschen Ergebnissen kommen.

von spess53 (Gast)


Lesenswert?

Hi

>ASo wie ich es im Handbuch las, liegt dort ein Takt von 3.686.400Hz an.

Darauf würde ich mich nicht unbedingt verlassen. Die Frequenz wird durch 
den Controller auf dem STK500 aus einer Quarzfrequenz von 7,37MHz 
mittels Timer erzeugt. Und da können nur 3,685MHz herauskommen.

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

Fabian Hoemcke schrieb:

> Jedoch geht daraus nicht hervor, weshalb ich eine global deklarierten
> Variablen innerhalb meines Programms ( main() ) nicht verwenden kann.

Das hat auch nichts damit zu tun.
Und die Sache mit dem Level 1 Cache ... vergiss sie am besten auch 
gleich wieder.

Bei volatile geht es einzig und alleine darum, dem Compiler mitzuteilen: 
Deine Datenflussanalyse wird bei dieser Variablen zu falschen Schlüssen 
kommen, also spar dir jegliche Optimierung auf dieser Variablen.

Alles andere sind Folgeerscheinungen davon und bei manchen Compilern so, 
bei anderen wieder nicht.

volatile hat damit zu tun, eine Variable von der Optimierung des 
Compilers auszunehmen, da der Compiler die komplettenm Zusammenhänge 
nicht übersehen kann.

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Karl heinz Buchegger schrieb:
> In deiner Hauptschleife steht zb
>
>    hour = 0
>
>    while( hour == 0 )
>      mach irgendwas in dem hour nicht vorkommt
>
> Für den Compiler, der sich nur diese Schleife ansiehst, sieht es so aus,
> als ob hour keine Chance hätte, innerhalb dieser Schleife seinen Wert zu
> verändern. Dass hour in der ISR verändert wird, erkennt der Compiler
> nicht, da es keinen direkten Aufruf der Funktion gibt. ISR werden von
> der Hardware aufgerufen, ohne dass es dazu im Programm einen expliziten
> Aufruf gäbe.

In dieser while-Schleife wird aber Hour verändert.

Aber danke bisher. Jetzt erschließt sich mir so einiges.

spess53 schrieb:
> Hi
>
>>ASo wie ich es im Handbuch las, liegt dort ein Takt von 3.686.400Hz an.
>
> Darauf würde ich mich nicht unbedingt verlassen. Die Frequenz wird durch
> den Controller auf dem STK500 aus einer Quarzfrequenz von 7,37MHz
> mittels Timer erzeugt. Und da können nur 3,685MHz herauskommen.
>
> MfG Spess

Entschuldige meine Haarspalterei,
aber liegen da nun 3,685MHz oder 3,6864MHZ an???

Gruß und Danke

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Karl heinz Buchegger schrieb:
>>
>> Stimmt! Danke! TCNT0 muss den Wert 31 erhalten!
>> Ich probiere es gleich mal aus, ob das der Teufel im Detail war!
>
> Nimm dir einen Taschenrechner und rechne es durch. Auch mit 235 geht es
> sich nicht aus.

ähmmm
3.686.400  1024  (256 - 31 = 225) = 16

Wo hab ich einen Denkfehler?

von Karl H. (kbuchegg)


Lesenswert?

Fabian Hoemcke schrieb:
> Karl heinz Buchegger schrieb:
>> In deiner Hauptschleife steht zb
>>
>>    hour = 0
>>
>>    while( hour == 0 )
>>      mach irgendwas in dem hour nicht vorkommt
>>
>> Für den Compiler, der sich nur diese Schleife ansiehst, sieht es so aus,
>> als ob hour keine Chance hätte, innerhalb dieser Schleife seinen Wert zu
>> verändern. Dass hour in der ISR verändert wird, erkennt der Compiler
>> nicht, da es keinen direkten Aufruf der Funktion gibt. ISR werden von
>> der Hardware aufgerufen, ohne dass es dazu im Programm einen expliziten
>> Aufruf gäbe.
>
> In dieser while-Schleife wird aber Hour verändert.

Nicht in dem Beispiel das ich konstruiert habe :-)

> aber liegen da nun 3,685MHz oder 3,6864MHZ an???

Ist ziemlich egal.
Denn auch ein 16Mhz Quarz hat nicht exakt 16000000 Hz :-)

Du kannst es drehen wie du willst, du musst die Uhr sowieso manuell 
abgleichen. Und daher ist auch der Ansatz mit dem Timer Overflow im 
Grunde für eine Uhr nicht zu gebrauchen. Du hast zuwenig Möglichkeiten 
aus der tatsächlich vorliegenden Frequenz auf 1 Hz genau deinen 
Uhrentakt abzuleiten.

CTC-Modus ist das Stichwort
http://www.mikrocontroller.net/articles/AVR-Tutorial:_Uhr

von Klaus (Gast)


Lesenswert?

Hallo,

woher kommen eigentlich diese 236 bzw 235 her ?

also 256-30=226 bzw 256-31=225.

und daher ist doch 3600/225=16 !

Gruß,
Klaus

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Karl heinz Buchegger schrieb:
> Fabian Hoemcke schrieb:
>
>> Jedoch geht daraus nicht hervor, weshalb ich eine global deklarierten
>> Variablen innerhalb meines Programms ( main() ) nicht verwenden kann.
>
> Das hat auch nichts damit zu tun.
> Und die Sache mit dem Level 1 Cache ... vergiss sie am besten auch
> gleich wieder.
>
> Bei volatile geht es einzig und alleine darum, dem Compiler mitzuteilen:
> Deine Datenflussanalyse wird bei dieser Variablen zu falschen Schlüssen
> kommen, also spar dir jegliche Optimierung auf dieser Variablen.
>
> Alles andere sind Folgeerscheinungen davon und bei manchen Compilern so,
> bei anderen wieder nicht.
>
> volatile hat damit zu tun, eine Variable von der Optimierung des
> Compilers auszunehmen, da der Compiler die komplettenm Zusammenhänge
> nicht übersehen kann.

Ahhh, danke!
Und bei mir macht der Compiler hat Optimierungen, denen ich zwar nicht 
folgen kann, aber die mir die Suppe gehörig versalzen.

Alles klar!

Man sollte sich halt nicht auf den Buschfunk verlassen.

von Karl H. (kbuchegg)


Lesenswert?

Fabian Hoemcke schrieb:

> ähmmm
> 3.686.400  1024  (256 - 31 = 225) = 16
>
> Wo hab ich einen Denkfehler?

:-)
Nicht du.
Ich.
Kopfrechnen.  256 - 31 ergibt 225. Zwanzig, nicht Dreissig. Ich hab mit 
235 gerechnet :-)

Spielt aber nicht wirklich die grosse Rolle. Deine Uhr wird zwar genauer 
gehen, aber nicht genau genug :-) Denn der Ausgangs-Takt ist nicht exakt 
3686400 sondern ein paar Zig-Herz daneben :-)

von Karl H. (kbuchegg)


Lesenswert?

Klaus schrieb:
> Hallo,
>
> woher kommen eigentlich diese 236 bzw 235 her ?

Ich muss nochmal zurück in die Grundschule Kopfrechnen im Zahlenraum 
über Hundert üben.

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Danke für den Tipp mit dem Tutorial!
Da füllt sich der Abend wieder!

von Karl H. (kbuchegg)


Lesenswert?

Fabian Hoemcke schrieb:
> Danke für den Tipp mit dem Tutorial!
> Da füllt sich der Abend wieder!

ISt zwar auch aus dem Tutorial heraus verlinkt. Aber trotzdem
http://www.mikrocontroller.net/articles/AVR_-_Die_genaue_Sekunde_/_RTC

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Vielen Dank für eure kompetenten, schnellen und sachlichen Antworten.
Leider blieben aber immer noch drei Fragen offen.

1.) Woher bekommt die util/delay library die Zeit?
2.) Wieso funktionieren Register als Switch-Bedingungen nicht?
3.) Mehr als abschließende Antwort.
    Dieser enorme Shift ist also normal?
    Unabhängig ob Quarz oder Software als Taktquelle?


Gruß und Danke nochmal
BrEin

von Karl H. (kbuchegg)


Lesenswert?

Fabian Hoemcke schrieb:

> 1.) Woher bekommt die util/delay library die Zeit?

util/delay braucht keine Zeit.
util/delay will nur wissen wie schnell der µC getaktet wird. Es rechnet 
sich dann aus, wie oft der µC Däumchen drehen muss, damit das in Summe 
eine gewisse Zeit dauert

> 2.) Wieso funktionieren Register als Switch-Bedingungen nicht?

Ich bin mir ziemlich sicher, dass sie funktionieren

> 3.) Mehr als abschließende Antwort.
>     Dieser enorme Shift ist also normal?
>     Unabhängig ob Quarz oder Software als Taktquelle?

Er ist genauso normal, wie eine Pendeluhr falsch geht, deren Pendel 
nicht auf die richtige Länge gebracht wurde. Ist das erst einmal 
erledigt, dann geht auch eine Pendeluhr genau.
Genauso bei dir: Sind alle Programmfehler draussen, dann bleibt noch die 
Abweichung, die dadurch entsteht das ein Quarz mit XYZMhz 
Nominalfrequenz eben nicht mit XYZMhz schwingt, sondern im Rahmen seiner 
Genauigkeit ein wenig schneller oder langsamer. Wobei ein Quarz seine 
Freuqenz dann auch noch mit der Temperatur ändert. Aber das tut das 
Pendel einer Pendeluhr auch (seine Länge mit der Temperatur ändern), 
nimmt man einen mittleren Wert dann gleicht sich das über den Tag 
gesehen in etwa aus.

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

@ Karl heinz Buchegger

Danke für Frage eins, ich hatte deshalb gefragt, da ich nicht einen 
Timer verwenden wollte, der schon implizit durch diese Library genutzt 
wird.

Danke für Frage drei, ich werde dann man beobachten, wie groß die 
Differenz morgen Früh ist.

Nun zur Frage zwei. Ich hatte ganz oben beschrieben dass das (zu mindest 
so wie ich es nutzte) nicht funktioniert. Wenn du jetzt aber sagst, dass 
es funktioniert, hättest du eine Idee wo mein Fehler sein könnte?

von spess53 (Gast)


Lesenswert?

Hi

>Aber das tut das Pendel einer Pendeluhr auch (seine Länge mit der >Temperatur 
ändern), nimmt man einen mittleren Wert dann gleicht sich das
>über den Tag gesehen in etwa aus.

Im dem Fall hinkt der Quarz der Pendeluhr hinterher. Mein Regulator aus 
den 30-er Jahren hat ein temperaturkompensiertes Pendel.

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

Fabian Hoemcke schrieb:

> Nun zur Frage zwei. Ich hatte ganz oben beschrieben dass das (zu mindest
> so wie ich es nutzte) nicht funktioniert. Wenn du jetzt aber sagst, dass
> es funktioniert, hättest du eine Idee wo mein Fehler sein könnte?

Kaprizier dich nicht darauf.
Das was du als Fehler angesehen hast, kann auch ein Nebeneffekt von 
einem ganz anderen Fehler gewesen sein.

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Kaprizier...
Das habe ich ja noch nie gehört! :D
Musste ich erstmal nachschlagen.

Aber gut, ich werde mich nicht darauf versteifen.
Wenn es mit einer Variable funktioniert, auch gut!

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

So, ich habe die vorgeschlagenen Verbesserungen vorgenommen und die Uhr 
wieder eine Nacht durchlaufen lassen.

Die Differenz diesmal: 40Min.
5 Minuten Verbesserung immerhin.
Aber mir ist das immer noch viel zu ungenau.

In dem genannten Tutorial 
http://www.mikrocontroller.net/articles/AVR_-_Die_genaue_Sekunde_/_RTC 
wurde die Ungenauigkeit durch die Fehlende Teilbarkeit der 
Ursprungsfrequenz erklärt.

In meinem Fall aber, geht die Rechnung genau auf, wodurch ein Wechsel 
vom Overflow-Interrupt zum CopareMatch-Interrupt mir in diesem Fall 
keinen Vorteil bringt.

Die Ungenauigkeiten, durch die Taktquelle (Quarz oder Software) müssten 
über diesem Zeitraum im Bereich weniger Sekunden liegen. 40 Minuten ist 
einfach zu viel. Selbst wenn ich den Kontroller im Tiefkühlfach gehabt 
hätte.

Also, entweder habe ich wirklich einen enormen Fehler gemacht oder die 
Taktquelle ist nutzlos. Ich meine, 4 Minuten in einer Stunde, dass ist 
nicht zu gebrauchen.

Hier nochmal der Code:
1
/*
2
 ============================================================================
3
 Name        : LCD_TestProgramm.c
4
 Author      : Fabian Hoemcke
5
 Version     :
6
 Copyright   : actually no copyright
7
 Description : currently a playground
8
 ============================================================================
9
 */
10
11
#include <avr/io.h>
12
#include <avr/interrupt.h>
13
#include <stdio.h>
14
#ifndef F_CPU
15
#define F_CPU 3686400
16
#endif
17
#include <util/delay.h>
18
#include "lcd-routines.h"
19
20
volatile unsigned char c = 0,
21
             Hours = 0,
22
             Minutes = 0,
23
             Secundes = 0;
24
char String[3] = "";
25
26
SIGNAL(TIMER0_OVF_vect)
27
{
28
  TCNT0 = 31;
29
  
30
  if(c++ == 16)
31
  {  c = 0;
32
    if(Secundes++ == 59)
33
    {  Secundes = 0;
34
      if(Minutes++ == 59)
35
      {  Minutes = 0;
36
        if(Hours++ == 23) Hours = 0;
37
      }
38
    }
39
  }
40
  
41
  return;
42
}
43
44
void entprellung( volatile uint8_t *port, uint8_t maske ) {
45
  uint8_t   port_puffer;
46
  uint8_t   entprellungs_puffer;
47
48
  for( entprellungs_puffer=0 ; entprellungs_puffer!=0xff ; ) {
49
    entprellungs_puffer<<=1;
50
    port_puffer = *port;
51
    _delay_us(150);
52
    if( (*port & maske) == (port_puffer & maske) )
53
      entprellungs_puffer |= 0x01;
54
  }
55
}
56
57
int main(void)
58
{
59
  char Buttons;
60
  DDRA = 0x00;  // Buttons at port A
61
  PORTA = 0xFF;  // enable pull ups | buttons default high
62
  TCCR0 = 0x05;  // 0000|0101 | pre-scaler Clck/1024
63
  TCNT0 = 31;
64
  TIMSK |= 0x02;  // 0000|0010 | enable Timer Counter Overflow Interrupt
65
66
  lcd_init();    // initialize LCD module
67
  sei();      // enable interrupts
68
69
  while(1)
70
  {
71
72
    lcd_setcursor(0, 1);
73
    sprintf(String, "%02i", Hours);
74
    lcd_string(String);
75
    lcd_string(":");
76
    sprintf(String, "%02i", Minutes);
77
    lcd_string(String);
78
    lcd_string(":");
79
    sprintf(String, "%02i", Secundes);
80
    lcd_string(String);
81
82
    if(PINA!=255)
83
    {
84
      entprellung( &PINA, 0xFF);
85
      Buttons = ~PINA;
86
      switch(Buttons)
87
      {
88
      case 1 : Minutes++; break;
89
      case 2 : Hours++; break;
90
      default: break;
91
      }
92
      while(!(PINA==255));
93
      entprellung( &PINA, 0xFF);
94
    }
95
  }
96
97
  return 0;
98
}

Eine Sekunde innerhalb von 2 Stunden wären mit zusammengekniffenen Augen 
noch zu verkraften. Denn dann könnte man mit dem DCF77 Signal die Uhr 
halbwegs genau halten. Aber bei 4 Minuten, geht die Uhr trotz DCF77 
immer wieder erheblich nach. Also bis zu 4 Minuten, und auch nur, wenn 
das Signal gut genug ist um es jede Stunde einmal exakt auszulesen.

Ich bin zwar noch weit weg davon, daran zu verzweifeln.
Aber es stört mich, wenn ich etwas nicht verstehe.
Und in diesem Fall verstehe ich nun wirklich nicht, woher dieser Fehler 
kommt.

Gruß und Danke
BrEin

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Ich bin jetzt kurz davor schon bei 55 Sekunden eine Minute hochzuzählen! 
:D

von spess53 (Gast)


Lesenswert?

Hi

>Ich bin jetzt kurz davor schon bei 55 Sekunden eine Minute hochzuzählen!

Ich habe heute mal an einem STK500 die Frequenz gemessen. Lag noch unter 
dem errechnenten Wert. Der Zettel mit dem genauen Wert liegt allerdings 
gerade 10km entfernt. Aber das was du machst ist Unsinn. Besorge dir 
einfach einen passenden Quarz für das STK und höre auf deine Zeit zu 
vergeuden.

MfG Spess

von spess53 (Gast)


Lesenswert?

Hi

Also bei eingestellten 3,686MHz messe ich 3,6535MHz.

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

Fabian Hoemcke schrieb:
> Ich bin jetzt kurz davor schon bei 55 Sekunden eine Minute hochzuzählen!
> :D

Du sollst endlich den Artikel ernst nehmen und zurückrechnen welche 
Taktfrequnz du wirklich hast!

Geh dein Programm durch und wenn du keine Fehler mehr siehst, dann ist 
es nur logisch, dass die Taktfrequenz mit der du zu rechnen angefangen 
hast, nicht stimmt.

Und jetzt ist es dann plötzlich auch nicht mehr egal ob du Overflow oder 
CTC Modus implementiert hast :-)

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Karl heinz Buchegger schrieb:
> Fabian Hoemcke schrieb:
>> Ich bin jetzt kurz davor schon bei 55 Sekunden eine Minute hochzuzählen!
>> :D
>
> Du sollst endlich den Artikel ernst nehmen und zurückrechnen welche
> Taktfrequnz du wirklich hast!
>
> Geh dein Programm durch und wenn du keine Fehler mehr siehst, dann ist
> es nur logisch, dass die Taktfrequenz mit der du zu rechnen angefangen
> hast, nicht stimmt.
>
> Und jetzt ist es dann plötzlich auch nicht mehr egal ob du Overflow oder
> CTC Modus implementiert hast :-)

Hallo Karl,

ich nehme diesen Artikel durchaus ernst.
Denn erstens hätte ich mir sonst nicht so viel Mühe gegeben ihn zu 
erstellen und zweitens wäre ich nicht so hartnäckig.

Aber dennoch habe ich den Quatsch mit den 55 Sekunden ausprobiert und 
siehe da, über eine Stunde absolut in Ordnung.
(Länger habe ich es nicht getestet, da diese Lösung keine ernsthafte 
Alternative darstellt.)

Ich hatte schon vor ein paar Tagen mal die eigentliche Taktfrequenz 
errechnet, aber gedacht ohne eine Methode sie wirklich zu Messen/Prüfen 
wäre es sinnlos. Denn der Fehler kann ja eigentlich überall sein.
(Und bei Messgeräten die das leisten können habe ich eine Hausnummer von 
300€ gehört.)
1
3.686.400Hz |  x
2
------------------
3
     60s    | 56s
4
x = 3.686.400Hz * 56s / 60s = 3.440.640Hz

Sollte das stimmen (also auch der Rechenansatz und die Lösung), so habe 
ich ein gravierendes Problem.
Dafür werde ich mal einen eigenen Thread auf machen, damit hier nichts 
durcheinander kommt. Denn mit einen Quarz (3,6864MHz) funktioniert es 
auch nicht.

Karl heinz Buchegger schrieb:
> Und jetzt ist es dann plötzlich auch nicht mehr egal ob du Overflow oder
> CTC Modus implementiert hast :-)

Das verstehe ich nun wirklich nicht.
Ich meine, du hast absolut recht, aber der Fehler (falscher Takt) müsste 
sich doch auf die gleiche Art zeigen.
Ich habe es mal umgeschrieben und nun habe ich 10 - 11 Sekunden Schwund 
pro Minute:
1
/*
2
 ============================================================================
3
 Name        : LCD_TestProgramm.c
4
 Author      : Fabian Hoemcke
5
 Version     :
6
 Copyright   : actually no copyright
7
 Description : currently a playground
8
 ============================================================================
9
 */
10
11
#include <avr/io.h>
12
#include <avr/interrupt.h>
13
#include <stdio.h>
14
#ifndef F_CPU
15
#define F_CPU 3686400
16
#endif
17
#include <util/delay.h>
18
#include "lcd-routines.h"
19
20
volatile unsigned char c = 0,
21
             Hours = 0,
22
             Minutes = 0,
23
             Secundes = 0;
24
char String[3] = "";
25
26
SIGNAL(TIMER0_COMP_vect)
27
{
28
29
  if(c++ == 16)
30
  {
31
    c = 0;
32
    if(Secundes++ == 59)
33
    {
34
      Secundes = 0;
35
      if(Minutes++ == 59)
36
      {
37
        Minutes = 0;
38
        if(Hours++ == 23) Hours = 0;
39
      }
40
    }
41
  }
42
43
  return;
44
}
45
46
void entprellung( volatile uint8_t *port, uint8_t maske ) {
47
  uint8_t   port_puffer;
48
  uint8_t   entprellungs_puffer;
49
50
  for( entprellungs_puffer=0 ; entprellungs_puffer!=0xff ; ) {
51
    entprellungs_puffer<<=1;
52
    port_puffer = *port;
53
    _delay_us(150);
54
    if( (*port & maske) == (port_puffer & maske) )
55
      entprellungs_puffer |= 0x01;
56
  }
57
}
58
59
int main(void)
60
{
61
  char Buttons;
62
  DDRA = 0x00;  // Buttons at port A
63
  PORTA = 0xFF;  // enable pull ups | buttons default high
64
  TCCR0 = 0x05;  // 0000|0101 | pre-scaler Clck/1024
65
  OCR0 = 225;
66
  TIMSK |= 0x01;  // 0000|0001 | enable Output Compare Match Interrupt
67
68
  lcd_init();    // initialize LCD module
69
  sei();      // enable interrupts
70
71
  while(1)
72
  {
73
74
    lcd_setcursor(0, 1);
75
    sprintf(String, "%02i", Hours);
76
    lcd_string(String);
77
    lcd_string(":");
78
    sprintf(String, "%02i", Minutes);
79
    lcd_string(String);
80
    lcd_string(":");
81
    sprintf(String, "%02i", Secundes);
82
    lcd_string(String);
83
84
    if(PINA!=255)
85
    {
86
      entprellung( &PINA, 0xFF);
87
      Buttons = ~PINA;
88
      switch(Buttons)
89
      {
90
      case 1 : Minutes++; break;
91
      case 2 : Hours++; break;
92
      default: break;
93
      }
94
      while(!(PINA==255));
95
      entprellung( &PINA, 0xFF);
96
    }
97
  }
98
99
  return 0;
100
}

Paradox - Paradox !

Der neue Thread folgt in etwa 15 Minuten.

Vielen Dank für eure Mühe!

Gruß
BrEin

von spess53 (Gast)


Lesenswert?

Hi

[c]
SIGNAL(TIMER0_COMP_vect)
{

  if(c++ == 16)
  {
    c = 0;
    if(Secundes++ == 59)
    {
      Secundes = 0;
      if(Minutes++ == 59)
      {
        Minutes = 0;
        if(Hours++ == 23) Hours = 0;
      }
    }
  }

  return;
}
[/]

Mit der Rechnung verlierst du eine Sekunde pro Minute, eine Minute pro 
Stunde und eine Stunde pro Tag.

MfG Spess

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Und bitte, wie kommst du darauf?

von spess53 (Gast)


Lesenswert?

Hi

>Und bitte, wie kommst du dadrauf?

Wenn deine Sekunden 58 sind, und du die Sekunden incrementierst kommst 
du auf 59. Und da stellst du die Sekunden auf Null und incrementierst 
die Minuten. Damit fehlt dir die Sekunde 59. Das darf erst passieren 
wenn

          Secundes++ == 60

ist. Rest analog.
Ist dir nicht aufgefallen, das du von 58 auf 0 springst?

MfG Spess

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Kennst du den Unterschied zwischen Prost- und Preincrement?

Pre
Bei
1
if( ++Secundes == 59 ) Secundes = 0;
hättest du recht. Da er erst inkrementiert und dann gleich auf Null 
zurück setzt. Somit würde die 59 immer übersprungen werden.

Post
Aber bei
1
if( Secundes++ == 59 ) Secundes = 0;
vergleicht er erst und zählt dann hoch. In dem kritischen Fall sieht er 
erst dass 58s nicht gleich 59s sind, zählt dann hoch und überspringt das 
Zurücksetzten. Bei 59s zählt er dann zwar auch erst hoch auf 60s und 
setzt es dann aber auch gleich auf Null.

Also kein Fehler!


Danke aber für deine Aufmerksamkeit.
Solche blöden Fehler sind es dann nämlich meist, die einem die größten 
Probleme bereiten.

Gruß
Brein

von spess53 (Gast)


Lesenswert?

Hi

>Kennst du den Unterschied zwischen Prost- und Preincrement?

Schon. Da ich aber eigentlich kein C kann, war mir das in dem Fall nicht 
so gegenwärtig. Entschuldige die Aufregung.

MfG Spess

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Dafür brauchst du dich nun wirklich nicht entschuldigen.

Aber zurück zum Thema.
Ich habe nun den 
Beitrag "Taktproblem mit dem STK500" fertig.

Hat natürlich länger gedauert. Aber ich hoffe es führt zu Ziel.

Gruß
BrEin

von Karl H. (kbuchegg)


Lesenswert?

Moment, da ist ein Fehler
1
SIGNAL(TIMER0_COMP_vect)
2
{
3
4
  if(c++ == 16)
5
  {
6
    c = 0;
7
    if(Secundes++ == 59)
8
    {
9
      Secundes = 0;
10
      if(Minutes++ == 59)
11
      {
12
        Minutes = 0;
13
        if(Hours++ == 23) Hours = 0;
14
      }
15
    }
16
  }
17
18
  return;
19
}
20
21
int main(void)
22
{
23
  char Buttons;
24
  DDRA = 0x00;  // Buttons at port A
25
  PORTA = 0xFF;  // enable pull ups | buttons default high
26
  TCCR0 = 0x05;  // 0000|0101 | pre-scaler Clck/1024
27
  OCR0 = 225;

du gehst zwar auf den Compare Interrupt, aber das bedeutet nichtr 
automatisch, dass der Timer nach Erreichen des Compare Wertes von sich 
aus auf 0 zurückgesetzt wird.
Dein Timer läuft nach wie vor von 0 bis 256. Nur dass du die 
Sekundenweiterschaltung nicht bei einem Timerwert von 0 machst, sondern 
bei einem Timerwert von 225. Das ändert aber nichts daran, dass der 
Timer nach wie vor 256 Zählvorgänge machen muss, bis er das nächste mal 
wieder den Compare Wert erreicht.

Du musst den CTC Modus auch aktivieren, wenn du ihn benutzen willst.

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Danke Karl!

Der Interrupt ist aktiviert.
1
TIMSK |= 0x01;  // 0000|0001 | enable Output Compare Match Interrupt

Aber das mit dem Reset des TCNT0 wollte ich noch fragen. Stimmt, hatte 
ich ganz vergessen.

Danke, wird gleich ausprobiert!

BrEin

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Fehler behoben! Sind wieder 4 Sekunden.
Macht also doch keinen Unterschied welchen Interruptus man verwendet, 
solange man es richtig implementiert! :D

von Karl H. (kbuchegg)


Lesenswert?

Fabian Hoemcke schrieb:
> Fehler behoben! Sind wieder 4 Sekunden.

In welchem Kontrollzeitraum?

> Aber das mit dem Reset des TCNT0 wollte ich noch fragen.
> Stimmt, hatte ich ganz vergessen.

Widersteh der Versuchung in der ISR den TCNT0 auf 0 zu setzen. Denn 
genau dazu hast du den CTC Modus.

 TCCR0 = ( 1 << WGM01 ) | ( 1 << CS02 ) | ( 1 << CS00 );

und schon setzt sich der Timer beim Compare Match selbst zurück auf 0.

Mit einem OCR Wert von 224 müsste es dann eigentlich ziemlich gut 
stimmen

3686400/1024/16 = 225

d.h. 225 Zählzyklen. d.h. der OCR Wert muss 224 sein, weil ja 0 auch ein 
Zählzyklus ist.


PS: Ich hätte das auch so geschrieben
1
  c++
2
  if(c == 16)
3
  {
4
   c = 0;
5
   Secundes++;
6
7
   if(Secundes == 60)
8
   {
9
     Secundes = 0;
10
     Minutes++;
11
     if(Minutes == 60)
12
     {
13
       Minutes = 0;
14
       Hours++;
15
       if(Hours == 24)
16
         Hours = 0;
17
      }
18
    }
19
  }

Über deine Version hab ich auch erst mal brüten müssen, bis ich wusste, 
dass sie richtig ist. So finde ich das einfach weniger verwirrend.

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Karl heinz Buchegger schrieb:
> Fabian Hoemcke schrieb:
>> Fehler behoben! Sind wieder 4 Sekunden.
>
> In welchem Kontrollzeitraum?

1 Minute! :D
War eher eine Stichprobe!

Kannst du mir hier erklären was gemacht wird?
1
TCCR0 = ( 1 << WGM01 ) | ( 1 << CS02 ) | ( 1 << CS00 );

von Karl H. (kbuchegg)


Lesenswert?

Fabian Hoemcke schrieb:
> Karl heinz Buchegger schrieb:
>> Fabian Hoemcke schrieb:
>>> Fehler behoben! Sind wieder 4 Sekunden.
>>
>> In welchem Kontrollzeitraum?
>
> 1 Minute! :D
> War eher eine Stichprobe!

Das ist schon eine ganze Menge.

>
> Kannst du mir hier erklären was gemacht wird?
>
1
TCCR0 = ( 1 << WGM01 ) | ( 1 << CS02 ) | ( 1 << CS00 );

Es werden die Bits WGM01, CS02 und CS00 im Register TCCR0 gesetzt.
Wenn du wissen willst was die machen: Steht im Datenblatt - mit genau 
den gleichen Bitnamen.


(Ach, ich vergass. Sag, welchen µC hast du nochmal am STK stecken. Ich 
hatte noch einen Mega16 im Hinterkopf, kann aber auf die Schnelle im 
Thread nicht mehr finden, welcher Prozessor es wirklich ist)

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

ATmega8515

von Karl H. (kbuchegg)


Lesenswert?

Fabian Hoemcke schrieb:
> ATmega8515

Passt.
Der kann das auch. Siehe Datenblatt Seite 91

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Sorry! Es ging um den TCCR0 und nicht um den TCNT0.

Das hatte mich schon gewundert.
CS02 und CS00 sind mir klar und habe ich auch gesetzt wie du siehst!
//Prescaler = 1/1024

Bit 6, 3 - WGM01:0: Waveform Generation Mode
______________________________________________
These bits control the counting sequence of the counter, the source for 
the maximum (TOP) counter value, and what type of waveform generation to 
be used. Modes of operation supported by the Timer/Counter unit are: 
Normal mode, Clear Timer on Compare Match CTC mode, and two types of 
Pulse Width Modulation (PWM) modes.

Wer lesen kann...


danke dir!
Wird ausprobiert!

von Karl H. (kbuchegg)


Lesenswert?

Fabian Hoemcke schrieb:
> Sorry! Es ging um den TCCR0 und nicht um den TCNT0.
>
> Das hatte mich schon gewundert.
> CS02 und CS00 sind mir klar und habe ich auch gesetzt wie du siehst!

Ja.
Der springende Punkt ist:
Gewöhn dir deine Bit-Schreibweise mit den hardcecodeten Bits wieder ab.

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Habe ich leider so gelernt bekommen.
Kann man natürlich auch wieder verlernen.

Aber eigentlich finde ich meine Schreibweise mit den Kommentaren besser.
Ist wohl aber Gewöhnungssache.

Ich habe den Autoreset eingestellt.
Aber selbes Probelem.
(Eine Minute getestet - 4 Sekunden verloren. Ich denke, bei einer 
gravierenden Änderung hätte man das schon vestgestellt.)

von Karl H. (kbuchegg)


Lesenswert?

Fabian Hoemcke schrieb:
> Habe ich leider so gelernt bekommen.
> Kann man natürlich auch wieder verlernen.
>
> Aber eigentlich finde ich meine Schreibweise mit den Kommentaren besser.
> Ist wohl aber Gewöhnungssache.

:-)
Das machst du so lange, bis du das erste mal ein Programm von Prozessor 
A nach Prozessor B portierst. Prozessor B hat dasselbe Steuerregister 
und in diesem Register sind sogar dieselben Bits drinnen, aber leider 
leider an einer anderen Stelle.

   TCCR0 = ( 1 << WGM01 ) | ( 1 << CS02 ) | ( 1 << CS00 );

solange das TCCR0 Register über das Bit WGM01 verfügt, muss´ich mich 
nicht darum kümmern, welches Bit im Register das genau ist. Der Compiler 
kümmert sich darum.
D.h. ich muss beim portieren gar nichts tun.

Schreibst du aber

   TCCR0 = 0x15;

dann bist du dafür verantwortlich, dass dieses 1 Bit an eine andere 
Stelle kommt, weil es beim anderen Prozessor woanders sitzt

  TCCR0 = 0x25;


Bist du einmal darüber gestolpert und hast 2 Stunden damit verbracht 
dieses Problem zu suchen, dann wirst du nie wieder die Bitschreibweise 
benutzen.

> (Eine Minute getestet - 4 Sekunden verloren. Ich denke, bei einer
> gravierenden Änderung hätte man das schon vestgestellt.)

das heißt jetzt dein Proz macht in 1 Minute nur 56 Sekunden?

Das hatten wir doch oben schon mal. Noch ehe der Timerreset bei 225 
gemacht wurde.

Irgendwas ist bei dir oberfaul.
Das hatte ich überhaupt noch nie. Egal mit elchem Quarz, mit der 
richtigen Teilerkette im Timer erreicht man auf Anhieb und mit 
Leichtigkeit eine Genauigkeit von ein paar Sekunden pro Tag. So weit 
kann der Quarz gar nicht daneben sein.

von Karl H. (kbuchegg)


Lesenswert?

Grr.

Bitte schreib das nicht so dicht sondern in der naheliegenden Form
1
SIGNAL(TIMER0_COMP_vect)
2
{
3
4
  if(c++ == 16)
5
  {
6
    c = 0;

Das zählt 17 ISR Aufrufe, bis dann endlich die Sekundenweiterschaltung 
in Gang kommt. Du hast die Teilerkette aber auf 16 ISR Aufrufe 
gerechnet. Pro Sekunde verlierst du also 1/16 Sekunde. In 1 Minute (=60 
Sekunden) sind das 3.75 Sekunden. Da hast du deine 4 Sekunden.

Ich sagte doch schon. Schreibs so
1
 c++
2
  if(c == 16)
3
  {
4
   c = 0;
5
   Secundes++;
6
7
   if(Secundes == 60)
8
   {
9
     Secundes = 0;
10
     Minutes++;
11
     if(Minutes == 60)
12
     {
13
       Minutes = 0;
14
       Hours++;
15
       if(Hours == 24)
16
         Hours = 0;
17
      }
18
    }
19
  }

da kann ein Blinder greifen, wann genau jeweils 1 weitergezählt wird. Es 
bringt nichts, wenn man in C zu clever coden will. Man legt sich 
meistens nur selber hinein.

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

STIMMT!
Das ist es!

Das ist genau die Art Fehler von denen ich weiter oben Sprach!

Fabian Hoemcke schrieb:
> Kennst du den Unterschied zwischen Prost- und Preincrement?
...
>
> Also kein Fehler!
>
>
> Danke aber für deine Aufmerksamkeit.
> Solche blöden Fehler sind es dann nämlich meist, die einem die größten
> Probleme bereiten.
>
> Gruß
> Brein

Danke dir!

Immerhin rechnete ich ja auch bis 59s, 59min oder 23h.
Warum habe ich dann hier so einen Mist gemacht.

Naja.

Aber,
was du da oben geschrieben hast, war schlicht weg ein Preincrement.

Ich denke aber, auch das sollte ich langsam lassen.
Immerhin gab es ja auch andere die das nicht ohne Probleme lesen konnten 
und mir ist der Fehler auch nicht sofort oder bei Zeiten aufgefallen.

Da muss ich halt der Lesbarkeit der Eleganz den Vorzug geben.

Danke dir und Asche auf mein Haupt.

Vielen Dank!

Die erste Minute hat es immerhin schon sauber geschafft.

Danke dir nochmal!

von Karl H. (kbuchegg)


Lesenswert?

Fabian Hoemcke schrieb:

> Immerhin rechnete ich ja auch bis 59s, 59min oder 23h.

Das ist der eigentliche Mist.
Du willst 60 Sekunden abzählen, also sollten auch die 60 im Vergleich 
auftauchen.

Das ist genau derselbe Grund warum man

   for( i = 0; i < N; i++ )

schreibt, wenn man eine Schleife haben will, die N mal wiederholt wird. 
Das N taucht in der Abbruchbedingung direkt auf.
Und nicht

   for( i = 0; i <= N-1; i++ )

und auch nicht

  for( i = 1; i == N; i++ )

und was es sonst noch so an diversen vermeintlich cleveren Formen gibt.

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Den Hinweis nehme ich dankend an!
(Das mit der Zählschleife, mache ich genauso.
Aber aus einem anderen Grund. Obwohl deiner deutlich besser ist.)

---

Ich habe einen Test über 15 Minuten laufen lassen.
Dabei gingen 12 Sekunden verloren.
Also mein Meisterwerk geht immer noch 9 Sekunden bei 10 Minuten nach.

Ist das bei einem SW-Takt schon normal?
Kann da ein Offset errechnet und eingestellt werden, wie bei den 
internen Clocks der µCs?

Oder sollte ich lieber so lange auf den Code schauen, bis ich auch 
diesen Fehler finde.

---

Ich möchte jetzt nicht päpstlicher sein als der Papst, ich möchte nur 
wissen, ob ich mit dieser Taktquelle das Limit schon erreicht habe oder 
ob es noch besser geht?

Gruß und tausend Dank
BrEin

von Karl H. (kbuchegg)


Lesenswert?

Fabian Hoemcke schrieb:

> Ich habe einen Test über 15 Minuten laufen lassen.
> Dabei gingen 12 Sekunden verloren.
> Also mein Meisterwerk geht immer noch 9 Sekunden bei 10 Minuten nach.

Das sind 1.5%

> Ist das bei einem SW-Takt schon normal?

Ich weiß jetzt nicht, wie das STK den Takt erzeugt.
Für einen Quarz ist das deutlich zu viel

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Karl heinz Buchegger schrieb:
> Das sind 1.5%

Oder auch nur noch 3.75% der ursprünglichen Differenz.
Darüber bin ich ja schon mal froh.

Dieser Takt wird vom Onbord-Kontroller in Software generiert.

Karl heinz Buchegger schrieb:
> Mit einem OCR Wert von 224 müsste es dann eigentlich ziemlich gut
> stimmen
>
> 3686400/1024/16 = 225
>
> d.h. 225 Zählzyklen. d.h. der OCR Wert muss 224 sein, weil ja 0 auch ein
> Zählzyklus ist.

Das verstehe ich jetzt nicht.
Wenn TCNT0 die 225 erreicht hat, wird er doch schon zurückgesetzt und 
die ISR ausgeführt oder etwa nicht.

Ich stelle die Frage etwas anderes.
1
Einen Zählzyklus lang ist der TCNT0 auf Null. Am ende wir er dann auf Eins Hochgezählt.
2
Einen Zählzyklus lang ist der TCNT0 auf Eins. Am ende wir er dann auf Zwei Hochgezählt.
3
Einen Zählzyklus lang ist der TCNT0 auf 223. Am ende wir er dann auf 224 Hochgezählt.
4
Einen Zählzyklus lang ist der TCNT0 auf 224. Am ende wir er dann auf 225 Hochgezählt. 
5
Der TCNT0 resettet und die ISR ausgelöst.

D.h. wenn meine Vorstellung von der Arbeitsweise des Kontrollers nicht 
falsch ist, müssten OCR0 = 225 richtig sein, oder ist sie falsch?

---

Danke, wir nähern uns an - haben es bald geschafft! :D

Gruß BrEin

von Karl H. (kbuchegg)


Lesenswert?

Fabian Hoemcke schrieb:

> Das verstehe ich jetzt nicht.
> Wenn TCNT0 die 225 erreicht hat, wird er doch schon zurückgesetzt und
> die ISR ausgeführt oder etwa nicht.

Nein. Erst im nächsten Schritt wird dann der Zähler zurückgesetzt. Der 
Zähler nimmt den Wert des Vergleichswertes noch an.


mit einem Vergleichswert von 8 hättest du dann

der Timer zählt      Anzahl Zählvorgänge

  0 ---+
       |                    1
  1 <--+ ---+
            |               2
  2      <--+
                            3
  3
                            4
  4
                            5
  5
                            6
  6
                            7
  7
                            8
  8
                            9
  0

Wenn der Zähler von 0 über 8 wieder bis 0 zählt, dann braucht er dazu 9 
Zählschritte.

Willst du also für einmal Rundum n Zählschritte haben, dann musst du den 
Vergleichswert auf n-1 setzen.

Du willst alle 225 Schritte einen Interrupt haben, also muss der 
Vergleichswert auf 224 eingestellt werden

von Fabian H. (Firma: Technische Universität Berlin) (brein)


Lesenswert?

Überzeugt!
Wird ausprobiert!

Entschuldigung, wenn ich dich immer dazu nötige ausführlich zu werden.
Erstens möchte ich verstehen, weshalb das, was andere sagen auch richtig 
sein soll und zweitens, copy & paste ist wirklich nicht meine Art.

Auch wenn ich mir nun bei dir ziemlich sicher bin, dass ich mir das 
sparen könnte.

Danke!

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.