Forum: Mikrocontroller und Digitale Elektronik dynamische Anpassung F_CPU


von grundschüler (Gast)


Lesenswert?

Problemstellung: Der AVR-Takt soll bestimmt und in den Programmcode 
eingepflegt werden. Gebraucht wird er für alles Mögliche, bei dem F_CPU 
wichtig ist: ds1820, Datenaustausch, etc. - eigentlich immer.

Gemessen wird der Takt über eine RTC, die nach 1h Laufzeit die 
Abweichung zwischen timer-Zeit und RTC-Zeit feststellt.

Wie bekomme ich die errechnete Taktrate in den Programmcode? bei ds1820 
könnte ich die Zeiten im Programm anpassen. Geht es irgendwie besser?

(Jörg W.: Dynamische Anpassung geht nur mit einem Timer, dessen Wert zur
Laufzeit aus der tatsächlichen (ggf. im EEPROM hinterlegten) Frequenz
ermittelt wird.

Aber: das hat mit dem Thema dieses Threads (serielle Kommunikation)
rein gar nichts zu tun.)

von Jim M. (turboj)


Lesenswert?

Wenn F_CPU keine per #define bestimmte feste Konstante ist, fliegt Dir 
auf dem AVR vieles auseinander.

Z.B. geht delay_ms() nur mit fester Konstante - aber das braucht man 
i.d.R. nicht supergenau.

Man muss also manuell an den Stellen an denen dies möglich ist, F_CPU 
durch seine eigene Variable ersetzen. Das setzt voraus dass man den 
umgebenen Code auch versteht und die Folgen abschätzen kann.

von grundschüler (Gast)


Lesenswert?

ich kann delay.h kopieren und die Konstante durch eine Variable 
ersetzen, die mit F_CPU vorbelegt ist.

Besser wäre es über den Makefile. Das ist ja auch Programmcode, in dem 
Variablen abgearbeitet werden:
1
ifeq ($(MCU), atmega644)
2
  #FUSE_BITS = -u -U lfuse:w:0xff:m -U hfuse:w:0xdf:m
3
  HEX_FILE_NAME = MEGA644
4
endif
5
ifeq ($(MCU), atmega32)
6
  #FUSE_BITS = -u -U lfuse:w:0xff:m -U hfuse:w:0xdf:m
7
  HEX_FILE_NAME = MEGA32
8
endif

HEX_FILE_NAME ist eine Variable, die vor dem Programmstart abgearbeitet 
wird. Wie bekomme ich Werte aus einer Datei auf dem PC in den Makefile?

Jeder avr bekommt dann eine Nummer der eine individuelle Taktrate auf 
dem PC zugeordnet ist.

von Falk B. (falk)


Lesenswert?

@ grundschüler (Gast)

>ich kann delay.h kopieren und die Konstante durch eine Variable
>ersetzen, die mit F_CPU vorbelegt ist.

Und was soll das bei einer DYNAMISCHEN F_CPU nützen, wenn diese erst zur 
Laufzeit gemessen wird?

>HEX_FILE_NAME ist eine Variable, die vor dem Programmstart abgearbeitet
>wird. Wie bekomme ich Werte aus einer Datei auf dem PC in den Makefile?

Wozu?

>Jeder avr bekommt dann eine Nummer der eine individuelle Taktrate auf
>dem PC zugeordnet ist.

Wozu?

Wenn schon, dann kalibriert man ALLE AVRs vorher auf Nennfrequenz und 
fertig und muss dann mit den Toleranzen incl. Temperaturdrift leben. 
Dann ist aber F_CPU wieder konstant und man muss keinerlei Problem 
lösen.

Die meisten modernen AVRs sind schon vom Hersteller bei einer bestimmten 
Spannung (meist 5V) auf +/-1% kalibriert, die Daten für die Einstellung 
von OSCCAL liegen in speziellen EEPROM/FUSE Bytes. Je nach Typ werden 
diese Einstellungen beim RESET schon geladen oder müssen per Firmware 
geladen werden. Genaueres sagt das Datenblatt.

: Bearbeitet durch User
von Theor (Gast)


Lesenswert?

