Forum: Mikrocontroller und Digitale Elektronik ARM7: Start geglückt aber noch Fragen


von Christian J. (elektroniker1968)


Lesenswert?

Hallo,

ist schon ein klasse Gefühl: Das Olimex Board mit dem Wiggler liegt vor 
einem und zwei LEDs blinken abwechselnd :-))))) Hast zwar fast 2h 
gedauert, bis ich so langsam dahinter gestiegen bin aber es läuft erst 
einmal.

Aber noch eine Frage:

Man kann mit den debug_prinft und debug_scanf Befehlen aus der 
Crossworks Lib eine virtuelle Console auf dem PC bedienen. Dh. printf 
gibt Ergebnisse auf dieser Console aus oder speichert sie in einem File 
auf der Platte, bzw liest scanf auch Zahlen ein. Habe mal ganz simpel 
eine Fliesskommaaddition mit Tasteneingabe gemacht. Toll, der ARM7 
rechnet richtig :-)

Die Frage ist aber nun, was passiert mit diesen debug befehlen, wenn ich 
den Code mit "ARM Relaese" compiliere und standalone laufen lasse. Dann 
hat das Board ja keine virtuelle Console mehr über den PC. Muss ich 
diese befehle dann ausklammern oder mit define Anweisungen nur beim 
Debuggen einbinden?

Zweite Frage: Hat jemand auf die Schnell Code um die Echtzeituhr mit der 
Zeit und dem Datum zu füttern? Oder spielt man nur die Werte in die 
register und das Ding läuft los? Ich finde so recht keine Steuerbits im 
ARM7 Manual.

PS: Ist schon ein klasse Teil! Besser als die 8 Bitter, endlich richtig 
mit vollem C Umfang arbeiten können jubel

Gruss,
Christian

von let (Gast)


Lesenswert?

Na dann herzlichen Glückwunsch. Wieder jemand der es geschafft
einen achso komplizierten ARM7 mit seinem nahezu unbeherrschbaren
Interrupthandling in Betrieb zu nehmen ;)

Wenn du uns jetzt auch noch mitteilst um welchen Controller es
sich handelt, könnte deine zweite Frage vielleicht beantwortet
werden. Zur ersten kann ich nichts sagen, da ich kein Crossworks
benutze. Aber ich habe hier irgendwo Code für die RTC des LPC2000.

von Peter D. (peda)


Lesenswert?

Christian J. wrote:
> PS: Ist schon ein klasse Teil! Besser als die 8 Bitter, endlich richtig
> mit vollem C Umfang arbeiten können *jubel*

???

In welchem Punkt entspricht der AVR-GCC bitte nicht dem C-Standard in 
"vollem Umfang"?

Wenn Du damit meinst, daß man nun für jeden Pups erstmal 2kB RAM 
alloziiert (man hats ja), dann ist das auch aufm ARM immer noch 
schlechter C Programmierstil.
D.h. irgendwann fällt man damit auf die Nase, nur eben etwas später.


Peter

von Peter D. (peda)


Lesenswert?

let wrote:
> Na dann herzlichen Glückwunsch. Wieder jemand der es geschafft
> einen achso komplizierten ARM7 mit seinem nahezu unbeherrschbaren
> Interrupthandling in Betrieb zu nehmen ;)

Von Interrupts hat er doch garnichts geschrieben.
Die Fallgruben komme auch dann erst, wenn man Interrupts temporär 
disablen muß, mehrere Interrupts und Prioritäten verwendet (und keinen 
Cortex M3 verwendet).


Peter

von Christian J. (elektroniker1968)


Lesenswert?

Soweit sind wir noch lange nicht. Jedenfalls rennt der Bursche.
Ist ein LPC2138, 512kb Flash und 32kb RAM. Stimmt,man hats ja :-) Wenn 
man aus dem Hause PIC kommt schon 768 Bytes schon viel. Allerdings zieht 
sich der Compiler schonmal 2kb für sich selbst rein aber das kann man 
sicher noch optimieren.

Ich habe gerade mal die PLL in Betrieb genommen und festgestellt, dass 
sie auch extrem übertaktet läuft, d.h. auf 100 Mhz kriegt man ihn schon, 
nur wird er da mollig warm. Bin mir nicht sicher wie lange die Chips das 
Tuning aushalten. Erstmal wieder runter auf 60 Mhz.

Auch die IRQ werden beherrschbar sein...... irgendwann mal.

von let (Gast)


Angehängte Dateien:

Lesenswert?

Die Beispielprogramme dimensionieren den Stack üblicherweise
recht großzügig. Muß in vielen Fällen nicht sein.

Lass dich wegen der IRQs nicht verrückt machen. Ein berüchtigtes
Problem mit der Kombination ARM7/VIC (PL190) ist der 'spurious
interrupt'. Der Name ist schwieriger als das Problem selbst.
Der PL192 wie er im LPC23xx/24xx eingesetzt wird kennt so etwas
nicht.
Ich bin davon ausgegangen das deine LEDs über Timerinterrupts
gesteuert werden. So machen es die 'Blinky' Beispiele meist.

Den RTC Code habe ich angehängt. Ist nichts besonderes aber
es tut.

von Peter D. (peda)


Lesenswert?

Christian J. wrote:
> Wenn
> man aus dem Hause PIC kommt schon 768 Bytes schon viel.

