Forum: Mikrocontroller und Digitale Elektronik Attiny 4313 - Änderung des clock prescalers ohne Effekt


von Viktor B. (coldlogic)


Lesenswert?

Hi Leute,
Ich bin gerade dabei, einen SPI-zu-ServoPWM-Wandler mit 6 Kanälen auf 
Basis des Attiny4313 zu programmieren. Hab auch schon den Teil mit der 
PWM - Erzeugung fast geschafft - außer, dass der Chip per se nicht auf 
den Clock Prescaler reagiert. Der Zugriff erfolgt wie im Datenblatt: 
erst 0x80, dann die gewünschte Bitkonstellation schreiben (hier: 0x02)
1
  TIMSK |= (1<<OCIE0A)|(1<<TOIE0);        // Enable interrupts on compare and overflow
2
  CLKPR = 0x80;                  // Set clock div
3
  CLKPR = 0x02;                  // clock div/4: 8Mhz int.osz. / 4 presc. = 2Mhz eff. freq.
4
  DDRB  = 0x1F;                         // Port B i/o: 7-iiiooooo-0

Das Problem ist, der Timer schert sich nicht drum und macht munter ohne 
sich zu verlangsamen weiter. Leider kann ich die Frequenz des Clocks 
nicht messen, der Oszi macht bei den Frequenzen nicht mehr mit.

Der (mittlererweile bis zu Löchern im HDD studierte) Datenblatt sagt 
nichts, außer dass es eigentlich funktionieren sollte - Prescaler hängt 
vor der CLK(I/O) und der Timer0 bezieht den Clock daraus mit einem 
Prescaler von 8
1
  sei();                      // #Interrupts
2
  TCCR0B |= (1<<CS01);              // Set clock input prescaler of 8, starting the timer (2 Mhz freq.)

Den gesamten Code könnte ich auf Anfrage posten, will ich aber wenn 
möglich vermeiden, da ISR's etwas hässlich geworden sind und die 
Variablennamen nur Leuten etwas sagen, die schon ein paar Nächten mit 
dem Projekt verbracht haben. Also, hätte jemand einen Ansatz?

MfG

von S. Landolt (Gast)


Lesenswert?

Auf welchem Wert steht das Low Fuse Byte?

von Bastian W. (jackfrost)


Lesenswert?

Was sagt das disasembly von dem Code , wird ser Presacler in weniger als 
4 Takten gesetzt ? Kann dir in Inerupt da schon da zwischen Funken ? 
Oder kommt das Sei() erst danach ?

Gruß JackFrost

von MWS (Gast)


Lesenswert?

Viktor B. schrieb:
> TIMSK |= (1<<OCIE0A)|(1<<TOIE0);        // Enable interrupts on
> compare and overflow
>   CLKPR = 0x80;                  // Set clock div

Das Ding um CLKPR benötigt eine sog. timed sequence, d.h. innerhalb 4 
Taktzyklen ab Setzen von CLKPCE muss der neue Prescalerwert geschrieben 
sein. Das kann von Interrupts gestört werden. Auch wenn der Code nicht 
optimiert wird, -O0, sollten immer noch die 4 Clocks einhaltbar sein, 
man könnte trotzdem mal -Os versuchen.

von Christian S. (roehrenvorheizer)


Lesenswert?

Hallo,

es muß wohl an dieser Stelle etwas nicht stimmen.

"To avoid unintentional changes of clock frequency, a special write 
procedure must be followed to change the CLKPS bits:
1.Write the Clock Prescaler Change Enable (CLKPCE) bit to one and all 
other bits in CLKPR to zero.
2.Within four cycles, write the desired value to CLKPS while writing a 
zero to CLKPCE.
Interrupts must be disabled when changing prescaler setting to make sure 
the write procedure is not interrupted. The Application software must 
ensure that a sufficient division factor is chosen if the selected clock 
source has a higher frequency than the maximum frequency of the device 
at the present operating conditions. The device is shipped with the 
CKDIV8 fuse programmed."

MfG

von Hubert G. (hubertg)


Lesenswert?

Was glaubst du was passiert wenn du in das Register erst 0x80 und dann 
0x02 schreibst. Was wird dann im Register stehen?

von Dieter F. (Gast)


Lesenswert?

Hubert G. schrieb:
> Was glaubst du was passiert wenn du in das Register erst 0x80 und dann
> 0x02 schreibst. Was wird dann im Register stehen?

Gut erkannt :-)

von Thomas E. (thomase)


Lesenswert?


von MWS (Gast)


Lesenswert?

Hubert G. schrieb:
> Was glaubst du was passiert wenn du in das Register erst 0x80 und
> dann
> 0x02 schreibst. Was wird dann im Register stehen?

W arum liest Du Dir den betreffenden Teil im DB nicht vor Deinem Post 
durch?

von Hubert G. (hubertg)


Lesenswert?

The CLKPCE bit must be written to logic one to enable change of the 
CLKPS bits. The CLKPCE
bit is only updated when the other bits in CLKPR are simultaneously 
written to zero.
Ich hätte das so interpretiert das ich zuerst CLKPS setzen muss und dann 
CLKPCE. Also genau umgekehrt wie im ersten Post geschrieben.
Lasse mich aber gerne belehren.

von S. Landolt (Gast)


Lesenswert?

Um zu kontrollieren, wie schnell der Controller vor und nach der 
Umstellung von CLKPR läuft, braucht man doch kein Oszilloskop, da reicht 
eine LED.

Und wenn das Low Fuse Byte im Auslieferungszustand ist, dann bewirkt
> CLKPR = 0x02;
kein
> verlangsamen
sondern ein Beschleunigen, deshalb meine Frage.

von MWS (Gast)


Lesenswert?

Hubert G. schrieb:
> The CLKPCE bit must be written to logic one to enable change of
> the CLKPS bits. The CLKPCE
> bit is only updated when the other bits in CLKPR are simultaneously
> written to zero.
> Ich hätte das so interpretiert das ich zuerst CLKPS setzen muss und dann
> CLKPCE. Also genau umgekehrt wie im ersten Post geschrieben.
> Lasse mich aber gerne belehren.

Übers.: "Das CLKPCE-Bit muss auf logisch 1 gesetzt werden, um die 
Änderung der CLKPS-Bits zu erlauben. Das CLCPCE-Bit wird nur geändert, 
wenn die anderen Bits in CLKPR gleichzeitig auf 0 gesetzt werden."

Wie würdest Du das interpretieren?

von M. K. (sylaina)


Lesenswert?

Hubert G. schrieb:
> Ich hätte das so interpretiert das ich zuerst CLKPS setzen muss und dann
> CLKPCE. Also genau umgekehrt wie im ersten Post geschrieben.

Ne, du musst immer zuerst das CLKPCE (das steht für CLocK Prescaler 
Change Enable, d.h. ist es nicht gesetzt lässt sich der Prescaler auch 
nicht ändern) setzen und dabei muss, lt. Datenblatt, alle anderen Bits 
gleichzeitig 0 werden. da kommen die 0x80 her, das setzt nämlich nur das 
CLKPCE auf 1 und alle anderen zu 0. Dann hat man vier Zyklen Zeit den 
gewünschten Prescaler zu setzen.

Viktor B. schrieb:
> Leider kann ich die Frequenz des Clocks
> nicht messen, der Oszi macht bei den Frequenzen nicht mehr mit.

Woher weißt du dann, dass das mit dem Clock setzen nicht klappt? Tipp: 
Mache erstmal ein simples Beispiel bei dem du mittels CLKPCE usw. den 
Clock änderst sodass du es sehen kannst.
Tipp von mir: Timer Overflow Interrupt einstellen und darin eine 
Variable hoch zählen lassen. Wenn die Variable einem Wert entspricht, 
der eine Sekunde darstellt, dann lässt du einen Pin umschalten, an dem 
du eine LED hast. Wenn das funktioniert versuchst du mit dem CLKPCE den 
Systemtakt mindestens zu halbieren. Am Blinken der LED siehst du dann, 
ob das wirklich geklappt hat.

: Bearbeitet durch User
von Viktor B. (coldlogic)


Lesenswert?

Die Lower Fuse steht auf "DC": Kein Runterteilen, Externer Quarz mit 
8Mhz, kein Clock auf D2.

Die Methode der Frequenzmessung mit einer LED ist mir nicht bekannt, wie 
soll das gehen - ohne (software-)Timer?

Disassembly sieht so aus:
1
  CLKPR = 0x80;                  // Set clock div
2
 35e:  e6 e4         ldi  r30, 0x46  ; 70
3
 360:  f0 e0         ldi  r31, 0x00  ; 0
4
 362:  80 e8         ldi  r24, 0x80  ; 128
5
 364:  80 83         st  Z, r24
6
  CLKPR = 0x02;                  // clock div/4: 8Mhz int.osz. / 4 presc. = 2Mhz eff. freq.
7
 366:  e6 e4         ldi  r30, 0x46  ; 70
8
 368:  f0 e0         ldi  r31, 0x00  ; 0
9
 36a:  82 e0         ldi  r24, 0x02  ; 2
10
 36c:  80 83         st  Z, r24
Ich verstehe beim besten Willen nicht, was die zwei LDIs vor LDI r24, 
0x02 sollen.

Und es können keine Interrupts dazwischen kommen, da das Ganze sich in 
dem Setup-Teil befindet, der zwischen cli(); und sei(); steht.

von Viktor B. (coldlogic)


Lesenswert?

M. K. schrieb:
> Woher weißt du dann, dass das mit dem Clock setzen nicht klappt?

Weil der Timer von der Clock abhängen soll(te). Und da sich der Timer 
nicht regt, vermute ich, die Clock ist unverändert geblieben.

von S. Landolt (Gast)


Lesenswert?

Das ist natürlich Mumpitz vom Compiler. Also doch:

Thomas Eckmann schrieb:
> Nimm clock-prescale_set aus der power.h

von M. K. (sylaina)


Lesenswert?

Viktor B. schrieb:
> M. K. schrieb:
>> Woher weißt du dann, dass das mit dem Clock setzen nicht klappt?
>
> Weil der Timer von der Clock abhängen soll(te). Und da sich der Timer
> nicht regt, vermute ich, die Clock ist unverändert geblieben.

Wenn der Timer sich nicht regt (woher weißt du das?) dann heißt das 
eigentlich, dass sich der Clock auch nicht regt.

Viktor B. schrieb:
> Die Methode der Frequenzmessung mit einer LED ist mir nicht bekannt, wie
> soll das gehen - ohne (software-)Timer?

Beispiel
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
volatile uint8_t myCounter = 0;
4
int main(void){
5
  // Timer1 einstellen
6
  TIMSK0 |= (1 << TOIE0);
7
  // Timer1 Prescaler auf 1024 einstellen (passt das mit den Registern? Prüfen)
8
  TCCR0B |= (1 << CS02)|(1 << CS00);
9
  // Pin mit der LED einstellen als Ausgang
10
  DDRB |= (1 << PB1);
11
  // Interrupts einschalten
12
  sei();
13
  for(;;){
14
  }
15
  return 0;
16
}
17
ISR(TIMER0_OVF_vect){
18
  myCounter++;
19
  if(myCounter > 3){
20
    // Pin toggeln
21
    PORTB ^= (1 << PB1);
22
    muCounter = 0;
23
  }
24
}
Ich weiß jetzt nicht ob das so auf dem Attiny4313 liefe, hab die 
Registernamen nicht geprüft. Bei einem AVR in 
Standardeinstellung/Werkseinstellung ist dieser üblich auf den 8 MHz RC 
Oszilator eingestellt mit einem Prescaler von 8, läuft also auf 1 MHz. 
Beim Timer0, hier gehe ich von einem 8 bit Timer aus, stelle ich den 
Overflow Interrupt ein und gebe ihn einen Prescaler von 1024, d.h. er 
läuft mit ca. 1 kHz. Der Rest sollte eigentlich klar sein. Die LED 
sollte mit ca. 1 Hz blinken. Jetzt ändert man nur den System-Prescaler 
mit dem CLKPCE, dann sollte die LED entsprechend schneller oder 
langsamer blinken. Das sieht man optisch prima und weiß dann, dass 
zumindest das funktioniert.

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Viktor B. schrieb:
> Disassembly sieht so aus:  CLKPR = 0x80;                  // Set clock
> div
>  35e:  e6 e4         ldi  r30, 0x46  ; 70
>  360:  f0 e0         ldi  r31, 0x00  ; 0
>  362:  80 e8         ldi  r24, 0x80  ; 128
>  364:  80 83         st  Z, r24
Bis Hier ganz toll, aber:

