Proportionale Kolbentanksteuerung Software V2
aus Modellbauwiki, der freien Wissensdatenbank
Proportionale Kolbentanksteuerung für Modellunterseeboote
Ursprünglicher Author Zoltan
Die Positionserfassung erfolg mit Hilfe eines Inkrementalgebers. Diese besteht aus zwei Lichtschraken, Reflexkopplern, oder auch Hall-Sensoren, welche eine an der Zahstange montierte schwarz-weisse Scheibe abtasten. Nach jedem Einschalten muss zuerst der komplette Kolben abgefahren werden, um die Position des Kolbens definieren zu können. Dies erfolg automatisch. Die Werte für die 'position', 'puls_l'.. können am PC verfolgt werden. Jedoch werden diese Werte als 2x8bit gesendet, was ein spezielles Terminalprogramm erfordert, welches die zwei 8-Bit Werte wieder zuammenfügt.
Wer keine Lust hat, das Programm zu übersetzen (compilieren), für den steht das fertige Hex-File zum Downlod bereit. Um das HEX-File auf einen Mikrocontroller zu "Brennen" benötigt man einen Programmieradapter. Das ist hier näher erklärt.
Bekannte Bugs: (Stand 05.05.2008) 1. Steht die Kolbenstange beim Einschalten im unmittelbaren Nahbereich eines der beiden Endschalter, so läuft der Motor möglicher Weise in die falsche Richtung an und presst den Kolben ohne weitere Abschaltung in die mechanischen Endstellungen. Je nach Auslegung des Motors sind mechanische und/oder elektronische Defekte(H-Brücke) wahrscheinlich. Hier ist eine weitere elektronische oder logische Verriegelung unerlässlich. 2. Erfassung der MAX-MIN Werte bzw. der Empfängersignalfilter arbeiten fehlerhaft und scheinbar nicht generell mit allen Empfängern zusammen. Nach Erfassung und Speicherung der MAX-MIN Werte, werden diese im Testlauf nur auf einen Bruchteil der Gesamtkolbenweglänge umgesetzt. Der Gesamtkolbenweg wird somit offenbar nicht fehlerfrei auf die MAX-MIN Werte umgesetzt oder diese selbst werden im Kalibriervorgang nicht korrekt ermittelt.
#include <compat/deprecated.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <inttypes.h>
//Um Register auch bitweise in Binärformat ansprechen zu können
//Der Doppelpfeil nach links und die Zahl dahinter gibt an um wieviel Binärstellen das Bit nach links geschoben wird.
//Die 8 Bits (0 bis 7) ergeben zusammen ein Byte, also ein unsigned char.
//Der senkrechte Strich ist ein logisches oder und bewirkt hier ein Verknüpfung.
#define BIN8(b7,b6,b5,b4,b3,b2,b1,b0) ((unsigned char) \
(b0 << 0) | (b1 << 1)| (b2 << 2)| (b3 << 3) | (b4 << 4) | (b5 << 5) | (b6 << 6) | (b7 << 7))
// DDR und Pul-Ups etsprechend anpassen !!!!!!!!!
#define led_b 5 // PORTC Pin x
#define led_a 4 // PORTC Pin y
#define led_c 3 // PORTC Pin z
#define endschalter_voll 2 // PORTC Pin x
#define endschalter_leer 1 // PORTC Pin x
// DDR und Pul-Ups etsprechend anpassen !!!!!!!!!
// ACHTUNG !!! Nie 1., und 2. oder 3. und 4. gleichzeitig einschalten -> H-Brücke kurzgeschlossen -> Futsch
uint8_t bbm = 1; // Break before make: Es muss sichergestellt sein, dass einer der beiden
// FET auf einer Seite getrennt hat, bevor der andere leitend wird:
// 1 -> 20ms Pause zwischen dem Umschalten.
uint8_t spiel= 2; // x Schritte bevor der Kolben die Soll-Position erreicht wird bereits der Strom
// vom Motor genommen und die Bremse aktiviert. 3 Spritze mit Motor
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
uint8_t pwm_verzoegerung = 6; // Abhänging von der Motorgröße: 8 grosser Motor, 4 Spritze mit Motor
// Spiel einstellen, z.B. wenn Sensor auf größtem Zahnrad montiert (0?)
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// pwm_verzoegerung an Motor anpassen
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#define leerlauf() {PORTB &=~_BV(1); PORTB |= _BV(2); PORTB |= _BV(4); PORTB &=~_BV(5);}
////////////////////////////////////////////////////////////////////////////////////////// // Prototypen ////////////////////////////////////////////////////////////////////////////////////////// void kalibrieren_tank(void); uint16_t u16from2u08(uint8_t a,uint8_t b); void uart_send_u8(uint8_t y); void uart_send_u16(uint16_t x); void motorsteuerung(void); void filter(void); void delay(uint32_t p); void bremsen(void); void links(void); void rechts(void); void bremsen_kalibrieren_autopilot(void); void links_kalibrieren_autopilot(void); void rechts_kalibrieren_autopilot(void); void EEPROM_write(unsigned int uiAddress, unsigned char ucData); unsigned char EEPROM_read(unsigned int uiAddress);
//////////////////////////////////////////////////////////////////////////////////////////
// Varaiblen...
//////////////////////////////////////////////////////////////////////////////////////////
int8_t quadrature_value;
int8_t TCNT0_reset_value=100;
int8_t pause_links=0, pause_rechts=0, pause=12;
int32_t position;
int16_t Puls_l;
int16_t wert_position;
int16_t Kalibrationswert;
int16_t wert_position_alt;
uint8_t Kalibrierung_fertig=0;
int16_t Puls_l_filter[16]={1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500};
int16_t alte_Puls_l;
int8_t li=0, re=0, br=0;
int16_t Puls_l_max=1850, Puls_l_min=1150;
uint16_t time_for_setup=0;
uint8_t Uart_Recieved[]={0,0,0};
// Variables stored in 1Byte
struct {
uint16_t b_l:2; //
uint16_t b_r:2; //
uint16_t b_kalib:2; //
uint16_t b_kalib_puls:1; //
uint16_t b_i:3; //
}x; // accessing values with x.b_l=...
//////////////////////////////////////////////////////////////////////////////////////////
// MAIN
//////////////////////////////////////////////////////////////////////////////////////////
int main(void) {
PORTB=BIN8(0,0,0,0,0,0,0,0); // pull up PINs... (nur wenn in DDR als Eingang def)
PORTC=BIN8(0,0,0,0,0,1,1,0); // pull up PINs... (nur wenn in DDR als Eingang def)
PORTD=BIN8(1,1,0,0,0,1,1,1); // pull up PINs... (nur wenn in DDR als Eingang def)
DDRB= BIN8(1,1,1,1,1,1,1,1); // Datenrichtung PortB (hier komplett als Ausgang)
DDRC= BIN8(0,0,1,1,1,0,0,1); // Datenrichtung PortC
DDRD= BIN8(0,0,0,0,0,0,0,0); // Datenrichtung PortD 0=Eingang 1=Ausgang
UBRRL=12; // Set baudrate 38400 (8MHz/ 16*(UBRR+1)=baud =12
UCSRB|= _BV(RXCIE); // UART RX complete interrupt enable
TIMSK=(1<<TOIE0); // TOIE0: Timer/Counter0, Overflow Interrupt Enable
TCCR0=(1<<CS02); // Start timer0 with CK/256, 8MHz
OCR2 = 125; // TIMER2 initialisation, PWM für Stannungsvervielfacher, Puls/Pausen Verhältniss 1:1
TCCR2 = (1<<WGM20)|(1<<COM21)|(1<<COM20)|(1<<CS21); // start - prescale:8 ~2,5kHZ
quadrature_value=(PIND&(BIN8(1,1,0,0,0,0,0,0)>>6)); // PD6, PD7 sind Signaleingänge vom Drehesignalgeber
// Hier wird der EEPROM mit den Stadard Kalibrierungswerten geladen, sofern noch nach dem Brennen
// keine Daten drinn stehen sollten.
if((EEPROM_read(0x00)==0xFF) & ((EEPROM_read(0x01)==0xFF))) {
EEPROM_write( 0x0,(1093 & 0xFF)); // Low Wert -> EEPROM (Puls_l_min)
EEPROM_write( 0x1,(1093 >> 8)); // High Wert ->EEPROM
EEPROM_write( 0x2,(1900 & 0xFF)); // Low Wert -> EEPROM (Puls_l_max)
EEPROM_write( 0x3,(1900 >> 8)); // High Wert ->EEPROM
EEPROM_write( 0x4,(83 & 0xFF)); // Low Wert -> EEPROM
EEPROM_write( 0x5,(83 >> 8)); // High Wert ->EEPROM
}
// Wenn der Kalibriereungs-Jumper (PD0 auf Masse) gesetzt wird, dann Verzweigt das Programm wärend des Kalibrierens zum
// Messen der Pulslänge des Empfängers ( ~1..2ms und die gesammten ~20ms werden Gemessen und ins EEProm abgelegt)
if(bit_is_set(PIND,0)) {
x.b_kalib_puls=0;
}
else {
x.b_kalib_puls=1;
delay(75000);
}
// nach jedem Einschalten muss der Kolbenweg erfasst werden, damit der Kolben eine definierte Position bekommt
kalibrieren_tank ();
// Dauerschleife. Die Daten werden alle Interuptgesteuert übergeben
for(;;){}
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////
// Erfassen der Kolbengröße bei Neustart
//////////////////////////////////////////////////////////////////////////////////////////
void kalibrieren_tank (void) {
if((Kalibrierung_fertig==0) & (x.b_kalib_puls==0)) {
uint8_t i; // Getriebe fährt bis zu einem der beiden Endschalter. Es kann auch nur einer der beiden Endschalter
// für beide Endstellungen verwendet werden, falls es konstruktiv günstiger ist !
links_kalibrieren_autopilot(); // Motor fährt los nach "links"
sei(); // enable global interrupt
delay(75000); // Warte 0,100 s.
i=0;
do { // Entprellen
// Nur wenn 100 Scans hinterreinander der Endschalter den Stop Signal gibt, erfolgt Kalibration
if((bit_is_clear(PINC,endschalter_voll) ) | ( bit_is_clear(PINC,endschalter_leer))) { i++; }
else { i=0; }
} while(i<100);
// Fährt vom Endschalter los nach rechts. Der Motor muss zuerst von dem Endschalter losfahren, so dass dieser wieder ausgeschaltet wird.
// Dann erst wird der NULLPUNKT für die Kolbenlänge gesetzt. Damit wird errecht, dass der Endschalter nie im Betrieb betätigt wird.
bremsen_kalibrieren_autopilot(); // Motor stop
delay(250000); // Warte 0,33 s
rechts_kalibrieren_autopilot();
do {
asm volatile ("nop");
} while((bit_is_clear(PINC,endschalter_voll) ) | ( bit_is_clear(PINC,endschalter_leer)));
delay(250000); // Warte 0,33 s
bremsen_kalibrieren_autopilot();// Motor stop
position=0; // Null-Wert erfasst
delay(250000); // Warte 0,33 s
rechts_kalibrieren_autopilot(); // fährt weiter nach rechts
delay(900000); // Warte ~ 1,2 s
i=0;
do { // Entprellen
// Nur wenn 100 Scans hinterreinander der Endschalter den Stop Signal gibt erfolgt Kalibration
if((bit_is_clear(PINC,endschalter_voll) ) | ( bit_is_clear(PINC,endschalter_leer))) { i++; }
else { i=0; }
} while(i<100);
// Fährt vom Endschalter nach links los. Der Motor muss zuerst von dem Endschalter losfahren, so dass dieser wieder
// ausgeschaltet wird. Dann erst wird die gesammte Kolbenlänge erfasst. Damit wird errecht, dass der Endschalter nie im Betrieb betätigt wird.
bremsen_kalibrieren_autopilot();// Motor stop
delay(250000); // Warte 0,33 s
links_kalibrieren_autopilot();
do {
asm volatile ("nop");
} while((bit_is_clear(PINC,endschalter_voll) ) | ( bit_is_clear(PINC,endschalter_leer)));
delay(250000); // Warte 0,33 s
bremsen_kalibrieren_autopilot(); // Motor stop
uart_send_u16(Puls_l); // Wert an PC
uart_send_u16(Puls_l); // Wert an PC
uart_send_u16(Puls_l); // Wert an PC
uart_send_u16(Puls_l); // Wert an PC
uart_send_u16(TCNT0_reset_value);// Wert an PC
uart_send_u16(TCNT0_reset_value);// Wert an PC
uart_send_u16(TCNT0_reset_value);// Wert an PC
uart_send_u16(TCNT0_reset_value);// Wert an PC
delay(250000); // Warte 0,33 s
cli();
// Empfängersignal wird ab jetzt auch erfasst
MCUCR=(0<<ISC01)|(1<<ISC00); // any logical change creates interrupt0
GIMSK=(1<<INT0); // enable external int0 (PD2)
// Puls_Überwachung /Autopilot
// Initialiesieren des Autopiloten. Wenn 100ms kein Puls kommt, dann wird ein Interrupt ausgelöst und
// die eine bestimmte Pulsänge vorgegeben ,siehe SIGNAL(SIG_OVERFLOW1).
TIMSK|=_BV(TOIE1); // TOIE1: Timer/Counter1, Overflow Interrupt Enable
TCNT1H=0; // Setup timer1 for 65ms
TCNT1L=0;
TCCR1B=(1<<CS11); // Start Timer, CK/8
Kalibrationswert=position;
Kalibrierung_fertig=1; // Kallibration abgeschlossen
x.b_l=0;
x.b_r=0;
UCSRB|= _BV(RXEN); // UART recieve on
sei(); // globale Interrupts ein
}
else {
sei();
MCUCR=(0<<ISC01)|(1<<ISC00); // any logical change creates interrupt0
GIMSK=(1<<INT0); // enable external int0 (PD2)
x.b_kalib=0; // Messen der Zeit für die Einstellung des polling, nämlich 1/4 des gesammten Pulslänge (~20ms)
MCUCR=(1<<ISC01)|(1<<ISC00); // The rising edge of INT0 generates an interrupt request.
GIMSK=(1<<INT0); // enable external int0 (PD2)
// Reset timer0 101 -> alle 5ms Prüfen der beiden Reflexkoppler-Signale
delay(50000); // Warte 0,066 s bis die gesammte Pulslänge erfasst wurde
TCNT0_reset_value=(255-(Puls_l/128)); // Überprüfen des Drehegebers alle 1/4 des gesammten Servo Pulsänge (20ms)
TCNT0=TCNT0_reset_value; // Aus den Puls_l~=200000 wird der entsprechende Wert für den Timer0 (Polling) berechnet.
MCUCR=(0<<ISC01)|(1<<ISC00); // any logical change creates interrupt0
GIMSK=(1<<INT0); // enable external int0 (PD2)
Kalibrierung_fertig=1; // Kallibration abgeschlossen
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// Verzögerungs (Delay) Funktion
//////////////////////////////////////////////////////////////////////////////////////////
void delay(uint32_t p) {
uint32_t j;
for(j = 0; j < p; j++) { asm volatile ("nop"); }
}
//////////////////////////////////////////////////////////////////////////////////////////
// input, mesures puls lenght from R/C reciever
//////////////////////////////////////////////////////////////////////////////////////////
SIGNAL(SIG_INTERRUPT0) { // Signal handler für den externen Interrupt int0
if(Kalibrierung_fertig==1) {
if(bit_is_clear(PIND,2)) { // Nach dem Interrupt wird überprüft, ob der Zustand high
// oder low ist d.h. timer1 starten oder stopen.
TCCR1B=0; // Stop timer1
uint8_t a=TCNT1L;
uint8_t b=TCNT1H; // READ LByte FIRST !
Puls_l=u16from2u08(a,b);
// Puls_Überwachung /Autopilot
// Initialiesieren des Autopiloten. Wenn 100ms kein Puls kommt, dann wird ein Interrupt ausgelöst und
// die eine bestimmte Pulsänge vorgegeben ,siehe SIGNAL(SIG_OVERFLOW1).
TIMSK|=_BV(TOIE1); // TOIE1: Timer/Counter1, Overflow Interrupt Enable
TCNT1H=0; // Setup timer1 for 65ms
TCNT1L=0;
TCCR1B=(1<<CS11); // Start Timer, CK/8
// CK/8 =>2, 8MHz..
uart_send_u16(Puls_l); // Wert an PC
// Durchlauf der Hauptprogrammschleife mit Filtern und Motorsteuerung immer nur nach neuen Pulsen vom Empfänger
// und nicht jedesmal, wenn der Status der Lichschranken/Reflexkopplern abgefragt werden
// ( Abfrage mit polling -> Timer0 overflow ).
filter(); // filter(): Filtert unuzlässige Pulslängen, Dämpfung gegen "Servozittern"
if(x.b_kalib_puls==0) {
motorsteuerung();
}
uart_send_u16(Puls_l); // Wert an PC
uart_send_u16(wert_position);// Wert an PC
}
else {
TCCR1B=0; // Stop timer1, Puls_Überwachung...
TCNT1H=0; // Reset timer1, Puls_Überwachung..
TCNT1L=0;
TCCR1B|=_BV(CS11); // Timer Setup, CK/8, 8MHz, 2ms-->2000 cycles
TCNT0=255; // TRIGGERN des Polling Sgnals: Reset timer0
uart_send_u16(abs(position)); // Wert an PC
}
}
else {
if(x.b_kalib==0) { // Messen der PulsLänge zwischen zwei positiven Flanken. Der Wert wird dann durch 4 geteilt, um den Timer0 damit zu steuern
TCCR1B=0; // Stop timer1
TCNT1H=0; // Reset timer1
TCNT1L=0;
TCCR1B|=_BV(CS11); // Timer Setup, CK/8, 8MHz
x.b_kalib=1;
}
else {
TCCR1B=0; // Stop timer1
uint8_t a=TCNT1L;
uint8_t b=TCNT1H; // READ LByte FIRST!!!!!!!!!!
Puls_l=u16from2u08(a,b);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// Timer Überlauf für timer 1 (16bit)
//////////////////////////////////////////////////////////////////////////////////////////
SIGNAL(SIG_OVERFLOW1) {
TCCR1B=0; // Stop Timer1
TCNT1H=0xB1; // Setup timer1 for 20ms (0x30D4)
TCNT1L=0xE0;
TCCR1B|=_BV(CS11); // Start Timer / Timer Setup, CK/8, 8MHz
uart_send_u16(Puls_l); // Sendet wärend der Kalibration die aktuelle Position
// Kolbenposition bei Senderausfall hier eingeben 0...~1000 voll-leer (anstelle (Puls_l_max- X ))
Puls_l=(Puls_l_max-2); // Position für den Fall, wenn Sender ausfällt
filter();
motorsteuerung();
}
//////////////////////////////////////////////////////////////////////////////////////////
// TIMER0 SIG_OVERFLOW
//////////////////////////////////////////////////////////////////////////////////////////
INTERRUPT(SIG_OVERFLOW0) { // Timer0 (8 bit) overflow interrupt
TCNT0=TCNT0_reset_value; // TRIGGERN des Polling Sgnals: Reset timer0 ~ alle 5ms Prüfen der beiden Reflexkoppler-Signale
int8_t table[]={0,1,-1,0,-1,0,0,1,1,0,0,-1,0,-1,1,0};
PORTC ^= _BV(0); //Anzeige am Oszi BIT toggeln : hin- und herschalten
quadrature_value=((quadrature_value<<2) | ((PIND&BIN8(1,1,0,0,0,0,0,0)) >>6)) & BIN8(0,0,0,0,1,1,1,1);
position+=table[quadrature_value]; // bin: 1,1,0,0,0,0,0,0 --> PD7,PD6 sind AB
// XY seien die alten Zustände, mit denen die neuen Zustände xy verarbeitet werden:
// 1.) ......XY << 2 --> ....XY..
// 2.) 11000000 & PIND --> xy000000 hier die Pins auswählen ( bei der Schaltung PD7,PD6 )
// 3.) xy000000 >> 6 --> 000000xy
// 4.) ....XY.. | 000000xy --> ....XYxy
// 5.) ....XYxy & 00001111 --> 0000XYxy liefert eine Zahl, die aus ' table[] ' den passenden
// Zustand wählt, aslo entweder +1, -1 oder 0
PORTC =(((PIND&BIN8(1,1,0,0,0,0,0,0))>>2) | ( PORTC&BIN8(1,1,0,0,1,1,1,1))); // für Oszi PIND 6,7 an PORTC 4,5
if(Kalibrierung_fertig==0) {
uart_send_u16(position); // Sendet während der Kalibration die aktuelle Position
}
// PWM in der Nähe der Soll Position
if((((Puls_l-1000)>(wert_position-pwm_verzoegerung)) & ((Puls_l-1000)<(wert_position - spiel))) & ((li==bbm) & (br==0))) {
if(bit_is_set(PINB,1)) {
PORTB |= _BV(2);
PORTB |= _BV(4);
}
else {
PORTB |=_BV(1);
PORTB &=~_BV(4);
}
}
if((((Puls_l-1000)>(wert_position + spiel)) & ((Puls_l-1000)<(wert_position+pwm_verzoegerung))) & ((re==bbm) & (br==0))) {
if(bit_is_set(PINB,5)) {
PORTB |= _BV(2);
PORTB |= _BV(4);
}
else {
PORTB &=~_BV(2);
PORTB |=_BV(5);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// Erfassen der Kolbengröße bei Neustart
//////////////////////////////////////////////////////////////////////////////////////////
void motorsteuerung(void) {
wert_position=((position*1000)/(Kalibrationswert)); //( ist immer positiv )
// wert_position=500; //test test test
if((bit_is_set(PINC,endschalter_voll)) && (bit_is_set(PINC,endschalter_leer))) {
PORTC &= ~_BV(led_c);
x.b_l=0;
x.b_r=0;
}
else {
PORTC |= _BV(led_c);
}
// Hier wird die Position des Kolbens mit dem Signal vom Empfänger verglichen und der
// Motor entsprechend angesteuert. ( IST<->SOLL )
if(((Puls_l-1000)<(wert_position - spiel)) & (x.b_l<=bbm)) { // Motor nach Links 2: Spiel
if(((bit_is_clear(PINC,endschalter_voll) ) | ( bit_is_clear(PINC,endschalter_leer))) & (x.b_r==0)) { // Endschalter
bremsen(); // Motor STOP
x.b_l++;
x.b_r=0;
}
else {
if(pause_links==0) {
links(); // Motor nach Links
}
else {
bremsen(); // Motor STOP
pause_links--;
}
}
pause_rechts=pause; // z.B. mit pause=12: ~0,25sec pause zwischen Motorumpolen
}
if(((Puls_l-1000)>(wert_position + spiel)) & (x.b_r<=bbm)) { // Motor nach Rechts -2: Spiel
if(((bit_is_clear(PINC,endschalter_voll) ) | ( bit_is_clear(PINC,endschalter_leer))) & (x.b_l==0)) { // Endschalter
bremsen(); // Motor STOP
x.b_l=0;
x.b_r++;
}
else {
if(pause_rechts==0) {
rechts(); // Motor nach Rechts
}
else {
bremsen(); // Motor STOP
pause_rechts--;
}
}
pause_links=pause; // z.B. mit pause=12: ~0,25sec pause zwischen Motorumpolen
}
if(((Puls_l-1000)>(wert_position - spiel)) && ((Puls_l-1000)<(wert_position + spiel))) { // +-2 Spiel
bremsen(); // Motor STOP
pause_links=pause; // z.B. mit pause=12: ~0,25sec pause zwischen Motorumpolen
pause_rechts=pause;
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// H-Brücken Steuereungsfunktionen
//////////////////////////////////////////////////////////////////////////////////////////
void links(void) {
if(li<bbm) {
leerlauf();
li++;
}
else {
PORTB |=_BV(1);
PORTB &=~_BV(4);
re=0;
br=0;
}
}
void rechts(void) {
if(re<bbm) {
leerlauf();
re++;
}
else {
PORTB &=~_BV(2);
PORTB |=_BV(5);
li=0;
br=0;
}
}
void bremsen(void) {
if(br<bbm) {
PORTB &=~_BV(1);
PORTB &=~_BV(5);
br++;
}
else {
PORTB &=~_BV(2);
PORTB &=~_BV(4);
li=0;
re=0;
}
}
void bremsen_kalibrieren_autopilot(void) {
PORTB &=~_BV(1);
PORTB &=~_BV(5);
delay(15000); // Warte 0,020 s.
PORTB &=~_BV(2);
PORTB &=~_BV(4);
}
void links_kalibrieren_autopilot(void) {
leerlauf();
delay(15000); // Warte 0,020 s.
PORTB |=_BV(1);
PORTB &=~_BV(4);
}
void rechts_kalibrieren_autopilot(void) {
leerlauf();
delay(15000); // Warte 0,020 s.
PORTB &=~_BV(2);
PORTB |=_BV(5);
}
//////////////////////////////////////////////////////////////////////////////////////////
// Erzeugt aus zwei 8 bit einen 16 bit Wert
//////////////////////////////////////////////////////////////////////////////////////////
uint16_t u16from2u08(uint8_t a,uint8_t b) { // a=LowBit, b=HigBit
uint16_t tmp;
tmp=b; // 2x 8bit in ...a=LowBit
tmp<<=8; // b=HigBit
tmp|=a; // ...16bit
return tmp;
}
//////////////////////////////////////////////////////////////////////////////////////////
// Filter für Empfängersignal
//////////////////////////////////////////////////////////////////////////////////////////
void filter (void) {
// Nachdem, die MAX-MIN Werte des Senders erfasst wurden, werden die ankommenden Pulslängen überprüft. ( 1000*0,020s =20 sec)
if(time_for_setup>=1000) {
PORTC &=~ _BV(led_c); // Anzeige am Oszi
if(Puls_l<(Puls_l_min-25)) { // Filtert unzulässige Pulslängen/-spitzen aus
// Wenn eine Pulslänge ausserhalb der 1..2ms liegt, dann wird dieser ignoriert
Puls_l=alte_Puls_l;
}
if(Puls_l>(Puls_l_max+25)) {
Puls_l=alte_Puls_l;
}
alte_Puls_l=Puls_l;
if(Puls_l<Puls_l_min) { // Grenzen für Pulslänge (Puls_l_min,Puls_l_max)
Puls_l=Puls_l_min;
}
if(Puls_l>Puls_l_max) {
Puls_l=Puls_l_max;
}
}
uint8_t j;
uint16_t summe=0; //Der Durchschnitt aus den 16 letzten Pulslängen bildet die aktuelle Pulslänge. (Filterung)
for(j=15;j>0;j--) { //shiften um 1 nach links
Puls_l_filter[j]=Puls_l_filter[(j-1)];
}
Puls_l_filter[0]=Puls_l; //an die "0-te" stelle schreiben
for(j=0;j<16;j++) { //aufsummieren
summe+=Puls_l_filter[j];
}
Puls_l=(summe+8)/16; //Durchschnitt ausrechnen ... 8)/16; weil der MC nicht "symetrisch" rundet
if((time_for_setup<1000) & (x.b_kalib_puls==1)) { // Erfassen der MAX-MIN Werte des Senders ( 1000*0,020s =20 sec)
PORTC |= _BV(led_c); // Anzeige am Oszi
// Merkt sich in den ersten 5 sec nach dem Einschalten des Bootes,
if(Puls_l>Puls_l_max) { // die minimale und maximale Knüppelstellung am Sender
// Die Differenz wird so vergrössert, dass immer ein Wert zwischen
Puls_l_max=Puls_l; // 1000..und..2000 als Sender Puls_Länge zur weiteren Verarbeitung
} // bereitsteht.
if(Puls_l<Puls_l_min) {
Puls_l_min=Puls_l;
}
time_for_setup++;
if(time_for_setup==1000) {
cli();
EEPROM_write( 0x0,(Puls_l_min & 0xFF)); // Low Wert -> EEPROM
EEPROM_write( 0x1,(Puls_l_min >> 8)); // High Wert ->EEPROM
EEPROM_write( 0x2,(Puls_l_max & 0xFF)); // Low Wert -> EEPROM
EEPROM_write( 0x3,(Puls_l_max >> 8)); // High Wert ->EEPROM
EEPROM_write( 0x4,(TCNT0_reset_value & 0xFF)); // Low Wert -> EEPROM
EEPROM_write( 0x5,(TCNT0_reset_value >> 8)); // High Wert ->EEPROM
sei();
}
}
else {
if(time_for_setup==1001) {
Puls_l_min=u16from2u08(EEPROM_read(0x0),EEPROM_read(0x1)); // Lesen der gespeicherten Pulsängen aus dem EEPROM
Puls_l_max=u16from2u08(EEPROM_read(0x2),EEPROM_read(0x3)); // Lesen der gespeicherten Pulsängen aus dem EEPROM
TCNT0_reset_value=u16from2u08(EEPROM_read(0x4),EEPROM_read(0x5));
}
time_for_setup=1001;
}
// Die Puslänge wrd so umgerechnet, dass immer ein Wert zischen
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// 1000 und 2000 für die weitere Berechnung zur Verfügung steht
// ENTWEDER Kompletter Kolben mit dem Senderknüppel regelbar
Puls_l=((((Puls_l-Puls_l_min)*((10000000/(Puls_l_max-Puls_l_min))))/10000)+1000); // Die Großen Zaheln ersetzen die Kommastellen
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// "simulieren" von 2 Kommastellen
}
//////////////////////////////////////////////////////////////////////////////////////////
// USART sendet 8 bit
//////////////////////////////////////////////////////////////////////////////////////////
void uart_send_u8(uint8_t data) {
UCSRB |= _BV(TXEN); // TX aktivieren
while (bit_is_clear(UCSRA,UDRE)); // Warten, bis UDRE eins ist-->UDR ist leer
UDR=(uint8_t)data; // data_to_send in das UDR Register schreiben
}
//////////////////////////////////////////////////////////////////////////////////////////
// USART sendet 16 bit
//////////////////////////////////////////////////////////////////////////////////////////
void uart_send_u16(uint16_t data_to_send) {
uart_send_u8((uint8_t)(data_to_send & 0xFF)); // Low Wert ->PC
uart_send_u8((uint8_t)(data_to_send >> 8)); // High Wert ->PC
}
//////////////////////////////////////////////////////////////////////////////////////////
// Speichert System-Variablen( Pulslängen...) ins EEPROM
//////////////////////////////////////////////////////////////////////////////////////////
void EEPROM_write(unsigned int uiAddress, unsigned char ucData) {
while(EECR & (1<<EEWE)); // Wait for completion of previous write
EEAR = uiAddress; // Set up address and data registers
EEDR = ucData; // Write logical one to EEMWE
EECR |= (1<<EEMWE);
EECR |= (1<<EEWE); // Start eeprom write by setting EEWE
}
//////////////////////////////////////////////////////////////////////////////////////////
// Lesen der System-Variablen ( Pulslängen...) aus dem EEPROM
//////////////////////////////////////////////////////////////////////////////////////////
unsigned char EEPROM_read(unsigned int uiAddress) {
while(EECR & (1<<EEWE)); // Wait for completion of previous write
EEAR = uiAddress; // Set up address register
EECR |= (1<<EERE); // Start eeprom read by writing EERE
return EEDR; // Return data from data register
}
//////////////////////////////////////////////////////////////////////////////////////////
// Empfängt vom PC Kalibrationswerte, welche für die Anpassung an den Tauchtank nötig sind
//////////////////////////////////////////////////////////////////////////////////////////
SIGNAL(SIG_UART_RECV) {
// Stoppt den Motor / Kurzschließen des Motors (EMK-Bremse)
PORTB &=~_BV(1);
PORTB &=~_BV(5);
delay(15000); // Warte 0,020 s.
PORTB &=~_BV(2);
PORTB &=~_BV(4);
leerlauf(); // Motor geht in Leerlauf
cli();
do {
while(bit_is_clear(UCSRA, RXC)){} // Wait for data to be received
Uart_Recieved[x.b_i] = inp(UDR); // get the character
x.b_i++;
}
while(x.b_i<3); // recieve 3 values
bbm = Uart_Recieved[0];
spiel = Uart_Recieved[1];
pwm_verzoegerung = Uart_Recieved[2];
x.b_i=0;
uart_send_u16(bbm); // Sendet die Empfangenen Zahlen als Bestätigung an den PC
uart_send_u16(spiel);
uart_send_u16(pwm_verzoegerung);
uart_send_u16(0);
uart_send_u16(0);
uart_send_u16(0);
uart_send_u16(0);
uart_send_u16(0);
// Diese Pause gibt 4 sec Zeit, um die empfangenen Bestätigungswerte abzulesen, bevor das Programm weiterläuft
delay(3000000);
sei();
}
