Forum: Mikrocontroller und Digitale Elektronik Encoder auslesen, fehlerhafte Werte -> Vorschubregelung (PI)


von yoshi (Gast)


Lesenswert?

Guten Tag Leute

Für meinen Laminator, welcher verschieden breite Platinen mit der 
Toner-Transfermethode "bedrucken" soll, brauche ich eine 
Vorschubregelung, damit die Platine homogen "bedruckt" wird. Das habe 
ich mit einem Quadraturencoder (HEDL 5540) und einem Mikrocontroller 
realisiert, was soweit auch funktioniert.
Der uC (c8051f330, silabs) besitzt ein PCA Modul, welches eine Capture 
Funktion hat. Ich habe einen Ausgang des Encoders an den uC verbunden. 
Bei jeder pos. Flanke dieses Encodersignals wird ein Interrupt 
ausgelöst. Da wird jedesmal ein "Capture" des im Hintergrund laufenden 
Zählers gemacht. Danach wird die Zeitdifferenz des aktuelle und des 
vorhergehenden Interrupts berechnet, was mir die Zeit zwischen zwei pos. 
Flanken ergibt, also den Kehrwehrt der Drehfrequenz (im code 'rpm') 
meines Motors.
Das sieht so aus:
1
void isr_capture() interrupt 11
2
{
3
  PCA0CN &= ~0x02;
4
  rpm = pca0cpl - capture_old; 
5
  capture_old = pca0cpl;
6
}

Das scheint Ansich auch alles zu funktionieren, jedoch wenn ich diesen 
Wert per UART auf dem PC in einem Diagramm anzeigen lasse, habe ich ca 
alle 2-3 Sekunden Spikes, die manchmal den doppelten Wert anzeigen.
Und genau da liegt das Problem. Diese Werte machen meine Regelung 
verrückt.
Ich könnte die zwar filtern, aber lieber möchte ich die Ursache dieses 
Verhaltens ermitteln! Ich dachte zuerst, dass der Encoder zu viele 
Interrupts generiert und der uC nicht mehr mit Abarbeiten nachkommt. 
Jedoch sind diese Spikes nicht von der Drehzahl abhängig (soweit ich das 
beobachten konnte). Weiter dachte ich, dass es evt. ein Overflow sein 
könnte. Dies ist jedoch nur möglich, wenn sich die Werte an einer 
Bereichsgrenze aufhalten würden. Das ist leider auch nicht der Fall.
Des weiteren habe ich versucht, alles anderen Interrupts abzuschalten. 
Das half leider auch nichts. Hat sowas auch schon jemand gehabt? Gibt es 
sonst noch Sachen, die ich austesten / beachten sollte?
Den kompletten Code könnte ich nachrücken, falls erwünscht, jedoch denke 
ich, dass es etwas grundlegenderes ist, was ich noch nicht verstanden 
habe.

Viele Grüsse,
yoshi

von Karl H. (kbuchegg)


Lesenswert?

Welche Datentypen haben pca0cpl und capture_old

Overflows des Timers kannst du nur dann ignorieren, wenn
* diese Variablen unsigned sind
* nicht mehr als 1 Overflow von einem Capture zum nächsten Auftritt

von hlhjghj (Gast)


Lesenswert?

Kann es sein, dass Du den Zaehler ueberlaufen laesst und die seltsamen 
Werte immer dann auftauchen, wenn die Differenz "ueber den Ueberlauf 
hinweg" berechnet wird?

Waere so mein erster Gedanke.

Gast

von yoshi (Gast)


Angehängte Dateien:

Lesenswert?

Karl heinz Buchegger

Es sind beides 16bit unsigned Typen. Es passiert meines Erachtens nicht 
mehr als 1 Overflow.

Ich habe ein Bild angehängt. Das linke Diagramm zeigt denn gemessenen 
Wert des Encoders. Also die Zeit zwischen zwei pos. Flanken.
Im Rechten Diagramm sieht man den PWM-Wert. Man sieht deutlich, dass er 
versucht diesen Spike auszuregeln.

Das komische ist ja, dass mehrer Messwerte nacheinander sehr valide 
scheinen, nur diese vereinzelten Spikes stören das Bild.

Grüsse,
yoshi

von Karl H. (kbuchegg)


Lesenswert?

Fangen wir mal damit an, dass du ein wenig mehr als nur diesen 
Winzigausschnit deines Programms zeigst.

Ich tippe irgendwo auf eine leichtfertige Berechnung, bei der du aus den 
unsigned Typen zwischendurch irgendwo signed Werte kriegst. Mach dein 
Diagramm größer und ich wette deine Spitze geht bis 65536 - ~4000

von Falk B. (falk)


Lesenswert?

@  yoshi (Gast)

>Bei jeder pos. Flanke dieses Encodersignals wird ein Interrupt
>ausgelöst. Da wird jedesmal ein "Capture" des im Hintergrund laufenden
>Zählers gemacht. Danach wird die Zeitdifferenz des aktuelle und des
>vorhergehenden Interrupts berechnet, was mir die Zeit zwischen zwei pos.
>Flanken ergibt, also den Kehrwehrt der Drehfrequenz (im code 'rpm')

MÖÖP! Falsch. Einen Drehgeber wertet man mit einer Abtastung im 
festen Intervall aus, siehe Artikel.

MfG
Falk

von yoshi (Gast)


