Hallo Volker

Mit der GB-Kamera wollte ich's auch mal versuchen, nur leider hatte ich keine rumliegen.

Mit der 8Mhz funktioniert es eigentlich schon recht gut. Man kann natürlich keine Wunder erwarten. Ein großes Problem ist auch die Datenmenge, mehr Pixel bedeutet eben auch mehr Daten.

In der ersten Variante habe ich auf den vsnyc (und den vertikalen Strahlrücklauf) gewartet, er dauert bei meiner Lesegeschwindigkeit ca. 47 Lesungen. Dann habe ich die hsyncs gezählt um die gewünschte Zeile anzusteuern (wegen der Datenmenge nur jeder Xte Zeile, das ergibt die vertikale Auflösung). Bei Erreichen der gewünschten Zeile habe ich dann so schnell wie möglich die Daten der Zeile eingelesen und komme so auf ca. 60 Pixel/Zeile (horizontale) Auflösung. Aus Speichermangel habe ich dann die Zeile verarbeitet und dann erst die nächste eingelesen. Deshalb benötigt ein kompletter Scan des Bildes pro eingelesene Zeile mindestens ein Halbbild für das eigentliche scannen und noch ein paar weitere fürs verarbeiten. In Summe deutlich zu lange.

Gegenüber diesen ersten Versuchen sieht das Einlesens nun wesentlich anders aus. Ich lese pro Scan nun nur noch einen (beliebigen) horizontalen Pixel pro Zeile, den dafür aber pro Halbbild in allen gewünschten vertikalen Linien auf einen Schlag. Das Bild wird dadurch Spaltenweise eingelesen. Der sichtbare Bereich meines Bildes liegt zwischen den Zeilen 30 und 260, also habe ich so eine maximal mögliche vertikale Auflösung von 230 Pixeln.

Da ich nun horizontal nur noch einen Pixel/Zeile einlesen muss, ist es nun nicht mehr kritisch, wie schnell man das kann. Man muss nur den Pixel finden. Und das mache ich in dieser Variante so:

Wie gewohnt warten auf den Start des Halbbildes, dann 30 Zeilen "Austastlücke für BTX" abwarten, jetzt beginnt das eigentliche Einlesen. Und hier nun die wesentliche Änderung, nach dem hsync wird erstmal eine kurze Zeit abgewartet:

h_delay=50; while (h_delay--);

Das ist der x-Versatz des Pixels, das ich einlesen will. Über die Dauer der Verzögerung kann ich unabhängig von der Lesegeschwindigkeit des ADCs erst den Lesezeitpunkt innerhalb einer Zeile festlegen (max für h_delay weiß ich noch nicht). Dann löscht eine Dummy-Lesung das alte ADC-Resultat und startet damit ein erneutes Sampeln des Signals. Nun lese ich das ADCH nochmal aus und habe damit den gewünschten Pixel. Jetzt bleibt noch genug Zeit um das Ende der aktuellen Zeile abzuwarten. Nach dem nächsten vsync kann das Pixel in der nächsten (oder Xten) Zeile eingelesen werden. Somit dauert das Lesen einer kompletten Spalte so lange wie ein Halbbild.

Die horizontale Auflösung entspricht den möglichen Werte für das h-delay, also etwas mehr als 0 bis Ende der Zeile. Das habe ich noch nicht gemessen, denn eigentlich reicht es, nur an bestimmten Punkten eine Spalte zu scannen um einen Punkt, Strich oder gar ein einfaches Muster zu erkennen. Deshalb ist meine Kamera auch um 90° gedreht, der Strich kommt von links ins Bild und die Lage des Strichs entspricht dadurch den Zeilen des Bildes. Dann reicht ein Scan einer Spalte, um den Strich zu erkennen. Mit 2 Scans in verschiedenen Spalten kann man dann schon die Richtung des Striches auswerten...

Hier noch mein aktueller, ungeschminkter Arbeitscode als Anregung:

Code:
#include "RP6RobotBaseLib.h"

#define mpower 50
#define spur 145

// Achtung! Die PWM-Werte werden hier OHNE Rampe verändert!
void setMotorPWM(uint8_t power_links, uint8_t power_rechts)
{
extern uint8_t mleft_ptmp, mright_ptmp;

	if(power_links > 210) power_links = 210;
	if(power_rechts > 210) power_rechts = 210;
	mleft_power=mleft_ptmp=power_links;
	mright_power=mright_ptmp=power_rechts;

	OCR1BL = power_links;
	OCR1AL = power_rechts;

	if(power_links || power_rechts)
		TCCR1A = (1 << WGM11) | (1 << COM1A1) | (1 << COM1B1);
	else
		TCCR1A = 0;
}

