Forum: PC-Programmierung Perl Seriellen Port Ein/Auslesen (Linux)


von Dieter S. (schmaddy)


Lesenswert?

Hallo Leute,

ich versuche mich schon seit einiger zeit damit meinen Atmaga mit dem PC 
kommunizieren zu lassen, was jedoch nur Manuell mit HTERM gelingt.

Jetzt soll dies Automatisch geschehen, von meinem Server aus in 
regelmäßigen Abständen. Als idee kam mir ein einfaches script per 
chronjob auszuführen, was daten vom Atmega speichert.

Zum test habe ich für meinen Atmega ein Programm geschrieben was per 
UART daten empfängt, die Daten binär auf 8 LEDs anzeigt (zur kontrolle) 
und die Daten wieder zurück zum PC sendet.

Nun zum Script...
1
#Name: Write_and_Read
2
#Author: Schmaddy
3
#Version Alpha-zu-Testzwecken Aug. 2011
4
5
#Befehle Atmega:
6
#0xA1 = Naechsten 8bit auf PA ausgeben
7
#0xB0 = Sende PortB
8
#0xF1 = Naechsten 8bit in Register1 schreiben
9
#unbekannt ausgabe auf 8bit led
10
11
use Sys::Syslog;
12
13
$port ="/dev/ttyS0";                    # Port festlegen
14
system "stty 1200 ixon < $port";        # Baudrate festlegen mit stopbit
15
16
#Senden
17
open(COM, "+>$port") or die "Kann $port nicht oeffnen."; # geraetedatei oeffnen
18
printf(COM '0xFF');                     # Geraet senden/schreiben
19
close(COM);                             # Verbindeung schliessen
20
21
#Lesen
22
sysopen(COM, "$port", O_RDONLY) or die "Kann nicht Lesen";
23
$bytes=sysread(COM, $buffer, 1);
24
print $buffer, "\n" if $bytes ==1;
25
close(COM);

Das script sendet Daten zu meinem Atmega, was ich an den Kontroll LEDs 
feststellen kann.
Aber es empfängt nichts.
Ausgeführt wird es ohne fehlermeldung, aber das script hat eine 
unendliche laufzeit. wenn ich es ohne den #Lesen abschnitt ausführe, 
sendet es und das script ist beendet. Aber mit beendet es sich nicht. 
Ich denke das
1
$bytes=sysread(COM, $buffer, 1);
irgendwie nicht richtig ist, kann das sein?

Ich habe um fehler am Atmega auszuschließen den code mal gekürzt...
1
//  Created: 05.08.2011 20:56:36
2
//****************************************************************
3
//*  AVR Communication                                           *
4
//*  (C)  []                                *
5
//*  Für Atmega16                                                *
6
//*                                                              *
7
//*  Empfängt daten, zeigt sie auf PORTD an, und sendet sie      *  
8
//*  zurück an Versender.                                        *
9
//*                                                              *
10
//*                                                              *
11
//*                                                              *
12
//*                                                              *
13
//*                                                              *
14
//*                                                              *  
15
//****************************************************************
16
17
.include "m16def.inc"
18
 
19
.def temp = R16                  //Temponäre Daten
20
.def zeichen = r17                //Daten der Zeichen
21
22
.equ F_CPU = 1000000                            // Systemtakt in Hz
23
.equ BAUD  = 1200                               // Baudrate
24
 
25
// Berechnungen
26
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  // Runden
27
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     // Reale Baudrate
28
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  // Fehler in Promille
29
 
30
.if ((BAUD_ERROR>10) || (BAUD_ERROR<-10))       // max. +/-10 Promille Fehler
31
  .error "Fehler Baudrate grösser 1 Prozent!"
32
.endif
33
 
34
// Stackpointer initialisieren
35
 
36
    ldi     temp, HIGH(RAMEND)
37
    out     SPH, temp
38
    ldi     temp, LOW(RAMEND)
39
    out     SPL, temp
40
 
41
// Port D = Ausgang
42
 
43
    ldi     temp, 0xFF
44
    out     DDRD, temp
45
 
46
// Baudrate einstellen
47
 
48
    ldi     temp, HIGH(UBRR_VAL)
49
    out     UBRRH, temp
50
    ldi     temp, LOW(UBRR_VAL)
51
    out     UBRRL, temp
52
 
53
// Frame-Format: 8 Bit
54
 
55
    ldi     temp, (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0)
56
    out     UCSRC, temp
57
58
// Uart Einschalten 
59
    sbi     UCSRB, RXEN                     // RX (Empfang) aktivieren
60
  sbi     UCSRB,TXEN                        // TX (Senden)  aktivieren
61
 
62
// Empfangsschleife
63
receive_loop:
64
   sbis     UCSRA, RXC                      // warten bis ein Byte angekommen ist
65
   rjmp     receive_loop
66
   in       temp, UDR                       // empfangenes Byte nach temp kopieren
67
   out      PORTD, temp                     // und an Port D ausgeben.
68
   rcall     seriell_out                    // Daten zurückschicken
69
   rjmp     receive_loop                    // zurück zum Hauptprogramm 
70
71
// Senden
72
seriell_out:
73
    sbis    UCSRA,UDRE                      // Warten bis UDR für das nächste
74
                                            // Byte bereit ist
75
    rjmp    seriell_out
76
    out     UDR, temp                                     
77
  ret                                       // zurück zum Hauptprogramm
78
79
// Pause zum Synchronisieren des Empfängers
80
81
// Sub                                 
82
sync:
83
    ldi     r16,0
84
sync_1:
85
    ldi     r17,0
86
sync_loop:
87
    dec     r17
88
    brne    sync_loop
89
    dec     r16
90
    brne    sync_1  
91
    ret
92
93
// Ende

Mit dem Manuellem auslesem an meinem Windows PC via HTERM habe ich keine 
Probleme. Ich sende beispielsweise 0b01010101 an den atmega und die leds 
zeigen 01010101 an und auf der konsole empfange ich ebenfalls wieder 
0b01010101. Sodass ich den fehler am Perlscript des Servers vermute.

Der Server läuft unter Fedora 14


Kann mir jemand beim Perl script einen guten tipp geben?

Danke und Gruß schmaddy

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Aber es empfängt nichts.

Ich vermute, dass es darauf wartet, bis das vermeintliche Modem einen
Carrier meldet.  Dass ein serieller Port nicht für ein Modem (seine
ursprüngliche Zweckbestimmung) sondern für eine Dreidrahtkommunikation
dient, muss man üblicherweise separat festlegen (stty clocal).

Ansonsten:

Serielle Schnittstelle unter Perl

BSDs arbeiten hier übrigens etwas anders: sie haben ein callout
device, /dev/cu*, das sich initial (bis zum ersten Auftreten eines
DCD) so verhält, als wäre implizit CLOCAL gesetzt.  Dieses device
bekommt dann üblicherweise das Nutzerzugriffsrecht (bspw. über die
Gruppe uucp), während das dialup device (/dev/tty*) nicht für den
Nutzer zugreifbar ist und beim open() bereits auf DCD blockiert.
Dadurch kann man ein getty auf /dev/tty* ansetzen, das bis zum
(dialin-)carrier des Modems wartet, während zwischenzeitlich das
callout device für abgehenden Traffic in Richtung Modem benutzt
werden kann, ohne dass dieser mit dem getty in Konflikt gerät.

Da Modems allmählich in Vergessenheit kommen, braucht man das kaum
noch; man muss sich lediglich dran erinnern, dass man für derartige
Aufgaben unter einem *BSD nicht auf /dev/tty* zugreift, sondern
auf /dev/cu*.

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.