Mich würde interessieren, welches Problem damit eigentlich gelöst werden 
soll.


Mal ganz grundsätzlich kann man beim Aufruf von make auch Variablen 
setzen. Etwa mit "make target F_CPU=121324" was dann beim Aufruf des 
Compilers auch mit "-D " übergeben werden muss.

Man kann auch in eine beliebige Header-Datei mit Perl oder Python 
irgendeine Definition reinschreiben. Damit wäre ein Wert von irgendeiner 
Instanz auf dem PC in den Code für den AVR transportiert.

Ganz ähnlich könnte ein Perl- oder Python-Script auch ein Makefile 
erzeugen, wenn die Zieldatei einen individuellen Namen haben soll.
Oder etwa in der Regel für das Hex-File (oder was auch immer) nach, der 
Erzeugung eben des Hex-Files, dieses noch einmal in eine Datei mit 
individuellem Namen kopieren, der make in der Kommandozeile übergeben 
worden ist.


Bemerkung:
Make-Dateien werden nicht ausgeführt sondern interpretiert. Auch werden 
Variablen nicht ausgeführt. Den Unterschied sollte man gedanklich 
berücksichtigen, denke ich.

von grundschüler (Gast)


Lesenswert?

Theor schrieb:
> Mich würde interessieren, welches Problem damit eigentlich gelöst werden
> soll.

Ich hab ca. 10 nanos rumfliegen, auf die unterschiedliche Programme 
aufgespielt werden. Jeder soll mit dem gleichen Programmcode laufen. Die 
Lösung: eine Datei fcpu.h, die von main.h nach Setzen von #define 
board_nr x aufgerufen wird.
1
#define F_CPU 8000000
2
3
#if boar_nr == 1
4
#define F_CPU 7900000
5
#endif 
6
....


die Kalibrierung:

