Raspberry mit Waveshare HAT 2-CH RS485

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
kiaralle
User
Beiträge: 204
Registriert: Donnerstag 19. August 2021, 19:11

Ein Hallo in die Runde,

ich versuche gerade ein Aufsatz für den Rapberry in Betreib zu nehmen.
Dazu habe ich den Code, eine Lib von Waveshare in meinen Code verbastelt, welcher so scheinbar läuft, aber ein paar Fragen für mich aufwirft.

https://www.waveshare.com/wiki/2-CH_RS ... 7cRwHaclk

Da ich einen Raspberry 5 verwende ist doch die Verwendung von RPi.GPIO nicht korrekt. Oder?

Die beiden RS485-Kanäle sind mit Kabeln zueinander verbunden, ich fahre also im Kreis ohne externe RS485-Geräte
Ich sende über "clock" raus und enpfange über "data". Funktioniert also.

das "dev = "/dev/ttyS0"" in der Class bei "def __init__" kann doch durch ein dev = "" ersetzt werden und es funktioiert immer noch.
Wird es durch
endat_clock = RS485config(dev = "/dev/ttySC0")
endat_data = RS485config(dev = "/dev/ttySC1")
definiert?

Code: Alles auswählen

import serial
import RPi.GPIO as GPIO
import time

import os
import sys
import logging


class RS485config(object):
    def __init__(ser, Baudrate = 115200, dev = "/dev/ttyS0"):
        print (dev)
        ser.dev = dev
        ser.serial = serial.Serial(ser.dev, Baudrate)
        GPIO.setmode(GPIO.BCM)
        GPIO.setwarnings(False)
        
        GPIO.setmode(GPIO.BCM)
        
        GPIO.setup(TXDEN_1, GPIO.OUT)
        GPIO.setup(TXDEN_2, GPIO.OUT)

        GPIO.output(TXDEN_1, GPIO.HIGH)
        GPIO.output(TXDEN_2, GPIO.HIGH)
        
    def Uart_SendByte(ser, value): 
        ser.serial.write(value.encode('ascii')) 
    
    def Uart_SendString(ser, value): 
        ser.serial.write(value.encode('ascii'))

    def Uart_ReceiveByte(ser): 
        return ser.serial.read(1).decode("utf-8")

    def Uart_ReceiveString(ser, value): 
        data = ser.serial.read(value)
        return data.decode("utf-8")
        
    def Uart_Set_Baudrate(ser, Baudrate):
         ser.serial = serial.Serial(ser.dev, Baudrate)
    
  
logging.basicConfig(level=logging.INFO)
libdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib')
if os.path.exists(libdir):
    sys.path.append(libdir)

TXDEN_1 = 27
TXDEN_2 = 22

endat_clock = RS485config(dev = "/dev/ttySC0")
endat_data = RS485config(dev = "/dev/ttySC1")

GPIO.output(TXDEN_1, GPIO.LOW) #send
GPIO.output(TXDEN_2, GPIO.HIGH) #read

clock = endat_clock.Uart_SendString("Hallo")
data = endat_data.Uart_ReceiveString(5)
    
print(data)
Benutzeravatar
DeaD_EyE
User
Beiträge: 1358
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Da ich einen Raspberry 5 verwende ist doch die Verwendung von RPi.GPIO nicht korrekt. Oder?
Ist aufgrund einer Hardware-Änderung inkompatibel.
The legacy RPi.GPIO library is incompatible with the Raspberry Pi 5 due to its new RP1 I/O chip. To run old code without rewriting it, use the rpi-lgpio wrapper, which translates RPi.GPIO calls to the modern lgpio library.
rpi-lgpio: https://pypi.org/project/rpi-lgpio/

Da das bei dir zu funktionieren scheint, hast du den Wrapper wahrscheinlich schon. Ansonsten müsste RPi.GPIO eine Fehlermeldung ausgeben.
das "dev = "/dev/ttyS0"" in der Class bei "def __init__" kann doch durch ein dev = "" ersetzt werden und es funktioiert immer noch.
Wird es durch
endat_clock = RS485config(dev = "/dev/ttySC0")
endat_data = RS485config(dev = "/dev/ttySC1")
definiert?
Ja und wenn du nur RS485config() aufrufst, wird die Zuweisung in der Funktionssignatur verwendet, also Baudrate=115200 und dev="/dev/ttyS0".
Da sich das ändern kann, würde ich die Default-Argumente entfernen:

Code: Alles auswählen

def __init__(ser):
Bleibt nur noch `ser` übrig, dass auf die Instanz der Klasse verweist. Aber man verwendet normalerweise das Wort `self` dafür.

Code: Alles auswählen

