Da der Clock Prescaler mich die letzten zwei Tage beschäftigt hat, habe
ich mal meine Ergebnisse zusammengetragen. Alles was ich hier schreibe,
habe ich am attiny2313 ausprobiert, das Problem besteht aber auch bei
anderen AVR µCs.
Um den Faktor des clock prescalers einzustellen, muss man innerhalb von
4 Taktzyklen zweimal auf das Register CLKPR zugreifen: im ersten Zugriff
setzt man das höchste Bit (CLKPCE) auf 1 alle anderen auf 0, im zweiten
Zugriff stellt man den Teilungsfaktor ein. Mit avr-gcc code sieht das
etwa so aus:
1 | CLKPR = (1<<CLKPCE);
|
2 | CLKPR = 1;
|
Sofern man Code-Optimierung benutzt (gcc Schalter -Os), funktioniert das
auch wie erwartet, verwendet man keine Optimierung (-O0), funktioniert
es auf einmal nicht mehr. Der Faktor des clock prescalers wird einfach
nicht geändert. Den Grund dafür kann man im Assemblerlisting finden.
Mit Optimierung erhält man:
1 | ldi r24,lo8(-128) ; tmp44,
|
2 | out 70-32,r24 ; ,, tmp44
|
3 | ldi r24,lo8(1) ; tmp46,
|
4 | out 70-32,r24 ; ,, tmp46
|
Ohne Optimierung wird daraus:
1 | ldi r24,lo8(70) ; D.2172,
|
2 | ldi r25,hi8(70) ; D.2172,
|
3 | ldi r18,lo8(-128) ; tmp54,
|
4 | movw r30,r24 ; , D.2172
|
5 | st Z,r18 ; , tmp54
|
6 | ldi r24,lo8(70) ; D.2173,
|
7 | ldi r25,hi8(70) ; D.2173,
|
8 | ldi r18,lo8(1) ; tmp55,
|
9 | movw r30,r24 ; , D.2173
|
10 | st Z,r18 ; , tmp55
|
Man erkennt direkt, dass die Zugriffe auf das Register CLKPR (das sind
die Zeilen mit "st Z,r18") nicht mehr innerhalb von 4 Taktzyklen
erfolgt.
Sofern man nicht auf die Optimierung zurückgreifen möchte, lässt sich
das Problem umgehen, indem man den Zugriff per inline assembler
realisiert:
1 | asm volatile (
|
2 | "st Z,%1" "\n\t"
|
3 | "st Z,%2"
|
4 | : :
|
5 | "z" (&CLKPR),
|
6 | "r" ((uint8_t) (1<<CLKPCE)),
|
7 | "r" ((uint8_t) 1) // neuer Wert für CLKPR
|
8 | );
|