Forum: Mikrocontroller und Digitale Elektronik lpcxpreeso SystemFrequency LPC1768


von Karl K. (leluno)


Lesenswert?

mein Board zeigt über die Core Clock Variable SystemFrequency=72Mhz an. 
Wenn ich die tatsächliche Taktzahl mit i++ messe, ergeben sich nur 
3.789.608 Zyklen. Wer kann mir die Differenz zwischen System-CPU und 
tatsächlicher Taktzahl erklären? Wie kann man die tatsächliche Taktzahl 
anheben? Die Funktion zur Messung der Taktzahl beruht auf dem rtc 
interrupt und ist augenscheinlich in Ordnung:

void cpuspeed(void){
  uint64_t zlfcpu=0;
  zlrtc=0;
  led2_off;
  while(zlrtc<3);
  led2_on;
  while(zlrtc<13)zlfcpu++;
  led2_off;
  F_CPU=zlfcpu/10;
  lwi(15,5,"F_CPU:");
  lgi(16,5,F_CPU);
  lcd_write("xx");
  lgi(17,5,SystemFrequency);
  }

Ich bin für jeden Hinweis dankbar

von Karl K. (leluno)


Lesenswert?

Das Problem ist hier schon einmal behandelt worden. Wenn man die 
Schleife teilweise auflößt - 100 x i++;i++;... - steigt die angzeigte 
Taktrate auf etwas über 7,1 Mhz. Braucht der ARM wirklich 10 Takte um 
die Operation i++ auszuführen? Jedenfalls scheint ein Prozessortakt 
eines ARM deutlich uneffektiver zu sein, als eines RISC-AVRs.

von Syliosha (Gast)


Lesenswert?

Hier kommt es nun drauf an, mit welchen Compilereinstellungen du das 
Programm übersetzen lassen hast. Dazu können auch immernoch Interrupts 
wärend des Zählens auftreten, die dein Ergebnis verfälschen würden.

von Syliosha (Gast)


Lesenswert?

Davon ab vergisst du, dass du mit jedem Schleifendurchlauf eine Variable 
vergleichst, die wohl noch volatile ist, da sie vom Interrupt erhöht 
wird. Der Vergleich kostet Zeit. Dazu rechnest du mit 64bit Variabel auf 
einen 32bit Prozessor. Hier muss der Code auch immer einen Vergleich 
durchführen um die Inkrementierung zu überprüfen, ob es keinen Überlauf 
einer 32bit Variabel gibt. Dieser Code auf einem AVR ausgeführt, würde 
dir ebenso ein "merkwürdiges" Ergebnis präsentieren wie auf einem ARM.

von Ralf (Gast)


Lesenswert?

> Wenn ich die tatsächliche Taktzahl mit i++ messe,
Das wird auch nicht über eine Variable gemessen. Und auch nicht über das 
Toggeln einer LED in einer Funktion -> man weiss nie, was der Compiler 
draus macht und man weiss nie, was der Compiler nach dem nächsten Update 
desselben draus macht.
Die tatsächliche "Taktzahl" wird immer durch Hardware gemessen, alles 
andere ist ein Zufallsgenerator.

- Kann der LPC den Systemtakt durch Hardware (also einen Pin 
entsprechend konfigurieren, den Rest macht die Hardware automatisch) auf 
einem Pin ausgeben? Wenn ja, nimm das und messe die Frequenz
- Wenn nicht, dann gibt's Timer-, PWM- und weitere Ausgänge. Wenn du 
hierfür eine bestimmte Frequenz annimmst und diese als 
Berechnungsgrundlage für die Einstellungen der o.g. Peripherie 
verwendest sollte an den Pins dann auch eine entsprechende Frequenz 
rauskommen (bspw. 10ms für einen Timer-Ausgang oder eine PWM-Frequenz 
von 1kHz oder oder oder).

Ralf

von Karl K. (leluno)


Lesenswert?

Vielen dank für die Tipps. Durch Austausch von 64bit gegen 32 bit steigt 
die "Tatktzahl" auf rund 14Mhz. Das gibt ein Verhältnis von 1:6 zur 
systemFrequency. Es geht mir um das Verständnis des PLLO-Systems. ich 
möchte im Prinzip sehen, wie sich die Änderung der Systemfrequenz 
auswirkt.

Praktisches Problem ist, dass ich für die Übernahme von AVR-Funktionen 
-z.B. DS1820- eine delay-funktion brauche. Etwas vergleichbares zu 
_delay_ms habe ich nicht gefunden. Um eine brauchbare delay-Funktion 
hinzubekommen, ist es schon sinnvoll zu wissen, wievil takte der Vorgang 
i++ braucht. Wahrscheinlich gibt es für delay bereits eine 
standardisierte Lösung, ich habe sie aber nicht gefunden.

von Jim M. (turboj)


Lesenswert?

Wenn man nicht von Compiler-Einstellungen und -Versionen abhängig sein 
möchte, dann muss Assembler her.