>   CLKPR = 0x02;                  // clock div/4: 8Mhz int.osz. / 4
> presc. = 2Mhz eff. freq.
>  366:  e6 e4         ldi  r30, 0x46  ; 70
>  368:  f0 e0         ldi  r31, 0x00  ; 0
>  36a:  82 e0         ldi  r24, 0x02  ; 2
>  36c:  80 83         st  Z, r24

braucht 'geringfügig' mehr Takte als erlaubt (die 4 Takte), bis der neue 
Wert in die Speicherstelle 0x46 geschrieben wird.

Wenn dort r30 und r31 nicht erneut gesetzt würden, sollte Das klappen 
(sofern die Werte passen und 0x46 der Ort des passenden Register ist - 
denke aber, daß dort der Compiler weiß, was Er tut)
Warum der Compiler hier Z mit dem bereits enthaltenen Wert neu lädt, 
kann Dir nur der Compiler selber beantworten.

MfG

: Bearbeitet durch User
von Viktor B. (coldlogic)


Lesenswert?

M. K. schrieb:
> Wenn der Timer sich nicht regt (woher weißt du das?) dann heißt das
> eigentlich, dass sich der Clock auch nicht regt.

Ich habe es mit dem Oszi nachgemessen. Und ich hab ja auch gesagt, dass 
sich weder der Timer noch die Clock verändert haben. Die Unterscheidung 
zwischen Timer und Clock hab ich nur gemacht, damit man nicht 
Überraschungen wie beim STM8 bekommt - "Ach der Timer, der hat ja 'ne 
ganz eigene Clock, die muss man auch noch aktivieren!"

M. K. schrieb:
> Beim Timer0, hier gehe ich von einem 8 bit Timer aus, stelle ich den
> Overflow Interrupt ein

Hier wurde der Timer 0 auch so eingestellt, nur halt dass da etwas mehr 
Code und eine verschachtelte FSM dahinter sind. Im Beispiel würde der 
Prescaler also auch keinen Einfluss auf den Timer nehmen.

Ich suche gerade nach einer Möglichkeit, die Disassembly-Datei zu 
editieren. Vielleicht sind die 4 Instruktionen doch zu viel.

von S. Landolt (Gast)


Lesenswert?

Patrick J. schrieb:

> Bis Hier ganz toll
Das ist jetzt aber schon keine Ironie mehr, sondern beißender Sarkasmus. 
In Assembler schrieben wir beide doch
1
 ldi r24,0x80
2
 out CLKPR,r24
und direkt danach
1
 ldi r24,0x02
2
 out CLKPR,r24

von S. Landolt (Gast)


Lesenswert?

> Ich suche gerade nach einer Möglichkeit,
> die Disassembly-Datei zu editieren.
Warum, funktioniert der Tipp von Thomas E. nicht?

> Vielleicht sind die 4 Instruktionen doch zu viel.
Sieht so aus, 5 Takte sind mehr als die 4 erlaubten.

von Viktor B. (coldlogic)


Lesenswert?

S. Landolt schrieb:
> Das ist jetzt aber schon keine Ironie mehr, sondern beißender Sarkasmus.
> In Assembler schrieben wir beide doch

Aha. Und etwas weiter oben schrieb ich

>Viktor B. schrieb:
> Disassembly sieht so aus:

- D.h. ich hab es in C geschrieben und dem Compiler gegeben. Der hat 
dann den ASM-Code erzeugt. Wieso er mich boykottiert? Gute Frage

von F. F. (foldi)


Lesenswert?