Bei PIC kann ich nicht mitreden.
Wenn man vorher schon Z80 und 8051 kannte, dann kommt man mit den PICs 
einfach nicht mehr klar.
Ich hatte mal das MPLAB installiert und das Datenblatt eines PIC12 
runtergeladen, dann etwas rumprobiert und dann alles kopfschüttelnd 
wieder beiseite gelegt.


Die AVRs haben ja gut zugelegt (8kB Flash/512Byte SRAM bei den 
8-Pinnern, 256kB Flash/8kB SRAM bei den 64-Pinnern.
Das finde ich schon sehr bequem, um "mit vollem C Umfang" zu 
programmieren.

Die 8- und 14-Pinner setze ich fast so oft ein, wie früher Logik-ICs (1 
AVR statt 2..20 TTLs).


Peter

von Christian J. (elektroniker1968)


Lesenswert?

Hallo Peter,

gut, ich habe seit 1996 nur mit PIC zu tun gehabt und die Dinger sind 
sehr einfach zu handhaben. Dafür aber nie einen Draht zu AVR gehabt. 
PICs programmiere ich heute in kurzer Zeit, wenn sie einfache Aufgaben 
machen sollen, einstecken und laufen lassen. Und mit den 18er kann man 
schon sehr komplexe Aufgaben bewältigen, zudem die Compiler einen HAL 
haben. I2C ist sonst ein Horror, so reduziert es sich auf wenige Befehle 
und die funktionieren auch. Schön auch die komfortable Gangschaltung von 
31 khz bis rauf nach 40 Mhz. Dennoch sind es eben 8 Bitter und bei Int32 
rödeln die sich schon einen ab. Ich werde sie auch weiterhin als kleine 
Helfer einsetzen, die Viecher mit 14 Pins sind teilweise randvoll mit 
Peripherie gepackt, alles was man sich wünschen kann. Im Beruf sind sie 
sehr oft in Sicherheitsschaltungen vorhanden.

Werde jetzt gleich einmal die RTC vom ARM ausprobieren, danke für den 
Code auch! Und an diese Konstrukte wie (1<<5) werde ich mich wohl 
gewöhnen müssen, denn bei PICs sind alle Steuerregister Bitadressierbar, 
d.h. man gibt den Steuerbits Namen und besetzt sie mit Boolschen Werten, 
erhält dadurch sehr übersichtlichen Code.

Womit ich beim ARM allerdings nicht klarkomme sind die Ports. Wer hat 
sich das bloss mit dem IOSET und IOCLR ausgedacht? Wie soll man denn da 
Datenwörter einspielen, die gleichzeitig anliegen müssen? Ich vermisse 
mein PORTA = 0x46.

Gruss,
Christian

von Andreas K. (a-k)


Lesenswert?

Christian J. wrote:

> gewöhnen müssen, denn bei PICs sind alle Steuerregister Bitadressierbar,

Das hat aber nicht nur mit den Prozessoren zu tun, sondern ist 
hauptsächlich eine Eigenschaft der Include-Files für den Compiler. Die 
gleiche Technik, Bitfelder in der Definition der I/O-Register zu 
verwenden, würde auch bei anderen Architekturen funktionieren. Nicht 
immer so effizient, aber auf Hochsprachenebene sähe es gleich aus.

> Womit ich beim ARM allerdings nicht klarkomme sind die Ports. Wer hat
> sich das bloss mit dem IOSET und IOCLR ausgedacht?

Derjenige, der merkte, dass konventionelle I/O-Register wie bei PIC oder 
AVR in einer Architektur ohne passende Bitset/clear-Befehle nicht nur 
umständlich sind, sondern in Zusammenhang mit Interrupts auch noch 
äusserst fehlerträchtig (1). In der ersten Generation der LPC2000 ist 
allerdings vergessen worden, das simple Schreibregister ebenfalls zu mit 
zu implementieren. Wurde später nachgeholt, beispielsweise im 
LPC2138/01.

Andere ARM Implementierungen verwenden andere Techniken für die 
I/O-Register. So ist die entsprechende ARM Primecell zu dieser Funktion 
(verwendet beispielsweise in STR9 und LM3) in dieser Hinsicht eleganter 
gebaut - dafür aber in an anderer Stelle problematisch. Ziemlich sauber 
ist eine ähnliche Technik im Thumb2 Core definiert (LM3, STM32), aber 
das ist immerhin auch die erste ARM Architektur speziell für Controller.

(1): 
http://www.mikrocontroller.net/articles/Interrupt#Interruptfeste_Programmierung

von let (Gast)


Lesenswert?

^ Ist IOnPIN bei der ersten LPC Generation read-only? Ich habe hier
nur den 2136/01 bzw. 2148 und 2368.

von Andreas K. (a-k)


Lesenswert?

let wrote:

> ^ Ist IOnPIN bei der ersten LPC Generation read-only? Ich habe hier
> nur den 2136/01 bzw. 2148 und 2368.

Bei den LPC2106 und LPC2129 nebst Verwandten ja.

Dem aktuellen Manual vom LPC2138 zufolge gibt es das schreibbare IOPIN 
auch schon, aber da gibt es auch den merkwürdigen Satz "Bit-level set 
and clear registers allow a single instruction set or clear of any 
number of bits in one port (LPC213x/01 only)", der suggeriert, dass 
IOSET/IOCLR erst mit /01 existieren würden. Was ja nun falsch ist.

von Christian J. (elektroniker1968)


Lesenswert?

Andreas,

danke für Deine Ausführungen. Der Wecker tickt jetzt bei mir, noch 
richtig, es ist ein externer Uhrenquarz dran.

Aber kann man diese Konstrukte

   CCR = CCR & ~(1<<0); // Uhr stoppen (Bit 0)
   CCR = CCR | (1<<CCR_CLKEN_BIT); // Uhr wieder starten

"Schreibe in das CCR den Wert, der sich aus dem CCR UND verknüpft mit 
dem invertierten Wert einer um 0 Positionen verschobenen 1 ergibt"
nicht vereinfachen? Ich kriege da jedesmal einen Knoten im Hirn :-((

Geht das nicht wie beim PIC, so:

#bit CLKEN = CCR.0 // Deklaration
CCR.CLKEN = 1;     // setzen

und man freut sich? Beim PIC sind die wirklich hardwaremässig 
bitadressierbar. Der Compiler übernimmt nur die Umrechung der Adresse 
und Position in eine Bitnummer, denn die ist fortlaufend über die 
Register und kann somit sehr gross werden.

Das Headerfile bei Crossworks ist auch so lieblos dahingeschmiert, alles 
untereinander geklatscht, keine Gruppen zusammengefasst, keine 
Kommentare. Etwas mehr Mühe hätten die sich ja schon geben können 
<grummel>

von Andreas K. (a-k)


Lesenswert?

Christian J. wrote:

> und man freut sich? Beim PIC sind die wirklich hardwaremässig
> bitadressierbar.

Nein. Die so ziemlich einzige Architektur mit echter Bitadressierung war 
TI990 (70er). Alles andere adressiert Bytes (oder auch schon mal Worte). 
Und setzt Bits indem gelesen, modifiziert und wieder zurückgeschrieben 
wird.

Auch die PICs machen das nicht anders. Nur haben sie Befehle, die das in 
einem Rutsch machen, während ARM dafür 3 braucht.

Aber niemand hindert dich daran, auf ARMs die I/O-Register als
1
extern struct {
2
   unsigned bit0:1, bit1:1, bit2:1, ...;
3
} ccr;
 zu definieren. Dann geht auch da
1
ccr.bit1 = 1;

von Andreas K. (a-k)


Lesenswert?

Christian J. wrote:

> #bit CLKEN = CCR.0 // Deklaration

Wobei das kein C ist, sondern einer mehr oder weniger kranken Phantasie 
eines Compilerbauers entspringt. Und zu genau dem Effekt führt, den du 
hier beklagst: Du klebst an einem ganz bestimmten Compiler.

Was dir oben einen Knoten ins Hirn macht, ist das, was alle machen die 
weder proprietäre Compilererweiterungen noch Bitfelder mögen. Und hat 
den Vorteil, dass es überall genauso funktioniert.

von Christian J. (elektroniker1968)


Lesenswert?

Danke für den Tip!

Was bedeutet "extern" in dem Falle?

Beim CCS Compiler ist es wirklich so, dass der Code nicht portabel ist, 
vor allem wegen des HAL. Aber wer damit zufrieden ist lebt damit sehr 
gut. zudem das portiren auf einen anderen Prozessor sowieso schwierig 
ist, wenn man Peripherie dran hat. Oder man übergibt die 
Hardwarefunktion als Zeiger an die High-level Ebene zB bei der SD Karten 
Verwaltung wurde das so gemacht, dass die Bitschieberei für jeden 
Prozessor selbst geschrieben werden muss, nur das Management ist 
allgemeingültig.

von Andreas K. (a-k)


Lesenswert?

Christian J. wrote:

> Was bedeutet "extern" in dem Falle?

Sorry, aber das C Tutorial findet grad woanders statt.

PS: Hauptsächlich Faulheit meinerseits. Hatte keine Lust, hier die 
übliche #define CCR (*(ccr_t *)0xAFFETOOT) Orgie konsequent 
durchzuspielen.

von Christian J. (elektroniker1968)


Lesenswert?

Ok, lassen wir das..... noch ein Döschen "Faxe" aufmachen und dann in 
die heia, für heute reicht es, morgen wieder an echte "Hardware" , siehe 
http://www.der-scirocco.de/viewtopic.php?t=66

von Christian J. (elektroniker1968)


Lesenswert?

Hallo,

ich habe mich jetzt im GCC Manual und mit Google totgesucht aber leider 
immer noch keine Lösung gefunden, wie man einen Struct auf eine feste 
Adresse im Speicher abbildet.
1
struct mystruct {
2
3
    short CLKEN  :1;
4
    short CTCRTST: 1;
5
    short CTTEST : 2;
6
    short CLKSRC: 1;
7
    short rest: 3
8
};

Den will ich auf die Adresse

CCR -> (*(volatile unsigned long *)0xE0024008)

abbilden, so dass ich einzelne Bits mit CCR.CLKEN = 1 oder 0 setzen 
kann. Nicht ganz klar ist mir auch, ob "short" da oben richtig ist.

Gruss,
Christian

von let (Gast)


Lesenswert?

So müßte es gehen:
1
struct ccr_s {
2
   unsigned int CLKEN   : 1;
3
   unsigned int CTCRTST : 1;
4
   unsigned int CTTEST  : 2;
5
   unsigned int CLKSRC  : 1;
6
} __attribute__((packed));
7
8
#define REG_CCR (*((volatile struct ccr_s*)&CCR))
9
10
int main()
11
{
12
   REG_CCR.CLKEN = 1;
13
}

Von der Performance her ist so etwas aber u. U. langsamer da solche
Zugriffe immer auf ein Read/Modify/Write hinauslaufen.
Das __attribute__((packed)) teilt dem Compiler mit das er bei
den Element der Struktur kein Word-Alignment zur Optimierung
durchführen, sondern alle Elemente Bit für Bit hintereinander
packen soll.

von Peter D. (peda)


Lesenswert?

let wrote:

> Von der Performance her ist so etwas aber u. U. langsamer da solche
> Zugriffe immer auf ein Read/Modify/Write hinauslaufen.

Muß nicht unbedingt sein.
Z.B. beim AVR wird das vom GCC korrekt in SBI/CBI umgesetzt.
Ich mag es auch lieber, wenn die Bits als Variablen definiert sind, ist 
besser lesbar und weniger fehlerträchtig.


Außerdem hat man ja gerade deshalb nen ARM genommen, weil man massiv 
32Bit-Berechnungen machen will und nicht viel Bitschubserei.

Bitschubsen können die 8Bitter nunmal besser, da muß man keinen ARM 
vergewaltigen.
Nur weil man sich mit dem ARM beschäftigt, ist es ja nicht plötzlich 
verboten, weiterhin 8Bitter einzusetzen.


Peter

von let (Gast)


Lesenswert?

>Muß nicht unbedingt sein.
>Z.B. beim AVR wird das vom GCC korrekt in SBI/CBI umgesetzt.
Stümmt. Aber der ARM7 von unserem Schrauber kann das nicht.

>Bitschubsen können die 8Bitter nunmal besser, da muß man keinen ARM
>vergewaltigen.
Du meinst "keinen ARM7". Der M3 dürfte die 8Bitter wieder übertreffen.
Der hat neben SBI/CLI auch Befehle für "Bit-Extraction" um auf
Bit-Bereiche (z. B. [2:5]) zuzugreifen. Aber auch der ARM7 kann mit
seinem Barrel-Shifter oft zaubern.

>ist es ja nicht plötzlich verboten, weiterhin 8Bitter einzusetzen.
Doch, doch. Sobald man sein erstes 32Bit .hex-File erstellt hat.
Und desweiteren darf man dann nicht mehr schlecht über ARM sprechen.
Bei Mac-Usern ist das ähnlich.

von let (Gast)


Lesenswert?

PS: Ist "Schrauber" politisch korrekt? Für mich ist das einfach
ein Wort wie "Bastler", kenne mich im KFZ-Bereich aber nicht aus.

von Christian J. (elektroniker1968)


Lesenswert?

Sicher ist "Schrauber" korrekt :-) Bei so einem Sesselpfurzer Job wie 
meinem die Erholung schlechthin.

Um die Bitschubserei kommt man leider bei den Steuerregistern nicht 
drumherum und sowas wie ein "Flag" möchte man ja auch haben. Aber dafür 
spendiert man beim ARM wahrscheinlich auch ein ganzes Byte.

Erstmal Danke für die Lösung! Werde das mal heute nacht ausprobieren. 
Insgesamt frage ich mich wer die vielen hundert Funktionen und 
Spezialausdrücke des GCC eigentlich braucht. Sachen wie "typeof" oder 
eine "union" habe ich noch nie im Leben gebraucht und diese 
_attribute_ Sache muss ich mir mal in Ruhe anschauen.

Gruss,
Christian

von Christian J. (elektroniker1968)


Lesenswert?

Nachtrag an "let":

Der Code funktioniert!

Nur frage ich mich grad, ob man den Ausdruck

#define REG_CCR (*((volatile struct ccr_s*)&CCR))

einfach hinnehmen soll wie er ist oder ob man den auch logisch verstehen 
kann, so dass man selbst drauf kommt? Das Wörtchen "volatile" ist mir 
noch heute suspekt, nach fast 20 Jahre mit C musste ich es noch nie 
verwenden.

& ist "Adresse von..."
<variable>* ist "Zeigertyp auf...."
*<variable> ist "Inhalt von Adresse..."

Wäre ich doch bloss bei Pascal geblieben :-(

PS: Die Performance spielt keine Rolle, der GCC ist ohnehin ein 
Bausteincompiler, der einiges mehr an Code erzeugt als es die reine 
Assemblerlösung brauchen würde. Das wird aber durch die Geschwindigkeit
der ARM wieder wett gemacht.


Gruss,
Christian

von Peter D. (peda)


Lesenswert?

Christian J. wrote:
> Das Wörtchen "volatile" ist mir
> noch heute suspekt, nach fast 20 Jahre mit C musste ich es noch nie
> verwenden.

Beim GCC mußt Du Dich dran gewöhnen und mußt es auch verwenden.

Der GCC ist da knallhart, wenn Du 2 Zuweisungen auf ne Variable machst, 
dann wird die 1. gnadenlos wegoptimiert.
Z.B. wenn Du nen Portpin auf 0 und dann auf 1 setzt, um nen Taktimpuls 
auszugeben.

Andere Compiler sagen sich, wenn der Autor 2 Zuweisungen hinschreibt, 
dann wird er sich ja was dabei gedacht haben und lassen sie stehen.
Oder wenn ne Abfrage innerhalb einer Schleife erfolgt.

Den GCC muß man aber erst dazu zwingen, indem man die Variable als 
volatile definiert.

Leider ist volatile kein Datentyp, zu dem man einfach casten kann.
Daher ist noch der zusätzliche Umweg über einen void Pointer nötig.


Peter

von Christian J. (elektroniker1968)


Lesenswert?

Peter,

kurze Frage: Was bewirkt volatile direkt?

Wenn a=5 und danach a=7 ist a=5 überflüssig, leuchtet ein.

In der GCC Beschreibung kommt das auch nicht eindeutig hervor.

Überdies habe ich mir mal meinen Kerninghan & Ritchie von 1989 mal 
wieder hervorgekramt, Zeiger pauken. Ist doch schon ein Unterschied 
vorhanden, wenn man sowas vorher nie gebraucht hat.

gruss,
Christian

von let (Gast)


Lesenswert?

Etwas einfacher geht es vielleicht.
1
#define CCR *((volatile unsigned long *)0xE0024008)
Wurde so definiert damit man Zuweisungen an CCR machen kann.

Für die Struktur ist nur die Adresse interessant:
1
#define REG_CCR ((volatile struct ccr_s*)0xE0024008)
Damit kann man dann über die üblich Dereferenzierung bei
Zeigern auf Strukuren arbeiten:
1
int main()
2
{
3
   REG_CCR->CLKEN = 1;
4
}

'volatile' verbietet dem Compiler, wie Peter schon geschrieben hat,
Optimierungen beim Umgang mit solchen Variablen vorzunehmen.
Bei
1
PORTB.2 = 1;
2
PORTB.2 = 0;
zum Togglen der 'E' Leitung bei einem LCD würde die erste
Zuweisung wegoptimiert werden.

Ich hoffe das du bei deiner Einschätzung zu Assembler vs. C nicht
den CCS Compiler als Grundlage nimmst. Der GCC steht zwei oder drei
Stufen über dem PIC-C. Was bei den C-Compilern für die 8Bitter
noch hinzukommt ist ihr notorischer Drang zu 16Bit Berechnungen,
auch wenn 8Bit genügen würden.
Im Internet gibt es diverse Vorlesungsskripte zur Sprache C. Kannst
ja mal bei Google dein Glück versuchen. Der PIC-C ist mehr oder
weniger nur an C angelehnt.

von Andreas K. (a-k)


Lesenswert?

Christian J. wrote:

> kurze Frage: Was bewirkt volatile direkt?

Man sagt dem Compiler damit, dass es in Bezug auf diese Variable Dinge 
zwischen Himmel und Erde gibt, die sich seiner Kenntnis entziehen.

Hat zur Folge, dass jeder Variablenzugriff auf Quelltextebene auch exakt 
einem Zugriff im erzeugten Code an entsprechender Stelle entspricht, 
lesend wie schreibend.

> In der GCC Beschreibung kommt das auch nicht eindeutig hervor.

Muss auch nicht, da die GCC-Doku Kenntnis des C Standards voraussetzt.

von Christian J. (elektroniker1968)


Lesenswert?

Hallo Let,

danke für die Antwort, werde das heute abend mal umsetzen für diverse 
Hardware.

Ach ja... da ich schon mal einen Fachmann hier habe :-)

Wie schafft man es unter Benutzung der IOSET und IOCLR Register eine 
gescheite parallele Datenübertragung zu zu bekommnen, zB für 4 Bit 
Displays?

Früher war das
1
PORTA = PORTA | (wert & 0b00001111);

Aber mit den SET und CLR schauts doch irgendwie anders aus. Muss man da 
mit den Bits jonglieren, zB eine Maske drüberlaufen lassen und je 
nachdem ob 1 oder 0 dann IOSET oder IOCLR aufrufen?

Gruss,
Christian

von Andreas K. (a-k)


Lesenswert?

1
IOCLRx = 0x0F;
2
IOSETx = wert & 0x0F;
was im Unterschied zu der AVR-Variante interrupt-fest ist.

von Peter D. (peda)


Lesenswert?

Hier mal was zum Ablachen, bin heute wieder voll in die volatile-Falle 
reingetappt:

Mit dem alten WINAVR liefs prima, mit dem von 2007 hat mir die 
Software-UART plötzlich nur noch das letzte Byte ausgegeben, alle 
anderen als 0x00.

Der Grund war, daß ich das Datenbyte nicht als volatile hatte, stört die 
putchar-Routine ja auch nicht.

Bloß hat der neue GCC in seinem Inline-Wahn die mit ins puts reingezogen 
und dann eben nur die letzte Zuweisung gemacht.

Man hatte ich ne Wut im Bauch.

Man muß sich bei jeder neuen GCC-Version immer auf derartige 
Überaschungen gefaßt machen.


Peter

von Rheinländer (Gast)


Lesenswert?

Hallo Peter,

ja, solche Sachen machen Spass, vor allem wenn man dann nach Stunden mit 
dem Kopf vor die Wand rennen will.

Ich probiere grad was anderes und frage mich: Welche kranken Hirne haben 
den Interrupt Controller VIC hier erfunden? <bäng> Das können nur 
käsebleiche, frauenlose, picklige VHDL Junkys hinter ihren zugezogenen 
Jalousien gewesen.

Mannomann, da blickt ja keiner mehr durch, so ein Drama um die paar 
Interrupts <crazy-smiley>

Habe das alles mal umgestrickt. Funktioniert soweit. Was nicht mehr 
funktioniert ist die Abfrage des PLOCK Bits in einer While Schleife, ob 
die PLL gelockt wurde. Das bleibt immer Null. Vorher hats geklappt nach 
der alten Methode.
1
while ( !( SCB_PLLSTAT & PLOCK ) );

Mit
1
while (!(PLLSTAT->PLOCK));

gehts nicht mehr :-(


1
////////////////////////////////////////////////////////////////////////////////
2
//              Register der PLL                    //  
3
////////////////////////////////////////////////////////////////////////////////
4
5
// PLLCON
6
struct pllcon_s {
7
   unsigned int PLLE  : 1;    // PLL enable
8
   unsigned int PLLC  : 1;    // PLL Connect
9
} __attribute__((packed));
10
11
#define PLLCON ((volatile struct pllcon_s*)0xE01FC080)
12
13
// PLLCFG 
14
struct pllcfg_s {
15
   unsigned int MSEL  : 5;    // Multiplikator Value
16
   unsigned int PSEL  : 2;    // PLL Divider Value
17
} __attribute__((packed));
18
19
#define PLLCFG ((volatile struct pllcfg_s*)0xE01FC084)
20
21
// PLLSTAT
22
struct pllstat_s {
23
  unsigned int MSEL  : 5;    // Multiplikator Value
24
  unsigned int PSEL  : 2;    // PLL Divider Value
25
  unsigned int res   : 1;    // reserviert
26
  unsigned int PLLE  : 1;
27
    unsigned int PLLC  : 1;
28
    unsigned int PLOCK : 1;
29
} __attribute__((packed));
30
31
#define PLLSTAT ((volatile struct pllstat_s*) 0xE01FC088)
32
33
// Feed Register AA,55
34
#define PLLFEED (*(volatile unsigned char*)  0xE01FC08C)

von Andreas K. (a-k)


Lesenswert?

Wenn du viel Pech hast, kriegt die Hardware mit, dass ein Bytezugriff 
auf ein Wortregister erfolgt, und nimmt übel. Bei solchen 
Steuerregistern ist die Bitfeld-Methode nicht anwendbar.

Bei der PLL steht darüber allerdings nichts drin. Beim VIC schon, der 
mag nur Worte.

von let (Gast)


Lesenswert?

^ Die Bits [11:15] fehlen in der Struktur. Werden die vielleicht
mit der einen oder anderen 1 beschrieben weil der Compiler meint es
sei egal?

von Andreas K. (a-k)


Lesenswert?

Er schreibt ja nicht, er liest. Und der Code sieht korrekt aus:
1
.L3:
2
        ldrb    r3, [r2, #137]
3
        tst     r3, #4
4
        beq     .L3
Schreibzugriff ist aber ebenfalls byteweise, daher meine Bedenken. Dass 
unbenutzte Bits auch mal gesetzt werden ist wenig wahrscheinlich (im 
Code auch nicht erkennbar).

von let (Gast)


Lesenswert?

Ok, ok. Da wird nur gelesen....

von Andreas K. (a-k)


Lesenswert?

Aber kann natürlich trotzdem sein, dass der Fehler woanders liegt. 
Funktioniert es denn, wenn genau nur diese eine Abfragezeile durch die 
klassische Version ersetzt wird, der Rest aber mit Bitfeldern arbeitet?

von Christian J. (elektroniker1968)


Lesenswert?

Hallo Andreas,

hier der ganze Code. Nein, sie funktioniert nicht, die anderen Bits habe 
ich noch nicht getestet, ob die richtige Inhalte haben. Es klappt erst 
wieder, wenn man auf die klassische Art zuzrückbaut. Die anderen 
Register der PLL funktionieren aber, wenn man sie auf Bitfehler umbaut.

Es mag eine dumme Frage sein aber welche Breite haben denn die 
Steuerregister? Hat der ARM unterschiedliche Längen, also mal 8 Bit, mal 
32 Bit für ein Register? Es wird ja immer ein Zeiger auf 32 Bit erzeugt 
durch "unsigned long..."

Wäre nett, was man das klären könnte, dann spare ich mir die Arbeit den 
Registersatz auf Bitfelder umzubauen. Nachteil ist ja, dass man auch 
mehr Code derzeugt, weil man keine zwei Bits gleichzeitig setzen kann, 
es sei denn man definiert das Register noch mit einem neuen Namen auf 
andere Art.

Das Programm läuft auch so, nur ist es blöd,  wenn das Problem anderswo 
wieder auftaucht. Was würde passieren, wenn man die Bitfehler auf 8 bzw. 
tatsächliche Registerbreite mit Leerbits auffüllen würde?

Anmerkung: Ich war mal 1 Jahr bei NEC 1998, da wurde ARM Peripherie mit 
entwickelt für die eigenen uCs, zumindest der IRQ Controller. Ich baute 
seinerzeit ein FPGA Board für den internen AMBA Bus und flantschte es an 
einen reinen ARM Core an, der als Softmakro vorhanden war. (Ein 
Riesen-Chip mit 400 Pins). Damals war es eine Diskussion, dass man die 
reserved Bits nicht beschreiben darf, weil das Verhalten undefiniert 
sei.
1
/ ---- Die PLL einstellen
2
void InitPLL(void) 
3
{
4
  
5
  PLLCFG->MSEL = PLL_M-1;
6
  PLLCFG->PSEL = PLL_P-1; 
7
  PLLFEED = 0xAA;    //PLL Feed-Sequenz
8
  PLLFEED = 0x55;
9
10
  PLLCON->PLLC  = 0;
11
  PLLCON->PLLE  = 1;  // PLL aktivieren
12
  
13
  while (!(PLLSTAT->PLOCK)); // Hier hängt er dann.
14
  
15
16
  // Mit MAMTIM werden die Waitstates beim Flashspeicherzugriff //eingestellt   
17
18
  MAMTIM = 3;    // 3 Wait States für > 40 Mhz
19
  MAMCR  = 2;    // Memory Manager voll aktivieren
20
 
21
  // PLL mit Taktgeber verbinden
22
  PLLCON->PLLC  = 1;
23
24
  PLLFEED = 0xAA;   // PLL Feed Sequence
25
  PLLFEED = 0x55;
26
27
}

von Christian J. (elektroniker1968)


Lesenswert?

Hallo,

hat jemand vielleicht eine Eagle Library mit dem ARM7 (64 Pin) für mich?

Gruss,
Christian

von let (Gast)


Lesenswert?

www.cadsoft.de -> Download -> Libraries

von Martin T. (mthomas) (Moderator) Benutzerseite


Lesenswert?

Beim letzten Mal, als ich mit gcc versucht habe die Register auf 
Bitfelder zu "mappen", ging das schief. Hatte versucht, Code für 
IAR-EWARM-compiler ohne Änderungen mit GCC compilieren zu können. IAR 
bietet einer herstellerspezifische Erweiterung für so etwas. Andreas 
Kaiser hat die Gründe angeschnitten. Der gcc hat Assembleranweisungen 
für Bytezugriffe generiert, die der Core nicht akzeptiert hat, wollte 
32-bit Zugriffe. (->exception, data-abort?). Grade nicht erinnerlich, ob 
nur beim Schreiben oder auch beim Lesen. Experimente waren allerdings 
mit einem LPC23xx, sollte aber nur eine untergeordnete Rolle spielen.

Vgl. auch: http://en.mikrocontroller.net/topic/81611#new und 
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=23623
Den Patch aus dem GNU bugzilla hatte ich vor einiger Zeit mal 
ausprobiert. Hat nicht funktioniert. Dann den Schreiber des Patches 
angeschrieben und gefragt, ob ich was falsch gemacht habe. Antwort in 
der Art: ja, weiß, Patch behebt Problem nicht.

Langer Rede: selbst wenn es in irgendeiner gcc-Version mal funktionieren 
sollte 32-Bit Zugriffe für bestimmte Adressen zu erzwingen, schreibt man 
wenig portablen Code. Ein Satz Macros, dürfte da deutlich portabler und 
dennoch halbwegs übersichtlich sein.

(Prosa: Diverser Beispielcode von Olimex ist auf die IAR-Erweiterung 
angewiesen. Ist eine elende "Dummenarbeit", diesen zu portieren. Könnte 
allerdings auch Absicht sein, Olimex stellt einige Eval-Boards und Code 
dazu her, die IAR in "Kickstart-Bundles" verkauft bzw. IAR-EWARM als 
Beispiele beilegt.)

Martin Thomas

von Christian J. (elektroniker1968)


Lesenswert?

Hallo,

hier mal die Antwort eines echten Profis, der eine Position bei einer 
ARM Schmiede bekleidet:

Hallo Christian,

ich bin fast sicher, daß das an dem schönen Zauberwort "volatile" liegt.
volatile besagt, daß sich diese Variable asynchron zum Programmfluß 
ändern
kann. Wenn volatile nicht angegeben ist, kann der Compiler die Variable
z.B. in ein Register legen oder anderweitig wegoptimieren. Am besten
schaust Du Dir mal den erzeugten Assemblercode an. Dann siehst Du, was
passiert.

Die Zeile "#define PLLSTAT ((volatile struct pllstat_s*) 0xE01FC088)"
definiert einen volatile Pointer auf die genannte Adresse. Damit darf 
der
Zeiger asynchron geändert werden. Du willst aber sagen, daß das wo er
hinzeigt volatile ist. Daher müsste m.E. in dem struct "volatile 
unsigned
int PLOCK : 1;" stehen. Außerdem ist mir nicht klar, wofür
"__attribute__((packed))" steht. Gepackte Strukturen sorgen 
normalerweise
dafür, daß Integer Variablen ohne Lücken abgelegt werden. Das ist aber 
nur
relevant, wenn zwischen mehreren ints ein char oder short steht. Wenn 
nur
ints in den Strukturen sind, sind sie automatisch gepackt.

Spiel mal ein wenig rum und analysiere mal den Assemblercode.

Viel Spaß und schönen Gruß,

Michael

von Andreas K. (a-k)


Lesenswert?

Christian J. wrote:

> Die Zeile "#define PLLSTAT ((volatile struct pllstat_s*) 0xE01FC088)"
> definiert einen volatile Pointer auf die genannte Adresse.

Wenn das ein C-Profi von ARM ist, dann gute Nacht ARM.
Zeiger volatile: struct pllstat *volatile.

Ist nicht anders wie beim weit besser bekannten "const char *s". Da ist 
schliesslich auch der "char" konstant, nicht der Pointer. Ein konstanter 
Pointer auf variable Zeichen wäre dementsprechend "char *const s".

von Christian J. (elektroniker1968)


Lesenswert?

Wie auch immer, jedenfalls können das Compiler wie keil oder IAR auch. 
Also muss der Kram irgendwie gehen und genau das werde ich heute abend 
ausprobieren.
1
/**** UART0 ****/
2
void UART0Initialize(unsigned int baud)
3
{
4
  unsigned int divisor = peripheralClockFrequency() / (16 * baud);
5
6
  //set Line Control Register (8 bit, 1 stop bit, no parity, enable DLAB)
7
  U0LCR_bit.WLS   = 0x3;    //8 bit
8
  U0LCR_bit.SBS   = 0x0;    //1 stop bit
9
  U0LCR_bit.PE    = 0x0;    //no parity
10
  U0LCR_bit.DLAB  = 0x1;    //enable DLAB
11
  //with one row
12
  // U0LCR = 0x83;
13
14
15
  //devisor
16
  U0DLL = divisor & 0xFF;
17
  U0DLM = (divisor >> 8) & 0xFF;
18
  U0LCR &= ~0x80;
19
20
  //set functionalite to pins:  port0.0 -> TX0,  port0.1 -> RXD0
21
  PINSEL0_bit.P0_0 = 0x1;
22
  PINSEL0_bit.P0_1 = 0x1;
23
  //with one row
24
  //PINSEL0 = PINSEL0 & ~0xF | 0x5;
25
}

von Andreas K. (a-k)


Lesenswert?

Das ist überhaupt kein Problem. Wirklich nicht. Ist ganz einfach. Du 
musst nur den GCC so umschreiben, dass er auf die hier zweifelhafte 
Optimierung von Bytezugriffen auf solche Bitfelder verzichtet. IAR/Keil 
tut sowas nicht und deshalb geht es dort.

von Christian J. (elektroniker1968)


Lesenswert?

Hallo,

ich hab es aufgegeben, es geht einfach nicht. Jedes Bit was über der 8 
Bit Grenze liegt kann nicht erreicht werden. Schade um die Arbeit.

Aber was anderes: Ich weiss, der Spruch "Besorg Dir ein C Tutorial!" 
kommt bestimmt aber ausser dem K&R habe ich nichts hier bzw. finde ich 
im Netz auch keine richtige. Wer wie ich nur mit einem eingeschränkten 
Funktionssatz bisher gearbeitet hat, der stösst schon auf so manche 
Hürde.

Ich möchte gern modularen Code schreiben, der die Hardware soweit als 
möglich abstrahiert, wenn ich mit der SD Karte oder anderen Geräten 
spiele, wie zB am SPI Bus angeschlossenen PIC Controllern. Daher habe 
ich mich mal mit der Übergabe von Funktionen in der Parameterliste 
befasst, denn das ging bei PICs mit dem CCS bisher nicht. Wollte ich 
aber schon immer mal haben :-)

Durch Herumprobieren entstand das, wobei das Ergebnis passt, ich habe 
einfach solange probiert, bis der Compiler durchlief und die Zahlen 
stimmten.

Ist das so ok?

1
int add(int a, int b)
2
{
3
  return(a+b);
4
}
5
6
7
int mult(int a, int b)
8
{
9
  return(a*b);
10
}
11
12
unsigned int rechnung(void* funk(int,int),  int zahl1, int zahl2)
13
{
14
  int ergebnis;
15
16
  ergebnis = (int)funk(zahl1,zahl2);
17
  debug_printf("\na=%u b=%u ergebnis=%u",zahl1,zahl2,ergebnis);
18
19
}
20
21
int main(void)
22
{
23
  short old_sek;
24
 
25
  rechnung((void*)add,6,9);
26
  rechnung((void*)mult,6,9);
27
28
  while(1);

Funktionieren tut auch das hier ohne Parameterliste
1
unsigned int rechnung(void*funk(), int zahl1, int zahl2)
2
{
3
  unsigned int ergebnis;
4
5
  ergebnis = (unsigned int)funk(zahl1,zahl2);
6
  debug_printf("\na=%u b=%u ergebnis=%u",zahl1,zahl2,ergebnis);
7
8
}


Kommt raus:

a=6 b=9 ergebnis=15
a=6 b=9 ergebnis=54

Warum muss man bei
1
ergebnis = (unsigned int)funk(zahl1,zahl2);

casten, damit keine Fehlermeldung "assignment makes integer from pointer 
without a cast" kommt?.

von let (Gast)


Lesenswert?

http://www.physik.uni-regensburg.de/studium/edverg/ckurs/ckurs.html
http://www.galileocomputing.de/openbook/c_von_a_bis_z

Deine Rechenfunktionen geben 'int' zurück, rechnung() erwartet
eine Funktion die void* zurückgibt.

Mit
1
unsigned int rechnung(int(*funk)(int,int), int zahl1, int zahl2)
sollte es gehen. Es wundert mich das der Compiler die fehlende
Klammer um 'funk' nicht bemängelt.

Die Parameterliste darf nur bei einem C Compiler leer sein. Das
bedeutet dann das die Funktion eine beliebige Anzahl Argumente
übergeben werden darf (oder auch gar keine). Bei C++ bedeutet
'leer' genau das was da steht: keine Parameter.

von Christian J. (elektroniker1968)


Lesenswert?

Hallo,

danke für die C Buch Links, sind schon ausgedruckt.

Naja, ein IRQ Beispiel für Timer 0 ist schon gestern gescheitert, obwohl 
der Code als beispiel beigelegt wurde und wirklich nichts anderes macht 
alos ein paar VIC Register zu setzen und eine Blinke-LED Routine für 
Timer 0 einzurichen. Blinkte nichts, IRQ wurde nicht angesprungen und ob 
Timer 1 läuft war auch nicht sicher, dá man die Timer Register nicht im 
Watch Fenster darstellen kann. Bei Crossworks vermisse ich diese 
Möglichkeit die Steuerregister sehen zu können, man muss sie erst auf 
Variablen zuweisen.

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.