Downad
04.01.2007, 22:46
hallo RN,
da ich im moment noch probleme habe meine programme auf das RC-Control (ATMEGA32 16MHz) zu flashen wende ich mich an euch.
könntet ihr den code mal anschauen ob der geht, oder noch wichtiger ob ich da logische fehler drin habe?
der servo soll in dem beispiel an PORTB.1 hängen
Idee: (abgewandelt aus einem c-Programm um die LED flackern zu lassen).
ich setze einen IRQ alle 10µs - damit kann ich die 1ms die ich zum ansteuern des Servos habe in 100 schritte unterteilen kann. (1ms - 2ms= 1ms + X)
in der ISP zähle ich mit interrupt_num_20ms_Servo1 bis ich bei 20 ms bin (Pause zwischen den signalen)
dann setze ich das signal für 1 - 2 ms refresh
#include <avr/io.h>
#include <avr/interrupt.h>
// Der Servo soll an B1 hängen
//
// PortB.1 und GND anschliessen.
#define PAD_Servo1 1
#define PORT_Servo PORTB
#define DDR_Servo DDRB
// Der MCU-Takt. Wird gebraucht, um Timer1 mit den richtigen
// Werten zu initialisieren. Voreinstellung ist 1MHz.
// (Werkseinstellung für AVRs mit internem Oszillator).
// Das Define wird nur gemacht, wenn F_CPU noch nicht definiert wurde.
// F_CPU kann man so auch per Kommandozeile definieren, z.B. für 8MHz:
// avr-gcc ... -DF_CPU=8000000
//
// ! Der Wert von F_CPU hat rein informativen Character für
// ! die korrekte Codeerzeugung im Programm!
// ! Um die Taktrate zu ändern müssen die Fuses des Controllers
// ! und/oder Quarz/Resonator/RC-Glied/Oszillator
// ! angepasst werden!
#ifndef F_CPU
#define F_CPU 16000000
#endif
// So viele IRQs werden jede Sekunde ausgelöst.
// Für optimale Genauigkeit muss
// IRQS_PER_SECOND ein Teiler von F_CPU sein
// und IRQS_PER_SECOND ein Vielfaches von 100.
// Ausserdem muss gelten F_CPU / IRQS_PER_SECOND <= 65536
#define IRQS_PER_SECOND 10000 /* 10 µs */
// Anzahl IRQs pro 10 Millisekunden
#define IRQS_PER_10MS (IRQS_PER_SECOND / 100)
// Gültigkeitsprüfung.
// Bei ungeeigneten Werten gibt es einen Compilerfehler
#if (F_CPU/IRQS_PER_SECOND > 65536) || (IRQS_PER_10MS < 1) || (IRQS_PER_10MS > 255)
# error Diese Werte fuer F_CPU und IRQS_PER_SECOND
# error sind ausserhalb des gueltigen Bereichs!
#endif
// Compiler-Warnung falls die Genauigkeit nicht optimal ist.
// Wenn das nervt für deine Werte, einfach löschen :-)
#if (F_CPU % IRQS_PER_SECOND != 0) || (IRQS_PER_SECOND % 100 != 0)
# warning Das Programm arbeitet nicht mit optimaler Genauigkeit.
#endif
// Prototypen
void timer1_init();
// Variablen. Werden in der ISR benötigt
static volatile uint8_t timer_10ms;
// Die Pulslänge zwischen den Servosignalen = 20 ms
static volatile uint8_t interrupt_num_20ms_Servo1;
// Die Pulslänge zum einstellen des Servos, sie variiert von 1ms - 2 ms
static volatile uint8_t interrupt_num_Xms_Servo1;
// hat die ISP eine Interrupt ausgelöst (20ms PulsPause auf Servo fertig)
// wird IRQ_Servo1 auf 1 gesetzt, so dass das Main-Programm bescheid weiß.
// IRQ_Servo1 wird in Main gelöscht
static volatile uint8_t IRQ_Servo1;
// IRQ_Servo1_refresh wird in der ISP auf true gesetz wenn die 20ms um sind
// und gelöscht nach dem refresh des Servo1_steuercode - Pulses
static volatile uint8_t IRQ_Servo1_refresh;
// Der Servo1_steuercode + 1ms entpricht der Pulslänge
// zum Ansteuern des Servo1
// Servo1_steuercode ist in 100 schritte zu 10µs aufgeteilt
static volatile uint8_t Servo1_steuercode; //wird auch in der main verwendet
// //////////////////////////////////////////////////////////////////////
// Implementierungen der Funktionen
// //////////////////////////////////////////////////////////////////////
#if !defined (TCNT1H)
#error Dieser Controller hat keinen 16-Bit Timer1!
#endif // TCNT1H
// //////////////////////////////////////////////////////////////////////
// Timer1 so initialisieren, daß er IRQS_PER_SECOND
// IRQs pro Sekunde erzeugt.
void timer1_init()
{
// Timer1: keine PWM
TCCR1A = 0;
// Timer1 ist Zähler: Clear Timer on Compare Match (CTC, Mode #4)
// Timer1 läuft mit vollem MCU-Takt: Prescale = 1
#if defined (CTC1) && !defined (WGM12)
TCCR1B = (1 << CTC1) | (1 << CS10);
#elif !defined (CTC1) && defined (WGM12)
TCCR1B = (1 << WGM12) | (1 << CS10);
#else
#error Keine Ahnung, wie Timer1 fuer diesen AVR zu initialisieren ist!
#endif
// OutputCompare für gewünschte Timer1 Frequenz
// TCNT1 zählt immer 0...OCR1A, 0...OCR1A, ...
// Beim überlauf OCR1A -> OCR1A+1 wird TCNT1=0 gesetzt und im nächsten
// MCU-Takt eine IRQ erzeugt.
OCR1A = (unsigned short) ((unsigned long) F_CPU / IRQS_PER_SECOND-1);
// OutputCompareA-Interrupt für Timer1 aktivieren
#if defined (TIMSK1)
TIMSK1 |= (1 << OCIE1A);
#elif defined (TIMSK)
TIMSK |= (1 << OCIE1A);
#else
#error Keine Ahnung, wie IRQs fuer diesen AVR zu initialisieren sind!
#endif
}
// //////////////////////////////////////////////////////////////////////
// Die Interrupt Service Routine (ISR).
// In Interrupt_num_20ms werden die IRQs gezählt.
// Sind 2*IRQS_PER_10MS Interrups geschehen,
// dann sind 20 ms vergangen. und der Servo benötigt einen neuen Impuls
SIGNAL (SIG_OUTPUT_COMPARE1A)
{
// interrupt_num_20ms_Servo1 erhöhen und mit Maximalwert vergleichen
if (++interrupt_num_20ms_Servo1 == (2*IRQS_PER_10MS))
{
// 20 Millisekunden sind vorbei
// interrupt_num_20ms_Servo1 zurücksetzen
interrupt_num_20ms_Servo1 = 0; //evtl. unnötig hier
// IRQ_Servo1 setzen
IRQ_Servo1=1; //damit kann man in main reagieren
// SETZE IRQ_Servo1_refresh setzen damit der refresh gemacht werden kann
IRQ_Servo1_refresh=1;
interrupt_num_Xms_Servo1=0; //dient nur dazu den servo zusand zu refreshen
} //endif (++interrupt_num_20ms == 2*(IRQS_PER_10MS))
//sende an Servo1 den Steuercode
if (IRQ_Servo1_refresh==1)
{
PORT_Servo |= (1 << PAD_Servo1);
//diesen zustand 1ms - 2 ms lang halten
//abhängig von Servo1_steuercode
// 1ms + Servo1_steuercode
if (++interrupt_num_Xms_Servo1 == (IRQS_PER_10MS/10 + Servo1_steuercode))
{
// SERVO1 aus
PORT_Servo &= ~(1 << PAD_Servo1);
// LÖSCHE IRQ_Servo1_refresh
IRQ_Servo1_refresh=0;
interrupt_num_20ms_Servo1 = 0;
} //endif (++interrupt_num_Xms_Servo1 == (IRQS_PER_10MS/10+Servo1_steuercode))
} //endif (IRQ_Servo1_refresh==1)
}
// //////////////////////////////////////////////////////////////////////
// Das Hauptprogramm: Startpunkt
int main()
{
int iCount_Down=1; //Runter oder hochzählen 1= TRUE = runterzählen
int IRQ_Servo1_Count =0; // Zähler für das Warten bei Servo1
//Servo1_steuercode in einem Interval von 1..100 (100 * 10µs = 1ms)
// wird die Position des Servos angegeben.
// 50 ist ein mittlerer Wert
// static uint8_t Servo1_steuercode = 50;
Servo1_steuercode = 50;
// SERVO-Port auf OUT
DDR_Servo |= (1 << PAD_Servo1);
// Timer1 initialisieren
timer1_init();
// Interrupts aktivieren
sei();
// Endlosschleife
while (1)
{
if (IRQ_Servo1!=0)
{
//IRQ_Servo1 löschen
IRQ_Servo1=0;
//Warte solange bis sich Servo1 erneut bewegen soll
// z.B. 100 ms = 10 * IRQS_PER_10MS
// im gewählten Beispiel sind das
// also muss ein IRQ_Servo1_Count her und abgeüprüft werden.
IRQ_Servo1_Count++;
if (IRQ_Servo1_Count> (10*IRQS_PER_10MS))
{
// den Zähler auf null setzen für den nächstnen durchlauf
IRQ_Servo1_Count =0;
//1/100 nach links und danach nach rechts und wieder nach links
if (Servo1_steuercode>0 && iCount_Down==1)
{
Servo1_steuercode--;
}
if (Servo1_steuercode<=0)
{
iCount_Down=0;
}
if (Servo1_steuercode<100 && iCount_Down==0)
{
Servo1_steuercode++;
}
if (Servo1_steuercode>=100)
{
iCount_Down=1;
}
} //endif (IRQ_Servo1_Count> (10*IRQS_PER_10MS))
} //endif (IRQ_Servo1!=0)
} //end while (1)
return 0;
// main braucht eigentlich keine return-Anweisung, aber ...
}
edit: code mit mehr kommentaren versehen
da ich im moment noch probleme habe meine programme auf das RC-Control (ATMEGA32 16MHz) zu flashen wende ich mich an euch.
könntet ihr den code mal anschauen ob der geht, oder noch wichtiger ob ich da logische fehler drin habe?
der servo soll in dem beispiel an PORTB.1 hängen
Idee: (abgewandelt aus einem c-Programm um die LED flackern zu lassen).
ich setze einen IRQ alle 10µs - damit kann ich die 1ms die ich zum ansteuern des Servos habe in 100 schritte unterteilen kann. (1ms - 2ms= 1ms + X)
in der ISP zähle ich mit interrupt_num_20ms_Servo1 bis ich bei 20 ms bin (Pause zwischen den signalen)
dann setze ich das signal für 1 - 2 ms refresh
#include <avr/io.h>
#include <avr/interrupt.h>
// Der Servo soll an B1 hängen
//
// PortB.1 und GND anschliessen.
#define PAD_Servo1 1
#define PORT_Servo PORTB
#define DDR_Servo DDRB
// Der MCU-Takt. Wird gebraucht, um Timer1 mit den richtigen
// Werten zu initialisieren. Voreinstellung ist 1MHz.
// (Werkseinstellung für AVRs mit internem Oszillator).
// Das Define wird nur gemacht, wenn F_CPU noch nicht definiert wurde.
// F_CPU kann man so auch per Kommandozeile definieren, z.B. für 8MHz:
// avr-gcc ... -DF_CPU=8000000
//
// ! Der Wert von F_CPU hat rein informativen Character für
// ! die korrekte Codeerzeugung im Programm!
// ! Um die Taktrate zu ändern müssen die Fuses des Controllers
// ! und/oder Quarz/Resonator/RC-Glied/Oszillator
// ! angepasst werden!
#ifndef F_CPU
#define F_CPU 16000000
#endif
// So viele IRQs werden jede Sekunde ausgelöst.
// Für optimale Genauigkeit muss
// IRQS_PER_SECOND ein Teiler von F_CPU sein
// und IRQS_PER_SECOND ein Vielfaches von 100.
// Ausserdem muss gelten F_CPU / IRQS_PER_SECOND <= 65536
#define IRQS_PER_SECOND 10000 /* 10 µs */
// Anzahl IRQs pro 10 Millisekunden
#define IRQS_PER_10MS (IRQS_PER_SECOND / 100)
// Gültigkeitsprüfung.
// Bei ungeeigneten Werten gibt es einen Compilerfehler
#if (F_CPU/IRQS_PER_SECOND > 65536) || (IRQS_PER_10MS < 1) || (IRQS_PER_10MS > 255)
# error Diese Werte fuer F_CPU und IRQS_PER_SECOND
# error sind ausserhalb des gueltigen Bereichs!
#endif
// Compiler-Warnung falls die Genauigkeit nicht optimal ist.
// Wenn das nervt für deine Werte, einfach löschen :-)
#if (F_CPU % IRQS_PER_SECOND != 0) || (IRQS_PER_SECOND % 100 != 0)
# warning Das Programm arbeitet nicht mit optimaler Genauigkeit.
#endif
// Prototypen
void timer1_init();
// Variablen. Werden in der ISR benötigt
static volatile uint8_t timer_10ms;
// Die Pulslänge zwischen den Servosignalen = 20 ms
static volatile uint8_t interrupt_num_20ms_Servo1;
// Die Pulslänge zum einstellen des Servos, sie variiert von 1ms - 2 ms
static volatile uint8_t interrupt_num_Xms_Servo1;
// hat die ISP eine Interrupt ausgelöst (20ms PulsPause auf Servo fertig)
// wird IRQ_Servo1 auf 1 gesetzt, so dass das Main-Programm bescheid weiß.
// IRQ_Servo1 wird in Main gelöscht
static volatile uint8_t IRQ_Servo1;
// IRQ_Servo1_refresh wird in der ISP auf true gesetz wenn die 20ms um sind
// und gelöscht nach dem refresh des Servo1_steuercode - Pulses
static volatile uint8_t IRQ_Servo1_refresh;
// Der Servo1_steuercode + 1ms entpricht der Pulslänge
// zum Ansteuern des Servo1
// Servo1_steuercode ist in 100 schritte zu 10µs aufgeteilt
static volatile uint8_t Servo1_steuercode; //wird auch in der main verwendet
// //////////////////////////////////////////////////////////////////////
// Implementierungen der Funktionen
// //////////////////////////////////////////////////////////////////////
#if !defined (TCNT1H)
#error Dieser Controller hat keinen 16-Bit Timer1!
#endif // TCNT1H
// //////////////////////////////////////////////////////////////////////
// Timer1 so initialisieren, daß er IRQS_PER_SECOND
// IRQs pro Sekunde erzeugt.
void timer1_init()
{
// Timer1: keine PWM
TCCR1A = 0;
// Timer1 ist Zähler: Clear Timer on Compare Match (CTC, Mode #4)
// Timer1 läuft mit vollem MCU-Takt: Prescale = 1
#if defined (CTC1) && !defined (WGM12)
TCCR1B = (1 << CTC1) | (1 << CS10);
#elif !defined (CTC1) && defined (WGM12)
TCCR1B = (1 << WGM12) | (1 << CS10);
#else
#error Keine Ahnung, wie Timer1 fuer diesen AVR zu initialisieren ist!
#endif
// OutputCompare für gewünschte Timer1 Frequenz
// TCNT1 zählt immer 0...OCR1A, 0...OCR1A, ...
// Beim überlauf OCR1A -> OCR1A+1 wird TCNT1=0 gesetzt und im nächsten
// MCU-Takt eine IRQ erzeugt.
OCR1A = (unsigned short) ((unsigned long) F_CPU / IRQS_PER_SECOND-1);
// OutputCompareA-Interrupt für Timer1 aktivieren
#if defined (TIMSK1)
TIMSK1 |= (1 << OCIE1A);
#elif defined (TIMSK)
TIMSK |= (1 << OCIE1A);
#else
#error Keine Ahnung, wie IRQs fuer diesen AVR zu initialisieren sind!
#endif
}
// //////////////////////////////////////////////////////////////////////
// Die Interrupt Service Routine (ISR).
// In Interrupt_num_20ms werden die IRQs gezählt.
// Sind 2*IRQS_PER_10MS Interrups geschehen,
// dann sind 20 ms vergangen. und der Servo benötigt einen neuen Impuls
SIGNAL (SIG_OUTPUT_COMPARE1A)
{
// interrupt_num_20ms_Servo1 erhöhen und mit Maximalwert vergleichen
if (++interrupt_num_20ms_Servo1 == (2*IRQS_PER_10MS))
{
// 20 Millisekunden sind vorbei
// interrupt_num_20ms_Servo1 zurücksetzen
interrupt_num_20ms_Servo1 = 0; //evtl. unnötig hier
// IRQ_Servo1 setzen
IRQ_Servo1=1; //damit kann man in main reagieren
// SETZE IRQ_Servo1_refresh setzen damit der refresh gemacht werden kann
IRQ_Servo1_refresh=1;
interrupt_num_Xms_Servo1=0; //dient nur dazu den servo zusand zu refreshen
} //endif (++interrupt_num_20ms == 2*(IRQS_PER_10MS))
//sende an Servo1 den Steuercode
if (IRQ_Servo1_refresh==1)
{
PORT_Servo |= (1 << PAD_Servo1);
//diesen zustand 1ms - 2 ms lang halten
//abhängig von Servo1_steuercode
// 1ms + Servo1_steuercode
if (++interrupt_num_Xms_Servo1 == (IRQS_PER_10MS/10 + Servo1_steuercode))
{
// SERVO1 aus
PORT_Servo &= ~(1 << PAD_Servo1);
// LÖSCHE IRQ_Servo1_refresh
IRQ_Servo1_refresh=0;
interrupt_num_20ms_Servo1 = 0;
} //endif (++interrupt_num_Xms_Servo1 == (IRQS_PER_10MS/10+Servo1_steuercode))
} //endif (IRQ_Servo1_refresh==1)
}
// //////////////////////////////////////////////////////////////////////
// Das Hauptprogramm: Startpunkt
int main()
{
int iCount_Down=1; //Runter oder hochzählen 1= TRUE = runterzählen
int IRQ_Servo1_Count =0; // Zähler für das Warten bei Servo1
//Servo1_steuercode in einem Interval von 1..100 (100 * 10µs = 1ms)
// wird die Position des Servos angegeben.
// 50 ist ein mittlerer Wert
// static uint8_t Servo1_steuercode = 50;
Servo1_steuercode = 50;
// SERVO-Port auf OUT
DDR_Servo |= (1 << PAD_Servo1);
// Timer1 initialisieren
timer1_init();
// Interrupts aktivieren
sei();
// Endlosschleife
while (1)
{
if (IRQ_Servo1!=0)
{
//IRQ_Servo1 löschen
IRQ_Servo1=0;
//Warte solange bis sich Servo1 erneut bewegen soll
// z.B. 100 ms = 10 * IRQS_PER_10MS
// im gewählten Beispiel sind das
// also muss ein IRQ_Servo1_Count her und abgeüprüft werden.
IRQ_Servo1_Count++;
if (IRQ_Servo1_Count> (10*IRQS_PER_10MS))
{
// den Zähler auf null setzen für den nächstnen durchlauf
IRQ_Servo1_Count =0;
//1/100 nach links und danach nach rechts und wieder nach links
if (Servo1_steuercode>0 && iCount_Down==1)
{
Servo1_steuercode--;
}
if (Servo1_steuercode<=0)
{
iCount_Down=0;
}
if (Servo1_steuercode<100 && iCount_Down==0)
{
Servo1_steuercode++;
}
if (Servo1_steuercode>=100)
{
iCount_Down=1;
}
} //endif (IRQ_Servo1_Count> (10*IRQS_PER_10MS))
} //endif (IRQ_Servo1!=0)
} //end while (1)
return 0;
// main braucht eigentlich keine return-Anweisung, aber ...
}
edit: code mit mehr kommentaren versehen