def __init__(self):
Danach musst du alle Referenzen von `ser.` nach `self.` ändern.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
kiaralle
User
Beiträge: 204
Registriert: Donnerstag 19. August 2021, 19:11

Ok, wunderbare Hilfe :-)

Hab alles umgeschrieben. Code funktioniert immer noch.

Die Sachen wie startbits, parity werden in diesen Fall nicht benötigt, weil es sich um einen HAT handelt?

Noch etwas, diese Sachen,

GPIO.output(TXDEN_1, GPIO.LOW) #send
GPIO.output(TXDEN_2, GPIO.HIGH) #read

steuern ob ich den Kanal zum lesen oder schreiben benutze. Hier hat also jeder Kanal seine feste Aufgabe.

Wenn ich also mit nur einem Kanal arbeiten muss, so muss ich jedesmal von HIGH auf LOW usw umschalten?

Gruß Ralf
Benutzeravatar
grubenfox
User
Beiträge: 656
Registriert: Freitag 2. Dezember 2022, 15:49

Sachen wie Startbits, Parity usw. müssen bei Sender und Empfänger identisch sein weil es eine serielle Schnittstelle ist... (wenn dafür nichts vorgegeben wird, dann wird das genommen was eben genommen wird wenn der Programmierer nichts vorgibt)

_______________________________________________________________________________
https://www.python-kurs.eu/index.php
https://learnxinyminutes.com/docs/python/

https://quickref.me/python https://docs.python-guide.org/
https://www.youtube.com/watch?v=qU3Rc6_B9es
kiaralle
User
Beiträge: 204
Registriert: Donnerstag 19. August 2021, 19:11

Ok, dan integrier ich das.
Danke
Benutzeravatar
DeaD_EyE
User
Beiträge: 1358
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Ganz vergessen auf die Namensgebung einzugehen. Normalerweise macht Blackjack das immer: https://peps.python.org/pep-0008/

Noch was zur Technik: RS485 ist Halb-Duplex. Zum Empfangen von Daten wird RE (Receiver Enable) auf Low gesetzt. Zum Senden muss DE (Driver Enable) auf High gesetzt werden. Viele Controller fassen RE und DE zusammen. Beim Waveshare ist das dann so, dass für den Empfang EN1/EN2 auf Low gezogen werden muss und zum Senden auf High.


Ich hab den Code mal etwas abgeändert (nicht getestet):

Code: Alles auswählen

import logging
from contextlib import contextmanager
from threading import Lock

import RPi.GPIO as GPIO
import serial

TXDEN_1 = 27
TXDEN_2 = 22


def setup_gpio():
    """
    GPIO einrichten
    """
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    GPIO.setup((TXDEN_1, TXDEN_2), GPIO.OUT, value=True)


@contextmanager
def enable_send(channel):
    GPIO.output(channel, GPIO.HIGH)
    yield  # ab dann wird der Code im with-Block ausgeführt
    # ab hier ist der Code des Kontext-Managers abgearbeitet
    # output auf 0 setzen, um wieder empfangen zu können
    GPIO.output(channel, GPIO.LOW)


class RS485config(object):
    def __init__(self, dev, enable_gpio, baudrate=115200):
        self.serial = serial.Serial(dev, baudrate)
        self.enable_gpio = enable_gpio
        # soll gleichzeitiges Senden und Empfangen unterbinden
        self.send_receive_lock = Lock()

    def send(self, text):
        # zuerst wird der Lock abgefragt, danach der GPIO durch den Kontextmanager gesetzt
        # wenn er z.B. gerade Daten empfängt, wartet er, bis der Lock wieder freigegeben ist
        # danach wird erst der GPIO auf High gesetzt
        with (
            self.send_receive_lock,
            enable_send(self.enable_gpio),
        ):
            self.serial.write(text.encode("ascii"))

    def receive(self, size):
        with self.send_receive_lock:
            data = self.serial.read(size)
        return data.decode("ascii")

    def set_baudrate(self, baudrate):
        self.serial.close()
        self.serial = serial.Serial(self.serial.port, baudrate)


logging.basicConfig(level=logging.INFO)

# ist eigentlich nicht notwendig
# libdir = Path(__file__).parent.joinpath("lib")
# if libdir.exists():
#    sys.path.append(str(libdir))

# Wenn irgendwas in lib ist, dann importire es
# from lib.modul import funktion


setup_gpio()

# Signatur geändert: Port, Enable-GPIO, Baudrate
endat_clock = RS485config("/dev/ttySC0", TXDEN_1)
endat_data = RS485config("/dev/ttySC1", TXDEN_2)

clock = endat_clock.send("Hallo")
data = endat_data.receive(5)

print(data)

sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Antworten