...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;
}
}
Lesezeichen