Hier mal meine aktuellen Ergebnisse
Ich hoffe einigermaßen sinnvoll programmiert und verständlich kommentiert.
Immerhin spreche ich GCC erst seit ner knappen Woche
Gruß Basti
Jitter konnte ich allein mit Analog Oszi nicht wirklich feststellen, und das obwohl ich alle Werte ca alle 100ms per UART ausgegeben habe.
Evtl kann das ja mal jemand nachmessen der besseres Gerät hat?
main.c
inout.hCode:#include <avr/io.h> #include <stdlib.h> #include <stdint.h> #include "inout.h" /* define CPU frequency in Mhz here if not defined in Makefile */ #ifndef F_CPU #define F_CPU 16000000UL #endif int main(void) { //Ein- und Ausgabe initialisieren init_inout(); while(1) { set_ch(1,get_ch(1)); set_ch(2,get_ch(2)); set_ch(3,get_ch(3)); set_ch(4,get_ch(4)); set_ch(5,get_ch(5)); set_ch(6,get_ch(6)); }; //Endlosschleife }
inout.cCode://Name: inout.c //Autor: Bastian Schroll //Date: 31.12.2014 //Description: //Ließt 6 getrennte Servo Kanäle über INT0 und INT1 ein //Die Eingangs Signale werden dem Programm per get_ch(KANAL) zur Verfügung gestellt //Nach Verarbeitung der Signale können diese per set_ch(KANAL,PULSLÄNGE) wieder Ausgegeben werden //Dabei werden die Signale auf PORTA.0 - .5 per Compare INT generiert #ifndef INOUT_H #define INOUT_H // ************************** // Prototypen // ************************** void init_inout(void);//Initialisiert INT0 und INT1 sowie Timer1 und Timer0 uint16_t get_ch(uint8_t channel); //Liefert aktuellen Wert zurück void set_ch(uint8_t channel, uint16_t puls); //Setzt Kanal 1-6 (Min. 1000 - Max. 2000) // ************************** // Deklarationen // ************************** uint8_t (fail_flag); //Flag für Fail Safe Modus #endif
Code://Name: inout.c //Autor: Bastian Schroll //Date: 31.12.2014 //Description: //Ließt 6 getrennte Servo Kanäle über INT0 und INT1 ein //Die Eingangs Signale werden dem Programm per get_ch(KANAL) zur Verfügung gestellt //Nach Verarbeitung der Signale können diese per set_ch(KANAL,PULSLÄNGE) wieder Ausgegeben werden //Dabei werden die Signale auf PORTA.0 - .5 per Compare INT generiert #include <avr/io.h> #include <stdint.h> #include <avr/interrupt.h> #include "inout.h" // ************************** // Prototypen // ************************** void init_inout(void);//Initialisiert INT0 und INT1 sowie Timer1 und Timer0 uint16_t get_ch(uint8_t channel); //Liefert aktuellen Wert zurück void set_ch(uint8_t channel, uint16_t puls); //Setzt Kanal 1-6 (Min. 1000 - Max. 2000) // ************************** // Deklarationen // ************************** //IN volatile uint16_t (input_channel)[7]; //Array für die einzelnen Kanäle uint8_t (fail_flag); //Flag für Fail Safe Modus volatile uint16_t (input_channel_tmp)[7]; //Array für Timer1 Messwerte uint8_t (in_ch_cnt); //Counter für aktuellen Kanal uint8_t (fail_cnt); //Zähler für Fehlmessungen //OUT volatile uint16_t (output_channel)[7]; //Array für die einzelnen Kanäle uint32_t (sig_rest) = 320000; //Wird auf 20ms gesetzt und dann wird jede Ch Ausgabe abgezogen. Was übrig bleibt muss Pause sein uint8_t (out_ch_cnt); //Counter für aktuellen Kanal void init_inout(void) { //PORTA als Ausgang DDRA = 0b11111111; //INT0 und INT1 als Eingang (vermutlich unnötig bei INT Konfiguration?) DDRD &= ~((1 << DDD2) | (1 << DDD3)); //Beide INT auf steigene Flanke konfigurieren MCUCR |= ((1 << ISC01) | (1 << ISC00)); //INT0 MCUCR |= ((1 << ISC11) | (1 << ISC10)); //INT1 //INT0 und INT1 aktivieren GICR |= ((1 << INT0) | (1 << INT1)); //Timer0 auf Pres 256 (~4ms) TCCR0 |= (1 << CS02); //Timer0 Overflow INT aktivieren TIMSK |= (1 << TOIE0); //Timer1 Prescaler auf 1 stellen TCCR1B |= (1 << CS10); //Timer1 CompareA Interrupt aktivieren TIMSK |= (1 << OCIE1A); //Interrupts anschalten sei(); }; //ISR von INT0 ISR(INT0_vect) { //speichere Zählerstand von Timer1 input_channel_tmp[in_ch_cnt] = TCNT1; //Ch Zähler erhöhen in_ch_cnt++; //Timer0 zurücksetzen TCNT0 = 0; //bei letztem Kanal INT0 auf fallende Flanke if(in_ch_cnt == 6){MCUCR &= ~(1 << ISC00);} } //ISR von INT1 ISR(INT1_vect) { //speichere Zählerstand von Timer1 input_channel_tmp[in_ch_cnt] = TCNT1; //Ch Zähler erhöhen in_ch_cnt++; //Timer0 zurücksetzen TCNT0 = 0; //bei letztem Kanal INT0 auf fallende Flanke if(in_ch_cnt == 6){MCUCR &= ~(1 << ISC10);} } //Erkennt die lange Synronitäts Pause im Eingangssignal //Wenn länger als 4ms kein Interrupt passiert wird diese ISR geworfen. ISR(TIMER0_OVF_vect) { //Prüfen ob wirklich alle INT kamen (Alle Signale erfasst) if(in_ch_cnt == 7) { //Wenn ja, Fail Flag und Zähler zurücksetzen fail_flag = 0; fail_cnt = 0; uint8_t i; for (i=1; i<=6; i++){ //Aktuelles Eingangssignal = Endwert - Startwert input_channel[i] = input_channel_tmp[i] - input_channel_tmp[i-1]; } } else { //Wenn mehr als 5 Fehlmessungen detektiert wurden, wird das Fail Flag gesetzt fail_cnt++; if(fail_cnt >= 5){fail_flag = 1;} } //Kanalzähler zurücksetzen in_ch_cnt = 0; //Beide INT auf steigene Flanke konfigurieren MCUCR |= ((1 << ISC01) | (1 << ISC00)); //INT0 MCUCR |= ((1 << ISC11) | (1 << ISC10)); //INT1 } //Kümmert sich um die Ausgabe der 6 Signale ISR(TIMER1_COMPA_vect) { //Wenn Ch1 beginnt, PORTA.0 auf High if(out_ch_cnt == 0) { PORTA = 1; } //Solange noch nicht alle Channel abgearbeitet if(out_ch_cnt <= 6) { //Bit in PORTA weiter schieben PORTA = (1 << out_ch_cnt); //Kanal Zähler erhöhen out_ch_cnt ++; //CompareA Register von T1 auf den Wert des Kanals setzen OCR1A = TCNT1 + output_channel[out_ch_cnt]; //Zeit von den 20ms Gesamtlänge abziehen sig_rest -= output_channel[out_ch_cnt]; } else //Wenn alle Kanäle durch sind { //PORTA abschalten PORTA = 0; //Prüfen ob noch mehr Zeit, als ein kompletter Durchlauf abzuwarten ist if(sig_rest > 65536) { //Einen vollen Zyklus abwarten und vom Rest-Zeitzähler abziehen OCR1A = TCNT1 + 65536; sig_rest -= 65536; } else//Wenn kein kompletter Durchlauf mehr möglich ist { //Die restliche Zeit warten und alles für einen neuen Durchgang voreinstellen OCR1A = TCNT1 + sig_rest; out_ch_cnt = 0; sig_rest = 320000 - 60000; //Entsprich 20ms bei Timer1 16MHZ/Prescaler1 //Da dir ISR natürlich auch ein wenig Rechnenzeit verbraucht ziehen wir einfach 60.000 Ticks ab //Damit kommen wir dann laut Oszi auf exakt 20ms Versatz zwischen den Pulsen (ausporbiert) } } } //Liefert Wert des Kanals zurück uint16_t get_ch(uint8_t channel) { return input_channel[channel] / 16; } //Zum setzen einzelner Kanäle void set_ch(uint8_t channel, uint16_t puls) { //Wert auf Min und Max begrenzen if(puls < 1000){puls = 1000;} else if(puls > 2000){puls = 2000;} output_channel[channel] = puls * 16; }







Zitieren

Lesezeichen