Hallo Leute,
ich habe seit geraumer Zeit das Problem, dass mein C-Programm mysteriöse
Dinge tut... nun habe ich das Problem, denke ich, ziemlich eingegrenzt:
1
var0=0xFF;
2
3
while(1)
4
{
5
var0=hallo();
6
7
}
8
9
10
unsignedcharhallo(){
11
return7;
12
}
Außerdem gibt es noch den Interrupt:
1
// Timer für ms- Zähler (bringt die LED A4 blinken)
2
3
staticvoidT1_isr(void)interrupt3using3
4
{TL1=0xC0;// L-Register laden insgesamt: 0xF831
5
TH1=0x07;// H-Register laden
6
if(LED_BLINK)
7
{
8
if(LED_COUNT--==0)// Zähler zurücksetzen
9
{LED_COUNT=50;// Schleifenzähler neu setzen
10
OUTA^=0x10;// LED umschalten
11
}
12
}
13
}
So, das Problem ist folgendes:
Die an den Interrupt-Port angeschlossene LED blinkt (allerdings viel zu
schnell im Vergleich zum "normalen" blinken; ist nur mit dem Oszilloskop
nachweisbar...) und das Register von der Variablen var0, das eigentlich
den Wert 7 enthalten müsste, enthält immer den vorher zugewiesenen Wert
0xFF.
Nunhabe ich das Gefühl, dass sich das Pogramm und der Interrupt
irgendwie gegenseitig die "Suppe versalzen..." Hat jemand eine Idee, an
was das liegen könnte?
Viele Grüße,
elko
PS: Ich nutze den µC AN2131 von Cypress
Hallo,
Wenn var0 auf 0xFF landet, ist er eben abgestürzt und hat von vorn
angefangen.
Im Fragment ist (wie oft) keine Ursache erkennbar.
Initialisierung des Timers und der Interrupts?
Falsche ISR?
Gruß aus Berlin
Michael
> TL1 = 0xC0; // L-Register laden insgesamt: 0xF831> TH1 = 0x07; // H-Register laden
was jetzt? 0x07c0 oder wie im Kommentar 0xf831 ?
Eins ist doch falsch, oder nicht?
Wie gesagt, mit den Initialisierungen kenne ich mich leider nicht aus...
ich habe mal die andere Variante wie im Kommentar ausprobiert,
funktioniert aber immer noch nicht...
Vom Programmieren hab ich Ahnung, ja (habe auch schon viel mit dem µC
programmiert...). Meine Programme haben immer auf einen winzigen
"Basisprogramm" aufgebaut, was eben die notwendigen Initialisierungen
enthält. Von diesen habe ich jedoch keine Ahnung. Logische Zusammenhänge
bezüglich dem Programmieren kann ich nachvollziehen, nur eben solche
-auf den ersten Blick willkürlichen Zeilen wie TH1=5 oder so- kann man
ja nicht mit Logik verstehen... und die Technical Manual von Cypress ist
so dick, dass man Monate bräuchte, sie durchzugehen...So sieht das
Basisprogramm aus:
Wie gesagt: Vom Programmieren an sich habe ich Ahnung, nur was solche
"Spezialregister" machen, weiß ich nicht.
Viele Grüße,
elko
PS: Ich arbeite an einem 1-Wire-Projekt, bei dem ich Temperatursensoren
auslesen will ;-)
>{ TL1 = 0xC0; // L-Register laden insgesamt: 0xF831> TH1 = 0x07; // H-Register laden
Diese Programmzeilen laden den Timer 1. Wenn der die Blinkfrequenz
vorgibt, so ist es essentiel, welcher Wert dort geladen ist.
Also Prozessorfrequenz, Teiler und gewünschte Blinkfrequenz
zusammensuchen und den Wert für den Timer 1 bestimmen. Sonst wird des
nix.
Also es wäre toll, wenn du mir erklären kannst, was diese Speziellen
Programmzeilen machen! (unter anderem auch, was in der "Blink-Funktion"
das
1
interrupt3using3
bedeutet). Was meinst du mit "Diese Programmzeilen laden den Timer 1."?
Er gibt die Blinkfrequenz offensichtlich vor, jedoch was bedeuten die
"L- und H-Register"?
Trotzdem noch mal zu meinem eigentlichen Problem: Das Blinken hat bisher
eigentlich funktioniert, jedoch irgendwie schmeckt dem Programm das
Aufrufen der Funktion "hallo()", die in diesem Test nicht mehr als einen
Wert zurückgibt, nicht... warum das? Programmtechnisch ist doch
eigentlich alles richtig, oder?
Viele Grüße,
elko
>Also es wäre toll, wenn du mir erklären kannst, was diese Speziellen>Programmzeilen machen! (unter anderem auch, was in der "Blink-Funktion">das interrupt 3 using 3
Die besagen das das eine Interruptroutine ist die den Interruptvector 3
belegt. Interrupt vector 3 ist der Interruptvector für den Timer 1.
Using 3 bedeutet das bei eintritt in die Interruptserviceroutine auf den
Registersatz 3 umgeschaltet werden soll. Damit geht der eintritt in die
Serviceroutine schneller als wenn man erst alle Register auf den Stack
retten muss.
Gruss Helmi
Also nachdem ich jetzt das Tutorial gelesen habe, ist ein
Interruptvektor eine Art Zeiger auf eine bestimmte Adresse, die beim
Auslösen des Interrupts angesprungen wird. Warum bedeutet die 3 denn
Timer1?
Kann mir jemand nochmal genau sagen, was die einzelnen "Wörter im
folgendem Codesegment bedeuten (und damit klar machen, wie man es
schafft, einen Timer einzuschalten)?
1
staticvoidT1_isr(void)interrupt3using3
2
{TL1=0xC0;// L-Register laden insgesamt: 0xF831
3
TH1=0x07;// H-Register laden
4
//...
Was bedeutet denn "auf den Registersatz 3 umschalten"? Könnte es so
passieren, dass Daten verloren gehen und das Programm "spinnt"?
Viele Grüße,
elko
>Warum bedeutet die 3 denn Timer1?
Weil das vom Hersteller so vorgegeben ist.
>Was bedeutet denn "auf den Registersatz 3 umschalten"?
Der 8051 Prozessor hat 4 Registerbänke. Also die Register R0 .. R7 sind
4 mal vorhanden. Du hast aber nur jeweils eine dieser Registerbänke im
direkten zugriff. Di anderen kann man dann nur über absolute Addressen
ansprechen. Umgeschaltet wird zwischen diesen 4 Bänken über 2 Bits im
PSW.
Und genau dieses umschalten bewirkt die Angabe "using 3"
>Könnte es so>passieren, dass Daten verloren gehen und das Programm "spinnt"?
Ja .
Okay, das hört sich schonmal sehr aufschlussreich an... jetzt stellt
sich mir nur die Frage, was man dagegen tun kann ;-) ich habe gelesen
(bei avr-µC), dass man die sfr temporär speichern kann, die sfr so
manipulieren, dass Interrupt ausgeschaltet wird, dann Code, der nicht
unterbrochen werden darf ausführen lassen kann und dann über die kopie
wieder die sfr so setzen, wie sie vorher waren. Doch dabei stellt sich
natürlich die Frage, ob und wie das beim an2131 so geht (wovon ich mal
ausgehe) und vor allem: Welche Programmteile muss ich dann schützen? Ich
kann ja unmöglich das ganze Programm "schützen", sonst kommt von den
Interrupts ja nix mehr an...
Der Compiler sollte beim eintritt in die Interruptserviceroutine schon
alles sichern. Die Interrupts must du im Hauptprogramm nur dann sperren
wenn du auf Variablen zugreifen möchtest die in der Serviceroutine
verändert werden können. Wenn du auf eine 16 Bit Variable zugreifen
möchtest die in deiner Serviceroutine verändert werden kann sokann es
passieren das du das erste Byte erwischst und dann kommt der Interrupt
der verändert jetzt das andere Byte der Variable. Danach stimmt der Wert
der Variable in der Hauptroutine nicht mehr. Das eine Byte stammt ja vor
dem eintritt in die Serviceroutine und das andere Byte nach ablauf der
Routine. Du must halt vorher die Interrupts mit "EA = 0" sperren und
danach wieder mit "EA = 1" freigeben.
Schön zu hören ;-)
Dann Frage ich mich, warum -obwohl ich bezüglich Variablen, die in der
ISR verwendet werden nichts geändert habe- sich die Blinkgeschwindigkeit
so krass ändert.
Gibt es eigentlich eine schöne Übersicht, in der alle Registernamen wie
EA und THirgendwas aufgelistet und erklärt sind? Das wäre für mich sehr
hilfreich, denn unter den vielen Abkürzungen kann ich mir herzlich wenig
vorstellen...
elko schrieb:
> Also es wäre toll, wenn du mir erklären kannst, was diese Speziellen> Programmzeilen machen! (unter anderem auch, was in der "Blink-Funktion"> das
1
interrupt3using3
bedeutet).
Bei allen 8051-Fragen, ist die Keil Webseite ein vorzügliche Referenz.
Da gibt es z.B. die Knowledgebase und die FAQs.
Die Interruptvektoren werden durchnumeriert (wie bei allen MCs). Welche
Nummer welcher Quelle entspricht, steht im Datenblatt Deines konkreten
8051-er.
Die ersten 6 Vektoren sind in der Regel gleich (0..5 = EX0, T0, EX1, T1,
UART, T2).
Das "using 3" ist in der Regel falsch.
Es richtet mehr Schaden als Nutzen an und sollte daher nur von
erfahrenen Programmierern verwendet werden.
Laß es weg!
Peter
Heyy :D
Die letzten beiden Nachrichten haben mir echt sehr weitergeholfen!
Vielen Dank dafür euch schonmal!
Ich habe mir jetzt mal die Datenblätter und Manuals durchgelesen und mir
ist jetzt einiges klarer ;-)
Außerdem habe ich festgestellt, dass die hallo-Funktion tatsächlich
durch den Interrupt gestört wird. Das habe ich dadurch erkannt, dass
ohne die Interrupt-Funktion 7 zurückgegeben wird, mit jedoch viele
Verschiedene Werte... Kann mir da jemand weiterhelfen und erklären,
woran das liegt?
Viele Grüße,
elko
Was passiert, wenn du die Int-Funktion komplett leerräumst (also
nichtmal TH/TL neu laden) ?
Wie groß ist denn das Programm ?
Hat es event. einen Sinn, wenn du den Compileroutput (asm-Listing) mal
vorzeigst ?
elko schrieb:
> Vom Programmieren hab ich Ahnung, ja (habe auch schon viel mit dem µC> programmiert...). Meine Programme haben immer auf einen winzigen> Wie gesagt: Vom Programmieren an sich habe ich Ahnung, nur was solche> "Spezialregister" machen, weiß ich nicht.>
Das kannst Du noch ein parmal wiederholen, obs dadurch allerdings
richtiger wird?
Ein Teil der Loesung steht im 1, Post vom Falk. Schon mal schlau gemacht
was volatile bedeutet?
Der andere Teil waere, sich zu verdeutlichen, dass der Zugriff auf den
XDATA memory nicht atomar vonstatten geht.
// Timer für ms- Zähler (bringt die LED A4 blinken)
21
22
staticvoidT1_isr(void)interrupt3
23
{TL1=0xC0;// L-Register laden insgesamt: 0xF831
24
TH1=0x07;// H-Register laden
25
if(LED_BLINK)
26
{
27
if(LED_COUNT--==0)// Zähler zurücksetzen
28
{LED_COUNT=50;// Schleifenzähler neu setzen
29
OUTA^=0x10;// LED umschalten
30
}
31
}
32
}
Wie man sieht, habe ich nicht viel verändert, aber es ergibt sich
wunderliches:
1. var0 nimmt NICHT den Wert 7 an
var1 nimmt NUR den Wert 3 an
die LED blinkt (wie zu erwarten)
Kommentiere ich die beiden Zeilen
1
TL1=0xC0;// L-Register laden insgesamt: 0xF831
2
TH1=0x07;// H-Register laden
aus, so ergibt sich folgendes:
2. var0 nimmt irgendwelche schnell wechselden Werte an
var1 nimmt sowohl den Wert 3, als auch 4 an (wie erwartet)
die LED blinkt (trotz entfernen der beiden Zeilen)
Ist die Interrupt-Funktion komplett leer, so sieht man:
1. var0 nimmt NICHT den Wert 7 an
var1 nimmt NUR den Wert 3 an
die LED blinkt NICHT (okay, hätte mich auch gewundert...)
@Robert:
>Das kannst Du noch ein parmal wiederholen, obs dadurch allerdings>richtiger wird?
Danke.
Ja, ich habe nachgeschaut, was es mit volatile auf sich hat und dabei
festgelstellt: Ich habe KEINE Variablen, die sowohl in der isr, als auch
im Hauptprogramm benutzt werden (die paar Aufrufe am Anfang zählen für
mich nicht, da das Programm im Prinzip immer in der while-Schleife
ist...
>Der andere Teil waere, sich zu verdeutlichen, dass der Zugriff auf den>XDATA memory nicht atomar vonstatten geht.
Das hört sich schon eher nach einer Lösung oder besser gesagt nach einem
Problem an. Was kann ich denn dagegen tun?
Viele Grüße,
elko
elko schrieb:
> Ja, ich habe nachgeschaut, was es mit volatile auf sich hat und dabei> festgelstellt: Ich habe KEINE Variablen, die sowohl in der isr, als auch> im Hauptprogramm benutzt werden (die paar Aufrufe am Anfang zählen für> mich nicht, da das Programm im Prinzip immer in der while-Schleife> ist...>
Fuer deinen Code, den um um 26.07.2009 18:22 gepostet hast trifft das
definitiv nicht zu.
@APW
> Wieso nagelst du eigentlich die meisten deiner Variablen auf bestimmten> Adressen fest ?
Beim 8051 kann man ueber das ext. memory Interface seine Peripherie in
den XDATA Space mappen und so wie auf normale Variablen zugreifen. Ob
das in diesem Fall so ist, kann nur der Author verraten. Sollten es nur
ganz "normale" Variablen sein, so macht im gezeigen Code weder das
festnageln, noch das xdata Sinn.
>> Ja, ich habe nachgeschaut, was es mit volatile auf sich hat und dabei>> festgelstellt: Ich habe KEINE Variablen, die sowohl in der isr, als auch>> im Hauptprogramm benutzt werden (die paar Aufrufe am Anfang zählen für>> mich nicht, da das Programm im Prinzip immer in der while-Schleife>> ist...>>>Fuer deinen Code, den um um 26.07.2009 18:22 gepostet hast trifft das>definitiv nicht zu.
Das stimmt... ich habe das Beispielprogramm so modifiziert, dass in der
while-Scleife nicht mehr auf Port A zugegriffen wird. Also von daher
stimmt dann doch meine Aussage, doer meintest du etwas anderes?
@"festnageln":
Ich habe nicht ganz verstanden, ob Robert das meint, was zumindest mein
Grund für das "festnageln" der Variablen ist: Irgendwie möchte ich ja
wissen, was im Chip vor sich geht. Wenn den Variablen Speicheradressen
fest zugewiesen sind, kann ich vom PC aus mir die Werte der Variablen
anschauen. So habe ich auch immer festgestellt, was ich in den vorigen
Posts geschrieben habe (" var0 nimmt NICHT den Wert 7 an", etc. etc.).
Gibt es vielleicht eine bessere oder einfacherere Art, in den Chip
"hineinzuschauen", damit man das Programm debuggen kann?
Ich weise nochmal darauf hin, dass ich in meinem Post vom 28.07.2009
01:08 beschrieben habe, was sich für Dinge in dem Programm ereignen.
Vielleicht hilft das zu einer Lösung weiter ;-))
Gruß,
elko
>Nachtrag: Ich habe leider keine Information gefunden, was TH1 und TL1>genau bedeutet (okay, high und low bytes, aber was für einen Sinn haben>sie?
Dann hast du dir das Datenblatt zum 8051 aber noch nicht genau
durchgelesen.
Diese beiden Register und auch TH0,TL0 sind die Zaehlregister von Timer
1 und Timer 0 und seit anbegin der 8051 Zeit in jedem Prozessor drin.
Beim ueberlauf dieser beiden Register wird ein Interrupt ausgeloest.
Innerhalb der Interruptserviceroutine muessen dann diese beiden Register
neu gesetzt werden. Unterbleibt das dann wird die Interruptroutine immer
nach 65536 Countercyclen wieder aufgerufen. Diese Zaehler zaehlen
aufwaerts. Wenn diese Zeit kuerzer werden soll muss du diese beiden
Register beim eintritt in die Serviceroutine wieder neu laden. Der
Reloadwert berechnet sich da die Zaehler ja aufwaerts zaehlen und der
Interrupt beim Ueberlauf von 0xffff -> 0x0000 aufgeloest wird
folgendermassen:
Reload(TL1,TH1) = 65536 - (t * fc / 12).
t ist deine Zeit zwischen 2 Interrupts
fc ist die Clockfrequenz deines Prozessors.
Gruss Helmi
Ah, also müsste dann die Blinkgeschwindigkeit abnehmen, wenn man das
Laden der beiden Register rausnimmt, oder?
Seeehr, schön :D habs mal ausprobiert und es klappt :D (endlich mal
wieder was, was auf Anhieb funktioniert...)
Trotzdem fuchst es mich teuflisch, dass allein der Aufruf der Funktion
hallo(), selbst wenn ich auch noch das "return 7" rausnehme, die LED zum
ausrasten bringt... Hat jemand dazu eine Idee?
Wenn gewünscht, hänge ich auch nochmal die entsprechenden Passagen
Quelltext dran ;-)
Gruß,
elko
>Ah, also müsste dann die Blinkgeschwindigkeit abnehmen, wenn man das>Laden der beiden Register rausnimmt, oder?
Richtig , dann macht der Zaehler eine komplette Umdrehung.
>(endlich mal wieder was, was auf Anhieb funktioniert...)
Kaum macht man es richtig funktionierts.
>Trotzdem fuchst es mich teuflisch, dass allein der Aufruf der Funktion>hallo(), selbst wenn ich auch noch das "return 7" rausnehme, die LED zum>ausrasten bringt... Hat jemand dazu eine Idee?
Hat deine IDE keinen integrierten Simulator wo du das ganze mal Step by
Step im PC durchsteppen kannst ?
Welche IDE / Compiler benutzt du ueberhaupt ?
Ansonsten lade die mal die Demo von Keil runter da ist ein Simulator mit
drin.
elko schrieb:
> @"festnageln":> Ich habe nicht ganz verstanden, ob Robert das meint, was zumindest mein> Grund für das "festnageln" der Variablen ist: Irgendwie möchte ich ja> wissen, was im Chip vor sich geht. Wenn den Variablen Speicheradressen> fest zugewiesen sind, kann ich vom PC aus mir die Werte der Variablen> anschauen. So habe ich auch immer festgestellt, was ich in den vorigen
Gibt es keinen vernuneftigen Grund, Variablen ins XDATA zu legen, dann
lass es bleiben. Nimm data, oder alternativ idata. Wenn XDATA, dann
musst Du sicherstellen, dass dein Contoller an den von dir festgelegten
Adressen auch wirklich XDATA Memory hat und evtl (haengt von Consoller
ab) auch aktiviert ist. Sonst laeuft der Speicherzugriff ueber das
externe Memeory interface in leere und man liest unsinnige Daten ueber
P0 ein.
Das Festlegen der Speicheradresse fuer Variablen sollte man tunlichst
dem Compiler ueberlassen. Schonmal eine Fehlerquelle weniger.
Du solltest dir mal wirklich die Zeit nehmen, und dir das Memory Layout
des 8051 verinnerlichen.
Jupp, ich nutze µVision3... aber das mit dem Simulator ist halt immer so
ne Sache. Wenn man dann einen Input simulieren will ist sdas meiner
Meinung nach sehr umständlich...
Vom assembler-Listing her müsste das eigentlich einwandfrei
funktionieren, meine ich.
Meine Befürchtung ist nur, dass die Variablen an Speicherstellen
platziert sind, die schon anderweitig benutzt werden (Debugger,
C-Hilfsfunktionen o.ä.). Wenn ich dich richtig verstehe, sind die
Var-Adressen echtes Ram, also keine Register von externen Chips ?
Ich kenne den speziellen uC nicht. Ist das XRAM extern oder on Chip ?
Wie debuggst du eigentlich ?
Wieviel XRAM hast du ? Was passiert, wenn du die Variablen etwa in der
Mitte des zur Verfügung stehenden XRAM-Bereiches platzierst ?
Oder du lässt die Variablen wie üblich vom Compiler/Linker reservieren
und schaust dann fürs debuggen im .map-File nach, wo diese platziert
wurden.
>Jupp, ich nutze µVision3... aber das mit dem Simulator ist halt immer so>ne Sache. Wenn man dann einen Input simulieren will ist sdas meiner>Meinung nach sehr umständlich...
Koennte ich so nicht sagen. Der Keil-Simulator ist so schon gut.
Aber trotzdem simulier doch mal dein Program. Dann siehst du doch wer
dir da in die Suppe spuckt.
elko schrieb:
> @Robert: und wie soll ich dann die einzelnen Variablenwerte auslesen?
Versteh ich nicht. Variablen haben doch Namen, damit kann man sie such
auslesen? Oder meinst Du im Simulator? Der kann auch mit Variablen Namen
umgehen.
Poste doch endlich mal das komplette Programm und verrate welchen
Controller du verwendest.
Robert schrieb:
>Poste doch endlich mal das komplette Programm und verrate welchen>Controller du verwendest.
Das mit dem Simulieren werde ich nachher nochmal ausprobieren, hier
schonmal das komplette Programm im jetzigen Zustand.
Ich verwende den AN2131 von Cypress...
Kann mir jemand erklären, wie ich einen Interrupt mit µVision3
simulieren kann? Ohne dass ich den Interrupt einbringe scheint es
simulieert nämlich zu funktionieren.
Draengel nicht so.
Wenn du deinen Timer korrekt initialisiert hast sollte der auch im
Simulator ausloesen. Ansonsten kannst du das entsprechende Bit das den
Interrupt ausloest auch in den Perepherieregistern von Hand setzen.
Das ist aber sehr seltsam. Wenn ich das ganze step-by-step durchgehe
komme ich nie in die isr, und wenn ich auf RUN klicke und eine Weile
später auf Stop, war das Programm auch nicht in der isr.
elko schrieb:
> Das ist aber sehr seltsam. Wenn ich das ganze step-by-step durchgehe> komme ich nie in die isr, und wenn ich auf RUN klicke und eine Weile> später auf Stop, war das Programm auch nicht in der isr.
Wie schon woanders beschrieben sind beim Steppen die Interrupts
ausgeschaltet.
Also: BP an den Beginn einer ISR setzen und dann Go!
Also: ich habe jetzt auch mal ausprobiert, einen Interrupt in der ISR zu
setzen und dann im debug-modus auf RUN zu klicken... wenn ich einen
Breakpoint in der Main-Funktion oder in eine von main() aufgerufene
Funktion setze, so hält das Programm am BP. Die ISR wird in der
Simulation nie ausgelöst. Eine Idee?
Ich glaube mein letzter Post ist etwas komisch formuliert:
Wenn ich einen BP in der ISR setze, hält das Programm dort nicht an,
weil der Interrupt nie ausgelöst wird... obwohl ich auf RUN geklickt
habe...
Jetzt eine Idee?
elko schrieb:
> Ich glaube mein letzter Post ist etwas komisch formuliert:> Wenn ich einen BP in der ISR setze, hält das Programm dort nicht an,> weil der Interrupt nie ausgelöst wird...
genau!
- interrupt ist eingeschaltet (enable?)
- bedingung für interrupt wird erfüllt? Z.B. timer überlauf?
Bedenke, dass die Simulation um eiiiiiniges langsamer (faktor 10...100)
laufen kann in bestimmten situationen als auf real HW.
Setze mal die Timer bedingung für den INT etwas runter (z.B. /10)
Kannst dir auch nen neuen Target machen, namens "Simulator".
In der "Options for Target" setzt du dann bei C/C++ ein #define namens
USE_SIMULATOR__
in deinem code machste dann:
#if defined USE_SIMULATOR
... timerPreload = val/10;
#else
... timerPreload = val;
#endif
VG,
/th.
Hallöle :D
ich habe nun die Lösung (bei den Debug-Einstellungen hat die Angabe
einer DLL gefehlt...)! Danke Thorsten!
Nun noch zu eienr anderen Frage zum gleichen Thema:
Durch verschiedene int-Variablen, die in Funktionen untergebracht
wurden, blinkt die LED, die den Timer-Interrupt anzeigt, viel
schneller... nachdem ich sie als unsigned char deklariert habe, blinkt
die LED wieder normal. Hat jemand eine Erklärung?
Viele Grüße,
elko