Ich verwende folgendes als Delay, braucht ca. 3 Cycles pro Iteration:
1
# Assember for Delay
2
3
# ---------------------------------------------
4
# mode of asembly
5
# ---------------------------------------------
6
      .syntax unified
7
      .thumb
8
      .arch armv7m
9
10
# ---------------------------------------------
11
# allocation
12
# ---------------------------------------------
13
.text
14
15
# DelayLoop
16
17
.global DelayLoopAsm
18
   .thumb_func
19
DelayLoopAsm:
20
   CBZ  R0, DelayLoopAsmEnd
21
DelayLoopAsmLoop:
22
  SUBS  r0, 1
23
  BNE DelayLoopAsmLoop
24
DelayLoopAsmEnd:
25
  BX LR
26
27
28
.end

Für C sieht das so aus:
1
void DelayLoopAsm(unsigned int delay);

von Ralf (Gast)


Lesenswert?

> Praktisches Problem ist, dass ich für die Übernahme von AVR-Funktionen
> -z.B. DS1820- eine delay-funktion brauche.
Och nöööööö, bitte nicht. Tut's dafür nicht ein ordentlich 
implementierter Timer?

Ralf

von Karl K. (leluno)


Lesenswert?

Ziel ist eine delay-funktion, die wie beim AVR immer ohne weiteres 
Nachdenken problemlos funktioniert. Ob mit timer oder ohne timer ist mir 
egal. Man müsste dies aus systemFrequency ableiten können. So 
funktioniert es aber auch:

void cpuspeed(void){
  uint32_t i=0;
  zlrtc=0;
  led2_off;
  while(zlrtc<1);
  led2_on;
  while(zlrtc<3)i++;
  led2_off;
  F_CPU=i/1836;
}

_delay_ms(uint32_t ms){//genau auf 100sec
uint32_t i,j=F_CPU*ms;
  for(i=0;i<j;i++);
}

von Roland H. (batchman)


Lesenswert?

karl k. schrieb:
> Ziel ist eine delay-funktion, die wie beim AVR immer ohne weiteres
> Nachdenken problemlos funktioniert.

Die funktioniert dort auch nicht immer problemlos :-)

> So funktioniert es aber auch:

Nun ja. Bei der nächsten Compiler-Version, andere Optimierungsstufe, 
anderer Frequenz etc. auch noch? Die leere Schleife bietet sich zum 
Wegoptimieren jederzeit an. Schon mal mit -Os probiert?

Mach es einfach so, wie es Ralf vorgeschlagen hat. Das ist exakt. Früher 
oder später wirst Du Dich eh mit den Timern befassen, es sind genug da.

Anregungen finden sich hier: Beitrag "STM32 Systicktimer wert in ms"

von temp (Gast)


Lesenswert?

Da kann man aber auch gewaltig reinfallen. Bei den Cortexen ist es nicht 
wie beim AVR, dass jede Instruction sofort und immer gleich ausgeführt 
wird. Da der Flash in der Regel langsamer ist als die 120MHz die beim 
LPC1769 einstellbar sind, kann es passieren, dass da Wartezustände der 
CPU dazukommen, die nicht calkulierbar sind. Insbesondere das 
Allignment, also ob der Code der innersten Schleife auf Wortgrenzen 
liegt oder nicht kann da mal leicht 50% Unterschied erzeugen. Das hat 
mich schon mal zur Weißglut gebracht obwohl ich diese Funktionen auch 
nur sehr selten benutze. Ich stelle mal meine um das richtige Alignment 
erweiterte Funktion hier rein. Leider kenne ich die Ursprungsquelle 
nicht mehr.
1
void DelayuS(uint32_t uS)
2
{
3
  uint32_t CyclestoLoops;
4
5
  CyclestoLoops = SystemCoreClock;
6
  if (CyclestoLoops >= 2000000) 
7
    {
8
    CyclestoLoops /= 1000000;
9
    CyclestoLoops *= uS;
10
    } 
11
  else
12
    {
13
    CyclestoLoops *= uS;
14
    CyclestoLoops /= 1000000;
15
    }
16
17
  if  (CyclestoLoops <= 100)
18
    return;
19
  
20
  CyclestoLoops -= 100; // cycle count for entry/exit 100? should be measured
21
  CyclestoLoops /= 4; // cycle count per iteration- should be 4 on Cortex M0/M3
22
23
  if (!CyclestoLoops)
24
    return;
25
26
  // Delay loop for Cortex M3 thumb2
27
  asm volatile 
28
    (
29
    // Load loop count to register
30
    " mov r3, %[loops]\n"
31
    " .align 4\n"
32
    // loop start- subtract 1 from r3
33
    "loop: subs r3, #1\n"
34
    " nop \n"
35
    // test for zero, loop if not
36
    " bne loop\n\n"
37
38
    : // No output registers
39
    : [loops] "r" (CyclestoLoops) // Input registers
40
    : "r3" // clobbered registers
41
    );
42
}

