-         

Ergebnis 1 bis 5 von 5

Thema: RP6Base: Video Grabber 1

  1. #1
    Erfahrener Benutzer Robotik Einstein Avatar von Dirk
    Registriert seit
    30.04.2004
    Ort
    NRW
    Beiträge
    3.791

    RP6Base: Video Grabber 1

    Anzeige

    Hallo Leute,

    für die Experimentierplatine, die wir hier beschrieben haben:
    http://www.roboternetz.de/phpBB2/zei...ag.php?t=53424
    ... und die hier:
    http://www.rn-wissen.de/index.php/RP...itmach-Projekt
    ... gebaut wurde, hier ein erstes Programm für die RP6Base. Es funktioniert ab der Phase 3 des Aufbaus.
    Ein 32x32 Pixel Bild wird auf dem Terminal ausgegeben. Das ursprüngliche Programm hat radbruch geschrieben.

    Was habe ich geändert:
    - Nutzung der Signale HSync und VSync des Sync-Separators auf der Exp
    Code:
    /* 
     * ****************************************************************************
     * RP6 ROBOT SYSTEM - ROBOT BASE TESTS
     * ****************************************************************************
     * Example: Grab video pictures and show them on the terminal
     * Author(s): radbruch, modified: Dirk
     * ****************************************************************************
     * Description:
     *
     * With this program (written by radbruch) the picture of a CMOS camera
     * (CONRAD 150001) can be displayed on the terminal.
     * The camera is connected to this hardware project:
     *   http://www.rn-wissen.de/index.php/RP...itmach-Projekt
     * The BAS video information (PG3) is connected to ADC4 (PG4).
     * The synchronization signals are connected as follows:
     *
     *	Signal		RP6 Name	Port	Function
     *	VSync		SCL			PC0		SCL
     *	HSync		SDA			PC1		SDA
     *
     * If you use SCL and SDA this way, you can not use the I2C bus any more!
     *
     * ****************************************************************************
     *
     * ACHTUNG: Den Jumper auf JP10 (Stellung H) nur aufstecken, wenn das Programm
     *          schon läuft! Bitte den Jumper entfernen, wenn kein Programm auf
     *          der RP6Base läuft! Der Bootloader würde sonst das zuletzt geladene
     *          Programm immer wieder neu starten!
     *
     * ############################################################################
     * The Robot does NOT move in this example! You can simply put it on a table
     * next to your PC and you should connect it to the PC via the USB Interface!
     * ############################################################################
     * ****************************************************************************
     */
    
    /*****************************************************************************/
    // Includes:
    
    #include "RP6RobotBaseLib.h" 	// The RP6 Robot Base Library.
    								// Always needs to be included!
    
    /*****************************************************************************/
    // Defines:
    
    #define HSYNC		(PINC & SDA)
    #define VSYNC		(PINC & SCL)
    #define IRon()		{statusLEDs.LED1 = false; updateStatusLEDs();}
    #define IRoff()		{statusLEDs.LED1 = true; updateStatusLEDs();}
    
    /*****************************************************************************/
    // Variables:
    
    uint8_t bildspeicher[1024], *bildzeiger; // 32*32=1KB * 8Bit Bildspeicher
    
    /*****************************************************************************/
    // Functions:
    
    void bild_einlesen(void) 
    { 
    	uint8_t pixel[32],*pixelzeiger; 
    	uint8_t i, zeilen, step, lines, rows, h_step, h_delay; 
    
    	zeilen=32; // Das fertige Bild soll 32 Zeilen haben 
    	step=7; // sichtbares TV-Bild ist ca. 30-260=230/32 ergibt Zeilensprung=7 
    	rows=0; // Anzahl der Spalten (32x32, rechengünstig,aber verzerrt) 
    
    	do 
    	{ 
    		lines=zeilen; // Anzahl der einzulesenden Zeilen 
    		pixelzeiger=&pixel[0]; // Zeiger auf Start Pixelspeicher 
    		cli(); 
    		// VSync abwarten (Seitenanfang) 
    		while(VSYNC);
    		// 40 Zeilen Austastzeit & ein Stück oberen Bildrand überlesen 
    		h_step=40; while (h_step) { while (HSYNC); while (!HSYNC); h_step--; }
        
    		// Der Lesecursor befindet sich jetzt oben links im TV-Bild 
    		// ab hier werden in step-Sprüngen in allen Zeilen jeweils das Pixel eingelesen, 
    		// das sich im zeitlichen h_delay-Abstand vom linken TV-Bildrand befinden 
    		// (= eine TV-Bildspalte) 
    
    		while (lines--) 
    		{ 
    		// auf die nächste gültige Zeile warten
    			h_step=step; while (h_step) { while (HSYNC); while (!HSYNC); h_step--; }
    		// mit h_delay steuern wir nun den Pixel an 
    		// Nach dem sync fängt das Bild etwas verzögert an (Schwarzschulter), bei mir 10 
    		// bei ca. 110 beginnt die 2.Schwarzschulter.
    			h_delay=10+3*rows; while (h_delay--) {nop();} 
    
    			*pixelzeiger=ADCH; // letzten ADC-Wert auslesen und wegwerfen 
    			*pixelzeiger++=ADCH;  // aktuellsten ADC-Werte speichern 
    		} 
    		sei(); 
    
    		pixelzeiger=&pixel[0]; 
    		bildzeiger=&bildspeicher[32*rows]; 
    		for (i=0; i<32; i++) *bildzeiger++ = *pixelzeiger++; 
    
    	} while (++rows < zeilen); 
    }
    
    /*****************************************************************************/
    // Main function - The program starts here:
    
    int main(void)
    {
    	initRobotBase(); // Always call this first! The Processor will not work
    					 // correctly otherwise.
    					 
    	// ---------------------------------------
    	// Write messages to the Serial Interface:
    
    	writeString_P("\n\n   _______________________\n");
    	writeString_P("   \\| RP6  ROBOT SYSTEM |/\n");
    	writeString_P("    \\_-_-_-_-_-_-_-_-_-_/\n\n");
    
    	writeString_P("################\n");
    	writeString_P("<<RP6     Base>>\n");
    	writeString_P("Video Grabber 1 \n");
    	writeString_P("  Version 1.10  \n");
    	writeString_P("################\n\n");
    	mSleep(2500);
    
    	setLEDs(0b111111); // Turn all LEDs on
    	mSleep(500);       // delay 500ms
    	setLEDs(0b000000); // All LEDs off
    
    	// Initialize the M32 SDA pin (PC1) as input: 
    	DDRC &= ~SDA;						// ==> HSync
    	PORTC |= SDA;						// Pullup on
    	// Initialize the M32 SCL pin (PC0) as input: 
    	DDRC &= ~SCL;						// ==> VSync
    	PORTC |= SCL;						// Pullup on
    
    	// Switch the IR-LEDs off: 
    	IRoff();							// IR LEDs off
    
    	uint16_t i, j;
    	extIntOFF(); // schaltet den E_INT1-Port auf Eingang für den ADC
    // ADC interne Referenz 2,56V, Ergebniss linksbündig, Kanal ADC4 
    	ADMUX = (1<<REFS1) | (1<<REFS0)  | (1<<ADLAR) | 4; 
    // setzte free running triggern 
    	SFIOR = (0<<ADTS2) | (0<<ADTS1) | (0<<ADTS0); 
    // kein interupt, Wandler einschalten, prescaler /2 
    	ADCSRA = (0<<ADIE) | (1<<ADEN) | (0<<ADPS2) | (0<<ADPS1)  | (1<<ADPS0); 
    // Autotriggern bedeutet jetzt free running aktivieren, altes Flag löschen 
    	ADCSRA |= (1<<ADATE) | (1<<ADIF); 
    // Initialisierung starten 
    	ADCSRA |= (1<<ADSC); 
    // und noch die wohl eher unnötige Initiallesung 
    	while (!(ADCSRA & (1<<ADIF))); 
    	ADCSRA |= (1<<ADIF); 
    
    	while(true)
    	{
    		bild_einlesen(); 
    
    		for (i=0; i<32; i++) 
    		{ 
    			for (j=0; j<32; j++) 
    			{ 
    				if (bildspeicher[j+32*i] > 55) writeString_P("*"); 
    				else writeString_P(" "); 
    			}
    			writeInteger(i,DEC); 
    			writeString_P("\n\r"); 
    		}
    		mSleep(200); 
    	}
    	return 0;
    }
    
    /******************************************************************************
     * Additional info
     * ****************************************************************************
     * Changelog:
     * - v. 1.1 (workout for the hardware project) 02.05.2010 by Dirk
     * - v. 1.0 (initial release) 20.08.2007 by radbruch
     *
     * ****************************************************************************
     */
    
    /*****************************************************************************/
    Viel Spaß!

    Dirk

  2. #2
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    54
    Beiträge
    5.785
    Blog-Einträge
    8
    Hallo Dirk

    Du legst dich ja mächtig ins Zeug.

    Aufgeschreckt durch dein Mitmachprojekt habe ich ja auch einen erneuten Anlauf mit der Kamera unternommen (BMP erzeugen) und ein paar neue Erkenntnisse zum Thema Auflösung gefunden.

    Wie schon mehrfach von verschiedenen Seiten angemerkt kann der ADC bei 4MHz pro Zeile maximal 16 Werte hintereinander samplen. Das läßt sich bei dieser Betriebsart (Dauerlauf) nicht ändern und auch die Startverzögerung am Anfang der Zeile (die ich bisher ja auch verwendete um die Auflösung zu "Erhöhen") ändert daran nichts. Es wird immer nur auf einen dieser 16 Werte zugegriffen.

    Abhilfe schafft hier das Anhalten und erneute Starten des ADCs. Sobald der Zeilensync erkannt ist, wird der ADC angehalten. Dann startet die Verzögerung für den gesuchten Zeilenpixel und erst dann wird der ADC erneut gestartet. Nachdem der erste eingelesene Wert (diesmal legal ermittelt mit Warten auf das Flag:) abgespeichert wurde, läuft alles wie gewohnt mit dem Warten auf den nächsten Sync weiter. Nun dauert das erste Samplen zwar einige Zyklen mehr als im Dauerlauf, aber wir lösen uns damit von den sturen 16 Werten die wir bisher erhalten hatten:

    Code:
                while(zeile--) {while (ADCH > 20); while (ADCH < 30);}
    
                ADCSRA = (1<<ADATE)|(0<<ADEN)|(1<<ADIF)|(0<<ADSC)|(1<<ADPS0); // ADC stoppen
                while(i--); // Pixel ansteuern
                ADCSRA = (1<<ADATE)|(1<<ADEN)|(1<<ADIF)|(1<<ADSC)|(1<<ADPS0); // ADC wieder starten
                while (!(ADCSRA & (1<<ADIF)));    // 26 ADC-Takte warten bis Wandlung fertig
                *bildzeiger++=ADCH;             // das sind ca. 6,5µs
                ADCSRA |= (1<<ADIF);
    Zur weiteren Steigerung der Auflösung habe ich noch an der Verzögerung rumoptimiert. Die while(i--)-Verzögerungeschleife benötigt ca. 3 Takte pro Durchlauf mit Sprung, deshalb lese ich zwei nacheinanderliegende Pixel mit einer "halben" Verzögerungsstufe ein:

    Code:
                ADCSRA = (1<<ADATE)|(0<<ADEN)|(1<<ADIF)|(0<<ADSC)|(1<<ADPS0); // ADC stoppen
    
                nop(); nop(); // nächste Spalte zwei NOPs später einlesen
    
                while(i--); // Pixel ansteuern
                ADCSRA = (1<<ADATE)|(1<<ADEN)|(1<<ADIF)|(1<<ADSC)|(1<<ADPS0); // ADC wieder starten
    Obwohl ich mit dieser Technik nun horizontal über 100 Pixel ansteuern könnte, funktioniert das in Phase1 noch nicht optimal. Das liegt wieder an den nur 16 Werten pro Zeile im Dauerlauf. Ich kann so nicht erkennen in welchem Takt das Syncsignal erkannt wurde. Diese kleine Ungenauigkeit beim Start der Pixelverzögerung führt in Phase1 dazu, dass die Pixel nicht ganz genau getroffen werden. Dass der ADC nicht syncron mit dem Bild läuft, verstärkt diesen Effekt auch noch.

    Ganz anders sind aber die Möglichkeiten der Phase3 mit ihrer Hardwaresyncerkennung. Da hierbei der Sync digital ausgewertet wird fällt die Wandelzeit des ADCs weg und das Ende des Sync kann nahezu auf den Takt genau erkannt werden. Dadurch sollte das gesuchte Pixel viel besser getroffen werden können. (Ich kann's nicht testen weil ich immer noch Phase1 verwende)

    Funktioniert es mit dem M32 eigentlich auch mit ADC-Prescaler /2, also 8MHz am ADC?

    Gruß

    mic

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  3. #3
    Erfahrener Benutzer Robotik Einstein Avatar von Dirk
    Registriert seit
    30.04.2004
    Ort
    NRW
    Beiträge
    3.791
    Hallo mic,

    also: Aufschrecken wollte ich dich eigentlich nicht! [-o<
    ... habe ich ... ein paar neue Erkenntnisse zum Thema Auflösung gefunden.
    Das klingt gut, wobei ich mich erst mal da rein fuchsen muss, was du da machst. Wenn ich das so sehe, sind wir noch nicht am Ende der Fahnenstange mit Auflösung und Bildqualität angekommen. Gute Aussichten!
    ... kann der ADC bei 4MHz pro Zeile maximal 16 Werte hintereinander samplen. Das läßt sich bei dieser Betriebsart (Dauerlauf) nicht ändern und auch die Startverzögerung am Anfang der Zeile (die ich bisher ja auch verwendete um die Auflösung zu "Erhöhen") ändert daran nichts. Es wird immer nur auf einen dieser 16 Werte zugegriffen.
    Das ist mir nur teilweise klar. Sicher bekomme ich free running mit Übertaktung des ADC auf 4 MHz nur 16 Werte pro Zeile. Allerdings ist das ja egal, wenn wir Spalten lesen, weil wir da ja eh nicht in Eile sind. Die Startverzögerung hat ja die Zeit von der steigenden Flanke von HSync bis zum Zeilenanfang (hintere Schwarzschulter) überbrückt. Aber sie hat ja nicht die Auflösung erhöht! Wenn wir ab Zeilenanfang 32 "Zeitmarken" setzen wollten, dann war das, was du mit h_delay bis jetzt gemacht hast, doch ganz ok, oder?
    (Anmerkung: Ich konnte das nur mit Oszi für die M32-Version einjustieren, wie hast du das eigentlich gemacht???)
    Funktioniert es mit dem M32 eigentlich auch mit ADC-Prescaler /2, also 8MHz am ADC?
    Ich war zu feige, es zu probieren.

    Gruß Dirk

    P.S.:
    Noch eine Frage: Wenn ich dein ursprüngliches Programm auf meiner Base laufen lasse, dreht sich rhythmisch die linke Kette mit. Das Programm funktioniert aber normal. Auch nach dem Umschreiben auf die Phase 3 Version war das noch genau so und ich habe mir einen Wolf gesucht, den Fehler zu finden. Läuft bei dir gel. auch die linke Kette? Ich konnte das schließlich nur durch Löschen der Timer 1 Einstellungen der RP6BaseLib (TCCR1A/B = 0) abstellen, ohne es verstanden zu haben. Hast du eine Idee?

  4. #4
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    54
    Beiträge
    5.785
    Blog-Einträge
    8
    Hallo Dirk

    Nein, mit der Kamera sind wir noch lange nicht am Ende der Fahnenstange :)

    Das Kettenproblem wird durch einen Zeigerüberlauf verursacht:
    http://www.roboternetz.de/phpBB2/vie...=456037#456037

    Es gibt im Dauerlauf nur diese 16 Werte. Es ist dabei völlig egal, wann wir diese Werte aus dem ADC auslesen.

    Mit der Verzögerung am Zeilenanfang warten wir auf das auszulesende Pixel. Da wir aber nicht wissen, wie "alt" der zuletzt gelesene ADC-Wert ist, lesen wir mehrere nebeneinander liegende Pixel aus dem selben Sample des ADC! Die Sequenz

    *pixelzeiger=ADCH; // letzten ADC-Wert auslesen und wegwerfen
    *pixelzeiger++=ADCH; // aktuellsten ADC-Werte speichern

    ist eigentlich schon der Hinweis auf das Problem.

    Was wurde am Ende der Verzögerung zuletzt gesamplet? In ADCH steht immer der Wert der letzten Digitalisierung, also einer der 16 Werte. Das ändert sich nicht, solange der ADC nicht gestoppt und wieder gestartet wurde. Denn dann kann man den ADC mit dem Kamerasignal für diese eine Zeile genauer als in 16 Schritten syncronisieren. Ohne diese Synchrionisation läuft der ADC-Takt quasi neben dem Videotakt mit einer anderen Schrittgröße (blöder Vergleich?).

    Der "Dauerlauf" des ADC ist die schnellste Methode die Werte einzulesen. Das war nötig um die Syncs zu erkennen. Mit der Phase3 ist das nicht mehr nötig. Nun könnte man den ADC entschärfen und generell nur noch einmalige Samples einlesen: Bildstart, Zeilenstart, Pixelverzögerung, ADC starten und Wert, nach Fertigmeldung des ADC, einlesen, warten auf nächstes Sync. Dann könnten wir uns auch von den 4MHz verabschieden, allerdings würde vermutlich die Einlesezeit im Gesamten deutlich steigen

    Hier scheiden sich wohl letzlich auch die Wege:
    Auf das Projekt zugeschnittene Auswertung oder universelle Anwendung?

    Gruß

    mic

    [Edit]
    Zufällig drübergestolpert: Das Edit in diesem Beitrag schreit quasi nach einem jumperbaren 75Ohm-Widerstand:
    http://www.roboternetz.de/phpBB2/vie...=456159#456159

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  5. #5
    Erfahrener Benutzer Robotik Einstein Avatar von Dirk
    Registriert seit
    30.04.2004
    Ort
    NRW
    Beiträge
    3.791
    Hi mic,
    Ohne diese Synchrionisation läuft der ADC-Takt quasi neben dem Videotakt mit einer anderen Schrittgröße (blöder Vergleich?).
    Ja, das war ein Nachteil. Aber könnte man nicht auch beim free running ADIF auswerten und z.B. in einer ISR den Wert sichern, wenn er wirklich fertig ist?
    Mit der Phase3 ist das nicht mehr nötig. Nun könnte man den ADC entschärfen und generell nur noch einmalige Samples einlesen: Bildstart, Zeilenstart, Pixelverzögerung, ADC starten und Wert, nach Fertigmeldung des ADC, einlesen, warten auf nächstes Sync. Dann könnten wir uns auch von den 4MHz verabschieden, allerdings würde vermutlich die Einlesezeit im Gesamten deutlich steigen
    Die Frage wäre, ob man die "legal" möglichen 4 Lesungen (tatsächlich verteilen sich aber nur 3 "Lesungszeitpunkte" auf die 52 µs) nutzen kann, um Einlesezeit zu sparen. Immerhin bekäme man auf einmal mind. 3 Punkte pro Zeile.
    ... Beitrag schreit quasi nach einem jumperbaren 75Ohm-Widerstand
    Ja, sollte man machen. Ich habe deine Empfehlung schon in Phase 2 aufgenommen.

    Gruß Dirk

Berechtigungen

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