Forum: Mikrocontroller und Digitale Elektronik Unklarheiten ATmega Programmierung


von Sheriff Silver (Gast)


Lesenswert?

Hallo liebe Community,

ich hab ein Projekt im Bereich ATmega übernommen. Übersichtlicher 
Quelltext, aber dennoch gibt es ein paar Ungereimtheiten.

Mikrocontrollerprogrammierung ist mir nicht fremd. Ich habe vorher 
einiges auf der STM32-Plattform gemacht. Deswegen ist mir AVR und ATmega 
etwas fremd.

Es handelt sich um den ATmega48PA. Was der Quelltext macht ist für mich 
kein Problem, es hängt aber etwas an den ATmega-spezifischen 
Einstellungen die man vornehmen muss.

Im Quelltext befindet sich folgende Textzeile:
1
#define F_CPU 1000000UL

Das ist auch soweit klar. Damit kann ich die Taktfrequenz einstellen.

Nun wird der uC aber mit folgender Fuse programmiert:

INTRCOSC_128KHZ_6CK_14CK_65MS

Laut Datenblatt ist das interne 128 kHZ Oszillator. Beim STM32 kann man 
ebenfalls einen niedrigeren Takt einstellen und diesen dann schnließend 
hochteilen. Geht das hier durch den clock prescaler auch? Entsprechende 
Codezeilen habe ich nicht gefunden.

Meine Frage ist nun: mit welcher Taktfrequenz arbeitet denn nun der uC?

Vielen Dank :)

von Tom (Gast)


Lesenswert?

Sheriff Silver schrieb:
> Damit kann ich die Taktfrequenz einstellen.

Damit gibt man lediglich die Frequenz an, damit sie im Code in 
Berechnungen von Delayzeiten etc. benutzt werden kann. Den uC 
interessiert das nicht, der richtet sich nach den Fuse-Einstellungen.

von Sheriff Silver (Gast)


Lesenswert?

Tom schrieb:
> Damit gibt man lediglich die Frequenz an, damit sie im Code in
> Berechnungen von Delayzeiten etc. benutzt werden kann. Den uC
> interessiert das nicht, der richtet sich nach den Fuse-Einstellungen.

Ich habe gerade auch noch folgendes gefunden:
1
// Der MCU-Takt. Wird gebraucht, um Timer1 mit den richtigen
2
// Werten zu initialisieren. Voreinstellung ist 1MHz.
3
// (Werkseinstellung für AVRs mit internem Oszillator).
4
// Das Define wird nur gemacht, wenn F_CPU noch nicht definiert wurde.
5
// F_CPU kann man so auch per Kommandozeile definieren, z.B. für 8MHz:
6
// avr-gcc ... -DF_CPU=8000000
7
//   
8
// ! Der Wert von F_CPU hat rein informativen Character für
9
// ! die korrekte Codeerzeugung im Programm!
10
// ! Um die Taktrate zu ändern müssen die Fuses des Controllers
11
// ! und/oder Quarz/Resonator/RC-Glied/Oszillator
12
// ! angepasst werden!
13
#ifndef F_CPU
14
#define F_CPU    1000000
15
#endif

Das bedeutet er arbeitet mit 128 kHz.

Okay. Vielen Dank :)

von M. K. (sylaina)


Lesenswert?

Sheriff Silver schrieb:
> Ich habe gerade auch noch folgendes gefunden:

An der 1000000 sollte immer ein UL stehen nicht dass der Compiler 
versucht zu optimieren, da kann es ggf. zu Fehlern sonst kommen.

Sheriff Silver schrieb:
> Das bedeutet er arbeitet mit 128 kHz.

Ja, in deinem obigen Post schon (sofern der Prescaler auf 1 ist), wie 
schon gesagt wurde, es zählt was gefused wurde.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Sheriff Silver schrieb:
> Ich habe gerade auch noch folgendes gefunden:
> ...

Das sollte man besser löschen.
Ansonsten sucht man sich nen Wolf, wo der Fehler ist, wenn man die 
Einstellung im make vergißt.

von Joachim B. (jar)


Lesenswert?

M. K. schrieb:
> es zählt was gefused wurde.

und was angeschlossen ist und F_CPU sollte nun passend gesetzt werden 
aber natürlich nur wenn es nicht schon an anderer Stelle 
deklariert/definiert wurde.

Ich denke das hier wäre dann sinnvoll!

Sheriff Silver schrieb:
> as bedeutet er arbeitet mit 128 kHz.

Sheriff Silver schrieb:
> #ifndef F_CPU
> #define F_CPU    128000
> #endif

folgendes nur für den ersten Lauf vor der Fuse Änderung mit intern 8MHz 
und div 8

Sheriff Silver schrieb:
> #ifndef F_CPU
> #define F_CPU    1000000
> #endif

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

Joachim B. schrieb:
> und was angeschlossen ist und F_CPU sollte nun passend gesetzt werden
> aber natürlich nur wenn es nicht schon an anderer Stelle
> deklariert/definiert wurde.

was aber sehr schlecht ist. Wenn es schon an anderer Stelle ist das ein 
Fehler der nicht ignoriert werden darf!

Also nur:
1
#define F_CPU    128000

was aber immer nicht schön ist, dann das define gehört eigentlich in die 
Projekteinstellungen und nicht in den Quellcode.

von Joachim B. (jar)


Lesenswert?

Peter II schrieb:
> Wenn es schon an anderer Stelle ist das ein
> Fehler der nicht ignoriert werden darf!

deswegen ja:

#ifndef F_CPU

oder wer weiss was er tut

#ifndef F_CPU
#undef F_CPU

und dann

#define F_CPU

: Bearbeitet durch User
von Draco (Gast)


Lesenswert?

Joachim B. schrieb:
> #ifndef F_CPU
> #undef F_CPU
>
> und dann
>
> #define F_CPU

Also wenn dann so:

1
#ifdef F_CPU
2
 #undef F_CPU
3
 #define F_CPU 128000
4
#endif

von Rene H. (Gast)


Lesenswert?

Peter II schrieb:
> was aber immer nicht schön ist, dann das define gehört eigentlich in die
> Projekteinstellungen und nicht in den Quellcode.

Draco schrieb:
> Also wenn dann so:
>
> #ifdef F_CPU
>  #undef F_CPU
>  #define F_CPU 128000
> #endif

Eher nicht. Siehe das post von Peter II. ifndef F_CPU <bla bla blubb> 
macht meiner Meinung nach viel mehr Sinn.

Grüsse,
René

von Draco (Gast)


Lesenswert?

Rene H. schrieb:
> Eher nicht. Siehe das post von Peter II. ifndef F_CPU <bla bla blubb>
> macht meiner Meinung nach viel mehr Sinn.
>
> Grüsse,
> René

Gruß Namensveter :D

Ich sagte ja "Wenn schon, dann..." - das dieses Define einmal reindarf 
steht außer Frage, das es sinnvoller ist es in die Porjekteinstellungen 
zu legen.

Aaaaber, ich trösel das mal auf, F_CPU ist im folgenden Fall schon 
definiert:
1
#ifdef F_CPU           //schauen ob F_CPU schon definiert wurde
2
 #undef F_CPU          //F_CPU "löschen" / "undefinieren"
3
 #define F_CPU 128000  //F_CPU neu setzen
4
#endif                 //Ende Schleife

Joachim B. schrieb:
> #ifndef F_CPU
> #undef F_CPU
>
> und dann
>
> #define F_CPU

Das macht nunmal kein Sinn -> mit #ifndef (IF NOT DEFINED) prüfen ob 
F_CPU NICHT gesetzt wurde um dann mit #undef das nicht gesetzte #define 
zu löschen?! Das macht garkein Sinn.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Draco schrieb:
> Also wenn dann so:
>
> #ifdef F_CPU
>  #undef F_CPU
>  #define F_CPU 128000
> #endif

Sorry, das ist suboptimal.