1
if(min==0){//hou
2
//+++++++++++++++ hou ++++++++++++++++++++
3
if(!smh_korrektur_sperre){  
4
5
u16 smh_abweichung;
6
7
8
if (ds1307gefunden==0){
9
  kk_ds1307_getAll_to_global_struct_zt();
10
  
11
  
12
  if(min>30){//59... mcu zu schnell
13
//  smh_abweichung=1;
14
      smh_abweichung=(60-min)*60-sec;
15
      if(smh_abweichung>19){
16
        var_ocr0a++;
17
        eeprom_update_byte(eep_ocr0a_adr,var_ocr0a);
18
        OCR0A = var_ocr0a;
19
      }
20
      
21
      
22
      
23
//      lg(4,9);
24
//      lw("+");li(smh_abweichung);lw("+");li(var_ocr0a);lw("+");
25
  }
26
  else {//mcu zu langsam
27
//  smh_abweichung=0;
28
    smh_abweichung=min*60+sec;
29
      if(smh_abweichung>19){
30
        var_ocr0a--;
31
        eeprom_update_byte(eep_ocr0a_adr,var_ocr0a);
32
        OCR0A = var_ocr0a;
33
      }
34
  //    lg(4,9);
35
  //    lw("-");li(smh_abweichung);lw("-");li(var_ocr0a);lw("-");
36
  }  
37
38
  
39
}
40
41
42
43
44
//sec = sec - smh_korrektur;
45
smh_korrektur_sperre=1;
46
}

Nach jeweils einer Stunde Laufzeit wird durch Abgleich mit der RTC der 
Timer kalibriert. Der Wert des jeweiligen nanos in fcpu.h eingetragen.

Was noch fehlt ist die Berechnung der Taktfrequenz aus der 
Timerabweichung.

von Max M. (jens2001)


Lesenswert?

grundschüler schrieb:
> auf die unterschiedliche Programme
> aufgespielt werden. Jeder soll mit dem gleichen Programmcode laufen

LÖL!

von Rolf M. (rmagnus)


Lesenswert?

grundschüler schrieb:
> ich kann delay.h kopieren und die Konstante durch eine Variable
> ersetzen, die mit F_CPU vorbelegt ist.

Das kannst du, aber dann wirst du viel mehr Flash brauchen, und deine 
Delays werden zu lang sein. In den delay-Funktionen werden nämlich 
Gleitkomma-Operationen durchgeführt, die aber komplett im Compiler 
berechnet werden können, da F_CPU ja eine Compilezeit-Konstante ist. Ist 
es das nicht mehr, muss die Berechnung zur Laufzeit gemacht werden, 
wofür dann zusätzlich die Floatingpoint-Lib benötigt wird und was auch 
eine gewisse Zeit für die Berechnungen zusätzlich zum Delay braucht.

grundschüler schrieb:
> #define F_CPU 8000000
>
> #if boar_nr == 1
> #define F_CPU 7900000
> #endif
> ....

Dir ist aber klar, dass #if vom Präprozessor ausgeführt wird, der deine 
Variable board_nr nicht sehen kann?

von Ingo Less (Gast)


Lesenswert?

Garnicht auszumalen was sich Apple dort für Probleme schafft wenn die an 
der CPU Geschwindigkeit drehen zum Stromsparen...

von Johannes S. (Gast)


Lesenswert?

Welcher Apple läuft denn mit einem AVR?

von grundschüler (Gast)


Lesenswert?

Rolf M. schrieb:
> Dir ist aber klar, dass #if vom Präprozessor ausgeführt wird, der deine
> Variable board_nr nicht sehen kann?

deswegen vorher
#define board_nr x

von Rolf M. (rmagnus)


Lesenswert?

grundschüler schrieb:
> Rolf M. schrieb:
>> Dir ist aber klar, dass #if vom Präprozessor ausgeführt wird, der deine
>> Variable board_nr nicht sehen kann?
>
> deswegen vorher
> #define board_nr x

Und was ist x?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Ich würde Falks Vorschlag folgen:

Falk B. schrieb:
> Wenn schon, dann kalibriert man ALLE AVRs vorher auf Nennfrequenz

Wenn das – aus welchen Gründen auch immer – keine Option ist, wäre
folgender Ansatz möglich, bei dem Make die ermittelten Frequenzen aus
einer Datei freqs liest und aus einer einzelnen Quellcodedatei prog.c so
viele Executables namens prog01.elf, prog02.elf usw. baut wie freqs
Einträge enthält:

Makefile (auf das Wesentliche reduziert):
1
PROGNAME = prog
2
3
FREQSFILE = freqs
4
FREQS = $(file <$(FREQSFILE))
5
INDICES = $(shell seq -f "%02.0f" $(words $(FREQS)))
6
TARGETS = $(patsubst %, $(PROGNAME)%.elf, $(INDICES))
7
8
all: $(TARGETS)
9
10
$(PROGNAME)%.elf: $(PROGNAME).c $(FREQSFILE)
11
  $(CC) -o $@ $< -DF_CPU=$(word $*, $(FREQS))

freqs:
1
8028476
2
8012731
3
7984765
4
8003345
5
7992374
6
7972472
7
8032387
8
8018434
9
7962847
10
7998347

prog.c (einfaches Beispiel):
1
#include <stdio.h>
2
3
int main(void) {
4
  printf("F_CPU = %d\n", F_CPU);
5
  return 0;
6
}

Anwendung:
1
$ make
2
cc -o prog01.elf prog.c -DF_CPU=8028476
3
cc -o prog02.elf prog.c -DF_CPU=8012731
4
cc -o prog03.elf prog.c -DF_CPU=7984765
5
cc -o prog04.elf prog.c -DF_CPU=8003345
6
cc -o prog05.elf prog.c -DF_CPU=7992374
7
cc -o prog06.elf prog.c -DF_CPU=7972472
8
cc -o prog07.elf prog.c -DF_CPU=8032387
9
cc -o prog08.elf prog.c -DF_CPU=8018434
10
cc -o prog09.elf prog.c -DF_CPU=7962847
11
cc -o prog10.elf prog.c -DF_CPU=7998347
12
$ ls *.elf
13
prog01.elf  prog03.elf  prog05.elf  prog07.elf  prog09.elf
14
prog02.elf  prog04.elf  prog06.elf  prog08.elf  prog10.elf
15
$ prog01.elf 
16
F_CPU = 8028476
17
$ prog10.elf 
18
F_CPU = 7998347

Statt alle Frequenzen in eine einzelne Datei freqs zu schreiben könnte
man auch für jeden Controller eine eigene Datei freq01, freq02 usw.
mit jeweils nur einem Eintrag anlegen und das Makefile entsprechend
anpassen. Das hätte den Vorteil, dass bei Änderung einer Frequenz nur
das jeweils betroffene Executable neu gebaut wird.

von Falk B. (falk)


Lesenswert?

Meine Lösung, dein Problem!

von grundschüler (Gast)


Lesenswert?

Falk B. schrieb:
> Meine Lösung, dein Problem!


war jedenfalls hilfreich, über das ganze noch mal nachzudenken. Die 
nanos haben ja einen 16 Mhz Quarz. Da ich 3,3V Peripherie benutze soll 
die mcu auch mit 3,3v arbeiten können. Deshalb  ist der 16mhz 
ungeeignet.

Dass man avrs kalibrieren kann, muss man wissen. Habe ich noch nicht 
gemacht und hatte das zumindest nicht present. Werde ich ausprobieren. 
Auch hier stellt sich noch die Frage der systematischen Messung der 
Ausgangsfrequenz und Umrechnung auf OSCCAL.

Wenn alles nicht hilft, bliebe noch Auslöten von 16mhz und Ersatz durch 
8mhz.

von Peter D. (peda)


Lesenswert?

grundschüler schrieb:
> Wenn alles nicht hilft, bliebe noch Auslöten von 16mhz und Ersatz durch
> 8mhz.

Dun kannst auch die CKDIV8-Fuse aktivieren, dann startet er mit 2MHz. 
Und nach dem Reset schaltest Du auf /2 um, das ergibt dann die 
gewünschten 8MHz.

von grundschüler (Gast)


Lesenswert?

Peter D. schrieb:
> Und nach dem Reset schaltest Du auf /2 um, das ergibt dann die
> gewünschten 8MHz.

Danke, das wäre die perfekte Lösung. Aber wie schalte ich auf /2 um?

von m.n. (Gast)


Lesenswert?

CLKPR = 0x80; CLKPR = 0x1; // Vorteiler / 2

von Peter D. (peda)


Lesenswert?

grundschüler schrieb:
> Aber wie schalte ich auf /2 um?
1
#include <avr/power.h>
2
3
clock_prescale_set(clock_div_2);

von grundschüler (Gast)


Lesenswert?

Peter D. schrieb:


danke, ich hab ja inzwischen einige Erfahrung mit avrs, aber man lernt 
nie aus.


Danke für alle Beiträge und alle Anregungen. War - wieder mal - sehr 
hilfreich.

von Schorsch (Gast)


Lesenswert?

Hallo,
sehr interessant !!!!

Aber an welcher Stelle setze ich den Befehl
""clock_prescale_set(clock_div_2); "" genau ins Programm ein.

Wenn ich ihn direkt am Anfang der "int main(void)" einsetze funktioniert 
die serielle Ausgabe erst wenn ich die vordefinierte Baudrate der 
Software Uart von 9600 auf die Hälfte, 4800 setze.

Oder doch einfacher den Quarz 8MHz gegen 4MHz austauschen ?

von Peter D. (peda)


Lesenswert?

Schorsch schrieb:
> Oder doch einfacher den Quarz 8MHz gegen 4MHz austauschen ?

Ich würde eine Zeile Code dem Umlöten vorziehen.
Ich benutze SMD-Quarze und die löten sich wirklich eklig, da die Pads 
darunter liegen.
Der Prescaler muß natürlich in F_CPU mit eingerechnet werden.

von Schorsch (Gast)


Lesenswert?

Moin peda,
in der Tat ist es so einfacher, selbst mit einem HC49U-S Quarz.

Reale Taktrate des Quarz = 8MHz

Eintragung in F_CPU
F_CPU = 4000000

und hier

int main(void)
{
 clock_prescale_set(clock_div_2);// Clock Divisor Faktor 2


So wird auch mit der definierten 9600 Baudrate die richtige Anzeige über 
die Software Uart erreicht.

Manchmal hilft ein Tag Abstand

DANKE DIR !!!!!!

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.