void get_lines(uint16_t delay, uint8_t zeilen)
{
	uint8_t pixel[256],*pixelzeiger;
	uint8_t i, step, lines, h_step, h_sync, h_delay;
	uint16_t gamma[16],g_hell, g_dunkel, g_sprung;
	uint16_t strich_links, strich_rechts, strich_mitte, strich_breite;

	step=(260-35)/zeilen; // 30-260 sind die sichtbare Zeilen
	lines=zeilen;   // Anzahl der einzulesenden Zeilen
	pixelzeiger=&pixel[0]; // Zeiger auf Start Pixelspeicher

	cli();
	do // vsync abwarten
	{
		h_sync=0; while (ADCH > 20); while (ADCH < 30) h_sync++;
	} while (h_sync < 40);

	h_step=35;
	while (h_step) // 30 Zeilen Austastzeit überlesen
	{
		while (ADCH > 20); while (ADCH < 30); h_step--;
	}

 	while (lines--)
	{
		h_step=step;
		while (h_step) // auf die nächste gültige Zeile warten
		{
			while (ADCH > 20); while (ADCH < 30); h_step--;
		}
		h_delay=delay;
		while (h_delay--); // auf richtiges Pixel warten
		*pixelzeiger=ADCH; // letzten ADC-Wert auslesen und wegwerfen
		*pixelzeiger++=ADCH;  // aktuellsten ADC-Werte speichern
	}
	sei();
	//*pixelzeiger=0; // Endekennung der Daten

	for (i=0; i<16; gamma[i++]=0); // 16 gammawerte zählen
	pixelzeiger=&pixel[0];
	for (lines=0; lines < zeilen; lines++) gamma[*pixelzeiger++ >> 4]++;

	g_sprung=5;
	g_dunkel=0;
 	for (i=1; i<16; i++)
	{
	   if (gamma[i] > gamma[i-1])
			if ((gamma[i] - gamma[i-1]) > g_sprung)
				if (!g_dunkel) g_dunkel=i;
	}
	g_hell=0;
 	for (i=14; i; i--)
	{
	   if (gamma[i] > gamma[i+1])
			if ((gamma[i] - gamma[i+1]) > g_sprung)
				if (!g_hell) g_hell=i;
	}
/*
	for (i=0; i<16; i++)
	{
		if (i) writeString_P("-");
		writeInteger(gamma[i],DEC);
	}
	writeString_P("\n\r");
	writeInteger(g_dunkel, DEC);
	writeString_P("-");
	writeInteger(g_hell, DEC);
	writeString_P("\n\r");
*/
	strich_links=0;
	strich_rechts=0;
	for (i=0; i<zeilen; i++)
	{
		if ((pixel[i] >> 4) == g_dunkel) writeString_P("*");
			else writeString_P(" ");
		if (((pixel[i] >> 4) == g_dunkel) && ((g_hell-g_dunkel)>1))
		{
			if (!strich_links) strich_links=i;
				else strich_rechts=i;
		}
	}
	strich_mitte=(strich_links+strich_rechts)/2;
	strich_breite=strich_rechts-strich_links;
	writeString_P(" | ");
	writeInteger(strich_links, DEC);
	writeString_P("-");
	writeInteger(strich_rechts, DEC);
	writeString_P("-");
	writeInteger(strich_mitte, DEC);
	writeString_P("\n\r");
}

int main(void)
{
//	uint16_t strich_links, strich_rechts, strich_mitte, strich_breite;
//	uint8_t pow_l,pow_r;

	initRobotBase();
	extIntOFF(); // schaltet den E_INT1-Port auf Eingang für den ADC
	//powerON();
// ADC interne Referenz 2,56V, Ergebniss linksbündig, Kanal ADC4 (E_INT1)
	ADMUX = (1<<REFS1) | (1<<REFS0)  | (1<<ADLAR) | 4;
// setzte free running triggern
	SFIOR = (0<<ADTS2) | (0<<ADTS1) | (0<<ADTS0);
// kein interupt, Wandler einschalten, prescaller /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 (1){
	get_lines(50,48);
	mSleep(50);
}
/*
	setMotorDir(BWD,BWD);
	pow_l=pow_r=mpower;
	setMotorPWM(pow_l/2,pow_r/2);
	mSleep(mpower);
	setMotorPWM(pow_l,pow_r);

	do
	{
	   strich_links=strich_rechts=0;
		zeile=30;
		do
		{
		   if (!strich_links && (get_line(zeile)<gamma))
			{
				strich_links=zeile;
				zeile+=5;
			}
		   if (strich_links && (get_line(zeile)>gamma)) strich_rechts=zeile;
			zeile+=10;
		} while ((zeile<260) && !strich_rechts);
		if (!strich_rechts) strich_rechts=260;

		strich_mitte=(strich_links+strich_rechts)/2;
		strich_breite=strich_rechts-strich_links;

		if ((strich_links < spur) && (strich_rechts > spur))
			pow_l=pow_r=mpower;
		else
		{
			if (strich_links > spur) {pow_l=pow_l/2; pow_r=mpower;}
			if (strich_rechts < spur) {pow_l=mpower; pow_r=pow_r/2;}
		}
		setMotorPWM(pow_l,pow_r);

		//writeInteger(spur,DEC);
		//writeString_P(" - ");
		writeInteger(strich_mitte,DEC);
		writeString_P(" - ");
		writeInteger(strich_breite,DEC);
		writeString_P("\n\r");
 		//mSleep(100);
	} while (strich_breite < 100);
	//setMotorDir(FWD,FWD);
	setMotorPWM(pow_l/2,pow_r/2);
	mSleep(mpower);
	setMotorPWM(0,0);
	//mSleep(500);
*/
	while (1);
	return(0);
}
Der Funktion get_line() wird der Wert für den h_delay und die Anzahl der gewünschten Pixel/Spalte übergeben (das Pixelfeld hat 256 Elemente, ich lese aber nur 48 Pixel ein).

Nach dem Stetzen der Startbedingungen wird eine Spalte in pixel[] eingelesen, dann ein Hystogramm für die oberen 4Bit von pixel[] in gamma[] berechnet. Dann wird in gamma[] eine "Kante" von mindestens g_sprung gesucht, von dunkel her ist das die Linie, von hell aus gesucht das Blatt. Jetzt folgt noch die (Test-)Ausgabe der erkannten Strich-Pixel und die Berechnung der Strichdaten Kante-links/-rechts, Strich-Breite und Mitte. Strichmitte soll übrigens mal der Rückgabewert dieser Funktion werden.

@Sommer
Vielleicht reichen dir die Erklärungen schon.

Gruß

mic