Der einzig sinnvolle Weg ist:
1
#ifndef F_CPU
2
#error F_CPU not defined.
3
#endif

Und dann im Projekt/Makefile F_CPU für alle Sources angeben. Jede IDE 
für AVRs untertützt das. So werden widersprüchliche Angaben in C-Sources 
innerhalb eines Projekts von vornherein vermieden.

von Draco (Gast)


Lesenswert?

Frank M. schrieb:
> Sorry, das ist suboptimal.
>
> Der einzig sinnvolle Weg ist:#ifndef F_CPU
> #error F_CPU not defined.
> #endif
>
> Und dann im Projekt/Makefile F_CPU für alle Sources angeben. Jede IDE
> für AVRs untertützt das. So werden widersprüchliche Angaben in C-Sources
> innerhalb eines Projekts von vornherein vermieden.

Da stimme ich dir vollkommen zu! Ohne Frage! Aber darum ging es oben ja 
nicht.

von Joachim B. (jar)


Lesenswert?

Draco schrieb:
> Das macht nunmal kein Sinn -> mit #ifndef (IF NOT DEFINED) prüfen ob
> F_CPU NICHT gesetzt wurde um dann mit #undef das nicht gesetzte #define
> zu löschen?! Das macht garkein Sinn.

hast du wohl falsch verstanden oder ich doof erklärt, ist aber egal es 
wurde eigentlich alles deutlich gemacht, dem TO bleibt zu prüfen ob 
F_CPU defined ist wie und wo überall!

Manchmal gibt es F_CPU auch nicht weil noch XTAL verwendet wurde!

: Bearbeitet durch User
von Sheriff Silver (Gast)


Lesenswert?

Ich habe noch eine Frage zu folgenden Codezeilen:
1
TCCR0B=(0<<WGM02) | (1<<CS02) | (0<<CS01) | (0<<CS00);  // ~500ms
2
TCNT0=0x00;
3
OCR0A=0x00;
4
OCR0B=0x00;
5
  
6
// Timer/Counter 0 Interrupt(s) initialization
7
TIMSK0=(0<<OCIE0B) | (0<<OCIE0A) | (1<<TOIE0);
8
9
ISR(TIMER0_OVF_vect)
10
{
11
  clock_prescale_set(0);  // Takt direkt
12
  
13
  
14
    PORTB ^= (1 << PB0);
15
16
  
17
  ADCSRA |= (1<<ADEN);  // ADC ein    
18
    
19
  //anderer Code
20
  
21
  alarms();
22
  ADCSRA &= ~(1<<ADEN); // ADC aus
23
  clock_prescale_set(1);  // Takt / 2
24
}

Taktfrequenz sind die beschriebenen 128 kHz.
Der Timer ist ja so eingestellt, dass er mit 256 vorgeteilt wird.

128 kHz/256/256 = 1,95 Hz = 512 ms

Jetzt habe ich über den Port B die ganze Sache mal nachgemessen und 
komme auf eine Frequenz von 1s. Sprich aller einer Sekunde wird die ISR 
ausgelöst.

Ich erkläre mir das jetzt so, dass zu Beginn der ISR der volle Takt 
aktiviert wird. Am Ende wird der Takt wieder auf 64 kHz gestellt.

Wenn ich das mit 64 kHz durchrechne, dann komme ich auch auf eine 
Sekunde.

Ist mein Gedankengang richtig?

von M. K. (sylaina)


Lesenswert?

Sheriff Silver schrieb:
> Jetzt habe ich über den Port B die ganze Sache mal nachgemessen und
> komme auf eine Frequenz von 1s. Sprich aller einer Sekunde wird die ISR
> ausgelöst.

Womit kommst du auf 1s? Misst du eine Frequenz von 1 Hz? Ist ja normal 
wenn der Timer OVF auf 512 ms steht, dann ändert sich der Pegel an PB0 
alle 512 ms und du musst zwei Pegeländerungen abwarten für eine Periode, 
also 2*512 ms ;)

PS: Übrigens, die ISR ist recht schlecht. In einer ISR soll man so wenig 
wie möglich machen. Du machst da anscheinend so viel wie geht. Ich würde 
in einer ISR nie den ADC starten und auf das Wandlerergebnis da drin 
warten, genau das scheinst du aber zu machen, das ist IMO grober Unfug.

: Bearbeitet durch User
von Rene H. (Gast)


Lesenswert?

Sheriff Silver schrieb:
> Mikrocontrollerprogrammierung ist mir nicht fremd. Ich habe vorher
> einiges auf der STM32-Plattform gemacht. Deswegen ist mir AVR und ATmega
> etwas fremd.

Hallo René,

wenn der obige Text korrekt wäre, ist die Fragestellung doch reichlich 
verstörend. Unrecht hat er da nicht. Nur die Tonart .....


Grüsse,
René

von Ralph S. (jjflash)


Lesenswert?

... vielleich hätte dem Threadersteller jemand sagen sollen, dass:

.. im Gegensatz zu STM32 der Prozessortakt nicht zur "Laufzeit" der MCU 
per Programm (HSE, HSI und diverser Prescaler) eingestellt wird (und 
meistens in einer SystemInit Datei erledigt wird), sondern der Takt, mit 
dem der MCU läuft über sogenannte "Fuses" eingestellt wird, oder 
anderst: Es werden Speicherstellen AUSSERHALB des C-Programms mit Werten 
beschrieben, die den Takt der MCU einstellen: interner RC-Generator, 
oder externer Takt (entweder als "fertiges" Taktsignal zugeführt oder 
über einen Quarz/Resonator erzeugt).

Welche Fuses wie zu beschreiben sind kann hier ermittelt werden:

http://www.engbedded.com/fusecalc/

Beispiel für ATMega48PA:

Interner RC-Generator, 8 MHz Taktfrequenz (F_CPU=8000000ul)
Fuses Low: 0xD2   Fuses High: 0xDC  Extended: 0x01

Interner RC-Generator, 1 MHz Taktfrequenz
Fuses Low: 0x52   Fuses High: 0xDC  Extended: 0x01

Externer Quarz (Quarz hier >= 8 MHz), Taktfrequenz die des Quarzes
Fuses Low: 0xFE   Fuses High: 0xDC  Extended: 0x01

Um diese Fuses für externen Quarz (bspw. mit einem STK500v2) mittels 
AVRDUDE zu schreiben, der an COM1 angeschlossen ist und mit einer 
Baudrate von 115200 betrieben wird, muß folgendes eingegeben werden:

Unter Windows:

avrdude -c stk500v2 -p m48 -P com1 -b 115200 -B 20 -U lfuse:w:0xFE:m -U 
hfuse:w:0xDC:m

Unter Linux (wenn der STK500 die Schnittstelle ttyACM0 besitzt):

avrdude -c stk500v2 -p m48 -P /dev/ttyACM0 -b 115200 -B 20 -U 
lfuse:w:0xFE:m -U hfuse:w:0xDC:m


Legende:

-c stk500v2     ... -c benennt den verwendeten Programmer
-p m48          ... -p benennt den verwendeten MCU
-P /dev/ttyACM0 ... -P benennt den Anschlussport an den der Programmer 
angeschlossen ist
-B 20           ... ISP Clock (20 ist hier langsam damit auch ein 
fabrikneuer MCU geflasht wird)
-U              ... -U wählt Speicher an (hier die Register der Fuses)
                       lfuse benennt die Low-Fuse
                       hfuse benennt die High-Fuse
                       :w bedeutet schreiben (write)
                       :m memory

Wird bspw. ein Programmer verwendet, der sein eigenes Interface über USB 
mitbringt (bspw. USBasp) und nicht über einen echten oder virtuellen 
Com-Port angesprochen wird, kann die Einstellung für Anschlussport und 
Baudrate weggelassen werden:

avrdude -c usbasp -p m48 -B 20 -U lfuse:w:0xFE:m -U hfuse:w:0xDC:m

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.