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
Auszug headerCode:// ============================================================================== = // === 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 Testroutine:Code:// ============================================================================== = // ============================================================================== = // ##### 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 Timer, Initialisierung-en und ISRCode:// ============================================================================== = // == 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) } // // ============================================================================== =
Code:// ============================================================================== = // == 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 ================================================== = // ============================================================================== =







Zitieren
Hoffentlich liegt das Ziel auch am Weg 


Lesezeichen