...zuletzt der Code.
Mark, ich melde mich noch einmal per E-Mail bei dir.
Code:#include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> //************************************************************** //Hardware-Konfiguration ATtiny45 //************************************************************** #define LRChannel 2 // ADC für Links/Rechts-Poti #define UDChannel 3 // ADC für Auf/Ab-Poti #define PORT PORTB // I/O-Port Output-Register) #define DDR DDRB // Data Direction Register #define PIN PINB // I/O-Port (Input-Register) #define PIN_DIR _BV(2) // Pin für Richtungsausgabe (Direction, CW/CCW) #define PIN_CLK _BV(0) // Pin für Takt-Ausgabe #define PIN_QUAD _BV(1) // Pin für Kennlinie (linear, quadratrisch) #define PIN_LED _BV(5) // Pin für LED-Anzeige //************************************************************** //*** Daten des Joysticks //************************************************************** //Mittelstellung liegt bei einem Messwert von etwa 128 (+/-10) //Minimum bei 3 (der Unterschied zu 0 ist vernachlässigbar) //Maximum liegt bei 255 //************************************************************** uint8_t LRZero = 0; //Null-Wert für LR-Kanal uint8_t UDZero = 0; //Null-Wert für UD-Kanal uint8_t LR = 0; //aktueller Messwert LR-Kanal uint8_t UD = 0; //aktueller Messwert UD-Kanal //************************************************************** //*** Definitionen der Zonen //************************************************************** typedef enum { green, //beginnt bei etwa 1/3 des Ausschlags (128/3 = ca. 42, also 128-42=86, Nullpunktkorrektur nicht notwendig) violett, //beginnt bei etwa 2/3 des Ausschlags (44) yellow, //beginnt bei etwa 1/3 des Ausschlags (170) orange, //beginnt bei etwa 2/3 des Ausschlags (212) red, //beginnt bei etwa 1/6 des Ausschlags (1/8 = UDZero + 16 (Nullpunktskorrektor notwendig)) pink, //beginnt bei etwa 1/6 des Ausschlags (1/8 = UDZero - 16 (Nullpunktskorrektor notwendig)) white, //endet bei etwa 1/3 des LR-Ausschlags und 1/6 des UD-Ausschlags (s.o.) blue //der Rest } Zones; // Beginn (ADC-Wert) der Zonen: #define StartGreen 86 #define StartViolett 44 #define StartYellow 170 #define StartOrange 212 #define StartContZone 16 //************************************************************** //*** Globale Variablen //************************************************************** volatile int8_t Direction = 0; // Richtung der Drehung (-1: links, 0: Stopp, 1: rechts) für kontinuierlichen Takt volatile uint16_t LedCnt = 0; // ISR zählt auf 0 und schaltet dann LED aus. volatile uint8_t OldSpeed = 0; // Merker für alte Geschwindigkeit (0 falls vorher Stopp) volatile uint16_t OvfsCnt = 0; // Zähler für Geschwindigkeit. ISR zählt auf 0 und startet dann wieder mit OvfsFill. // Bei 0 wird jewiels ein Taktimpuls von 0,256 ms Dauer erzeugt. volatile uint16_t OvfsFill = 0; // Nach Ablauf einer Zählperiode wird wieder bei diesem Wert gestartet. //************************************************************** //*** Prototypen //************************************************************** uint8_t readADC(uint8_t channel); // ADC auslesen Zones GetZone(void); // Zone ermitteln in der der Joystick steht void HandleZones(void); // Statemachine für den Joystick void DoSingleStep(int8_t Direction); // erzeugt einzelnes Clock-Signal void SetSpeed(uint8_t Speed, int8_t Dir); // ermittelt die Timer-Paramter bei kontinuierlichem Takt inline void LedOn(uint16_t ms) // Schaltet die LED für ms Millisekunden an { cli(); LedCnt = ms * 4; sei(); } inline void LedOff() // Schaltet die LED vorzeitig wieder aus { cli(); LedCnt = 0; sei(); } //************************************************************** //*** Hauptprogramm //************************************************************** int main(void) { DDR |= PIN_DIR | PIN_CLK | PIN_LED; //Pins auf Output PORT |= PIN_QUAD; //Pullup einschalten //LED für ca. 1/2 Sec. an _delay_ms(500); PORT |= PIN_LED; //LED aus TCCR0B |= _BV(CS01); //Timer aktivieren. Prescaler: 8. OVF-Int erfolgt alle 256µs (3.9 kHz) bei 8MHz Systemtakt #if defined (__AVR_ATmega1284P__) TIMSK0 |= _BV(TOIE0); //OVF-Interrupt freigeben #else TIMSK |= _BV(TOIE0); //OVF-Interrupt freigeben #endif sei(); // enable Interrupts // ADC-Werte in Mittelstellung ermitteln LRZero = readADC(LRChannel); UDZero = readADC(UDChannel); while(1) // immer wieder den Zustandsautomaten aufrufen { HandleZones(); } } //************************************************************** //*** Zustandsautomat für Joystick-Bewegungen //************************************************************** void HandleZones(void) { Zones Zone = GetZone(); if((Zone != red) && (Zone != pink)) //Motor abschalten { Direction = 0; OldSpeed = 0; } if(Zone == blue) { while (GetZone() != white); //Blau kann nur durch weiß aufgelöst werden. return; } if(Zone == orange) { //SingleStep + LedOn(150); DoSingleStep(1); while (GetZone() != white); //Weiß muss erreicht werden, bevor es weitergeht LedOff(); return; } if(Zone == violett) { //SingleStep - LedOn(150); DoSingleStep(-1); while (GetZone() != white); //Weiß muss erreicht werden, bevor es weitergeht LedOff(); return; } if(Zone == red) { //SingleStep + SetSpeed(UD - UDZero - StartContZone, 1); return; } if(Zone == pink) { //SingleStep - Direction = -1; SetSpeed((UDZero - StartContZone) - UD, -1); return; } } //************************************************************** //*** berechnet die Paramter für die Timer-ISR //************************************************************** //Speed: 0..112, proportional zur Auslenkung in den Zonen "Rot" und "Rosa" (256/2-StartContZone). void SetSpeed(uint8_t Speed, int8_t Dir) { uint16_t SchrittFrequenz; uint16_t OVFs; if(Speed == 0) Speed=1; if(Speed > 112) Speed = 112; if(PIN & PIN_QUAD) { // quadratischer Verlauf // Schrittfrequenz = 0,003 * Speed ^ 2 + 0,025 * Speed + 2 SchrittFrequenz = (uint32_t)3 * (uint32_t)Speed * (uint32_t)Speed / (uint32_t)1000 + (uint32_t)25 * (uint32_t)Speed / (uint32_t)1000 + 2; } else { // linearer Verlauf // Schrittfrequenz = 0,36 * Speed + 2 SchrittFrequenz = (uint16_t) 36 * (uint16_t) Speed / (uint16_t)100 + 2; } //OVFs = Anzahl OVF-Interrupts für Periodendauer = 1.000.000 / 256 / Schrittfrequenz OVFs = (uint16_t)3900 / SchrittFrequenz; cli(); OvfsFill = OVFs; if(OldSpeed) { if(OldSpeed > Speed) // soll langsamer werden {/* if(OvfsCnt < OvfsFill) OvfsCnt = OvfsFill; // Zeit verkürzen Das würde dazu führen, dass bei zunehmender Verlangsamung OvfsCnt immer wieder mit neuen Werten geladen würde und nie auf 0 kommen würde. Dass dies im gegengesetzen Fall (siehe nächstes "if") funktioniert, liegt am asymetrischen Algorithmus. Der Timer zählt herunter! */ } if(OldSpeed < Speed) // soll schneller werden { if(OvfsCnt > OvfsFill) OvfsCnt = OvfsFill; // Zeit verkürzen } } else { OvfsCnt = 1; // Bewirkt, dass nach einem Stopp der erste Takt unmittelbar erfolgt. } Direction = Dir; sei(); OldSpeed = Speed; } //************************************************************** //*** Ermittlung der Zone, in der der Joystick steht //************************************************************** Zones GetZone() { LR = readADC(LRChannel); UD = readADC(UDChannel); if((UD < UDZero + StartContZone) && (UD > UDZero - StartContZone)) // Test auf violett, grün, gelb und orange { if(LR < StartViolett) return violett; if(LR < StartGreen) return green; if(LR > StartOrange) return orange; if(LR > StartYellow) return yellow; return white; } if ((LR > StartGreen) && (LR < StartYellow)) { if (UD >= UDZero + StartContZone) return red; else return pink; } return blue; } //************************************************************** //*** ADC auslesen //************************************************************** uint8_t readADC(uint8_t channel) { uint8_t i; uint16_t result = 0; // Den ADC aktivieren und Teilungsfaktor auf 64 stellen ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1); // Kanal des Multiplexers wählen // VCC als Referenzspannung verwenden (also ca. 5V) ADMUX = channel | (0<<REFS1) | (0<<REFS0); // Den ADC initialisieren und einen sog. Dummyreadout machen ADCSRA |= (1<<ADSC); while(ADCSRA & (1<<ADSC)); // Jetzt 3x die analoge Spannung and Kanal channel auslesen // und dann Durchschnittswert ausrechnen. for(i=0; i<3; i++) { //Eine Wandlung ADCSRA |= (1<<ADSC); // Auf Ergebnis warten... while(ADCSRA & (1<<ADSC)); result += ADCW/4; } // for // ADC wieder deaktivieren ADCSRA &= ~(1<<ADEN); result /= 3; return (uint8_t)result; } // readADC //************************************************************** // EinzelSchritt durchführen //************************************************************** void DoSingleStep(int8_t Direction) { //Direction setzen if(Direction > 0) PORT |= PIN_DIR; else PORT &= ~PIN_DIR; _delay_us(10); //Puls für Schritt ausgeben (Pulslänge 0.256 ms) PORT |= PIN_CLK; _delay_us(256); PORT &= ~PIN_CLK; _delay_us(10); } //************************************************************** //Die ISR regelt folgende zeitabhängigen Funktion //Takterzeugung //LED-Steuerung //************************************************************** ISR(TIMER0_OVF_vect) { if(LedCnt) { PORT &= ~PIN_LED; LedCnt--; } else PORT |= PIN_LED; if(!Direction) //Direction ist 0, also Stopp. Keine Takte notwendig return; if(Direction > 0) //Direction Pin setzen PORT |= PIN_DIR; else PORT &= ~PIN_DIR; if(OvfsCnt == 0) //Puls für Schritt ausgeben (Pulslänge 0.256 ms = 1 OVF) { OvfsCnt = OvfsFill; PORT |= PIN_CLK; LedOn(50); } else { OvfsCnt--; PORT &= ~PIN_CLK; } }







Zitieren

Lesezeichen