dynamische Anpassung F_CPU

Gast #5545683
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.)
#5545691
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.
Gast #5545754
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.
#5545782
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.
Gast #5545826
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.
Gast #5545961
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.
#5545995
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?
Moderator #5546066
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.
Gast #5546268
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.
Gast #5895970
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 ?
Gast #5897043
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 !!!!!!

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren