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();
 }
'Persönliche Werkzeuge