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
Code:
#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.h
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
#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
inout.c
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;
}
Lesezeichen