Hallo

"Mehr als 16 Werte hintereinander kann der ADC auch bei 4MHZ-Takt nicht digitalisieren"!

Das wußten wir eigentlich schon immer, nur wirklich wahrhaben wollten wir es bisher noch nicht. Nach einem erneuten Anstoß habe ich das nun endlich mal aufgegriffen und das Einlesen einer Bildschirmzeile daraufhin "optimiert". Anstelle von "lies Werte so schnell du kannst" verwende ich nun einen regelkonformeren Ansatz:

Der ADC wandelt weiterhin im Dauerlauf, Erkennung von Bildstart und Anfang der gewünschten Zeile funktionieren wie gehabt.

Neu ist nun der Abschnitt nach dem Start der gewünschten Zeile: Bei gefundener Zeile steht nach while(ADC<30) schon der Wert des ersten Zeilenpixel in ADCH. Dieser wird abgespeichert und das ADIF-Flag wird gelöscht. Dann werden die nächsten 15 Werte eingelesen, nachdem der ADC jeweils die Fertigmeldung der Wandlung über ADIF signalisiert hat.

Nach dem Zeilensprung zur nächsten gesuchten Zeile wird das dann bis zum Bildende wiederholt. Ich verwende im Demo unten einen Zeilensprung von 10, das ergibt bei 24 Zeilen und 30 übersprungenen Zeilen eine Bildlänge von 270 Pixel (ungefähr;)

Hier nun ein paar Bilder dazu. Links der Versuchsaufbau (im Bild vom Blitz der Kamera extrem ausgeleuchtet, im Versuch war es deutlich dunkler), das TV-Kontrollbild und schließlich die Terminalausgabe:

Bild hier   Bild hier   Bild hier  

Terminal ist ZOC das ich hier vorgestellt habe. Der Code unten funktioniert aber mit dem RP6Loader (Infos zur Anpassung im Quelltext):

Code:
// Ein 24*16 Ascii-Bild                                                    3.5.10 mic

// Mit neuen Erkenntnissen ein Versuch, diese umzusetzen. Ziel war möglichst
// schnell einen Überblick über den Bildinhalt einzulesen und ohne zu Scrollen
// im Terminal darzustellen. Dazu verwende ich anstelle des Loaders ein anderes
// Terminalprogramm: https://www.roboternetz.de/phpBB2/viewtopic.php?t=54087

// Es funktioniert aber in dieser Version auch mit dem RP6Loader!

// Es werden in einem Halbbild(!) von 24 Linien jeweils die 16 Zeilenwerte am Stück
// eingelesen. Das ergibt zwar ein extrem verzerrtes Bild, aber mit 90°-Drehung
// und mit Ausgabe der Werte im Hexformat hat man so durchaus ein Bildgefühl ;)

#include "RP6RobotBaseLib.h"

#define vt100_Black        0 // VT100 Farbdefinitionen
#define vt100_Red          1
#define vt100_Green        2
#define vt100_Yellow       3
#define vt100_Blue         4
#define vt100_Magenta      5
#define vt100_Cyan         6
#define vt100_White        7

#define vt100_Reset			0
#define vt100_Bright			1
#define vt100_Dim				2
#define vt100_Underscore	4
#define vt100_Blink			5
#define vt100_Reverse		7
#define vt100_Hidden			8

uint8_t c; // freies Char
uint16_t x, y;
uint8_t std, min, sec;

void init(void);
void setMotorPWM(uint8_t power_links, uint8_t power_rechts);
void vt100_cls(void);
void vt100_set_cursor(uint8_t line, uint8_t column);
void vt100_set_color(uint8_t foreground, uint8_t background);
void vt100_set_attrib(uint8_t attrib);
void vt100_writeGrafic(uint8_t *string);
void vt100_writeGraficAt(uint8_t line, uint8_t column, uint8_t *string);

void Bild_aufnehmen(void)
{
	// Variablen dürfen NICHT global sein!
	uint8_t bildspeicher[24][16], *bildzeiger;
	uint8_t zeile, sync, c;

	zeile=30; // 30 Zeilen am Bildanfang überlesen
	// Warten auf langen Syncbereich = Bildstart
	cli();
	do{sync=0;while (ADCH > 20);while (ADCH < 30) sync++;}while (sync < 40);
	for(c=0; c<24; c++) // 24 Zeilen einlesen
	{
		bildzeiger=&bildspeicher[c][0];
		sync=15; // 15 Werte sollen am Stück gelesen werden
		while(zeile--){while (ADCH > 20);while (ADCH < 30);} // auf Zeile warten
		*bildzeiger++=ADCH; // erster Wert!
		ADCSRA |= (1<<ADIF);
		while(sync--) // Werte 2-16 einlesen
		{
		   while(!(ADCSRA & (1<<ADIF)));
			*bildzeiger++=ADCH;
			ADCSRA |= (1<<ADIF);
		}
		zeile=10; // 10 Zeilen überlesen (30+ 24*10 = 270 Zeilen) Sind auch 11 möglich?
	}
	sei();

	// Hier kann man die Darstellung mit dem VT100-Terminal verbessern
	writeChar('\n'); // extra Vorschub für RP6Loader
	writeChar('\n');
	// vt100_set_cursor(3,1); // Bildstart bei VT100-Terminal, beim RP6Loader störend

	for(c=0; c<16; c++)
	{
		for(zeile=0; zeile<24; zeile++)
		{
			writeChar(' ');
			//writeIntegerLength(bildspeicher[23-zeile][c], HEX, 2); // Hexwerte
			writeChar((bildspeicher[zeile][c]-30)/10+'0');
  		}
		writeChar('\n');
	}
}

int main(void)
{
	init();
	vt100_cls();
	writeString_P(" Ein 24*16 Ascii-Bild per VT100                               3.5.10 mic");

	setStopwatch1(0);
	startStopwatch1();
	while(1)
	{
		if(getStopwatch1() >999) // Uhr
		{
		   setStopwatch1(0);
			sec++;
			if(sec>59) {sec=0; min++;}
			if(min>59) {min=0; std++;}
			if(std>23) std=0;
			vt100_set_cursor(26,72);
			writeIntegerLength(std,10,2);
			writeChar(':');
			writeIntegerLength(min,10,2);
			writeChar(':');
			writeIntegerLength(sec,10,2);
			
			Bild_aufnehmen(); // Bildrefresh nach einer Sekunde
		}
	}
	return(0);
}
void init(void)
{
   initRobotBase();
   //DDRC |= (SCL | SDA);         // Servopins auf Ausgang setzen
   //TIMSK |= (1 << TOIE1);       // Die Timer1 Overflow-ISR zur Servoansteuerung

	extIntOFF(); // schaltet den E_INT1-Port auf Eingang für den ADC
// 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);
	//powerON();
}
// 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 vt100_cls(void)
{
	writeString_P("\x1B[2J"); // clear screen ESC [ 2 J
	writeString_P("\x1B[H"); // cursor home ESC [ H
}
void vt100_set_cursor(uint8_t line, uint8_t column)
{
   writeString_P("\x1B["); // set cursor position  ESC [ Pl ; Pc H
   writeInteger(line, 10);
   writeString_P(";");
   writeInteger(column, 10);
   writeString_P("H");
}
void vt100_set_color(uint8_t foreground, uint8_t background)
{
	writeString_P("\x1b[");
	writeInteger(30+foreground, 10);
	writeString_P(";");
	writeInteger(40+background, 10);
	writeString_P("m");
}
void vt100_set_attrib(uint8_t attrib)
{
	writeString_P("\x1b[");
	writeInteger(0, 10);
	writeString_P(";");
	writeInteger(attrib, 10);
	writeString_P("m");
}
void vt100_writeGrafic(uint8_t *string)
{
	while(*string)
		writeChar(128|*string++); // Grafikzeichen mit gesetztem Bit7 senden
}
void vt100_writeGraficAt(uint8_t line, uint8_t column, uint8_t *string)
{
	vt100_set_cursor(line, column); // Cursor postionieren
	while(*string)
		writeChar(128|*string++); // Grafikzeichen mit gesetztem Bit7 senden
}
Das Bild wird einmal in der Sekunde neu dargestellt, mit ZOC steht dabei das Bild still und wird nicht hochgescrollt.

Gruß

mic

PS:
Bei der Ausgabe der Werte dreht sich alles um diese Zeile:
writeChar((bildspeicher[zeile][c]-30)/10+'0');

Vom Helligkeitswert von Bildpunkt x, y wird der Grundwert 30 abgezogen, der Rest durch 10 geteilt. Das ergab bei meinem Demo Werte bis 4. Um die Ausgabe z.B. auf einstellige Werte oder begrenzte Graustufen zu optimieren müßte man an dieser Formel schrauben. Die '0' sollte wohl klar sein ;)

Auf diese Bildbasis könnte man schon einfache Bildanalysen wie hellster/dunkelster Punkt/Zeile/Spalte aufsetzen oder Faltungen anwenden um Kanten zu finden ;)