MWS schrieb:
> Übers.: "Das CLKPCE-Bit muss auf logisch 1 gesetzt werden, um die
> Änderung der CLKPS-Bits zu erlauben. Das CLCPCE-Bit wird nur geändert,
> wenn die anderen Bits in CLKPR gleichzeitig auf 0 gesetzt werden."
>
> Wie würdest Du das interpretieren?

Da gibt es doch nichts zu "interpretieren". Es steht da so, wie du das 
übersetzt hast.

von Viktor B. (coldlogic)


Lesenswert?

Meine Eclipse-IDE stellt sich auch an. Unresolved inclusion, obwohl die 
Datei daneben gefunden und akzeptiert wird. Ich werd hier wahnsinnig

von Bastian W. (jackfrost)


Lesenswert?

Mit -O1 beim Atmelstudio 7 kommt das raus und das sollte gehen.
1
   TIMSK |= (1<<OCIE0A)|(1<<TOIE0);        // Enable interrupts on compare and overflow
2
00000032  IN R24,0x39    In from I/O location 
3
00000033  ORI R24,0x03    Logical OR with immediate 
4
00000034  OUT 0x39,R24    Out to I/O location 
5
   CLKPR = 0x80;                  // Set clock div
6
00000035  LDI R24,0x80    Load immediate 
7
00000036  OUT 0x26,R24    Out to I/O location 
8
   CLKPR = 0x02;                  // clock div/4: 8Mhz int.osz. / 4 presc. = 2Mhz eff. freq.
9
00000037  LDI R24,0x02    Load immediate 
10
00000038  OUT 0x26,R24    Out to I/O location 
11
   DDRB  = 0x1F;                         // Port B i/o: 7-iiiooooo-0
12
00000039  LDI R24,0x1F    Load immediate 
13
0000003A  OUT 0x17,R24    Out to I/O location 
14
   sei();
15
0000003B  SEI     Global Interrupt Enable

Gruß JackFrost

von Viktor B. (coldlogic)


Lesenswert?

So, mit der Funktion aus der power.h klappt jetzt alles. Damit 
funktionieren schon mal die PWMs. SPI funktioniert zwar immer noch 
nicht, aber das ist eine ganz andere Geschichte (c)

von M. K. (sylaina)


Lesenswert?

Viktor B. schrieb:
> Im Beispiel würde der
> Prescaler also auch keinen Einfluss auf den Timer nehmen.

Öhm, doch. Der Prescaler gibt immerhin die Geschwindigkeit vor, wie 
schnell der Timer zählt. Machst du den Prescaler größer sinkt die 
Taktrate und der Timer zählt langsamer, machst du den Prescaler kleiner 
steigt die Taktrate und der Timer zählt schneller.

Viktor B. schrieb:
> Ich habe es mit dem Oszi nachgemessen.

Du hast geschrieben, dass dein Oszi bei diesen Frequenzen nicht mehr mit 
macht. Von daher bin ich davon ausgegangen, dass du mit dem Oszi in 
diesem Fall nichts messen kannst.

Viktor B. schrieb:
> So, mit der Funktion aus der power.h klappt jetzt alles.

Du könntest auch versuchen
1
...
2
asm("ldi r24, 0x80" "\n\t"
3
    "out CLKPR"     "\n\t"
4
    "ldi r24, 0x20" "\n\t"
5
    "out CLKPR");
6
...

zu schreiben, das spart noch etwas Code bei dir (bei mir macht der 
AVR-GCC aus dem C-Code übrigens genau das Obige bei Optimierungslevel 
-Os). Du brauchst ja eigentlich die power.h nicht wirklich.

von Illitid (Gast)


Lesenswert?

Viktor B. schrieb:
> - D.h. ich hab es in C geschrieben und dem Compiler gegeben. Der hat
> dann den ASM-Code erzeugt. Wieso er mich boykottiert? Gute Frage

Vielleicht wirst du unterdrückt  :-)

von Peter D. (peda)


Lesenswert?

Viktor B. schrieb:
> Disassembly sieht so aus:

So sieht es aus, wenn man dem Compiler das Optimieren verbietet (-O0).
Mit -Os paßt es dann mit den 4 Zyklen.

Man sollte aber die Funktion aus der power.h bevorzugen.

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.