Zeigerlänge am Drehspulinstrument

von Gerald Kreisler
Im Beispiel der Simulation eines klassischen Drehspulinstrumentes habe ich versucht Ihr Programmbeispiel mit einer variablen Steuerspannung zu versehen. Sie haben auf der Buchseite 80 kurz darauf verwiesen, die Zeigerlänge entsprechend der Aussteuerung des Instrumentes über Winkelfunktionen anzupassen. Ich habe zur Berechnung der Zeigerlänge eine recht einfache Möglichkeit gefunden, die ich gern zur Weitergabe anbieten möchte.
Variable Zeigerlänge über Winkelfunktionen
Die Steuerung des Instrumentenzeigers habe ich durch ein Potentiometer realisiert, das eine positive Spannung von maximal 5 Volt abgreift. Diese Spannung wird über einen ADC digitalisiert und als digitaler 8-bit Spannungswert zur weiteren Berechnung verwendet. Da der Zeiger sowohl am Anfang als auch bei Maximalauschlag verkürzt werden muss, während er in Mittelstellung seine Originallänge erreicht, mit der er nur wenig in den Zahlenbereich des Instrumentes hineinreicht, kam mir die Idee, die mathematische Kurve einer Wurfparabel zur Zeigerlängenkorrektur zu verwenden. Die Parabel hat die Höhe Null am Anfang und Ende, während die grösste Auslenkung (Höhe) in der Mitte erreicht wird. Schnell war ein C++ Programm geschrieben, mit dem ich die Variablen der Parabel solange veränderte, bis sie für das Zeigerbeispiel passte. Maximale Höhe der Parabel 10 Pixel (y-Werte) bei einer gesamten "Wurflänge" von 127 Pixeln (x-Werte) für die Displaybreite.
Berechnung
Die Zeigerlängenänderung erfolgt in der ISR, ihre Berechnung in der Funktion parabel(). Explizite Darstellung der Wurfparabel:
y = (a*x) - (b*x*x)
Optimierte Pixelvariante: h = (0.683*v) - (v*v*0.0105)
Lösung für ATmega328p und GLCD libraries
Benutzt habe ich ein Grafikdisplay EA DOGM128-6 von Electronic Assembly mit einem ATmega328p und den GLCD libraries. Die Messergebnisse des ADC füge ich per Interruptroutine ein. Über die Funktion "int parabel(uint8_t div)" steuere ich den Zeiger in der ISR.
Zusatz: Eine Anpassung des Beispiels "VUMeter" aus dem Lernpaket ist als Download verfügbar (s.u.)
Testsystem mit ATmega328p und EA DOGM128-6
Eingebunden in den Sourcecode ergibt sich damit folg. Lösung:
void loop(void); // Prototyp
int parabel(uint8_t div); // Prototyp
int main(void)
{
glcd_init();
ADCSRA |= (1<<ADPS2) | (1<<ADPS1);// divisor 64
ADMUX |= (1<<ADLAR);
ADMUX |= (1<<REFS0);
ADCSRA |= (1<<ADIE);
ADCSRA |= (1<<ADEN);
//sei();
ADCSRA |= (1<<ADSC);
while(1) {}
return 0;
}
ISR(ADC_vect)
{
uint8_t div = ADCH/2; // ADC teilt 5 V in 8 bit ( 0....254); div soll 0....127 können
// für max. Zeigerausschlag
uint8_t ref = parabel(div); // Korrektur Zeigerlänge
_delay_ms(50); // Zeigerträgheit
glcd_clear_buffer();
loop(); // Instrumentenhintergrund zeichnen
glcd_draw_line(64, 90, div, ref, 1); // Zeiger mit ref = f(div)
glcd_write();
ADCSRA |= (1<<ADSC);
} // Instrumenthintergrund
void loop(void)
{
glcd_draw_ellipse( 64, 50, 100, 50, 1); // x, y, a, b, color=1
glcd_set_font(Arial5x12,5,12,46,57);// nur Zahlen zwischen ASCII 46 und 57
glcd_draw_string_xy(3,15,"0");
glcd_draw_string_xy(25,9,"1");
glcd_draw_string_xy(48,6,"2");
glcd_draw_string_xy(74,6,"3");
glcd_draw_string_xy( 98,9,"4");
glcd_draw_string_xy(121,15,"5");
glcd_draw_ellipse( 64, 70, 100, 50, 1);
glcd_draw_line(0, 32, 64, 100, 1);
glcd_draw_line(127, 32, 64, 100, 1);
glcd_set_font(Liberation_Sans17x17_Alpha,17,17,65,90);
glcd_draw_string_xy(50,40,"VU");
glcd_set_font(Arial5x12, 5, 12, 46, 57);// nur Zahlen zwischen ASCII 46 und 57
glcd_draw_line(7, 25, 11, 28, 1);// Pos. 0
glcd_draw_line(28, 19, 29, 23, 1); // Pos. 1
glcd_draw_line(51, 16, 52, 20, 1); // Pos. 2
glcd_draw_line(76, 16, 75, 20, 1); // Pos. 3
glcd_draw_line(99, 19, 98, 23, 1); // Pos. 4
glcd_draw_line(120, 25, 118, 27, 1); // Pos. 5
}
int parabel(uint8_t div) // Funktion zur
//Längenberechnung des Zeigers per Wurfparabel
{
uint8_t h, ref;
double v;
v = div/2; // v optimiert für 254 Pixel; für Display Division durch 2
h = ((0.683*v) - (0.0105*v*v)) - 1;// Wurfparabel konfiguriert
ref = 27 - h; // Anpassung Zeigerausgangswert (linker Anschlag)
return (ref);
}
Download: