Forum: Mikrocontroller und Digitale Elektronik LANC Implementierung


von emc2 (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich bin gerade dabei mir zur Steuerung eines Multikamerasetups einen 
Controller für das Sony LANC Kamerasteuerungsprotokoll zu bauen.
Ich habe schon einiges dazu gelesen und mir auch ein paar 
implementierungen angeschaut, die aber allesamt nicht zuverlässig 
funktioniert haben. Hier wird meistens mit externen Interrupts, timern 
und manchmal ganz schlimm mit blockierenden delay's gearbeitet.

Wenn ich mir das Protokoll aber so ansehe 
(http://www.boehmel.de/lanc.htm) dann ist das doch nichts anderes als 
ein invertiertes UART mit 9600 Baud. So versuche ich das gerade auch auf 
einem ATmega8 zu realisieren: zunächst möchte ich nur mal den 
Kamerastatus empfangen. Leider möchte das nicht so ganz.

Meine Schaltung für die eindrahtige Open-Collector Verbindung incl. 
Inverter siehe Anhang.

Mein AVR läuft mit 16Mhz und externem Quarz.
Hat jemand eine Idee? Ich bekomme rein gar keinen RX Interrupt und 
leider habe ich keinen LA zur Hand.

Vielen Dank!

1
#define BAUD 9600
2
3
#include <avr/io.h>
4
#include <util/setbaud.h>
5
#include <avr/interrupt.h>
6
7
volatile char lanc[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
8
volatile uint8_t count = 0;
9
10
void uart_init(void)
11
{
12
    UBRRH = UBRR_VALUE;
13
    UBRRL = UBRR_VALUE;
14
    
15
    UCSRB |= (1<<TXEN) | (1<<RXEN) | (1<<RXCIE);
16
    
17
    // 8N1
18
    UCSRC = (1<<UCSZ1) | (1<<UCSZ0);
19
}
20
21
ISR(USART_RXC_vect)
22
{
23
    char buffer = UDR;
24
    
25
    // first Byte?
26
    if(buffer == 0b00011000)
27
        count = 0;
28
    
29
    lanc[count] = buffer;
30
    
31
    if(count < 7)
32
        count++;
33
}
34
35
int main(void)
36
{
37
    // init LED
38
    DDRC = 0b00100000;
39
    PORTC = 0b00100000;
40
    
41
    uart_init();
42
    sei();
43
    
44
    while(1)
45
    {
46
        // Rec?
47
        if(lanc[1] == 0x3A)
48
            PORTC &= ~(1<<PC5);
49
        // Stop?
50
        else if(lanc[1] == 0x30)
51
            PORTC |= (1<<PC5);
52
    }
53
    
54
    return 0;  
55
}

von emc2 (Gast)


Lesenswert?

Update: ich bekomme sehr wohl den Interrupt, aber nur wenn ich TX nicht 
mit dem Controller verbinde !? Und zwar ganz egal welchen Leitungspegel 
ich an TX rausgebe. Klar, wenn ich die Leitung auf Masse ziehe bin ich 
ja dran mit Senden, aber sonst?

Jetzt bekomme ich zwar fleißig die Interrupts, und mein LANC Wort füllt 
sich auch, aber den Rec State bekomme ich nicht zu sehen. Die Kamera 
sendet ihn aber definitiv.

von c-hater (Gast)


Lesenswert?

emc2 schrieb:

Deine ganze Schaltung beruht auf einem Denkfehler. Ja, LANC ist 
gegenüber RS232 invertiert, soweit ist das richtig. *ABER:* Auch die 
UART ist gegenüber RS232 invertiert. D.h.: deine Schaltung darf NICHT 
invertieren.


Deine ganze Schaltung ist also komplett überflüssig und durch eine 
einfache Diode und einen Widerstand zu ersetzen, deren einzige Aufgabe 
es ist, den TX-Ausgang zu einem "open collector" Ausgang zu machen. Etwa 
sowas:


                    Pullup
                 --|===|-----o V+
                |
RX o------------*
                |
TX o------|<----*------------o LANC

GNDo-------------------------o GND

von emc2 (Gast)


Angehängte Dateien:

Lesenswert?

So, nun, es hat ein bisschen gedauert, bis ich mich dem Projekt vor ein 
paar Tagen wieder widmen konnte ;-).

Hardware ist klar soweit und ich bewege mich erstmal noch nur auf der 
Empfangsseite: ich möchte nur ein paar Statusbytes der angeschlossenen 
Kamera auslesen.

Das Empfangen eines 8byte LANC Frames klappt nun auch, das habe ich 
soweit getestet. Ich habe nun das Problem, dass die Bedingung in meiner 
Hauptschleife, die ein byte auf einen bestimmten Wert hin überprüft nie 
zutrifft:
1
if(lanc[4] == 0xEB)
2
            PORTC &= ~(1<<PC5);
(sollte dann eine LED schalten, die auch an sich funktioniert)

Ich kann nun aber mit sicherheit sagen, dass 0xEB in lanc[4] drinstehen 
müsste: zum einen ist das 4. byte meines Quellsignals aus der Kamera mit 
diesem Wert belegt (Logic analyzer) und zum anderen kommt auch genau 
0xEB raus, wenn ich lanc[4] über den UART kurz vorher wieder ausgebe 
(ebenfalls durch Logic analyzer verifiziert):
1
while(!(UCSRA & (1<<UDRE)));
2
        UDR = lanc[4];

Der Wert in dem Byte ist im Quellsignal auch konstant, weshalb es nicht 
sein kann, dass er sich durch den RXC interrupt in der Zwischenzeit 
ändert.
Auch Vergleiche mit anderen Bytes, die immer 0xFF sind klappen so nicht. 
Ich habe den Wert schon invertiert, die Bitreihenfolge umgekehrt, etc. 
alles ohne erfolgreichen Vergleich. Ich habe das Gefühl irgendwie liegt 
der Fehler am Vergleich an sich. Hat vielleicht jemand eine Idee 
diesbezüglich?

Anbei noch der gesamte Code. Vielen Dank!

von emc2 (Gast)


Lesenswert?

Nachtrag:

selbst wenn ich die interrupts ausschalte und unmittelbar vor dem 
Vergleich lanc[4] manuell auf 0xEB setze, wird die Bedingung nicht 
ausgeführt. Ich verstehe das nicht.
1
lanc[4] = 0xEB;
2
if(lanc[4] == 0xEB)
3
            PORTC &= ~(1<<PC5);

von emc2 (Gast)


Lesenswert?

habe es gerade noch mal ohne Abfrage probiert:
1
while(1)
2
    {
3
        // Rec?
4
        PORTC = lanc[4];
5
    }

Da ich in genau diesem Byte entweder 0xEB oder 0xFB drinstehen habe, 
ergibt dies genau eine Änderung des 3. Bits, an besagtem Portausgang ist 
aber keinerlei Pegeländerung auszumachen.
Dass lanc[4] den richtigen Wert enthält habe ich noch mal ausgiebig 
verifiziert (direkte Ausgabe über den UART Tx), ansonsten hat sich an 
obigem Code nichts geändert.

Ich bin gerade wirklich etwas verzweifelt damit und wäre über eine 
Hilfestellung sehr froh!

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

emc2 schrieb:
> Ich bin gerade wirklich etwas verzweifelt damit und wäre über eine
> Hilfestellung sehr froh!

Vermutlich wäre es das einfachste, erstmal mit HTerm am PC deine 
Theorien zu verifizieren. Dazu brauchst du, weil dein PC evtl. keine 
echte serielle Schnittstelle mehr hat, einen USB-Seriell Adapter und 
einen Pegelwandler (Max232), der die das gleiche Signal erzeugt, wie der 
AVR. Und nun noch dein Dioden-Interface.
Sprich dann erstmal mit der Kamera und schau, was sie wirklich sendet.

Das mit dem Dioden Interface sollte gut klappen, ich habe vor einiger 
Zeit auf die gleiche Art das Protokoll der JVC Data Battery's geknackt.

Du kannst auch ein LCD an den AVR andocken und dir anzeigen lassen, was 
da nun wirklich aus Sicht des AVR ankommt, aber das dauert vermutlich 
länger, als die PC Nummer. Zu sehen, was wirklich passiert ist immer 
besser als auf Vermutungen das Programm aufzubauen.

: Bearbeitet durch User
von Nils P. (ert)


Lesenswert?

LANC ist aber auch nicht schön ;-(

Wo hast du das Timing berücksichtig? Woher willst du wissen ob der 
Controller sich nicht verschluckt hat? Also das jetzt Lanc[4] an 
Rx[4+-x] steht?

Teste doch erstmal ob überhabt einmal dein Rec-Status gelesen wird.
while i<7 && Led_flag!=1
   if lanc[i]=0xEB;
     PORTC |= (1<<PC5);
     Led_flag=1;
   end;
i++;
end
...

Wenn ja schalte deine Debug LED an und blockier diese Abfrage für ein 
paar Sek

Viel Erfolg
G Termi

Ps: syntax ist Müll :-)

: Bearbeitet durch User
von emc2 (Gast)


Angehängte Dateien:

Lesenswert?

Nun, lanc lässt nach einem Frame a 8 Bytes eine längere Pause. Ich 
resette also nach jedem empfangenen Byte meinen Timer0, den ich so 
vorgeladen habe, dass in der langen Pause der Overflow interrupt kommt. 
Der setzt mir dann ein Flag, dass das nächste Byte der Beginn eines 
neuen Frames ist.

Die ankommenden Daten habe ich schon in aller Kleinlichkeit geprüft: ich 
gebe mit folgender Hauptschleife den gerade eben Empfangenen Frame 
wieder übers UART aus:
1
while(1)
2
    {
3
        if(framecomplete)
4
        {
5
            uint8_t i;
6
            for(i = 0; i<8; i++)
7
            {
8
                while(!(UCSRA & (1<<UDRE)));
9
                UDR = lanc[i];
10
            }
11
            framecomplete = 0;
12
        }
13
    }
(die framecomplete Variable ist im vollständigen code weiter oben noch 
nicht drin, hab ich eben erst eingeführt). Da mein USB Seriell Wandler 
kaputtgegangen ist, guck ich mir nun das LANC Eingangssignal und den 
Output des UARTs mit meinem Logicanalyzer an (siehe Anhang) und siehe 
da, die Daten, die aus dem UART rauskommen entsprechen exakt dem was 
reingeht.

Sobald ich aber nun eines dieser Bytes manuell auf seinen Wert hin 
prüfen möchte, trifft die Bedingung NIE zu (was ich oben schonmal 
geschildert habe)

von ert (Gast)


Lesenswert?

lanc[5] ☺


g ert

von emc2 (Gast)


Lesenswert?

Hä? Nö!

Aber als dashier auch nicht zutraf wurde ich gerade doch sehr stutzig:
1
char x = 0xEB;
2
if(x == 0xEB)
3
...

ändere ich den Datentyp in ein uint8_t, ist alles in Ordnung. So 
funktioniert auch mein ganzer obiger code, wenn das lanc Array ein 
uint8_t array und nicht wie von mir ursprünglich ein char Array ist. 
Aber das verstehe ich nicht. Nach allen guten C Geistern ist doch ein 
char genau ein Byte!? Was geht denn da schief?

von emc2 (Gast)


Lesenswert?

Okay, böse Falle. Mir war nicht klar, dass beim avr-gcc ohne 
-funsigned_char ein einfacher 'char' signed ist... das erklärt natürlich 
so einiges...

Dann mache ich mich jetzt mal ans Senden ;-)

von emc2 (Gast)


Lesenswert?

nun, mein Senden klappt noch nicht ganz und ich habe auch eine Vermutung 
warum: wenn ich mit der Schaltung oben von c-hater sende, wird bei einer 
1 der Bus nie ganz auf low gezogen, sondern c.a. 1,2V darüber. Ich gehe 
davon aus, dass das mit der Durchlassspannung der Diode zusammenhängt.

Wenn ich ein probietäres LANC Steuerungsgerät messe, zieht es beim 
senden den Bus ganz runter. Wie kann ich das auch erreichen?

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

emc2 schrieb:
> sondern c.a. 1,2V darüber.

Dann hast du aber eine ganz schlechte Diode erwischt, das sollten 
höchstens so 0,7 Volt sein. Am besten ist ein Open Drain Ausgang am 
Sender in Form einen kleinen MOSFet, wie den BS170. Dummerweise braucht 
der, um den Ausgang auf Masse zu ziehen, ein positives Signal am Gate, 
so das du TX doch nochmal invertieren musst. Oder du nimmst zwei Gatter 
eines 74HC(T)06, der 6 invertierende Treiber mit Open Drain beinhaltet. 
Oder ein Gatter eines 74HC(T)07.

Übrigens sendet die Kamera 2 Stopbit, das solltest du evtl. auch machen.

: Bearbeitet durch User
von emc2 (Gast)


Angehängte Dateien:

Lesenswert?

können auch c.a. 0,7V gewesen sein, auf dem Oszi ist das immer nicht so 
ganz genau ablesbar...
Das mit den zwei Stopbits ist mir auch aufgefallen, sollte aber egal 
sein, da ich ja zwei einzeln getriggerte bytes sende. Aber habs der 
vollständigkeit halber angepasst.

Mangels MOSFET habe ich mir nun schnell mal einen Open collector mit 
einem BC547 gebaut und noch so einen als zusätzlichen Inverter davor.
Wunderbar, die Kommunikation läuft nun einwandfrei! Vielen Dank.

Ein Problem gibt es noch: wenn T2 länger als c.a. 100ms durchschaltet 
(z.B. durch längeres Low auf dem TX pin, oder trennen der TX Leitung) 
bricht die ganze Versorgungsspannung auf c.a. 2,3V ein und der µC kommt 
damit natürlich nicht mehr hoch. Die Spannungsversorgung erfolgt 
komplett durch die V+ Leitung des LANC ports der Kamera.
Ich denke ich belaste die Kamera in diesem Falle zu stark mit dem Strom, 
der in besagtem Falle durch T2 und R5 fließt. Ich habe auch schon 
versucht den Lanc Pullup (R5) zu vergrößern, hat aber nicht geholfen. Im 
Normalbetrieb, wenn T2 nur mal für eine Bitdauer durchschaltet ist alles 
in Ordnung, spätestens aber beim Reset des Controllers (oder 
dementsprechend auch beim Programmieren) ist es vorbei...

Hat da jemand eine Idee, wie ich das lösen kann? Die Schaltung soll auch 
im Endeffekt von der Kamera versorgt werden.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

emc2 schrieb:
> Hat da jemand eine Idee, wie ich das lösen kann?

Du sorgst mit einem z.B. 5,6k Pullup an PD1 (TXD) des MC dafür, das bei 
undefiniertem Portpin T2 sicher sperrt.

emc2 schrieb:
> Das mit den zwei Stopbits ist mir auch aufgefallen, sollte aber egal
> sein, da ich ja zwei einzeln getriggerte bytes sende. Aber habs der
> vollständigkeit halber angepasst.

Wenn du mal längere Sequenzen sendest, kann es sein, das die Kamera das 
2te Stopbit als Verarbeitungszeit braucht, das wird bei DMX z.B. genauso 
gemacht.

von Nils P. (ert)


Lesenswert?

Wenn du mir per PM deine Email-Adr schickst, kann ich dir meinen 
LANC-Schaltplan schicken. ich hatte auch ein paar Probleme damit und im 
Netz damals was gefunden... Finde ich aber auf die Schnelle momentan 
nicht. Ich habe mir den ganze Tüddelkram mit FETs geschenkt und einfach 
nur zwischen Rx/Tx einen 560Ohm R gehangen, läuft zumindest bei mir ohne 
Probleme.

Und Vorsicht bei meiner Sony kommen bis zu 8V aus dem LANC-VCC

: Bearbeitet durch User
von Axel P. (ddmo22)


Lesenswert?

@emc2 Wie steht es denn um dein Projekt? Suche nach einem einfachen 
Beispiel für den ATMega um Befehle an die Kamera zu senden.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.