Beim OneWire sollte aber eine komplett andere Technik zum Einsatz 
kommen.
Code dazu habe ich schon mal hier eingestellt:

Beitrag "DS18x20 unter FreeRTOS und LPCXpresso 1769"

von Karl K. (leluno)


Lesenswert?

Vielen Dank für die Hinweise

Der Link auf den systicktimer endet mit

Das ganze hat noch 2 Probleme:
- Ein Überlauf des Systick timers wird nicht beachtet, falls (mtime +
end) > 2^32 ist hört die Funktion sofort auf
- Die Funktion ist nur auf 1mS genau. Ein delay(2) kann zwischen 1ms und
2ms dauern.

Das hilft zumindest für _delay_us nicht wirklich weiter.



Die funktion mit
 CyclestoLoops -= 100; // cycle count for entry/exit 100? should be 
measured
hat - wenn ich das richtig verstanden habe - den Vorteil dass so eine 
Art Multitasking mit mehreren Anwendungen gleichzeitig unterstützt wird, 
muss aber anscheinend auch kalibriert werden.

Ich habe mein lpc-board seit weihnachten. Es war für mich äußerst 
Schwierig, überhaupt einen Zugang zu finden. (programmieren bootlader 
ging nicht, altera-clone ging nicht, erst die anschaffung des 
lpcxpresso-boards mit anschließendem Zersägen hat zum Zugang zum board 
geführt).

Ich bin also erst dabei, meine avr-amwendungen auf arm zu transferieren, 
d.h. das lpc-board läuft (noch) nicht im multitasking.

Aufgabenstellung für das delay ist damit nur die kalibrierte Verzögerung 
im us und ms- Bereich. das Board hält während des delays an und macht 
nichts anderes. Interrupts, die zu berücksichtigen wären gibt es nicht.

Die kalibrierung über die rtc ist dann wohl naheliegend, da die 
rtc-clock nach meinem verständnis die einzige taktquelle von 
gleichbleibender berechenbarer größe ist.

Die von mir vorgestellte Funktion ist offensichtlich geeignet, die 
Messung einer Anzahl von Takten während einer Secunde durchzuführen. Es 
wird aus dem Code auch nichts wegoptimiert. Das Blinken der Led und die 
Anzeige auf dem Lcd zeigen ausreichend an, dass es funktioniert.

Zumindest beim Single Task ist eine einfache Schleife auch ausreichend 
geeignet, eine Zeitmessung vorzunehmen. Es geht nur um die Kalibrierung 
der  Anzahl der Schleifendurchläufe.

Interessant ist das 1wire-projekt. gibt es dazu noch neuere versionen? 
Ich habe eine Heizungssteuerung mit ca. 15 Sensoren, die ich von avr auf 
arm umstellen möchte.

was mir noch vorschwebt, ist ein mp3 player mit einem vs1053. was ich 
bislang habe, ist eine startdatei für gpio, rtc und lcd. Nächste 
Schritte sind USB-Stick und sd-karte, dann rj45.

von Roland H. (batchman)


Lesenswert?

karl k. schrieb:
> - Ein Überlauf des Systick timers wird nicht beachtet, falls (mtime +
> end) > 2^32 ist hört die Funktion sofort auf

Sorry, aber an dem zusätzlichen Vergleich wird es hoffentlich nicht 
scheitern. Du kannst auch einfach den Zähler vorher auf Null setzen.

> - Die Funktion ist nur auf 1mS genau. Ein delay(2) kann zwischen 1ms und
> 2ms dauern.
>
> Das hilft zumindest für _delay_us nicht wirklich weiter.

Doch, das geht. Absolut zuverlässig in Produktion.

karl k. schrieb:
> Das Blinken der Led und die
> Anzeige auf dem Lcd zeigen ausreichend an, dass es funktioniert.

Das ist Zufall. Es ist so, wie es temp sehr schön dargestellt hat: Dort 
findest Du das "alignment" und ein (hoffentliche gezieltes) "nop".

Und ich bin mir zu 99% sicher, dass die "static inline" Timer Variante 
feiner auflösen kann, weil sie weniger Instruktionen benötigt. Denn dort 
wird im Prinzip das gleiche wie in temp's ASM-Variante gemacht, nur halt 
mit dem Timer.

von leluno (Gast)


Lesenswert?

Vielen Dank für die Hinweise. Ich werde bei Gelegenheit die 
Delay-Funktion auf einen eigenen timer umstellen, der dann über die rtc 
kalibriert wird.

von Roland H. (batchman)


Lesenswert?

leluno schrieb:
> der dann über die rtc kalibriert wird.

Du kommst von einem Extrem ins andere :-) Zuerst eine ziemlich 
unsichere, und jetzt ultra-korrekt :-)

Also wenn vorher die Variante mit "ohne Timer" funktioniert hat, dann 
wird die "mit Timer" auch ohne Kalibrierung funktionieren.

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.