// Schrittmotor-Controller mit ATtiny2313
#define F_CPU 4000000UL // 4 MHz (fuer delay.h)
// Benötigte Bibliotheken:
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
// BinaryNr.h ermöglicht eine bequeme Binärzahlen-Darstellung
#include <BinaryNr.h>
// Zuordnung der Eingänge zu den Portpins
#define STEP PD2
#define DEMOMODE PD3
#define DIRECTION PD4
#define ENABLE PD5
// Die Ansteuerungstabelle wird im StepArray[] gespeichert
#define STEP_ARRAY_LENGTH 4
#define TIMER1DIVIDER 8 // Faktor zum Verlängern des Timer1-Taktes (130 ms)
volatile uint8_t Timer1Counter = 0;
volatile uint8_t StepArrayCounter = 0; // Zeiger auf Array
volatile uint8_t StepArray[STEP_ARRAY_LENGTH] =
{
B00001010, B00000110, B00000101, B00001001
};
// Nicht vergessen - Fuses einstellen mit "AVR8 Burn-O-Mat"
// Fuse-Bits: --- für internen Clock ---
// efuse = FF
// hfuse = DF
// lfuse = E2 (4 MHz Grundtaktakt ohne Teilung)
// -> Frequenz : 4.000.000 Hz
// Timer1-Interrupt als STEP-Zeitgeber initialisieren
void Init_Timer1(void)
{
// Timer 1 ist 16-Bit-Timer
// CS12=0, CS11=1, CS10=0 => Vorteiler 8; -> 4.000.000 Hz : 8 = 500 000
// Der Overflow-Interrupt wird aber erst ausgelöst,
// wenn der Zähler die Zahl 65 536 (16 Bit) erreicht
// Zeitintervall = 500 000 : 65 536 = 7,63 Hz (ca. alle 130 ms)
// Damit lassen sich Frequenzen von ca. 1, 2 und 0,5 Hz. ableiten
TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10); // Vorteiler 8
// Enable timer 1 interrupt -> ohne die anderen Bits zu verändern
TIMSK |= (1 << TOIE1);
}
void MakeStep(void)
{
// Jeder Aufruf der Routine bringt einen Step am Schrittmotor
// Dabei werden die Schalter ENABLE und DIRECTION abgefragt
// ENABLE auf HIGH geschaltet? Dann Motorspannungen an!
if ( PIND & (1<<ENABLE) )
{
// Der Schalter DIRECTION wird abgefragt:
if ( PIND & (1<<DIRECTION) )
{
// Linkslauf bei Taster DIRECTION = HIGH
if (StepArrayCounter > 0)
{
StepArrayCounter--;
PORTB = StepArray[StepArrayCounter];
}
else
{
StepArrayCounter = STEP_ARRAY_LENGTH-1; // -1 -> ARRAY zählt 0, 1, 2, 3
PORTB = StepArray[StepArrayCounter];
}
}
else
{
//Rechtslauf bei Taster DIRECTION = LOW
PORTB = StepArray[StepArrayCounter];
StepArrayCounter++;
if (StepArrayCounter == STEP_ARRAY_LENGTH)
{
StepArrayCounter = 0;
}
}
}
else
{
PORTB = B00000000; // Alle Motorspannungen aus!
}
}
ISR(TIMER1_OVF_vect)
{
// Bei eingeschaltetem Schalter DEMOMODE wird automatisch ein Takt von ca. 500 ms erzeugt,
// indem der Takt des Timer1 von ca. 130 ms mit dem TIMER1DIVIDER multipliziert wird.
// Ein Schritt am Schrittmotor wird nur erzeugt, wenn der Schalter DEMOMODE = HIGH ist.
if (Timer1Counter < TIMER1DIVIDER)
{
Timer1Counter++;
}
else
{
// DEMOMODE auf HIGH geschaltet? Dann mache einen Step!
if ( PIND & (1<<DEMOMODE) )
{
MakeStep();
}
Timer1Counter=0; // Zurücksetzen
}
}
// Interrupt-Routine -> Verwendung des INT0 als STEP-Eingang
ISR(INT0_vect)
{
// DEMOMODE auf LOW geschaltet? Dann mache einen Step!
if ( PIND & (1<<DEMOMODE) )
{
}
else
{
MakeStep();
}
}
// Interrupt INT0 initialisieren
void INT0_Init(void)
{
MCUCR = 1<<ISC01;// INT0 mit steigender Flanke
GIMSK |= 1<<INT0; //Enable INTO in Global Interrupt Mask
}
int main(void)
{
// Port B Pin0, Pin1, Pin2, Pin3 auf Ausgang setzen
DDRB |= (1 << PIN0) | (1 << PIN1) | (1 << PIN2) | (1 << PIN3);
PORTB = B00000000; // Beim Start erst einmal auf Null setzen
// Portpins als Eingänge setzen
DDRD &= ~((1 << STEP) | (1 << DIRECTION) | (1 << ENABLE) | (1 << DEMOMODE));
// Interrupt INT0 vorbereiten:
INT0_Init();
Init_Timer1();
_delay_ms(500); // Warten bis alle Spannungen da sind
sei(); //Enable Global Interrupt
while (1)
{
}
return 0;
}