Minimallösung: Kamera für den RP6
Hallo
In diesem Thread möchte ich euch zeigen, wie man unglaublich einfach und kostengünstig eine Kamera an den RP6 anschliessen kann.
Grundsätzlich geht es allerdings darum, mit einem 8MHz-ATMega ein Composite-Video-Signal auszuwerten. Ob das, wie in meinem Fall, eine ausgeschlachtete Überwachungskamera liefert, oder ein 15€-Teil vom großen c (art-nr: 150001-62) oder gar der Video-Ausgang einer Digicam oder Foto-Handys, ist völlig egal.
Die Hardware:
- Ein Kameramodul, das mit 5V funktioniert und ein Composite-Video-Signal liefert (5V, 1VSS/75Ohm)
- Ein Stecker, der in die RP6-XBUS1/2-Buchse passt
- Ein Cinch-Stecker, der das Kontrollsignal für einen Bildschirm auskoppelt.
Der Anschluß:
Die 5V-Kamera wird am Pin 3/5(Vcc) und 1/2(GND) angeschlossen. Der Vid-Ausgang der Kamera (oder des beliebigen Composite-Video-Lieferanten) kommt an Pin 8 (E_INT1). Dies ist, außer den User_ADCs der einzig freie ADC-Kanal, der zudem noch praktischerweise am XBus liegt. Außerdem hat er einen PullDown von 10K, die optimale Last für ein Composite-Signal. Der Chinch-Stecker wird zusätzlich zwischen Pin 8 (Signal) und Pin 1/2 (GND) angeschlossen. Der ist eigentlich nur zu Kontrolle gedacht, damit man sehen kann, was der RP6 sieht. Gam(ma) ist bei meiner Kamera nicht angeschlossen, sollte man aber als Jumper vorsehen.
Die Software:
Erste Hürde war der ADC und die damit möglichen Samplezeiten. Die im Datenblatt des ATMegas angegebenen 200kHz reichen für die 5MHz eines fbas-Signals bei weitem nicht aus. Aber er ist schnell genug, um wenigstens ein paar Daten einzulesen. Hier das Setup des ADC:
Code:
// 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);
Referenzspannung sind die interenen 2,56V. Das ist recht praktisch, den ich lese das linksbündige Ergebniss als Byte-Variable ein und jeder Schritt entspricht nun 0,01V. (mein Signal hat 1,4V mit dem Multimeter gemessen) Im "8-Bit-Modus" ließt man nur die MSB der Ergebnisse, die der ADC bei "free running" mit 4MHz digitalisiert.
Nächste Hürde war das Timing des Signals. Mit der Samplerate kann ich das vsync-Signal gut erkennen, das hsync ist deutlich länger und markiert den Start des Halbbildes (Alle Fachbegriffe und Kentnisse habe ich von hier). Einige auf einander folgende Zeilen sehen etwa so aus:
http://radbruch.roboterbastler.de/rp...nc-signale.pdf
Man sieht deutlich die vsyncs, allerdings schaffe ich so nur ca. 60 Werte pro Zeile. Mehr geht einfach nicht mit C, vielleicht kann man mit Assembler hier noch etwas "rauskitzeln". Mein Code für das Einlesen eines Pixels:
do *pixelzeiger=ADCH; while (*pixelzeiger++ > 20);
Alternativen wie:
while (count_pixel) pixel[--count_pixel]=ADCH;
for (;count_pixel; pixel[--count_pixel]=ADCH);
wurden fast identisch übersetzt und brachten nur ca. 50 Lesungen pro Zeile. Mit
Code:
while (line) // Zeile abwarten
{
while (ADCH > 20); while (ADCH < 30); line--;
}
kann ich nun das vsync erkennen, hsync dauert bei mir ca. 47 Zyclen, also prüfe ich so:
Code:
do // hsync abwarten
{
vsync=0; while (ADCH > 20); while (ADCH < 30) vsync++;
} while (vsync < 40);
Da ich so vertikal "nur" ca. 60 Werte erfassen kann, aber horizontal alle Zeilen zwischen ca. 30 und 260 direkt ansteuern kann, habe ich meine Kamera zum Linienfolgen um 90 Grad gedreht. Zudem verwende ich nur die Pixel 10 bis 14, also einen recht kleinen Bereich der verfügbaren Daten:
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;
}
uint16_t get_line(uint16_t line)
{
uint8_t pixel[256],*pixelzeiger, vsync;
uint16_t temp=0;
pixelzeiger=&pixel[0]; // Zeiger auf Start Pixelspeicher
cli();
do // hsync abwarten
{
vsync=0; while (ADCH > 20); while (ADCH < 30) vsync++;
} while (vsync < 40);
while (line) // Zeile abwarten
{
while (ADCH > 20); while (ADCH < 30); line--;
}
do *pixelzeiger=ADCH; while (*pixelzeiger++ > 20); // Zeile einlesen
sei();
*pixelzeiger=0; // Endekennung der Daten
pixelzeiger=&pixel[10]; // Summe der Pixel 10-14 bilden
while (pixelzeiger < &pixel[15]) temp+=*pixelzeiger++;
return (temp);
}
int main(void)
{
uint16_t zeile;
uint16_t gamma, i;
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);
gamma=0; i=0;
for (zeile=30; zeile<260; zeile+=5)
{
gamma+=get_line(zeile);
i++;
}
gamma=gamma/i;
gamma-=gamma/3;
writeInteger(gamma,DEC);
writeString_P("\n\r");
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);
}
Für OCR oder das Erkennen von Personen dürfte diese Lösung nicht taugen. Noch fehlt die KI, aber dafür haben wir ja andere Spezialisten.
Gruß
mic
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo
Es wächst und gedeiht, hier mein einfacher Linienfolger:
Code:
#include "RP6RobotBaseLib.h"
#define mpower 50
#define spur 20
// 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;
}
uint8_t get_line(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();
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;
}
strich_links=0;
strich_rechts=0;
for (i=0; i<zeilen; i++)
{
if (((pixel[i] >> 4) == g_dunkel) && ((g_hell-g_dunkel)>1))
{
if (!strich_links) strich_links=i; // wenn linker Rand gefunden,
else strich_rechts=i; // weitersuchen bis rechter Rand
}
}
strich_mitte=(strich_links+strich_rechts)/2;
strich_breite=strich_rechts-strich_links;
return(strich_mitte);
}
int main(void)
{
uint8_t i;
uint8_t pow_l, pow_r, dir_l, dir_r;
uint8_t strich, keine_strich, out_l, out_r, abweich;
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);
out_l=true;
out_r=false;
keine_strich=0;
dir_l=dir_r=BWD;
setMotorDir(dir_l,dir_r);
pow_l=pow_r=mpower;
setMotorPWM(pow_l/2,pow_r/2);
mSleep(mpower);
setMotorPWM(pow_l,pow_r);
do
{
strich=get_line(50,48);
abweich=2*abs(spur-strich);
if (strich)
{
out_l=out_r=false;
if (strich < (spur-15)) out_l=true;
if (strich > (spur+15)) out_r=true;
if (strich < spur)
{
pow_l=mpower+abweich;
pow_r=mpower-abweich;
}
else
{
pow_l=mpower-abweich;
pow_r=mpower+abweich;
}
}
else
{
if (out_l) {pow_l=mpower; pow_r=0;}
if (out_r) {pow_l=0; pow_r=mpower;}
}
setMotorPWM(pow_l,pow_r);
writeString_P("\n\r");
writeInteger(strich, DEC);
writeString_P("-");
writeInteger(abweich, DEC);
mSleep(100);
} while (1);
return(0);
}
und ein paar Videos der Tests. Zuerst die RN-Teststrecke:
Bild hier
(http://www.youtube.com/watch?v=txMYl7aKTBA)
Dann ein USB-Kabel, das am Boden liegt:
http://www.youtube.com/watch?v=NFQz49vDDfg
Selbes Kabel auf einem anderen Untergrund:
http://www.youtube.com/watch?v=paXpHySWn7g
http://www.youtube.com/watch?v=qVBPT5tGpjg
Ganz nett, wenn man anschaut, wie es mit der KI dieses Linienfolgers bestellt ist. Zudem scanne ich den Strich nur von einer Seite...
Zitat:
Ich kapier das nicht richtig.
kannst du den code mal so abspecken das er das bild in ein array schreibt?
Euch ist hoffentlich klar, dass dies alles hier keine "Ready to use"-Lösung ist. Die Kamera ist nur das sehr einfache Auge. Wie der Roboter das Gesehene interpetiert, ist eure Aufgabe:
Code:
#include "RP6RobotBaseLib.h"
uint8_t bildspeicher[1024], *bildzeiger; // 32*32=1KB * 8Bit Bildspeicher bereitstellen
void bild_einlesen(void)
{
uint8_t pixel[32],*pixelzeiger;
uint8_t i, zeilen, step, lines, rows, h_step, h_sync, 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();
// h_sync abwarten (syncsignal länger 40 bedeutet Seitenanfang)
do { h_sync=0; while (ADCH > 20); while (ADCH < 30) h_sync++; } while (h_sync < 40);
// 30-35 Zeilen Austastzeit überlesen (der Rest des hsyncs+nicht darstellbare BTX-Infos)
h_step=35; while (h_step) { while (ADCH > 20); while (ADCH < 30); 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 (ADCH > 20); while (ADCH < 30); 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 20
// bei ca. 150 beginnt die 2.Schwarzschulter. Bei 150-20 möglichen Bildpunkten
// ergibt sich eine maximale Auflösung von 128 horizontal. Zusammen mit der
// vertikalen Auflösung von 230 kämen wir dann bei einem 8MHz-ATMega auf stolze
// 128*230 Bildpunkte.
h_delay=20+4*rows; while (h_delay--);
*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);
}
int main(void)
{
uint16_t i, j;
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)
{
bild_einlesen();
for (i=0; i<32; i++)
{
for (j=0; j<32; j++)
{
if (bildspeicher[j+32*i] >90) writeString_P("*");
else writeString_P(" ");
}
writeInteger(i,DEC);
writeString_P("\n\r");
}
mSleep(200);
}
return(0);
}
Damit macht man z.B. 32x32-"Bilder", wenn das jemand braucht. Die Ausgabe dazu findet ihr im Anhang.
Ein kleines Filmchen zeigt, wie es funktioniert:
Bild hier
(http://www.youtube.com/watch?v=5PCvCAti1RY)
Hier wird übrigens nur per Wert > 90 ein Punkt gesetzt oder nicht. Quasi nur die Rohdaten ohne Bearbeitung.
Gruß
mic
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Sommer
Zitat:
Aber wenn ich ins Datenblatt schaue, sehe ich das der Atmega32
für eine ADC Wandlung im bestenfall 13µS benötigt.
Darüber habe ich mich auch schon gewundert. Allerdings steht das nur oben im Datenblatt. Bei der Beschreibung des ADCs wird dann immer nur von 13,5 "Taktzyklen" gesprochen. Und die verwendet er auch, ich habe keinen Weg gefunden, um das Samplen nach weniger als 10Bit abzubrechen. Mit der Zyklenrechnerei hab ich's nicht so, aber der Ansatz wäre wohl (Bei 8MHZ ATMega und perscaler /2): 1/4000000Hz Clock*14 Zyklen oder 3,5us. Dann würde ich das 4,7us-hsync mindestens einmal treffen. Wenn man genau hinschaut, sieht man im Beitrag oben im Diagramm die Schwarzschultern und die hsyncs. Allerdings ist das blöderweise spiegelverkehrt, weil ich die Werte rückwärts ausgegeben hatte. Hier ein Code der 256 Werte in Zeile 100 einliest und tabellengerecht an den PC sendet:
Code:
// Liest ab der 100. Zeile 256 Werte am Stück ein
// und sendet die Daten als Tabellenvorlage zum PC.
#include "RP6RobotBaseLib.h"
int main(void)
{
uint8_t pixel[256],*pixelzeiger, *endezeiger;
uint8_t vsync, lines;
initRobotBase();
extIntOFF();
//powerON();
// interne Referenz 2,56V, linksbündig, Kanal ADC4
ADMUX = (1<<REFS1) | (1<<REFS0) | (1<<ADLAR) | 4;
// free running triggern
SFIOR = (0<<ADTS2) | (0<<ADTS1) | (0<<ADTS0);
// kein interupt, einschalten, prescaller /2
ADCSRA = (0<<ADIE) | (1<<ADEN) | (0<<ADPS2) | (0<<ADPS1) | (1<<ADPS0);
// free running aktivieren, altes Flag löschen
ADCSRA |= (1<<ADATE) | (1<<ADIF);
// Initialisierung starten
ADCSRA |= (1<<ADSC);
while (!(ADCSRA & (1<<ADIF)));
ADCSRA |= (1<<ADIF);
pixelzeiger=&pixel[0];
endezeiger=&pixel[255];
lines=100;
cli();
do // vsync abwarten
{
vsync=0;
while (ADCH > 20);
while (ADCH < 30) vsync++;
}while (vsync < 40);
while (lines) // zeile abwarten
{
while (ADCH > 20);
while (ADCH < 30);
lines--;
}
// 256 Werte am Stück einlesen und als Basis für ein Diagramm senden
do *pixelzeiger=ADCH; while (pixelzeiger++ < endezeiger);
sei();
writeString("------------------------\n\r");
lines=0;
do
{
writeInteger(lines, DEC);
writeString_P("; ");
writeInteger(pixel[lines], DEC);
writeString_P("; ");
writeString("\n\r");
}while (++lines);
while (1)
return(0);
}
Damit schafft man mit einem 8MHz-ATMega "nur" knapp 50 Lesungen pro Zeile, weil sich die Formulierung des Einlesebefehls und damit der Code geändert hat. Im Anhang die Ausgabe des Programms mit einem schwarzen Strich vor der Kameralinse.
Wie gut sich meine Kamera an das BAS-Timeing hält, weiß ich allerdings nicht. Ich sollte mal eine alternative Signalquelle testen (oder Abwarten, bis es einer von euch nachgebaut hat).
Das zeilenweise Einlesen des TV-Signals ist inzwischen "Schnee von gestern". Meine aktuellen Programme lesen das Bild spaltenweise ein, mit je einem h_delay zwischen hsync und Lesen des Pixels pro Zeile. Damit schaffe ich nun, wie im Quellcode der Smilieerkennung zu lesen ist, locker 150 unterschiedliche Lesepositionen innerhalb einer BAS-Zeile.
Gruß
mic
[Edit]
Wenn ich das so lese, stelle ich auch fest, dass da was nicht stimmen kann. Wenn eine Zeile (laut Wiki) 64us dauert, kann ich mit 3,5us pro Lesung niemals auf 50 oder gar 60 Werte/Zeile kommen. Da passt etwas noch nicht, aber trotzdem funktioniert es irgendwie. 8-[
Liste der Anhänge anzeigen (Anzahl: 1)
So, ich bin, galube ich, ein Stück weiter. Wie es scheint, liefern die Zeilensyncs bei mir so ungefähr 31 zurück, und eben scheine ich einen Bildanfang erwischt zu haben, einen haufen '31' und dazwischen ziemlich konstant '60', danach schienen nach den '31' spikes Zeilendaten zu kommen, ich werde das jetzt mal genauer untersuchen.
Übrigens mal am Rande, die Beschaltung ist im Prinzip folgende:
Atmega8 mit 16MHz, Conrad S/W Kameramodul an ADC3, Pulldown von 10KOhm am Videosignal
Den ADC initialisiere ich so
Code:
void adc_init()
{
// ADC interne Referenz 2,56V, Ergebniss linksbündig, Kanal ADC3
ADMUX = (1<<REFS1) | (1<<REFS0) | (1<<ADLAR) | 3;
// Wandler einschalten, prescaler /2
ADCSRA = (1<<ADEN) | (0<<ADPS2) | (0<<ADPS1) | (1<<ADPS0);
// free running
ADCSRA |= (1<<ADFR);
// Initialisierung starten
ADCSRA |= (1<<ADSC);
// und noch die wohl eher unnötige Initiallesung
while (!(ADCSRA & (1<<ADIF)));
ADCSRA |= (1<<ADIF);
}
Die Daten lese ich so
Code:
for (cnt=0;cnt<512;cnt++)
{
*zeiger=ADCH;
*zeiger++=ADCH;
}
Danach schubse ich die Daten per RS232 in eine Excel Tabelle
Liste der Anhänge anzeigen (Anzahl: 1)
Moin!
Da machen sich wohl die 16MHz meines Mega8 bemerkbar, ich habe mal folgendes zusammenprogrammiert:
Code:
void synctest()
{
uint8_t h_sync;
unsigned int cnt;
uint8_t daten[512];
cli();
//auf bildanfang warten
for (cnt=0;cnt<512;cnt++)
{
h_sync=0;
while (ADCH > 50); //Zeile abwarten
{
while (ADCH < 35) h_sync++; //Syncdauer zählen, Schwarzschulter bei 48, sync bei 31
}
//if (h_sync > 20) PORTC ^= (1<<Laser);
daten[cnt]=h_sync;
}
//debugging...
for (cnt=0;cnt<512;cnt++)
{
txd(daten[cnt]);
}
}
und im Anhang mal die Dauer der Syncs, ich würde sagen, ich erwische alle 5, damit kann ich doch jetzt endlich mal was anfangen!!!!
Die Nacht ist ja noch jung.
MfG Volker
Ich stelle gerade fest, das die Pegel zwischen den Kameramodulen stark abweichen, bei meinem Farbmodul liegen die Syns bei 31 und der Schwarzpegel bei 48, bei meinem S/W Modul sind die Syncs bei 56 und der Schwarzpegel bei 96.......
da macht es evtl. Sinn, einfach vorher mal den Minimalwert zu ermitteln und als Referenz für den Sync abspeichern.....