- LiTime Speicher und Akkus         
Seite 2 von 3 ErsteErste 123 LetzteLetzte
Ergebnis 11 bis 20 von 25

Thema: NIBObee: Hier ist eine alternative C Library

  1. #11
    Neuer Benutzer Öfters hier
    Registriert seit
    26.02.2010
    Ort
    Kreuzung BAB3 und B470
    Beiträge
    25
    Anzeige

    LiFePo4 Akku selber bauen - Video
    Bei meiner 10 bit Variante hört man die Motoren manchmal leise piepsen
    7Khz hört man halt - aber als Nachteil finde ich das nicht. Ich fänd's cool, wenn er proportional zur Geschwindigkeit brummen tät. Irgendwas mit Prescaler 64 und Fast PWM oder so..


    Code:
    /*********************************************************************
    
    
    
    			N I B O B E E  Feste Zykluszeit für Mainloop und 
    
    			zyklische (Sensor) Ein- und (LED) Ausgabe 
    
    
    
    			Version 1.07 für alternative Nibobee C Bib von s.frings
    
    			20100411 Birger Töpelmann 
    
    
    
    *********************************************************************/
    
    #ifndef _ZYKLUS_H_
    #define _ZYKLUS_H_
    
    
    
    
    // Folgende Zeile auskommentieren für "normalen" NiboBee
    
    #define _LED6_MUX_          // 6 LEDs an PB0..PB2; PB3 ist frei
    
    /*--------------------------------------------------------------------
    
    
    - PB2 -|100R|-->(x)			es leuchtet pro Takt immer nur eine LED;
    
    - PB1 -|100R|-->(y)			ein PIN ist Plus, ein PIN Minus, ein Pin offen
    
    - PB0 -|100R|-->(z)			LED (Vor-)Widerstand 2 x 100R
    
    
    
    LED_Pattern
    
    Bit 			  Anode - Kathode  	DDRB	PORTB
    
     0	DUO links grün	(y) - (z)		..110	..010
    
     1	DUO links rot	(x) - (z)		..101	..001
    
     2	DUO rechts grün	(z) - (y)		..110	..100
    
     3	DUO rechts rot	(x) - (y)		..011	..001
    
     4	ROTAUGE links	(z) - (x)		..011	..010
    
     5	ROTAUGE rechts	(y) - (x)		..101	..100
    
    
    
    
    
    Zuordnung LED_Pattern Bit# und LED anpassbar in led_ausgabe():
    
    
    
    	case 0:{ ddr = 0x06; pin = 0x02;} break; // DUO links grün
    
    	case 1:{ ddr = 0x05; pin = 0x01;} break; // DUO links rot
    
    	case 2:{ ddr = 0x06; pin = 0x04;} break; // DUO rechts grün
    
    	case 3:{ ddr = 0x03; pin = 0x01;} break; // DUO rechts rot
    
    	case 4:{ ddr = 0x03; pin = 0x02;} break; // ROTAUGE links
    
    	case 5:{ ddr = 0x05; pin = 0x04;} break; // ROTAUGE rechts
    
    
    
    --------------------------------------------------------------------*/
    
    
    
    
    //#include "nibobee.h" 		// s.frings Bibliothek
    
    #include "zyklus.h" 		// also includes "nibobee.h"
    
    #include <stdio.h>
    #include <avr/interrupt.h>
    #include <util/atomic.h>
    #include <util/delay.h>
    
    
    /******************************************* ZYKLUS & CLOCK ***********/
    
    
    
    // 				zählt bis 1000 (1s) in 1ms Schritten
    volatile uint16_t takt_ms;
    //				zählt die Sekunden seit dem Systemstart
    volatile uint32_t takt_sec;
    // 				Zyklustimer wird in ISR inkrementiert und in Zyklus()
    
    volatile uint8_t zyklus_ms;		// überwacht und genullt
    
    
    // 	bremst die Hauptschleife auf feste Zykluszeit (Zzeit in ms)
    
    // 	organisert LED Ausgabe und Sensoren Eingabe mit Flankenerkennung
    
    uint8_t Zyklus(uint8_t Zzeit);
    
    
    
    
    
    /****************************************** Sensoren ******************/
    
    
    
    volatile uint8_t Sensors;	// Aktueller Eingabestatus
    
    volatile uint8_t SensSFL;   // Steigende Flanke erkannt
    
    volatile uint8_t SensFFL;   // Fallende Flanke erkannt
    
    
    
    /*---------------------------------------------------------------------
    
    Flankenerkennung (Trigger):
    
    
    
    		0101 Bitmuster vorher
    
    		0011 Bitmuster aktuell
    
    		0010 Bitmuster steigende Flanke
    
    		0100 Bitmuster fallende Flanke
    
    
    
    ----------------------------------------------------------------------*/
    
    
    
    void sens_eingabe();		// SensorPins lesen/Flankenerkennung
    
    
    
    #define SENS_SW1_1 readBit(Sensors,4)
    #define SENS_SW2_1 readBit(Sensors,5)
    #define SENS_SW3_1 readBit(Sensors,7)
    #define SENS_SW4_1 readBit(Sensors,6)
    
    #define SENS_SW1_S readBit(SensSFL,4)
    #define SENS_SW2_S readBit(SensSFL,5)
    #define SENS_SW3_S readBit(SensSFL,7)
    #define SENS_SW4_S readBit(SensSFL,6)
    
    #define SENS_SW1_F readBit(SensFFL,4)
    #define SENS_SW2_F readBit(SensFFL,5)
    #define SENS_SW3_F readBit(SensFFL,7)
    #define SENS_SW4_F readBit(SensFFL,6)
    
    
    
    /***************************************** LED MULTIPLEX **************/
    
    
    
    volatile uint8_t LED_Pattern; // LED Ausgabemuster
    
    volatile uint8_t LED_PattSFL;
    
    volatile uint8_t LED_PattFFL;
    
    volatile uint8_t LED_Blinker; // gesetztes Bit lässt LED blinken
    
    
    
    #ifdef _LED6_MUX_ // umgebauter NiboBee
    
    // Kompatibilität zu s.frings: entsprechendes Bit in Pattern setzen
    
    #define set_LED0(value) writeBit(LED_Pattern,1,value)
    #define set_LED1(value) writeBit(LED_Pattern,4,value)
    #define set_LED2(value) writeBit(LED_Pattern,5,value)
    #define set_LED3(value) writeBit(LED_Pattern,3,value)
    
    // Returns 1, if the LED is on
    #define LED0 readBit(LED_Pattern,1)
    #define LED1 readBit(LED_Pattern,4)
    #define LED2 readBit(LED_Pattern,5)
    #define LED3 readBit(LED_Pattern,3)
    
    
    #else // normaler NiboBee
    
    
    
    // Kompatibilität zu s.frings: entsprechendes Bit in Pattern setzen
    
    #define set_LED0(value) writeBit(LED_Pattern,0,value)
    #define set_LED1(value) writeBit(LED_Pattern,1,value)
    #define set_LED2(value) writeBit(LED_Pattern,2,value)
    #define set_LED3(value) writeBit(LED_Pattern,3,value)
    
    // Returns 1, if the LED is on
    #define LED0 readBit(LED_Pattern,0)
    #define LED1 readBit(LED_Pattern,1)
    #define LED2 readBit(LED_Pattern,2)
    #define LED3 readBit(LED_Pattern,3)
    
    
    
    
    #endif //_LED6_MUX_
    
    
    
    
    
    // meine LED Makros basieren auf LED Nummer
    
    
    
    #define LED_EIN(NR) 	LED_Pattern |=  (1 << NR);
    
    #define LED_AUS(NR) 	LED_Pattern &= ~(1 << NR); LED_Blinker &= ~(1 << NR);
    
    #define LED_BLINK(NR) 	LED_Pattern |=  (1 << NR); LED_Blinker |=  (1 << NR);
    
    #define LED_TOGGLE(NR) 	LED_Pattern ^=  (1 << NR);
    
    
    
    
    
    void led_ausgabe();         // LED Multiplex Ausgabe
    
    
    
    // Debughilfen: vordere LED links/rechts bei ea==0 aus-, 
    
    // sonst einschalten, !! schaltet jew. die andere LED aus
    
    void augeL(uint8_t ea);		
    
    void augeR(uint8_t ea);     
    
    
    
    
    
    /***************************************** LED MULTIPLEX **************/
    
    
    
    #endif // _ZYKLUS_H_


    Code:
    /*********************************************************************
    
    
    
    			N I B O B E E  Feste Zykluszeit für Mainloop und 
    
    			zyklische (Sensor) Ein- und (LED) Ausgabe 
    
    
    
    			Version 1.07 für alternative Nibobee C Bib von s.frings
    
    			20100411 Birger Töpelmann 
    
    
    
    *********************************************************************/
    
    
    
    #include "zyklus.h" 			
    #include <stdio.h>
    #include <avr/interrupt.h>
    #include <util/atomic.h>
    #include <util/delay.h>
    
    
    /***************************************** CLOCK & MOTPID ***********/
    
    
    
    volatile uint16_t takt_ms;
    volatile uint32_t takt_sec;
    
    
    
    //#define Zykluszeit 1  	// Zykluszeit der Hauptschleife in ms
    
    volatile uint8_t zyklus_ms;	// Zyklustimer wird in ISR inkrementiert
    
    							// und in Zyklus() überwacht und genullt
    
    
    
    
    /************************************************* SENS *************/
    
    
    
    volatile uint8_t Sensors = 0;	// Aktueller Eingabestatus
    
    volatile uint8_t SensSFL = 0;   // Steigende Flanke erkannt
    
    volatile uint8_t SensFFL = 0;   // Fallende Flanke erkannt
    
    
    
    
    
    void sens_eingabe()
    
    {
    
    	SensSFL = ~Sensors;  // vorherigen Zustand merken für Flankenerkennung
    
    	SensFFL =  Sensors;
    
    
    
    	Sensors ^= Sensors;  // bits nullen für neuen Zustand
    
    	Sensors |= ~(PINC & 0xf0);  // 1 == Sensor aktiv
    
    //  Hier Eingänge von Ext-Ports Pins verOdern
    
    
    
    	SensSFL &=  Sensors; // Steigende Flanke erkennen
    
    	SensFFL &= ~Sensors; // Fallende Flanke erkennen
    
    }
    
    
    
    
    /************************************************* LED **************/
    
    
    
    /*
    
    #define LED_EIN(NR) 	LED_Pattern |=  (1 << NR);
    
    #define LED_AUS(NR) 	LED_Pattern &= ~(1 << NR); LED_Blinker &= ~(1 << NR);
    
    #define LED_BLINK(NR) 	LED_Pattern |=  (1 << NR); LED_Blinker |=  (1 << NR);
    
    #define LED_TOGGLE(NR) 	LED_Pattern ^=  (1 << NR);
    
    */
    
    
    
    volatile uint8_t LED_Pattern = 0;
    
    volatile uint8_t LED_Blinker = 0;
    
    
    
    void led_ausgabe()
    
    {
    
    	static 	uint16_t  tmux  = 0;
    
    
    
    	if (0 == tmux--) tmux = 640;  // !Wert = vielfaches von 8
    
    
    
    	uint8_t  leds = LED_Pattern;  // Pattern für Ausgabe
    
    	if (tmux < 400) leds &= ~LED_Blinker; // Blinkbits
    
    
    
    #ifdef _LED6_MUX_ // umgebauter NiboBee 
    
    
    
    	uint8_t  ddr = 0;
    
    	uint8_t  pin = 0;	
    
    
    
    	if ((1 << (tmux & 0x07)) & leds) // welche LED soll leuchten?
    
    	{
    
    		switch((tmux & 0x07))
    
    		{
    
    			case 0:{ ddr = 0x06; pin = 0x02;} break; // DUO links grün
    
    			case 1:{ ddr = 0x05; pin = 0x01;} break; // DUO links rot
    
    			case 2:{ ddr = 0x06; pin = 0x04;} break; // DUO rechts grün
    
    			case 3:{ ddr = 0x03; pin = 0x01;} break; // DUO rechts rot
    
    			case 4:{ ddr = 0x03; pin = 0x02;} break; // ROTAUGE links
    
    			case 5:{ ddr = 0x05; pin = 0x04;} break; // ROTAUGE rechts
    
    //			case 6:{ ddr = 0x00; pin = 0x00;} break;
    
    //			case 7:{ ddr = 0x00; pin = 0x00;} break;
    
    		}
    
    	}
    
    	DDRB  = (DDRB  & ~0x07) | ddr; // zwei PINS als Ausgang
    
    	PORTB = (PORTB & ~0x07) | pin; // ein Pin = 1, Rest = 0
    
    
    
    #else  // nicht umgebauter NiboBee 
    
    
    
    	// Auch hier wird immer nur eine LED eingeschaltet 
    
    	// 1 * Zykluszeit Ein; 3 * Zykluszeit Aus
    
    	PORTB = (PORTB & ~0x0f) | (leds & (1 << (tmux & 3)));
    
    
    
    
    
    #endif //_LED6_MUX_
    
    
    
    }
    
    
    
    /*
    
    // Debughilfen
    
    void augeL(uint8_t ea)
    
    {
    
    	uint8_t ddr = 0;
    
    	uint8_t pin = 0;
    
    	if(ea){ ddr = 0x03; pin = 0x02;}  // ROTAUGE links
    
    	DDRB  = (DDRB  & ~0x07) | ddr; // zwei PINS als Ausgang
    
    	PORTB = (PORTB & ~0x07) | pin; // ein Pin = 1, Rest = 0
    
    }
    
    void augeR(uint8_t ea)
    
    {
    
    	uint8_t ddr = 0;
    
    	uint8_t pin = 0;
    
    	if(ea){ ddr = 0x05; pin = 0x04;}  // ROTAUGE rechts
    
    	DDRB  = (DDRB  & ~0x07) | ddr; // zwei PINS als Ausgang
    
    	PORTB = (PORTB & ~0x07) | pin; // ein Pin = 1, Rest = 0
    
    }
    
    */
    
    
    
    
    
    /******************************* TIMER 1 CONFIG  PWM & TAKT **********/
    
    
    
    // s.frings setup:
    
        // Initialize the motor PWM
        // Mode 2 = phase correct 10 bit PWM,
        // channel A+B outputs are set on compare match when upcounting
        // and cleared on compare match when downcounting.
    
    
    	//	TCCR1A =		   PWM invertiert  + 						   + 10bit PWM Betrieb
    
    	//  TCCR1A = (1<<COM1A1) | (1<<COM1A0) | (1<<COM1B1) | (1<<COM1B0) | (1<<WGM11); | (1<<WGM10);
    
    
    
    
    // Original nibobee (springob) setup
    
    	//	TCCR1A =		   PWM invertiert  + 						   +  9bit PWM Betrieb
    
    	//	TCCR1A = (1<<COM1A1) | (1<<COM1A0) | (1<<COM1B1) | (1<<COM1B0) | (1<<WGM11);
    
    
        // Use I/O clock without prescaling
    	//  TCCR1B = (1<<CS10);
    
    
    
    
    	// Overflow Interrupt enable
    //	TIMSK  |= (1<<TOIE1);
    
    // F_CPU    = 15 Mhz
    // Prescale = 1
    
    // Frequenz bei 10 bit 15000000/(2*1023)= 7.331 kHz ; OVF alle 136 uS
    // Frequenz bei  9 bit 15000000/(2* 511)=14.677 kHz ; OVF alle  68 uS
    
    
    
    
    #define T1PERIODE 136  // Periodendauer zwischen Overflows in uS
    
    
    
    /************************************* TIMER 1 OVERFLOW ISR **********/
    
    
    
    ISR(TIMER1_OVF_vect) 
    
    {
    	static uint16_t takt_us=0;
    
    
    
    		takt_us += T1PERIODE;
    
    
    		if (takt_us >= 1000) 
    
    		{
    			takt_us -= 1000;
    			takt_ms++;
    			zyklus_ms++;
    
    
    
    		}
    
    		if (takt_ms >= 1000) 
    
    		{
    			takt_ms -= 1000;
    			takt_sec++;
    
    		}
    }
    
    
    
    /**************************************** ZYKLUS & WATCHDOG **********/
    
    
    
    uint8_t Zyklus(uint8_t Zzeit)
    
    {
    
    	
    
    
    
    	if(Zzeit == 0)              // Zyklustimer Reset / Init
    
    	{
    
    	// Timer 1 Overflow Interrupt freigeben, damit die Uhr tickt
    
    	#ifdef TIMSK1
            // ATmega644
      		TIMSK1 |= (1<<TOIE1);
    	#else
            // ATmega16
      		TIMSK  |= (1<<TOIE1);
    	#endif
    
    		zyklus_ms = 0;
    
    		return(0);
    
    	}
    
    
    
    	sei();						// Interrupts freigeben
    
    
    
    	led_ausgabe();				// Ausgänge aktualisieren
    
    
    
    	uint8_t zms = zyklus_ms;    // aktuelle Zykluszeit merken
    
    	while(zyklus_ms < Zzeit){};	// Zykluszeit abwarten
    
    
    
    	sens_eingabe();				// Eingänge lesen
    
    
    
    	// -------------------- WATCHDOG ---------------------------
    
    	// es wird kein Reset/Neustart ausgelöst -> Endlosschleife	
    
    	// LED blinken im Binär Muster der erfassten Zykluszeit
    
    	if(zyklus_ms > Zzeit)  		// Zykluszeit überschritten ?
    
    	{
    
    		uint8_t zyt = 0;        // alternativ Zyklustimer (Blinken) 
    
    		if (zms > 15) zms = 15; // Zykluszeit >= 15 -> alle LED ein
    
    		while(1==1)				// Fehlerfalle Zykluszeit überschritten
    
    		{
    
    			_delay_ms(10);			// Klassische Verzögerung 
    
    			if(zyt-- == 0) zyt = 70;// Blinktakt 700ms
    
    			if(zyt > 50) {
    
     				LED_Pattern = zms;	// Ist-Zykluszeit Binär ausgeben
    
    			} else {
    
    				LED_Pattern = 0;	// LED aus für Blinklicht
    
    			}
    
    		led_ausgabe();          // LED Muster am Port ausgeben
    
    		}
    
    	}//--------------------------------------------------------
    
    	else zyklus_ms = 0;			// Zykluszeit Timer Reset
    
    	return(zms);				// Interessant bei Zykluszeit > 1:
    
    								// Wie lang war der Zyklus tatsächlich
    
    }
    Und soweit bin ich bisher mit der Main() von s.frings gekommen: hier sind noch etliche Anpassungen nötig; _delay_ms(), while{} oder blockierende Funktionen sind eigentlich No Gos. Aber ich verspreche mir davon, dass mehrere "prozesse" quasi gleichzeitig bearbeitet werden können.
    Code:
    /*********************************************************************
    
    
    
    			N I B O B E E  Projekt Alternative s.frings und Birger.T
    
    			
    
    			zum Selberbasteln und Ändern Ver.20100411
    
    
    
    *********************************************************************/
    
    
    
    #define 	Zykluszeit 1 	// Hauptschleife Zykluszeit in ms
    
    #define 	USE_SERIAL 0
    
    
    
    
    
    #include "nibobee.h"
    #include "zyklus.h"
    
    
    uint16_t previousSpeed;
    
    // Display battery status on the 4 led's
    // LED0 = >4.1V
    // LED1 = >4.3V
    // LED2 = >4.5V
    // LED3 = >4.7V
    void battery_check() {
        set_AREF(REF_256);
        for (uint8_t i=0; i<10; i++) {
    //        _delay_ms(50);
    	    for (uint8_t t=0; t<50; t++) {
            	_delay_ms(1);
    
    			led_ausgabe();
    
    		}
            set_LED0(analog(VBAT)>(410*2));
            set_LED1(analog(VBAT)>(430*2));
            set_LED2(analog(VBAT)>(450*2));
            set_LED3(analog(VBAT)>(470*2));
    //        _delay_ms(50);
    	    for (uint8_t t=0; t<50; t++) {
            	_delay_ms(1);
    
    			led_ausgabe();
    
    		}
            set_LED0(0);
            set_LED1(0);
            set_LED2(0);
            set_LED3(0);
        }
        set_AREF(REF_VCC);
        _delay_ms(50);
    }
    
    
    // Wait for start signal (touch any sensor)
    // While waiting, display debug information from sensors:
    //   LED0: Left odometer sensor
    //   LED3: Right odometer sensor
    //   LED1: System timer
    //   LED2: Center line sensor
    void wait_for_start() {
        while (!(SENS_SW1 || SENS_SW2 || SENS_SW3 || SENS_SW4)) {
            // Display status of odometry sensors while waiting
            set_LED0(ODO_L);
            set_LED3(ODO_R);
            // Display system timer (flashes every second)
            set_LED1((system_time() % 1000) < 20);
            // Display line sensor
            set_LED2(analog(LINE_C)>600);
    
        	_delay_ms(1);
    
    		led_ausgabe();
        }
        set_LED0(0);
        set_LED3(0);
        _delay_ms(10);
        while ((SENS_SW1 || SENS_SW2 || SENS_SW3 || SENS_SW4)) {}
        _delay_ms(400);
    }
    
    
    #define FORWARD 1
    #define BACKWARD 0
    
    volatile	uint8_t  running = 0;
    
    
    // Drive forward or backward.
    // Accellerate or decellerate softly to the maxSpeed.
    // The distance is measured in 1/20 wheel rotations.
    // LED0 or LED3 light if a which motor runs too fast.
    uint8_t drive(uint8_t direction, uint16_t distance, uint16_t maxSpeed) {
    
    
    static  uint16_t speed= 0;
    
    static  uint32_t lastTime=0;
    
    
    
      if(!running){
    
    
    
        if (direction) {
            set_DIR_L(1);
            set_DIR_R(0);
        }
        else {
            set_DIR_L(0);
            set_DIR_R(1);
        }
        // Start with the previous speed
    //    uint16_t speed=previousSpeed;
    //    uint32_t lastTime=system_time();
    
        speed=previousSpeed;
        lastTime=system_time();
    
        reset_odometer();
    	running = 1;
    
      }
    
       
    
    //    while (odometer_left()<distance || odometer_right()<distance) {
    
    
    	if(!(odometer_left()<distance || odometer_right()<distance)) running = 0; // Ziel erreicht
    
    	else{
    
            int16_t diff=odometer_left()-odometer_right();
            // If left motor is too fast
            if (diff>0) {
                set_LED0(1);
                PWM_L=speed/2;
                PWM_R=speed;
            }
            // If right motor is too fast
            else if (diff<0) {
                set_LED3(1);
                PWM_R=speed/2;
                PWM_L=speed;
            }
            // Speed of both motors is equal
            else {
                set_LED0(0);
                set_LED3(0);
                PWM_L=speed;
                PWM_R=speed;
            }
            // If a millisecond has elapsed, increase or decrease the speed
            // a little until the maxSpeed has been reached.
            uint32_t currentTime=system_time();
    
    		if (currentTime>lastTime) {
                if (speed<maxSpeed)
                    speed++;
                else if (speed>maxSpeed)
                    speed--;
                lastTime=currentTime;
            }
        } // vom while
        previousSpeed=speed;
    
    
    
    	return(running);
    }
    
    
    // Stop driving.
    void stop() {
        PWM_L=0;
        PWM_R=0;
        previousSpeed=0;
    
    	running = 0;
    }
    
    
    
    
    
    
    // Main program
    int main() 
    
    {
    
    	uint8_t tour = 0; // Merker für den Fahrtabschnitt
    
    	uint16_t warten = 0;
    
    
    
    //	nibobee_init();
    
    
    
        // Display battery status
        battery_check();
        // Wait for a start signal before start driving
        wait_for_start();
    
        // Drive forward and backward repeatedly, to demonstrate
        // that the drive() function keeps straight on properly.
    
    
    
    	Zyklus(0);	//Zyklustimer Reset / Init
    
    	while(1) 
    
    	{
    //		deactivate_output_bit(IO_LINE_EN); 	// Front IR Leds aus
    
    		writeBit(PORTB,PB5,0);
    
    		Zyklus(Zykluszeit);  				// Zykluszeit abwarten
    
    // 		activate_output_bit(IO_LINE_EN); 	// Front IR LEDs blitzen im Zyklustakt für
    
    										 	// Zeitmessung per IR Transistor und Oszi
    
    		writeBit(PORTB,PB5,1);
    
    		
    
    		// Kollision (oder an die Fühler getippt) = Stop
    
    
    
    		if(SENS_SW2_S || SENS_SW3_S) {
    
    				tour = 100;
    
    				stop();
    
    		}
    
    		
    
    		// Neustart bei Sensor nach vorne tippen und loslassen
    
    		if((tour == 100) && (SENS_SW1_F || SENS_SW4_F)) tour = 0;
    
    
    
            // Drive forward 2 meters, accellerating up to nearly maximum speed
          if((tour ==  0) && !drive(FORWARD,2000/6,900)) tour = 10;
            // Drive forward 30cm, decellerating down to the minimum speed
          if((tour == 10) && !drive(FORWARD,300/6,200)){
            stop();
    
    		tour = 20;
    
    		warten = 500;
    
    		}
    //	    _delay_ms(500);
          if((tour == 20) && !warten--) tour = 30;
    
            // Drive backward 2 meters, accellerating up to nearly maximum speed
          if((tour == 30) && !drive(BACKWARD,2000/6,900)) tour = 40;
            // Drive backward 30cm, decellerating down to the minimum speed
          if((tour == 40) && !drive(BACKWARD,300/6,200)) {
            stop();
    
    		tour = 50;
    
    		warten = 500;
    
    		}
    //        _delay_ms(500);
          if((tour == 50) && !warten--) tour = 0;
    
    
    
        }
        return 0;
    }
    Oder ich weiß nicht - liege ich mit meiner Vorstellung einer Controllerprogrammierung total daneben? Dann geb' ich's auf und bastel mir 'ne Logo oder zwei auf Räder...

    Eigentlich wollte ich noch ein Foto machen: Meine Erfahrung LiPos halten so lange, bis man sie dann mal braucht.. bis zum WE.

  2. #12
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    19.03.2010
    Beiträge
    161
    Ich hatte mich bemüht, ein möglichst simples Programmbeispiel abzugeben, welches zeigt, wie man die wesentlichen Funktionen der Library nutzt.

    Natürlich stören blockierende Funktionen in einer anständigen Anwendung. Das sehe ich absolut genau so. Deswegen sind in der Library solche Sachen auch nicht drin.

  3. #13
    Benutzer Stammmitglied
    Registriert seit
    12.01.2010
    Beiträge
    53
    Hallo,

    na prima, so bauen wir alle unsere eigenen Libs für die Biene Ich baue ja auch daran herum - noch ein bißchen für mich selber, weil sie meines erachtens noch nicht so den Stand zur Veröffentlichung hat. Vielleicht liege ich damit auch falsch und sollte einfach ein freies Repository auf sourceforge anlegen, damit wir alle daran herumbauen können?

    Jetzt aber zu Deiner Lib:

    Mir ist gerade noch etwas wichtiges aufgefallen:

    _Alle_ Variablen, die sowohl in den IRQs als auch in anderen Programmteilen verändert werden, mit "volatile" deklarieren! Sonst kann das unvorhersehbare Ergebnisse liefern: Der Compiler optimiert sonst unter Umständen unzulässig. Die atomaren Blöcke ersetzen _nicht_ volatile. Außerdem empfehle ich, alle vermeidbaren atomaren Blöcke wirklich wegzulassen.

    Zitat Zitat von s.frings
    Da passieren einige Dinge, die mit unnötig aufwendig erscheinen, manches ist wohl auch etwas konfus umgesetzt.
    Dazu kann ich nur den Tipp geben: Genau darüber nachzudenken, was der Autor uns wohl damit sagen wollte. Denn bei einigen Dingen hatte ich auch immer zuerst das Gefühl "Was macht er es sich so schwer???". Aber nachdem ich das Datenblatt des ATmega gelesen hatte, wusste ich oft genug, warum

    Zum Beispiel die PWM Steuerung: Wenn man die Fahrtrichtung ändert, wird der Motor zuerst gestoppt und erst beim nächsten Interrupt die neue Fahrtrichtung und Geschwindigkeit eingestellt. Das klingt ja erstmal sinnvoll, aber beim nächsten Interrupt steht der Motor noch lange nicht. Also muss man entweder viel länger warten oder man lässt es ganz bleiben.
    Prima Beispiel für das, was ich oben meinte. Der Original-Code ist an dieser Stelle tadellos korrekt, alles andere ist mutig. Denn: Es wird nicht gewartet, bis der Motor stoppt, sondern bis der Motor nicht mehr durch die PWM bestromt wird, bevor die Fahrrichtung gewechselt wird. Und das finde ich durchaus sinnvoll, den Motor nicht mitten im bestromten Zustand andersherum zu polen. Wo ich Dir recht gebe: Der Kommentar an den ISRs ist etwas blöde - denn er wartet in der Tat nicht, bis der Motor steht.

    Irgendwie nervt mich auch, dass die eigentlich winzigen Funktionen über mehrere include-Files und mehrere (nicht gleich lautende) Libraries (*.a files) verstreut sind.
    Dazu habe ich zwei Anmerkungen: Zum einen gebe ich Dir recht, dass die Lib etwas konfus aufgeteilt ist - vor allem auch die Aufteilung auf mehrere .a-Dateien habe ich gar nicht verstanden. Sinn solcher Archive ist ja gerade die Zusammenfassung mehrerer Object-Dateien ist ( .o ).

    Zum anderen sind verfolgen Libs einen anderen Ansatz als "normale" Programme. Deshalb ist es durchaus üblich und sinnvoll, die einzelnen Funktionen für die verschiedenen Peripherieteile in eigene C-Dateien zu schreiben, so etwas nennt man auch modulare Software. Das hat viele Vorteile: Wenn irgendwo ein Fehler bei einem Peripherieteil auftritt, weiß man sofort, in welcher Datei man suchen muss. Wenn mehrere Leute an einem Projekt arbeiten, hat jeder "seine" (bzw. "ihre") Dateien. Wenn man ein neues Projekt mit ein paar (aber nicht allen) gleichen Aufgaben / Peripherieteilen und ein paar neuen Teilen hat, kann man die Teile aus dem alten Projekt durch einfaches Kopieren übernehmen - und muss den Code nicht noch filetieren - welche Teile gehören jetzt wie wozu? Des weiteren sind in der Lib-Datei weiterhin die einzelnen .o-Dateien vorhanden. Der C-Compiler bindet in ein Projekt nur die .o-Dateien aus der .a-Datei ein, die wirklich eingesetzt werden. Bei nur einer .o-Datei also immer alles.

    Wenn Du also eine _echte_ Lib bauen willst, die nicht jeder für sich anpasst und so wie sie ist weiter genutzt werden können soll, rate ich Dir unbedingt zu einem modularen Aufbau.

    Übrigens ist in der Modularität und der Übernahme aus einem anderen Projekt auch die leichte Konfusität der Codes zu entnehmen: Übereinstimmende Funktionen sind von dem Nibo2 übernommen worden - schaut euch einfach mal dessen Sourcecode an

    Und ich vermisse, dass der serielle Port als standart Eingabe und Ausgabe für stdio.h eingerichtet wird, so dass Befehle wie fgets, puts, prinf, etc.funktionieren.
    Tja, klingt nach einer prima Idee An dieser Stelle müsste ich dann mal überlegen, wie das bei meiner Lib funktionieren könnte. Denn ich nutze die USART ganz anders, als eigentlich gedacht... Wie man dazwischen hin und herschalten kann - mal drüber nachdenken.

    Überhaupt stecken in Deinem Code gute Ideen, mal sehen, ob ich etwas übernehmen kann

    Viel Spaß noch beim weiter entwickeln

    Ciao bantyy

  4. #14
    Benutzer Stammmitglied
    Registriert seit
    12.01.2010
    Beiträge
    53
    Hallo,

    Zitat Zitat von BirgerT
    Was ist für die Motorenansteuerung eigentlich besser, hohe oder niedrigere PWM Frequenz?
    Das kommt darauf an

    Bei hohen Frequenzen werden die Umsteuerverluste in den Transistoren auch höher, allerdings sind diese kHz-Frequenzen alle noch nicht in einem kritischen Bereich. Ich habe aus technischen Gründen schon mit 36 kHz experimentiert - und das geht prima. Die Trägheit des Motors und die elektrischen Eigenschaften der Spulen im Motor (Strom ändert sich stetig) führen dazu, dass beim Motor bei höheren PWM-Frequenzen die eingestellte Leistung umgesetzt wird.

    Niedrige Frequenzen (unter 20 kHz) sind hörbar. Habt ihr schon mal einen IC losfahren gehört? Da gibt es eine Baureihe (oder war es doch eine ICE-Baureihe?), da ist eine PWM im hörbaren Bereich aktiv - und das klingt wie eine Tonleiter. Klingt schon bescheuert, wenn man neben dem losfahrenden Zug steht und der spielt einem eine Tonleiter vor... Das ist btw auch der Grund, warum ihr bei 7 kHz etwas hört: noch fieser sind 1 kHz. Hab ich alles ausprobiert Bin ja schließlich auch neugierig...

    Die niedrigen Frequenzen haben den Vorteil, dass dort ein höheres Anfahrtmoment zur Verfügung steht. Schließlich werden die Spulen im Motor dort über einen längeren Zeitraum voll bestromt, in dem sie das volle Moment auf die Achse übertragen können.

    In der Praxis habe ich herausgefunden, dass es kaum einen Unterschied macht, ob man mit einer 36 kHz oder einer 1 kHz PWM arbeitet. Lediglich der minimal notwendige PWM-Wert sinkt geringfügig - aber nicht der Rede Wert.

    Übrigens ist meine Erfahrung, dass sich die Motoren ab etwa 1/8 Vollgas in Bewegung setzen, vorher steht alles still. Ich halte eine "Auflösung" von 10 Bit in der Praxis nicht für relevant. Selbst mit 8 Bit lassen sich die Motoren noch ganz entspannt genau genug steuern. Ich habe mich übrigens für Timer-Mode 10 entschieden: Damit kann ich eine beliebige "Auflösung" und damit Frequenz einstellen.

    Und warum ich immer so mit 36 kHz rumwurschtel: Ich würde mir da gerne einen IR-Sender (RC5) mit dran hängen. Zur Zeit bin ich allerdings noch bei einem Empfänger. Der ist mir erstmal wichtiger. Allerdings ist mein Ansatz nicht ATmega16-kompatibel. Dafür nutze ich den SPI-Mode der USART - und den gibts erst ab ATmega xx4. Also 164 / 324 / 644 / 1284 (ja, in den Datenblättern existiert auch der letztere!!!).

    Ich habe btw auch einen Systemtimer und ich muss sagen, meine "Vergangenheit" hat mich evtl. eingeholt: Die Idee, den Timer auf 32 Bit aufzubohren und darauf setzen, dass die Biene niemals länger als 49 Tage am Stück eingeschaltet ist, ist schon OK. Ich nutze statt dessen 16 Bit, die auch überlaufen dürfen, dafür kann meiner nur etwa eine Minute in die Zukunft arbeiten. Meine bisherige Software waren alle Langläufer - musste also fünf Jahre und länger ohne Probleme mit Timerüberläufen zurecht kommen.

    Für den Systemtimer nutze ich btw timer2 und den Capture Compare, um wirklich auf die 1 ms zu kommen.

    Meine Biene ist nur ein klein wenig stromoptimiert und ich habe ihr einen ATmega644 gegönnt, ansonsten unverändert. Ich erkenne auf jeden Fall, dass eine allgemeine Bibliothek nicht so einfach zu erstellen ist, da sich die Hardware der Biene eh jeder mehr oder weniger anpasst.

    So, ich hoffe, dass ich weder langweile noch nerve mit den langen Texten

    Ciao bantyy

  5. #15
    Neuer Benutzer Öfters hier
    Registriert seit
    26.02.2010
    Ort
    Kreuzung BAB3 und B470
    Beiträge
    25
    Servus; ich habe jetzt meinen aktuellen Stand der Zyklus-Dateien und der überarbeiteten Demo von s.frings hier im Thread (weiter oben) rein editiert.

    zyklus baut auf der nibobee Lib von s.frings auf bzw. soll sie ergänzen; damit das ohne Fehler & Warnungen funktioniert, musste ich allerdings in der folgendes ergänzen:
    in der nibobee.c
    Code:
    #if USE_SERIAL
    #include <util/setbaud.h>
    #endif
    und in der nibobee.h den gesamten Block unter "Status Led's" auskommentieren.

    Nun das Demoprogramm funktionierte bei mir nicht so doll; mein NiboBee hat 'nen Rechtsdrall, den die Regelung der Demo auch nicht ausregeln konnte (auch nicht das Original ohne zyklus). Ich muss wohl mal wieder Staubmäuse aus den Getrieben befreien.

    Ach ja, und wen's interessiert wie ich mein Nibobee hergerichtet habe, anbei mal die Bilder:
    Miniaturansichten angehängter Grafiken Miniaturansichten angehängter Grafiken nibounten.jpg   nibooben.jpg  

  6. #16
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    19.03.2010
    Beiträge
    161
    Danke bantyy, für Deine ausführlichen Infos zur PWM Freuqenz.

  7. #17
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    19.03.2010
    Beiträge
    161
    Ich befasse mich gerade ein wenig mit Lejos. Dort finde ich toll, dass bei Fahrmanövern der geschätzte Bremsweg berücksichtigt wird. Dort kann man mit Höchstgeschwindigkeit eine bestimmte Strecke abfahren, ohne ungewollt über das Ziel hinaus zu schießen. Rechtzeitig vor dem Ziel wird abgebremst.

    Diese Idee sollte man auch in eine NIBObee Library übernehmen, finde ich.

  8. #18
    Benutzer Stammmitglied
    Registriert seit
    12.01.2010
    Beiträge
    53
    Hallo,

    Zitat Zitat von s.frings
    Ich befasse mich gerade ein wenig mit Lejos.
    Welch Zufall, ich habe inzwischen auch einen NXT und bin deshalb - und wegen eines Verkehrsunfalls - mit meinem libbee-Tutorial noch nicht fertig. Aber auf einem guten Weg, ich schätze ich habe so etwa die Hälfte fertig. Und ab nächster Woche Urlaub, also ein wenig Zeit dafür

    Dort finde ich toll, dass bei Fahrmanövern der geschätzte Bremsweg berücksichtigt wird.
    Der Bremsweg ist dort aber auch manipulierbar. Schließlich nutzen die keine diskrete Minimal-Motorbrücke sondern einen Chip, der die volle Funktionalität bietet.

    Bei der Biene ist das sehr viel schwieriger: Die Motorbrücke ist vereinfacht und müsste dafür erstmal geändert werden, um einen Bremsbetrieb zu ermöglichen.

    Der eigentliche Bremsweg ist darüber hinaus sehr abhängig von dem Untergrund. Wie machen sie denn das bei LeJOS genau? Ich hab nicht in den Quellcode reingeschaut, aber dort wissen sie ja nicht einmal, was für eine Art von Antrieb an dem Roboter ist (Räder, Gummi- oder Plaste-Raupen, Reifendurchmesser, Geometrie...)

    Was natürlich möglich wäre: Ein annähern mit voller Geschwindigkeit auf die Sollstrecke - x % und den Rest dann mit geringster Geschwindigkeit fahren. So etwas in der Art ist schon realisierbar. Aber richtig bremsen und Motoren "festhalten", wie es beim NXT möglich ist, geht mit der Biene ohne Umbau nicht.

    Ciao bantyy

  9. #19
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    19.03.2010
    Beiträge
    161
    Bei Lejos gibt es einen default Wert für die geschätzte Bremsleistung. Damit kann der Bremsweg geschätzt werden und wiederum eine zielsichere Bremsung hingelegt werden - vorausgesetzt der default Wert stimmt. Den kannst Du programmatisch anpassen. Da die Lego Reifen richtig guten Grip haben, die Getriebemotoren sehr stark bremsen können, und der NXT Computer viel Gewicht auf die Reifen bringt, kommt es auf einen exaktem Schätzwert nicht so sehr an. Die Lego Hardware kann beeindruckend kurze Vollbremsungen hinlegen.

    Zurück zum Nibo: Die Motorbrücke kann auch bremsen, nur einen Freilauf kennt sie nicht (wie es Lego kann). Die Bremsleistung ist aber um Welten schlechter, als bei Lego, weil die Motoren winzig klein sind, weil das Getriebe nur eine geringe Übersetzung hat, weil die Reifen (zumindest auf Laminat) wenig Grip haben und weil der ganze Roboter sehr wenig Gewicht auf die Achsen bringt. Einen Stein mit Gummiband auf's Batteriefach klemmen hilft übrigens prima.

    Wegen der geringen Übersetzung und dem geringen Gewicht kann der Nibo viel schneller fahren, als der Lego Roboter. Die Bremsleistung ist aber schwach, deswegen muss der Nibo Roboter sehr viel früher bremsen.

    Ich hatte es erstmal so versucht, dass während der Fahrt fortlaufend die aktuelle Geschwindigkeit ermittelt wird (alle 100ms) und der sich daraus ergebende Bremsweg geschätzt wird. Ist der dann kleiner oder gleich der Distanz zum Ziel, dann wird gebremst. Problem: Schätze ich zu viel, dann kommt der Roboter zu früh zum Stillstand.

    Also dachte ich mir, lass ich die Bremse wieder los, wenn ich merke, dass ich mich verschätzt habe. Das führt aber zu unschönem Ruckeln auf den letzten Zentimetern vor dem Ziel. Man kann dann buchstäblich sehen, dass der Bremsweg falsch geschätzt wurde.

    Letztendlich muss man aber mehr schätzen, als der tatsächliche Bremsweg ist, weil - wie Du schon bemerkt hast - der Bremsweg sehr vom Untergrund abhängt. Ein Staubkorn auf dem Boden kann sich da schon signifikant auswirken, insofern muss man schon wenigstens 20% Reserve einplanen.

    Gestern habe ich etwas anderes versucht, mit Erfolg: Alle 100ms berechne ich die geschätzte maximal zulässige Geschwindigkeit, mit der eine Zielgenaue Bremsung gerade noch möglich wäre. Dann begrenze ich die soll-Geschwindigkeit auf genau diesen Wert. Wobei ich bei der Konstante BREMSLEISTUNG schon 20% Reserve vorgesehen habe.

    Code:
    sollTempo=200cm/sec;
    motorPower=250;
    BREMSLEISTUNG=85;
    while (ziel_noch_nicht_erreicht) {
        if (100ms_vergangen) {
            maxTempo=sqrt(restDistanz*BREMSLEISTUNG);
            if (sollTempo>maxTempo) 
                sollTempo=maxTempo;
            fehler=sollTempo-istTempo;
            motorPower=motorPower+fehler;
        }
    }
    motorPower=0;
    (Das ist Pseudo-code, kann man so nicht 1:1 übernehmen)

    Aber ganz so einfach geht es leider nicht, weil das Wurzelziehen zu lange dauert. Deswegen die folgende Annäherungsschleife:

    Code:
    sollTempo=200cm/sec;
    motorPower=250;
    BREMSLEISTUNG=85;
    while (ziel_noch_nicht_erreicht) {
        if (100ms_vergangen) {
            while (1) {
                bremsweg=(sollTempo*sollTempo)/BREMSLEISTUNG;
                if (bremsweg>restDistanz)
                    sollTempo=soll_tempo*0.9;
                else
                    break;
            }
            fehler=sollTempo-istTempo;
            motorPower+=fehler;
        }
    }
    motorPower=0;
    Letztendlich führt das Ganze dazu, dass der Roboter mit sehr geringer Geschwindigkeit am Ziel ankommt, wo ich dann getrost per Vollbremsung endgültig anhalten kann. Ich komme also stets wenige Millimeter hinter dem Ziel zum stehen.

    Das ist bei Lejos auch so, nur fährt der wieder ein Stück zurück, wenn er das Ziel um mehr als eine gewisse Toleranzgrenze überschritten hat (denn Lejos verwendet einen PID Regler, mein Nibo jedoch nur einen PI Regler).

    Ein PID Regler wäre beim Nibo problematisch, denn wenn er erstmal rückwärts in Bewegung ist, kann er nicht schnell genug stoppen. Die Abweichung vom Ziel würde dadurch eher noch größer, als kleiner. Außerdem macht man das mit einem Auto vor der Ampel ja auch nicht. Wenn ich die Haltelinie knapp verfehlt habe, setzte ich ja auch nicht zurück, da käme ich mir blöd vor.

    Zudem würde ein Zurücksetzen richtig viel Zeit kosten. Ein Flüssiger Unterbrechungsfreier Übergang zwischen geradeaus-fahrt und anschließender Drehung wäre dann unmöglich (ist bei Lejos auch unmöglich).

  10. #20
    Benutzer Stammmitglied
    Registriert seit
    12.01.2010
    Beiträge
    53
    Hallo,

    Zitat Zitat von s.frings
    Da die Lego Reifen richtig guten Grip haben, die Getriebemotoren sehr stark bremsen können, und der NXT Computer viel Gewicht auf die Reifen bringt, kommt es auf einen exaktem Schätzwert nicht so sehr an. Die Lego Hardware kann beeindruckend kurze Vollbremsungen hinlegen.
    Das schreibe ich weder dem Getriebe noch dem Gewicht des Roboters zu. Die NXT-Motoren können regelrecht "festgehalten" werden - wenn Du versuchst, den von Hand zu drehen, wird das schwer ohne zerstörende Wirkung.

    Irgendwie haben die eine Bremsen / Halten-Funktion implementiert - wie habe ich mir zugegebenermaßen nicht so genau angesehen. Ach ja, ich hab die Original-Firmware auf dem NXT, bei dem geht das jedenfalls.

    Zurück zum Nibo: Die Motorbrücke kann auch bremsen, nur einen Freilauf kennt sie nicht (wie es Lego kann).
    Ich hätte das jetzt spontan anders gesehen - denn die Motoren laufen eigentlich immer nach.

    Die Bremsleistung ist aber um Welten schlechter, als bei Lego, weil die Motoren winzig klein sind, weil das Getriebe nur eine geringe Übersetzung hat, weil die Reifen (zumindest auf Laminat) wenig Grip haben und weil der ganze Roboter sehr wenig Gewicht auf die Achsen bringt.
    Letzteres ist erscheint mir nicht logisch zu sein. Wenn der Roboter leicht ist, müsste er auch viel schneller stehen - Massenträgheit lässt grüßen.

    [/quote]Einen Stein mit Gummiband auf's Batteriefach klemmen hilft übrigens prima.[/quote]

    Damit erhöhst Du die Reibung im Getriebe -> Der Roboter beschleunigt auch langsamer und du verbesserst den Grip.

    Wegen der geringen Übersetzung und dem geringen Gewicht kann der Nibo viel schneller fahren, als der Lego Roboter.
    Ich hab den Lego-Roboter zur Zeit als Humanoid. Als Rollen-Roboter kann ich mir durchaus vorstellen, ihn auf die Nibobee-Geschwindigkeit zu bringen.

    dann wird gebremst. Problem: Schätze ich zu viel, dann kommt der Roboter zu früh zum Stillstand.
    Ich hatte schonmal versucht, mit bremsen zu arbeiten. Das ist mir leider so gar nicht gelungen. Wie lässt Du denn die Bee praktisch bremsen?

    Das führt aber zu unschönem Ruckeln auf den letzten Zentimetern vor dem Ziel. Man kann dann buchstäblich sehen, dass der Bremsweg falsch geschätzt wurde.
    Blöd

    maxTempo=sqrt(restDistanz*BREMSLEISTUNG);
    OK, Du verlangsamst also mit einer Wurzelfunktion. Wie wäre es denn mit einer e-Funktion? Die ist doch immer am schnellsten am Ziel, oder hab ich das falsch in Erinnerung?

    Interessant übrigens. Ich habe meine "Regelung" im Prinzip genau so nur für das Beschleunigen aufgebaut. Ich nähere mich quadratisch dem Soll.

    Aber ganz so einfach geht es leider nicht, weil das Wurzelziehen zu lange dauert.
    Wie so oft Aber deshalb gibt es ja Näherungslösungen.

    Letztendlich führt das Ganze dazu, dass der Roboter mit sehr geringer Geschwindigkeit am Ziel ankommt, wo ich dann getrost per Vollbremsung endgültig anhalten kann. Ich komme also stets wenige Millimeter hinter dem Ziel zum stehen.
    Wieviel vor dem Ziel ist denn der Bremsvorgang wirklich merkbar / sichtbar? Das könnte ich in meine Lib ja auch noch einbauen - nachdem ich die anderen Aufgaben mal erledigt habe...

    Ein PID Regler wäre beim Nibo problematisch, denn wenn er erstmal rückwärts in Bewegung ist, kann er nicht schnell genug stoppen.
    Mh. Dann gäbe das ein unendliches hin- und her. In dem Fall wären die Parameter irgendwie falsch gewählt...

    Zudem würde ein Zurücksetzen richtig viel Zeit kosten. Ein Flüssiger Unterbrechungsfreier Übergang zwischen geradeaus-fahrt und anschließender Drehung wäre dann unmöglich
    OK, das mag ich an meiner Biene

    (ist bei Lejos auch unmöglich).
    Mh, wie gesagt, mit dem NXT hab ich erstmal die Füßchen des AlphaRex versucht, in den Gang zu bekommen. Wenn ich mal etwas fahrendes habe, werde ich da bestimmt auch eine Menge herumdoktorn...

    Also: Es gibt einiges zu tun Viel Spaß beim weiter probieren...

    Wenn ich neue Erkenntnisse habe, schreibe ich mal wieder

    Ciao bantyy

Seite 2 von 3 ErsteErste 123 LetzteLetzte

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

LiFePO4 Speicher Test