oberallgeier
19.10.2012, 11:10
Hallo C-Spezialisten,
heute kommt wieder (m)ein C-Spezialproblem, das ich seit vier Tagen nicht lösen kann. Tests auf zwei verschiedenen Targets und Controllern (Steckbrett mit mega328/20MHz und RNControl mit m1284/20MHz) bringen gleiche Ergebnisse - soweit ein eher zufällig aussehender Fehler dieses Urteil zulässt. Recherchen zum Stackpointer bei AVR-GCC sagten mir, dass ich den SP nicht anfassen muss/müsste. Die Dokumentationen der Controller zu den Timern brachten mir auch keinen Fortschritt. Nun bleibt mir nur noch diese Anfrage.
Aufgabenstellung (Fernziel):
i) Ein Controller soll sechs bis sieben Servos treiben, maximal 8
ii) Normale Servos (bzw. die billigen Analogmodelle)
iii) Die Verstellgeschwindigkeit MUSS variabel sein
iv) Die Servos müssen unabhängig voneinander fahren
v) Mein Standardtimer 20kHz toggelt eine "Herzschlag"-LED
Lösungsweg:
v) Timer1 (16 bit) erzeugt mit Kanal-A/OCR1A ein Achtel (1/ 8 der Servoperiode
OCR1A ist üblicherweise 800 - ergibt insgesamt dann ca. 20 ms Periode
vi) Timer1 erzeugt mit Kanal-B/OCR1B die Servorampe (=Verstellwinkel)
OCR1B wird zwischen 300 und 600 schwanken - ca. 1 bis 2 ms,
getestet wurde erfolgreich Werte zwischen 280 und 620
vii) Ein Feld mit 8 int16-Werten enthält die gewünschten Rampenlängen
viii) Die Rampenlänge OCR1B ist deutlich kürzer als OCR1A - s.o.
ix) AVR-STudio4
Realisierung (derzeit vorwiegend mit konstantem Schaltpin = Kontroll-LED)
x) Timer1_A startet im ersten Durchlauf den Servo-Pin/LED und
startet Timer1_B = enable CompareB Match : TIMSK1 |= (1<<OCIE1B)
xi) Timer1_B löscht in seinem ersten Interrupt den Servopin
und disabled den eigenen Interrupt : TIMSK1 &= ~(1<<OCIE1B)
xii) Testweise wird OCR1B in den Grenzen rauf- und runtergefahren.
xiii) Ein Test mit OCR1A = 6400 <=> der Timer1_A erzeugt die 20-ms-Periode in einem Durchlauf und startet NUR einmal den Timer1_B für eine auf-und-ab-gehenden OCR1B-Rampe - läuft bisher störungsfrei. Der zugehörige Servo lässt sich mit Stellgeschwindigkeit von "normal" bis ca. 1/4 der üblichen Geschwindigkeit praktisch ruckelfrei fahren. Langsamere Schwenkgeschwindigkeiten erzeugen mehr oder weniger deutliches Ruckeln. Dabei dreht der angeschlossene Servo mustergültig. Es scheinen nach längerer Laufzeit - mehreren Minuten - auch Fehler aufzutreten.
xiv) Das "Servosignal" wird im Oszilloskop beobachtet und durch das Leuchten der LED angezeigt.
xv) Controller und Servo werden getrennt versorgt, GND´s sind verbunden.
Später soll ein umlaufender Pointer die verschiedenen Werte der Rampenlänge als OCR1B nehmen und damit die zugehörigen Pinne schalten. Derzeit wird NUR die Kontroll-LED geschaltet.
Beobachteter Fehler:
xxi) Beim Betrieb des Timer1_A mit 20ms und nur einer Rampe - also das Servo-Standardsignal läufts problemlos.
xxii) Der Betrieb mit dem Timer1_A mit 2ms und der darin erzeugten Rampe von 1 bis <2 ms läuft teilweise minutenlang.
xxiii) Die erzeugte Rampe läuft programmgemäß rauf und runter.
xxiv) Meist fällt die erzeugte Rampe nach (dem ersten) Auf-Ab-Zyklus praktisch aus, es ist nur ein mikrosekundenlanger Peak im 2ms-Takt zu sehen.
xxv) Seltsameweise kommt die Rampe programmgerecht nach einiger Zeit wieder . . .
xxvi) Wiederholen der Initialisierung des Timer1 - am Ende eines Auf-Ab-Zyklus brachte weniger Störungen - aber keine Störungsfreiheit.
Wer es bisher gelesen hat - danke für die Aufmerksamkeit.
Frage: Wo könnte der Fehler (noch) liegen?
Danke für Eure Geduld, Aufmerksamkeit und Hilfe.
Zur Erläuterung noch der Code (einige Kommentarzeilen, Portinitialisierungen etc. rausgekürzt)
Auszug Hauptprogramm
// ================================================== ============================ =
// === HAUPTProgramm ================================================== ========= =
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int main(void)
// ......
// - - - - - - - - - - - - - - -
// Ports+Pins als Ein- (0) oder Ausgänge (1) konfigurieren, Pull Ups (1) aktivieren
// A = Ausgang, E = Eingang ohne , EU = Eingang MIT PullUp
DDRB = 0b11111111; // siehe aktuell oben
PORTB = 0b00000000; // und Port/Pull Ups (1) aktivieren
//
DDRC = 0b11111111; // PC0 .. 6 , kein PC7-Pin bei m168/328 in THT
PORTC = 0b00000000; // PC0 und ~1 sind ADC-Eingänge ##>> OHNE Pullup !!
//
DDRD = 0b11111110; // -> siehe unter DDRB
PORTD = 0b00001001; // Pull Ups aktivieren, NICHT bei extINT0/~1
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// ......
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TC1TMR_init(); // Init Tmr/Cntr1 für PWMs/Servo ~tmr~
// - - - - - - - - - - - - - - -
Izeit_1 = 20000; // Der ZeitHorizont für ISR(TIMER2_COMPA_vect)
Izthrznt = 20000; // Der ZeitHorizont für ISR(TIMER2_COMPA_vect)
Isecundn = 1; // Sekundenzähler, max 9 Stunden - NUR hier nullen
TC2TMR_init(); // Init Timer/Cntr2-Interrupt 20 kHz/50 µsec ~tmr~
// ISR gibt auf PC3 ein Taktsignal aus
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sei(); //Globalen Interrupt freigeben
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
init_USART0(MYUBRR); // USART0 initialisieren m wählbarer Baudrate (s.o.) ~inf~
// ......
// ================================================== ============================ =
// Testabschnitt(e).
// ================================================== ============================ =
// ......
uart_puts("\tEs folgt Aufruf I2CTST01()\r\n");
I2CTST01(); //
// ......
// ===== Ende main
// ================================================== ==============================
/* Es folgt der aktuelle Übersetzungskommentar:
Build started 19.10.2012 at 09:51:33
avr-gcc -mmcu=atmega328p -Wall -gdwarf-2 -std=gnu99
-DF_CPU=20000000UL -Os -funsigned-char -funsigned-bitfields
-fpack-struct -fshort-enums -MD -MP -MT R5Sv1.o
-MF dep/R5Sv1.o.d -c ../R5Sv1.c
avr-gcc -mmcu=atmega328p -Wl,-Map=R5Sv1.map R5Sv1.o -o R5Sv1.elf
avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature
R5Sv1.elf R5Sv1.hex
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load"
--change-section-lma .eeprom=0 --no-change-warnings
-O ihex R5Sv1.elf R5Sv1.eep || exit 0
avr-objdump -h -S R5Sv1.elf > R5Sv1.lss
AVR Memory Usage
----------------
Device: atmega328p
Program: 1566 bytes (4.8% Full)
(.text + .data + .bootloader)
Data: 408 bytes (19.9% Full)
(.data + .bss + .noinit)
Build succeeded with 0 Warnings...
================================================== ========== */
Auszug header
// ================================================== ============================ =
// ================================================== ============================ =
// ##### Variablenliste, global #####
// ================================================== ============================ =
// ================================================== ============================ =
//
#define SetBit(ADDR,BIT) ((ADDR) |= (1<<(BIT))) // Setzt Bit
#define ClrBit(ADDR,BIT) ((ADDR) &= ~(1<<(BIT))) // Löscht Bit
#define ToggleBit(ADDR,BIT) ((ADDR) ^= (1<<(BIT))) // Toogelt Bit
// - - - - - - - - - - - - - - - -
// ......
volatile int16_t Izeit_1; // Timer mit 20 kHz Wertbereich int16: 32.767
volatile int16_t Izthrznt; // Der zeitliche Horizont, z.B. 20000 für 1 sec
volatile int16_t Isecundn; // Sekunden Programmlaufzeit, 32.767 sec sind ...
// ================================================== ============================ =
// ================================================== ============================ =
Auszug Testroutine:
// ================================================== ============================ =
// == Routine zum "Normalbetrieb"
// ================================================== ============================ =
void SrvTST_03(void) // Servo Testbetrieb, übernommen aus :
// war SrvTST_01 in ~r1n01d
{ //
uint16_t ilng; // Siehe for-Loops in RCBB == 25 und RCBB == 6
uint16_t nochnl; // for-Loop "nochnloop"
uint16_t PWMmin, PWMmax; // PWM-Grenzen
uint16_t srvwt; // wait-Millisekunden für Servoloop
// - - - - - - - - - - - - - - -
PWMmin = 300; //
PWMmax = 640; //
srvwt = 25; //
// - - - - - - - - - - - - - - -
uart_puts ("\tStart Servo-Test03 ~r1n01\r"); //
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
while (1) //
{ //
for (ilng = PWMmin; ilng <= PWMmax; ilng++)
{ //
cli(); // ###
OCR1B = ilng; //
sei(); // ###
waitms ( srvwt); //
} // ### Ende for (ilng = ...
// - - - - - - - - - - - - - - -
for (ilng = PWMmax; ilng >= PWMmin; ilng--)
{ //
cli(); // ###
OCR1B = ilng; //
sei(); // ###
waitms ( srvwt); //
} // ### Ende for (ilng = ...
} // ### Ende while (1)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uart_puts ("\tEnde Servo-Test03 ~r1n01\r"); //
return; // ### Ende von void SrvTST_03(void)
} //
// ================================================== ============================ =
Auszug Timer, Initialisierung-en und ISR
// ================================================== ============================ =
// == Timer Aufgabe: Servo mit Soft-PWM ansteuern auf wählbarem Port
// - - - - - - - - - - - - - - - -
void TC1TMR_init(void) // Init Timer/Counter 1 für 2 ms Servoperiode
{ //
TCCR1B |= (1<<WGM12); // WGM12 => CTC, TOP = OCR1A S133
TCCR1B |= (1<<CS11)|(1<<CS10); // CS11/10 <=> clk/64 => 312 500 Hz S134
OCR1A = 800; // Mit OCR1A = 6250 => alle 20 ms ein Interrupt
TIMSK1 |= (1<<OCIE1A); // Tmr/Cntr1 Oput CompA Mtch intrrpt enabled
}
// ================================================== ============================ =
// ================================================== ============================ =
// === Nicht unterbrechbare ISR für TIMER1_COMPA_vect ====================== =
ISR(TIMER1_COMPA_vect) // VECTOR 8 S 65
{ //
SetBit (PORTB, 2); // LED/PB2 high, wird von Timer1_COMPB gelöscht
TIMSK1 |= (1<<OCIE1B); // Tmr/Cntr1 CompB Match interrupt enabled
} //
// ================================================== ============================ =
// ================================================== ============================ =
// === Nicht unterbrechbare ISR für TIMER1_COMPB_vect ====================== =
ISR(TIMER1_COMPB_vect) // VECTOR 9 S 65
{ //
ClrBit (PORTB, 2); // LED/PB2 gelöscht
TIMSK1 &= ~(1<<OCIE1B); // Tmr/Cntr1 CompB Match interrupt disabled
} //
// ================================================== ============================ =
// ================================================== ============================ =
// === Initialisierung fuer Timer2 mega168 ===================================== =
void TC2TMR_init(void) // Init Tmr/Cntr 2, 8-Bit auf 20 kHz = 50 µs
{ //
TCCR2A |= (1<<WGM21); // Timer im CTC-Mode, Top=OCR2A doc S 157
TCCR2B |= (1<<CS21); // Prescaler 1/8 / Clock <- CPU doc S 158
OCR2A = 124; // Preset 124 für 50µs bei 20Mhz
TIMSK2 |= (1<<OCIE2A); // Tmr/Cntr2 CompareA interrupt enabled
} //
// ================================================== ============================ =
// ================================================== ============================ =
// === Nicht unterbrechbare ISR für timer2 ===================================== =
// Routine zählt hoch im Takt 20 kHz = 50 µs. Der Zählerwert wird von den ISR für
// EXT_INT0 und -INT1 ausgelesen und den Werten Iz_yseci zugewiesen
ISR(TIMER2_COMPA_vect) // Vektor 7
{ //
if (Izeit_1) //Interrupt-Timer = 1 ... 20 000 ... (1 sec blink)
{ //
Izeit_1 --; // ###>>> Izeit_1 ist aktuell int16_t ==>>
// Izeit_1 bleibt bis 32000 in der int16-Grenze
} //
else // Eine Sekunde ist voll =>
{ //
Izeit_1 = 20000; // Rückstellen auf 20000
Isecundn ++; // Sekundenzähler hochtackern, max 9 Std
ToggleBit (PORTC, L1g); // LED/PB2 gelöscht
} // Ende if (Izeit_1 < Izthrznt)
return; //
} //
// ================================================== ============================ =
// ================================================== ============================ =
// ================================================== ============================ =
//### Programm pausieren lassen !! Der Pausenwert ist nur experimentell !
void waitms(uint16_t ms)
{
for(; ms>0; ms--)
{
uint16_t __c = 4000;
__asm__ volatile (
"1: sbiw %0,1" "\n\t"
"brne 1b"
: "=w" (__c)
: "0" (__c)
);
}
}
// ================================================== ============================ =
// ================================================== ============================ =
// ===== ENDE Subroutinen ================================================== =
// ================================================== ============================ =
heute kommt wieder (m)ein C-Spezialproblem, das ich seit vier Tagen nicht lösen kann. Tests auf zwei verschiedenen Targets und Controllern (Steckbrett mit mega328/20MHz und RNControl mit m1284/20MHz) bringen gleiche Ergebnisse - soweit ein eher zufällig aussehender Fehler dieses Urteil zulässt. Recherchen zum Stackpointer bei AVR-GCC sagten mir, dass ich den SP nicht anfassen muss/müsste. Die Dokumentationen der Controller zu den Timern brachten mir auch keinen Fortschritt. Nun bleibt mir nur noch diese Anfrage.
Aufgabenstellung (Fernziel):
i) Ein Controller soll sechs bis sieben Servos treiben, maximal 8
ii) Normale Servos (bzw. die billigen Analogmodelle)
iii) Die Verstellgeschwindigkeit MUSS variabel sein
iv) Die Servos müssen unabhängig voneinander fahren
v) Mein Standardtimer 20kHz toggelt eine "Herzschlag"-LED
Lösungsweg:
v) Timer1 (16 bit) erzeugt mit Kanal-A/OCR1A ein Achtel (1/ 8 der Servoperiode
OCR1A ist üblicherweise 800 - ergibt insgesamt dann ca. 20 ms Periode
vi) Timer1 erzeugt mit Kanal-B/OCR1B die Servorampe (=Verstellwinkel)
OCR1B wird zwischen 300 und 600 schwanken - ca. 1 bis 2 ms,
getestet wurde erfolgreich Werte zwischen 280 und 620
vii) Ein Feld mit 8 int16-Werten enthält die gewünschten Rampenlängen
viii) Die Rampenlänge OCR1B ist deutlich kürzer als OCR1A - s.o.
ix) AVR-STudio4
Realisierung (derzeit vorwiegend mit konstantem Schaltpin = Kontroll-LED)
x) Timer1_A startet im ersten Durchlauf den Servo-Pin/LED und
startet Timer1_B = enable CompareB Match : TIMSK1 |= (1<<OCIE1B)
xi) Timer1_B löscht in seinem ersten Interrupt den Servopin
und disabled den eigenen Interrupt : TIMSK1 &= ~(1<<OCIE1B)
xii) Testweise wird OCR1B in den Grenzen rauf- und runtergefahren.
xiii) Ein Test mit OCR1A = 6400 <=> der Timer1_A erzeugt die 20-ms-Periode in einem Durchlauf und startet NUR einmal den Timer1_B für eine auf-und-ab-gehenden OCR1B-Rampe - läuft bisher störungsfrei. Der zugehörige Servo lässt sich mit Stellgeschwindigkeit von "normal" bis ca. 1/4 der üblichen Geschwindigkeit praktisch ruckelfrei fahren. Langsamere Schwenkgeschwindigkeiten erzeugen mehr oder weniger deutliches Ruckeln. Dabei dreht der angeschlossene Servo mustergültig. Es scheinen nach längerer Laufzeit - mehreren Minuten - auch Fehler aufzutreten.
xiv) Das "Servosignal" wird im Oszilloskop beobachtet und durch das Leuchten der LED angezeigt.
xv) Controller und Servo werden getrennt versorgt, GND´s sind verbunden.
Später soll ein umlaufender Pointer die verschiedenen Werte der Rampenlänge als OCR1B nehmen und damit die zugehörigen Pinne schalten. Derzeit wird NUR die Kontroll-LED geschaltet.
Beobachteter Fehler:
xxi) Beim Betrieb des Timer1_A mit 20ms und nur einer Rampe - also das Servo-Standardsignal läufts problemlos.
xxii) Der Betrieb mit dem Timer1_A mit 2ms und der darin erzeugten Rampe von 1 bis <2 ms läuft teilweise minutenlang.
xxiii) Die erzeugte Rampe läuft programmgemäß rauf und runter.
xxiv) Meist fällt die erzeugte Rampe nach (dem ersten) Auf-Ab-Zyklus praktisch aus, es ist nur ein mikrosekundenlanger Peak im 2ms-Takt zu sehen.
xxv) Seltsameweise kommt die Rampe programmgerecht nach einiger Zeit wieder . . .
xxvi) Wiederholen der Initialisierung des Timer1 - am Ende eines Auf-Ab-Zyklus brachte weniger Störungen - aber keine Störungsfreiheit.
Wer es bisher gelesen hat - danke für die Aufmerksamkeit.
Frage: Wo könnte der Fehler (noch) liegen?
Danke für Eure Geduld, Aufmerksamkeit und Hilfe.
Zur Erläuterung noch der Code (einige Kommentarzeilen, Portinitialisierungen etc. rausgekürzt)
Auszug Hauptprogramm
// ================================================== ============================ =
// === HAUPTProgramm ================================================== ========= =
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int main(void)
// ......
// - - - - - - - - - - - - - - -
// Ports+Pins als Ein- (0) oder Ausgänge (1) konfigurieren, Pull Ups (1) aktivieren
// A = Ausgang, E = Eingang ohne , EU = Eingang MIT PullUp
DDRB = 0b11111111; // siehe aktuell oben
PORTB = 0b00000000; // und Port/Pull Ups (1) aktivieren
//
DDRC = 0b11111111; // PC0 .. 6 , kein PC7-Pin bei m168/328 in THT
PORTC = 0b00000000; // PC0 und ~1 sind ADC-Eingänge ##>> OHNE Pullup !!
//
DDRD = 0b11111110; // -> siehe unter DDRB
PORTD = 0b00001001; // Pull Ups aktivieren, NICHT bei extINT0/~1
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// ......
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TC1TMR_init(); // Init Tmr/Cntr1 für PWMs/Servo ~tmr~
// - - - - - - - - - - - - - - -
Izeit_1 = 20000; // Der ZeitHorizont für ISR(TIMER2_COMPA_vect)
Izthrznt = 20000; // Der ZeitHorizont für ISR(TIMER2_COMPA_vect)
Isecundn = 1; // Sekundenzähler, max 9 Stunden - NUR hier nullen
TC2TMR_init(); // Init Timer/Cntr2-Interrupt 20 kHz/50 µsec ~tmr~
// ISR gibt auf PC3 ein Taktsignal aus
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sei(); //Globalen Interrupt freigeben
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
init_USART0(MYUBRR); // USART0 initialisieren m wählbarer Baudrate (s.o.) ~inf~
// ......
// ================================================== ============================ =
// Testabschnitt(e).
// ================================================== ============================ =
// ......
uart_puts("\tEs folgt Aufruf I2CTST01()\r\n");
I2CTST01(); //
// ......
// ===== Ende main
// ================================================== ==============================
/* Es folgt der aktuelle Übersetzungskommentar:
Build started 19.10.2012 at 09:51:33
avr-gcc -mmcu=atmega328p -Wall -gdwarf-2 -std=gnu99
-DF_CPU=20000000UL -Os -funsigned-char -funsigned-bitfields
-fpack-struct -fshort-enums -MD -MP -MT R5Sv1.o
-MF dep/R5Sv1.o.d -c ../R5Sv1.c
avr-gcc -mmcu=atmega328p -Wl,-Map=R5Sv1.map R5Sv1.o -o R5Sv1.elf
avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature
R5Sv1.elf R5Sv1.hex
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load"
--change-section-lma .eeprom=0 --no-change-warnings
-O ihex R5Sv1.elf R5Sv1.eep || exit 0
avr-objdump -h -S R5Sv1.elf > R5Sv1.lss
AVR Memory Usage
----------------
Device: atmega328p
Program: 1566 bytes (4.8% Full)
(.text + .data + .bootloader)
Data: 408 bytes (19.9% Full)
(.data + .bss + .noinit)
Build succeeded with 0 Warnings...
================================================== ========== */
Auszug header
// ================================================== ============================ =
// ================================================== ============================ =
// ##### Variablenliste, global #####
// ================================================== ============================ =
// ================================================== ============================ =
//
#define SetBit(ADDR,BIT) ((ADDR) |= (1<<(BIT))) // Setzt Bit
#define ClrBit(ADDR,BIT) ((ADDR) &= ~(1<<(BIT))) // Löscht Bit
#define ToggleBit(ADDR,BIT) ((ADDR) ^= (1<<(BIT))) // Toogelt Bit
// - - - - - - - - - - - - - - - -
// ......
volatile int16_t Izeit_1; // Timer mit 20 kHz Wertbereich int16: 32.767
volatile int16_t Izthrznt; // Der zeitliche Horizont, z.B. 20000 für 1 sec
volatile int16_t Isecundn; // Sekunden Programmlaufzeit, 32.767 sec sind ...
// ================================================== ============================ =
// ================================================== ============================ =
Auszug Testroutine:
// ================================================== ============================ =
// == Routine zum "Normalbetrieb"
// ================================================== ============================ =
void SrvTST_03(void) // Servo Testbetrieb, übernommen aus :
// war SrvTST_01 in ~r1n01d
{ //
uint16_t ilng; // Siehe for-Loops in RCBB == 25 und RCBB == 6
uint16_t nochnl; // for-Loop "nochnloop"
uint16_t PWMmin, PWMmax; // PWM-Grenzen
uint16_t srvwt; // wait-Millisekunden für Servoloop
// - - - - - - - - - - - - - - -
PWMmin = 300; //
PWMmax = 640; //
srvwt = 25; //
// - - - - - - - - - - - - - - -
uart_puts ("\tStart Servo-Test03 ~r1n01\r"); //
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
while (1) //
{ //
for (ilng = PWMmin; ilng <= PWMmax; ilng++)
{ //
cli(); // ###
OCR1B = ilng; //
sei(); // ###
waitms ( srvwt); //
} // ### Ende for (ilng = ...
// - - - - - - - - - - - - - - -
for (ilng = PWMmax; ilng >= PWMmin; ilng--)
{ //
cli(); // ###
OCR1B = ilng; //
sei(); // ###
waitms ( srvwt); //
} // ### Ende for (ilng = ...
} // ### Ende while (1)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uart_puts ("\tEnde Servo-Test03 ~r1n01\r"); //
return; // ### Ende von void SrvTST_03(void)
} //
// ================================================== ============================ =
Auszug Timer, Initialisierung-en und ISR
// ================================================== ============================ =
// == Timer Aufgabe: Servo mit Soft-PWM ansteuern auf wählbarem Port
// - - - - - - - - - - - - - - - -
void TC1TMR_init(void) // Init Timer/Counter 1 für 2 ms Servoperiode
{ //
TCCR1B |= (1<<WGM12); // WGM12 => CTC, TOP = OCR1A S133
TCCR1B |= (1<<CS11)|(1<<CS10); // CS11/10 <=> clk/64 => 312 500 Hz S134
OCR1A = 800; // Mit OCR1A = 6250 => alle 20 ms ein Interrupt
TIMSK1 |= (1<<OCIE1A); // Tmr/Cntr1 Oput CompA Mtch intrrpt enabled
}
// ================================================== ============================ =
// ================================================== ============================ =
// === Nicht unterbrechbare ISR für TIMER1_COMPA_vect ====================== =
ISR(TIMER1_COMPA_vect) // VECTOR 8 S 65
{ //
SetBit (PORTB, 2); // LED/PB2 high, wird von Timer1_COMPB gelöscht
TIMSK1 |= (1<<OCIE1B); // Tmr/Cntr1 CompB Match interrupt enabled
} //
// ================================================== ============================ =
// ================================================== ============================ =
// === Nicht unterbrechbare ISR für TIMER1_COMPB_vect ====================== =
ISR(TIMER1_COMPB_vect) // VECTOR 9 S 65
{ //
ClrBit (PORTB, 2); // LED/PB2 gelöscht
TIMSK1 &= ~(1<<OCIE1B); // Tmr/Cntr1 CompB Match interrupt disabled
} //
// ================================================== ============================ =
// ================================================== ============================ =
// === Initialisierung fuer Timer2 mega168 ===================================== =
void TC2TMR_init(void) // Init Tmr/Cntr 2, 8-Bit auf 20 kHz = 50 µs
{ //
TCCR2A |= (1<<WGM21); // Timer im CTC-Mode, Top=OCR2A doc S 157
TCCR2B |= (1<<CS21); // Prescaler 1/8 / Clock <- CPU doc S 158
OCR2A = 124; // Preset 124 für 50µs bei 20Mhz
TIMSK2 |= (1<<OCIE2A); // Tmr/Cntr2 CompareA interrupt enabled
} //
// ================================================== ============================ =
// ================================================== ============================ =
// === Nicht unterbrechbare ISR für timer2 ===================================== =
// Routine zählt hoch im Takt 20 kHz = 50 µs. Der Zählerwert wird von den ISR für
// EXT_INT0 und -INT1 ausgelesen und den Werten Iz_yseci zugewiesen
ISR(TIMER2_COMPA_vect) // Vektor 7
{ //
if (Izeit_1) //Interrupt-Timer = 1 ... 20 000 ... (1 sec blink)
{ //
Izeit_1 --; // ###>>> Izeit_1 ist aktuell int16_t ==>>
// Izeit_1 bleibt bis 32000 in der int16-Grenze
} //
else // Eine Sekunde ist voll =>
{ //
Izeit_1 = 20000; // Rückstellen auf 20000
Isecundn ++; // Sekundenzähler hochtackern, max 9 Std
ToggleBit (PORTC, L1g); // LED/PB2 gelöscht
} // Ende if (Izeit_1 < Izthrznt)
return; //
} //
// ================================================== ============================ =
// ================================================== ============================ =
// ================================================== ============================ =
//### Programm pausieren lassen !! Der Pausenwert ist nur experimentell !
void waitms(uint16_t ms)
{
for(; ms>0; ms--)
{
uint16_t __c = 4000;
__asm__ volatile (
"1: sbiw %0,1" "\n\t"
"brne 1b"
: "=w" (__c)
: "0" (__c)
);
}
}
// ================================================== ============================ =
// ================================================== ============================ =
// ===== ENDE Subroutinen ================================================== =
// ================================================== ============================ =