Lesenswert?

@Karl heinz Buchegger:
Komischerweise sind bei gleichbleibender Drehgeschwindigkeit die Spikes 
nicht immer gleich hoch.
Ich werde, wenn ich zuhause bin, den Code soweit abspecken, dass er sich 
nurnoch auf das Problem beschränkt und ich ihn hier zeigen kann ;-)

@Falk:
Da sich der Encoder nur sehr langsam dreht, habe ich gedacht, wäre es 
sinnvoller wenn die Zeit zwischen zwei Flanke gemessen wird (um auf 
etwas höhere Werte zu kommen). Wenn ich bloss die Encoder Incs zählen 
würde, wären diese Werte viel zu klein um die für die Regelung verwenden 
zu können. Darum habe ich nicht die dort vorgeschlagenen Methoden 
verwendet.
Liege ich da falsch?

von Karl H. (kbuchegg)


Lesenswert?

yoshi schrieb:

> Liege ich da falsch?

Kommt drauf an.

Ist dein Encoder prellfrei?

(Die Richtung willst du ja nicht feststellen. Sehe ich das richtig?
Im Grunde verwendest du den Encoder nur als vorhandenen Ersatz für eine 
Lochscheibe mit Lichtschranke)

von yoshi (Gast)


Lesenswert?

@Karl heinz Buchegger:
Ja, es ist einen optischen Encoder (besitzt sogar eine Lochscheibe mit 
Lichtschranke).

Genau, ich will bloss ein Mass für die Drehgeschwindigkeit erhalten.


Grüsse,
yoshi

von Karl H. (kbuchegg)


Lesenswert?

yoshi schrieb:

> Genau, ich will bloss ein Mass für die Drehgeschwindigkeit erhalten.

:-)
Dann nennen wir das ganze nicht 'Drehencoder auswerten' sondern 
'Frequenz der Unterbrechungen der Lichtschranke' feststellen und alles 
ist paletti :-)

Also:
Ohne Programm ist das jetzt schwer zu sagen. Schau dir noch mal alle 
Datentypen an. Schau dir alle Berechnungen an. Schau dir an, ob 
Berechnungen überlaufen können, etc. ...

von yoshi (Gast)


Angehängte Dateien:

Lesenswert?

Habe nun das Programm mal stark vereinfacht.
Die Datentypen sollten stimmen.

Hier der Code:
1
#include <c8051f330.h>
2
#include "drehzahlregler.h"
3
4
sfr16 pca0cpl = 0xe9;
5
unsigned int capture_old, rpm;
6
7
// main program
8
void main (void){
9
  Init_Device();
10
11
  rpm = 0;
12
  set_motor_speed(30);
13
14
  // main loop
15
  while(1){
16
  }
17
}  
18
19
20
void isr_capture() interrupt 11
21
{
22
  PCA0CN &= ~0x02;
23
  rpm = pca0cpl - capture_old; 
24
  capture_old = pca0cpl;
25
26
  SBUF0 = rpm >> 8;
27
  while(!TI0);
28
    TI0 = 0;
29
  SBUF0 = rpm;
30
  while(!TI0);
31
    TI0 = 0;
32
}
33
34
void set_motor_speed(unsigned char speed)
35
{
36
  PCA0CPH0 = 0xff - speed;
37
}
38
39
40
void PCA_Init()
41
{
42
    PCA0CN    = 0x40;
43
    PCA0MD    &= ~0x40;
44
    PCA0MD    = 0x08;
45
    PCA0CPM0  = 0x42;
46
    PCA0CPM1  = 0x21;
47
}
48
49
void Timer_Init()
50
{
51
    TCON      = 0x45;
52
    TMOD      = 0x20;
53
    CKCON     = 0x08;
54
    TL1       = 0xBA;
55
    TH1       = 0xA9;
56
    TMR2CN    = 0x04;
57
    TMR2RLL   = 0x7E;
58
    TMR2RLH   = 0xF9;
59
    TMR2L     = 0x7E;
60
    TMR2H     = 0xF9;
61
}
62
63
void UART_Init()
64
{
65
    SCON0     = 0x10;
66
}
67
68
void Port_IO_Init()
69
{
70
    P0MDIN    = 0xF3;
71
    P0MDOUT   = 0x50;
72
    P0SKIP    = 0x0F;
73
    XBR0      = 0x01;
74
    XBR1      = 0x42;
75
}
76
77
void Oscillator_Init()
78
{
79
    int i = 0;
80
    OSCXCN    = 0x67;
81
    for (i = 0; i < 3000; i++);  // Wait 1ms for initialization
82
    while ((OSCXCN & 0x80) == 0);
83
    CLKSEL    = 0x01;
84
    OSCICN    = 0x00;
85
}
86
87
void Interrupts_Init()
88
{
89
    IE        = 0x80; 
90
    IP        = 0x10;
91
  EIP1    = 0x10;
92
    EIE1      = 0x10;
93
    IT01CF    = 0x98;
94
}
95
96
void Init_Device(void)
97
{
98
    PCA_Init();
99
    Timer_Init();
100
    UART_Init();
101
    Port_IO_Init();
102
    Oscillator_Init();
103
    Interrupts_Init();
104
}

von yoshi (Gast)


Lesenswert?

Habs nun raus, der Encoder war defekt :(
;-)
Trotzdem vielen Dank,
yoshi

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.