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.
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.)
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: