Forum: Mikrocontroller und Digitale Elektronik Schnelles Pin-Toogle auf arm7 / mbed (Cortex-M3) - wie?


von Norbert S. (prote)


Lesenswert?

Hallo,

ich versuche jetzt schon eine ganze Weile erfolglos, meinen mbed zu 
schnellen Pin-Toggles zu überreden. Über 900kHz bin ich bisher 
allerdings nicht gekommen und das reicht vorn und hinten nicht für 
größere Datenübertragungen. Mein Hauptproblem ist, dass ich wegen der 
Verbrauchsrandbedingungen den ARM nur mit 12 MHz betreiben kann. Aber 
auch damit sollten doch wenigstens ein paar MHz machbar sein.

Als Bibliothekssammlung nutze ich Tollos mit mbed, kompiliert wird also 
mit arm-none-eabi-gcc. In diesem Mini-RTOS arbeite ich derzeit 
ausschließlich in der init-Routine, habe also noch die "volle" 
Rechenleistung zur Verfügung. Da die vom RTOS zur Verfügung gestellten 
Routinen Dank OOP-Overhead zu langsam sind (PinToggle bei ca. 30 kHz), 
muss ich direkt an die Register.

Hier die relevanten Codefragmente:
1
int init(void) {
2
...
3
 uint32_t p0=(1<<23)|(1<<24)|(1<<25)|(1<<26);        // Port0 Pin23-26 sind mbed p15-18; Port1 Pin 18/20/21/23 sind LEDs
4
 uint32_t* GPIO0_DIR = (uint32_t)0x2009C000;
5
 uint32_t* GPIO0_MASK= (uint32_t)0x2009C010;
6
 uint32_t* GPIO0_PIN = (uint32_t)0x2009C014;
7
 uint32_t* GPIO0_SET = (uint32_t)0x2009C018;
8
 uint32_t* GPIO0_CLR = (uint32_t)0x2009C01C;
9
 *GPIO0_DIR=p0;
10
 *GPIO0_MASK=!p0;
11
 while(1){
12
  *GPIO0_PIN=p0;
13
  sysPause(1);
14
  *GPIO0_PIN=!p0;
15
  sysPause(1);
16
 }
17
}
So funktioniert das Togglen einwandfrei, Dank der zwei sysPause-Aufrufe 
mit nur 500 Hz, erleichtert aber den 1. Funktionstest via Oszi ;) So und 
jetzt das eigentliche Problem: Wenn man die sysPause herausnimmt, kommt 
das Löschen schneller als das Setzen und die Pins bleiben gelöscht. Beim 
Herumprobieren habe ich mittels sinnloser for-Schleifen und anderer 
Verzögerungen das Tooglen zwar auch hinbekommen, aber halt mit unter 
1/12 der Taktfrequenz. Auch die SET-/CLR-Register arbeiten nicht 
schneller als das PIN-Reg. In anderen Threads hatte ich gelesen, dass es 
einige Leute schon geschafft haben, auf ca. 1/5 und mehr zu kommen - nur 
wie?

Gibt es evtl. einen Befehl, der dem ARM sagt, dass er seine 
Port-Register sofort auswerten soll oder eine Möglichkeit, diese 
Auswertungsgeschwindigkeit hochzustellen? Im User-Manual habe ich bisher 
nichts gefunden, also zumindest rund um die Port-Reg-Definitionen steht 
nichts zu dem Thema.

von Albert .. (albert-k)


Lesenswert?

Ich gehe mal davon aus das du das mbed mit dem LPC1768 verwendest? Wenn 
nicht sag mal welcher Controller.

Nur zur Feststellung was du bisher machst und ich dich auch richtig 
verstanden habe:
- Der Scheduler (zuständig für Context Switches zwischen Tasks) des RTOS 
ist noch nicht losgelaufen?
- Du befindest dich noch ganz am Anfang? hast du schon den Controller 
initialisiert (Clock Source, PLL, BUS Clock, etc.)
- Hast du auch nachgeprüft ob du wirklich die 12MHz external Clock 
verwendest? Und nicht vl. versehentlich nur den 4MHz internal 
Oszillator? Du musst im CLKSRCSEL Register den Wert für CLKSRC 0x01 
reinschreiben, bei 0x00 verwendest du für die PLL den internen 
Oszillator.

Masked Access verwendest du ja bereits, daran kann es also nicht liegen. 
Dein Problem das der Portpin immer auf Null bleibt kann ich derzeit 
nicht nachvollziehen. Von den Zugriffen her stimmt es schon. hast du 
eine Möglichkeit dir den Assembler Code dieses Abschnittes anzusehen?

von Norbert S. (prote)


Lesenswert?

Danke schon mal für die schnelle Antwort! Ja das ist der LPC1768 und ich 
habe diese Toggle-Versuche in der init-Routine drin, der Scheduler läuft 
nicht. Die Initialisierung habe ich komplett Tollos überlassen (erfolgt 
noch vor dem Aufruf der init()) und nur den Takt (sowie die 
USB-Baudrate) geändert:
...\tollos\lpc17xx\lpc1768.ld
chipMaxClock  = 12000000;

Die 12MHz werden auch von sysQuery(SYS_CLOCK) zurückgegeben.

Kann da das Problem liegen, muss ich wirklich manuell über CLKSRCSEL 
rangehen? Ich hatte auch schon mal geguckt, ob ich irgendwo einen Pin 
mit CLK-Frequenz finde, habe ich allerdings nicht gefunden.

Das die PINs beim direkten Wechsel auf 0 bleiben, kann man im UserManual 
im Punkt "9.6.2 Writing to FIOSET/FIOCLR vs. FIOPIN" (S. 140) zwischen 
den Zeilen lesen, zumindest interpretiere ich das so, dass man 
gleichzeitig Bits setzen und löschen kann und das geht ja nur, wenn die 
Verarbeitung langsamer ist als das Setzen. Andererseits wird dort FIOPIN 
als schnellere Alternative zu FIOSET/FIOCLR beschrieben und ich sehe 
geschwindigkeitsmäßig keine Unterschiede, nur die binären Filter sind 
anders. Oder versteckt sich dort irgendwo noch ein Trick?

Wenn Du mir verrätst, wie ich an den Asm-Code rankomme, kann ich diesen 
gerne posten. Außer der .bin habe ich nur noch eine .out im Angebot.

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

Norbert Schröder schrieb:
> Wenn man die sysPause herausnimmt, kommt das Löschen schneller als
> das Setzen und die Pins bleiben gelöscht.

Vermutlich kommt das Setzen gar nicht, weil die Register nicht als 
volatile deklariert wurden, und der Compiler den ersten Zugriff in der 
Schleife für überflüssig hält.

Gruß
Marcus

von Norbert S. (prote)


Lesenswert?

Oha - tausend Dank!
Ergebnis der Sache:
 - 2 MHz auf den Pins, also 1/6 des Clocks mit der while-Schleife und 
FIOPIN
 - knappe 6 MHz ohne die while-Schleife mit FIOSET/FIOCLR - super :)

Scheinbar hat der Compiler das Togglen wirklich wegoptimiert. Dabei ist 
mir vor allem schleierhaft, weshalb auch die testweise eingebauten 
Verzögerungsschleifen (selbst mit nop-Aufrufen) ebenfalls wegoptimiert 
wurden.

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.