Hi,
ich habe ein uC Programm für einen AT89C51AC3, das mir 3 Schrittmotoren
ansteuert und regelt. Funktioniert auch einwandfrei. Dazu müssen aber
erstmal die drei Sollwerte vom PC zum uC per serieller Schnittstelle
gesendet werden.
Damit habe ich irgendwie beträchtliche Probleme:
Programmiert ist alles in c und mit sdcc kompiliert (Editor ist M-IDE).
Ich wäre Euch wirklich dankbar, falls jemand mal drüber sehen könnte,
denn ich bin langsam am verzweifeln.
Chris
Deine Routine speichert immer 2 Byte in sollwerte[].
Aber woher soll der MC wissen, welches Byte was bedeutet?
Du brauchst ein Protokoll, damit sich der MC auf den PC synchronisieren
kann.
Für ein Binärprotokoll brauchst Du ein Terminalprogramm, was Binärwerte
senden kann.
Daher werden oftmals Textprotokolle verwendet.
Ein Kommando endet mit Zeilenende.
Zahlenwerte werden per sscanf oder atoi nach binär umgewandelt.
Peter
x wird nicht initialisiert, damit überschreibst Du möglicherweise andere
Variablen im interen RAM, also:
x = 0; // noch vor der Interruptfreigabe machen
EA = 1;
Würde ich erst am Ende machen, wenn alles korrekt initialisiert ist und
nicht gleich als ersten Befehl!
Hi,
ich habe nun mal alles etwas umgeschrieben und muss das morgen nach der
Vorlesung nochmal ausprobieren.
Das Problem mit dem oberen Code ist, dass er nicht in die ISR der
seriellen Schnittstelle springt und das empfangene Zeichen in dem Puffer
abspeichert.
Den Code im Timer2 sowie das "Protokoll" habe ich der Übersichthalber
weggelassen.
Warum wird eigentlich durch Einschalten von Timer0 das TI-Flag gesetzt?
1
#include<stdio.h>
2
#include<8051.h>
3
4
charRS232Buffer[24];
5
intRS232Index=0;
6
intsollwerte[3]={0,0,0};
7
8
voidmain(void)
9
{
10
//Timer0 initialisieren
11
TMOD=0x01;//Timer0 im 16Bit-Betrieb (Modus1)
12
TL0=0x9B;
13
TH0=0xFF;//Startwert=65435
14
15
//serielle Schnittstelle
16
SCON=0x50;
17
18
//Timer1
19
TMOD=(TMOD&0x0F)|0x20;
20
PCON=0x00;
21
TL1=0xFD;
22
TH1=0xFD;// 256-12MHz/(384*9600Baud)
23
REN=1;
24
PS=1;
25
TI=0;
26
RI=0;
27
28
ES=1;//RS232 Interrupt freigeben
29
30
TF1=0;//Timer0 Flag löschen
31
TR1=1;//Timer1 einschalten
32
TR0=1;//Timer0 einschalten
33
ET0=1;//Timer0 Interrupt freigeben
34
35
EA=1;//alle Interrupts freigeben
36
37
while(1)
38
{
39
if(RS232Index==24)//24 Bytes empfangen (pro Motor 4 Bytes)
Das ist noch der Code von Timer0, mit dem die Schrittmotoren angesteuert
werden, falls jemand auch mal ähnliches vor hat. Jetzt hab ich euch nur
einige Variablendef., eine Funktion ChartoInt und noch die Funktion
vorenthalten, mit der "Antriebsdrehzahlneu[x]" berechnet wird.
Ein Win-Programm habe ich bereits: Es sendet an die zwei
Schrittmotorenkarte ständig die Positionen für alle 6 Schrittmotoren als
Zeichenkette z.B. "100010001000100010001000" (alle 6 Motoren auf Pos
1000).
Ich glaube ich sollte noch ein Kommando für Anfang und Ende mitsenden.
@Matthias
Das mit dem Initialisieren stimmt zwar, aber deine Aussage stimmt nicht
ganz. Wenn die Variable nicht initialisiert wird ist der Startwert zwar
unbekannt, mehr aber auch nicht.
Alleine schon durch die Definition der Variablen reserviert der Compiler
ihr einen festen Speicherplatz. Der Compiler kennt ja alle Variablen und
deren Lebenszeiten, weshalb durch eine fehlende Initialisierung keine
anderen Variablen überschrieben werden könnten.
Ciao,
Rainer
RX und TX sind nicht vertauscht, denn sonst könnte ich den MC nicht auf
der Schrittmotorenkarte programmieren.
Wenn ich aus M-IDE mit JSIM simuliere bekomme ich bei einem Reloadwert
von 65435 (100us Abtastrate) einen stackoverflow (0x02F6, 0x02EE,
0x033D). Könnte das Grund für die Probleme sein?
Gibt es Methoden, die Funktionen von Timer0 leistungsmäßig zu
verbessern?
Poste doch bitte mal den gesamten Code bzw. noch besser, pack ihn in ein
Zip-File, so dass wir uns das gesamte Projekt mal angucken können.
Z.B. die Aussage:
> Warum wird eigentlich durch Einschalten von Timer0 das TI-Flag gesetzt?
ist so nicht erklärbar (für mich), weil TI nix mit Timer 0 zu tun hat.
Ralf
Kann es vielleicht sein dass Du das TI-Flag zu früh zurücksetzt?
Dein Hinweis war der Reload auf knapp unter dem Überlauf. Wenn Du nun
den Interrupt durch TI=0 direkt wieder freigibst, wird nach ~ 100 Zyklen
der Interrupt wieder angesprochen. Wenn man grob 2-3 Zyklen pro
Assembler-Befehl rechnet, komme ich auf etwa 33-50 Assembler Befehle für
den ISR - mehr nicht. Wenn in dieser Zeit aber der Interrupt wieder
auftritt (weil TI ja schon gecleart wurde), springt er wieder da rein.
Da aber die Abarbeitung des ersten Einsprungs noch nicht fertig ist,
muss der Stack hochgezählt werden.
Warum machst Du nicht einen Timer x (derer du ja drei hast) in dem Mode
wo das eine Byte den Reload-wert angibt und das andere den aktuellen
Timer (weiss grad den Mode nicht). In die neue Timer-ISR darfst Du aber
nur ein paar kleine Befehle reinsetzen, sonst bekommst Du wieder dein
Lastproblem.
cu,
olly...
anbei mal ein kleiner Denkanstoß. Verwende mal ein Terminal Program wie
z.B. TeraTerm. Das Zeichen, welches der MC empfängt, wird als "echo"
zurück gesendet.
und laß mal das "using" weg.
Hi,
vielen Dank für eure großartige Hilfe. Momentan läuft das Programm (es
bekommt von der Steuersoftware per RS232 die Sollwerte permanent
zugesendet und regelt Lage und Drehzahl der Motoren), auch wenn das
Programm unter programmiertechnischen Aspekten wohl unausgereift ist :).
Dafür bin ich auch eher der c# Programmierer, wenns denn sein muss.
Im Anhang findet ihr das c Programm für meine Schrittmotorenkarte. Zum
Glück hatte ich wenigstens mit dem Platinenlayout wenig Ärger.
Des Weiteren habe ich ein Screenshot von meiner Steuersoftware gemacht.
Es ist zum Steuern eines Hexapoden. Der ist zum Glück auch fertig
(endlich Zeit für Klausuren :( ).
Einziges Problem ist noch, dass ich das Programm nach jedem reset über
Flip neuladen muss, damit der Uart richtig empfängt ("BLJB" bei Flip ist
nicht gesetzt)
Grüße, Chris
> Gibt es Methoden, die Funktionen von Timer0 leistungsmäßig zu verbessern?
Natürlich gibts die :)
Das, was du im Timer-Interrupt machst, ist MURKS. Interrupts MÜSSEN so
kurz wie möglich gehalten werden. Du berechnest aber im Timer-Interrupt
munter, in welche Richtung usw. Das ist der falsche Ansatz!
Überleg dir mal, was der Timer-Interrupt machen soll... Richtig, er soll
alle 100µs (das war doch die Zeit, oder?) die Motoren ansteuern - SONST
NIX.
Das, was du da drin hast, mit den ganzen Berechnungen usw. gehört da
m.E. nicht rein.
Mach folgendes:
Die Berechnung findet im Hauptprogramm statt. Jedesmal, wenn die
Berechnung fertig ist, setzt du ein Flag. Der Timer-Interrupt prüft das
Flag. Ist es nicht gesetzt, verlässt du den Interrupt wieder. Ist das
Flag gesetzt, holst du die neuen Ausgabewerte in eine Variable des
Interrupts, steuerst die Motoren an, und löschst das Flag. Das
Hauptprogramm darf erst dann neue Werte schreiben/berechnen, wenn der
Interrupt die aktuellen Werte ausgegeben hat, sonst überspringst du
evtl. einen Datensatz.
Das Flag brauchst du, um erst dann neue Werte im Interrupt auszugeben,
wenn die Berechnungen abgeschlossen sind, sonst würde der Interrupt,
wenn er mitten in einer Berechnung Daten holt, eine Mischung aus alten
und neuen Daten ausgeben.
Ralf
> Einziges Problem ist noch, dass ich das Programm nach jedem reset über> Flip neuladen muss, damit der Uart richtig empfängt ("BLJB" bei Flip ist> nicht gesetzt)
Hm, ich meine mal etwas gelesen zu haben, dass das BLJB bei Flip
invertiert dargestellt wird, könnte es das sein?
Weitere Möglichkeiten sind:
- Ist der PSEN-Pin zufällig beim Resetten auf Low? Das startet den
Bootloader.
- Wird das BLJB-Flag zufällig in der Software gesetzt (evtl. durch einen
unerwünschten API-Aufruf des Bootloaders)?
Ralf
@Fox Mulder
> char sollwerte[3];> sollwerte[x]=SBUF> x++;> if(x==2) x=0;
Wenn x nicht (mit 0,1 oder 2) initialisiert wird, überschreibt ein
größerer x Wert den 'sollwerte' Buffer. Dadurch werden ggf. andere
Variablen, die in diesem Bereich liegen, überschrieben.
@Matthias
Okok, du hast ja Recht. Ich habe deine Aussage dann etwas falsch
verstanden gehabt und nicht explizit auf diesen Code bezogen gesehen.
Ich dachte du meinst damit, daß ohne Initialisierung der Variablen noch
kein fester Speicherplatz zugewiesen werden würde.
Also damit hätten wir das Missverständnis ja aus der Welt geräumt. In
diesem speziellen Code Beispiel muß die Variable in der Tat
initialisiert werden, damit versehentlich keine Speicherbereice darüber
überschrieben werden. :)