Linux中串行端口的读写

7
我需要在树莓派上(使用最新的Raspbian系统)通过串口实现两个设备之间的通信。两个设备都使用CP2102控制器并连接到树莓派。

Data flow

终端:

pi@pi ~ $ ls -l /dev/serial/by-id
total 0
Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 -> ../../ttyUSB2

pi@pi ~ $ ls -l /dev/serial/by-path
total 0
platform-bcm2708_usb-usb-0:1.2.1:1.0-port0 -> ../../ttyUSB1
platform-bcm2708_usb-usb-0:1.2.4:1.0-port0 -> ../../ttyUSB2
platform-bcm2708_usb-usb-0:1.3:1.0-port0 -> ../../ttyUSB0

通常情况下,当我向串口A发送命令时,设备A会通过串口A回复并通过串口B发送数据。然后,我需要将这些数据重新翻译给设备B(串口C),并从串口C接收答案。
问题在于,串口A的回复出现在串口B上,而串口B的数据出现在串口A上。
我尝试过不同的语言和串行库,但结果都是一样的。因此问题是:为什么在使用树莓派时会发生这种情况?如何在树莓派上实现这个功能?
附注:两个设备都能正常工作。我的C#代码运行良好。我使用了System.IO.Ports.SerialPort类来实现它,它看起来像Pi4J和RXTX解决方案。
附注2:下面是我尝试在RPi上使用的一些代码:
Serial, C ++:(非常糟糕的代码片段)
Serial port("/dev/ttyUSB2", 115200U);
Serial port1("/dev/ttyUSB1", 115200U);
port1.setTimeout(Timeout::max(), 250, 0, 250, 0);
port.setTimeout(Timeout::max(), 250, 0, 250, 0);

cout << "Is the serial port open?";
if (port1.isOpen()) {
    cout << " Yes." << endl;
    uint8_t data[2] = { 0xAA, 0x00 };
    port1.write(data, 2);
    data[1] = 0xFF;
    sleep(1);
    port1.write(data, 2);
    while (port.available() < 7);
    int av = port.available();
    string ss;
    port.read(ss, av);
    for (int i = 0; i < av; i++){
        cout << (uint)ss.at(i) << " ";
    }
    cout << "av: " + (uint)av << endl;
}

RXTX,Java:

    public class Bridge_rxtx {
        public static final int baudrate = 115200;

        protected SerialPort spDevB_Data;
        SerialReader devB_DataListener;

        protected SerialPort spDevA_Data;
        SerialReader DevA_DataListener;

        protected SerialPort spDevA_Control;
        SerialPortEventListener DevA_ControlListener;

        public Bridge_rxtx(String comDevB_Data, String comDevA_Data, String comDevA_Control) {
            try {
            spDevB_Data = setupPort(comDevB_Data);
            spDevA_Data = setupPort(comDevA_Data);
            spDevA_Control = setupPort(comDevA_Control);
            } catch (Exception ignored){
                ignored.printStackTrace();
            }

            try {
                devB_DataListener = new SerialReader(spDevB_Data.getInputStream(), spDevA_Data.getOutputStream(), "B-A");
                DevA_DataListener = new SerialReader(spDevA_Data.getInputStream(), spDevB_Data.getOutputStream(), "A-B");
                DevA_ControlListener = new SerialPortEventListener() {

                    @Override
                    public void serialEvent(SerialPortEvent spe) {
                        throw new UnsupportedOperationException("Not supported yet.");
                    }
                };

            spDevB_Data.notifyOnDataAvailable(true);
            spDevA_Data.notifyOnDataAvailable(true);
            spDevA_Control.notifyOnDataAvailable(true);

            } catch (IOException ex) {
                Logger.getLogger(Bridge_rxtx.class.getName()).log(Level.SEVERE, null, ex);
            }

        }

        public void launchBridge(){
            System.out.println("Starting...");
            try {
                spDevA_Control.getOutputStream().write(new byte[] {(byte)0xAA, (byte) 0x00}, 0, 2);
            } catch (IOException ex) {
                Logger.getLogger(Bridge_rxtx.class.getName()).log(Level.SEVERE, null, ex);
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException ex) {
                Logger.getLogger(Bridge_rxtx.class.getName()).log(Level.SEVERE, null, ex);
            }
            try {
                spDevA_Control.getOutputStream().write(new byte[] {(byte)0xAA, (byte) 0xFF}, 0, 2);
            } catch (IOException ex) {
                Logger.getLogger(Bridge_rxtx.class.getName()).log(Level.SEVERE, null, ex);
            }
            System.out.println("Started");
        }

        SerialPort setupPort(String portName) throws Exception {
            SerialPort serialPort = null;
            CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
            if (portIdentifier.isCurrentlyOwned()) {
                System.out.println("Error: Port is currently in use");
            } else {
                CommPort commPort = portIdentifier.open(this.getClass().getName(), 2000);
                if (commPort instanceof SerialPort) {
                    serialPort = (SerialPort) commPort;
                    serialPort.setSerialPortParams(baudrate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
                } else {
                    System.out.println("Error: Only serial ports are handled by this code.");
                }
            }
            return serialPort;
        }

        public static void main(String[] args) {
            Bridge_rxtx bridge = new Bridge_rxtx("/dev/ttyUSB0", "/dev/ttyUSB2", "/dev/ttyUSB1");
            bridge.launchBridge();
        }
    }

Pi4J,Java:

public class Bridge {
    public static Bridge instance;

    public static final int baudrate = 115200;

    protected Serial spDevB_Data;
    SerialDataListener devB_DataListener;

    protected Serial spDevA_Data;
    SerialDataListener devA_DataListener;

    protected Serial spDevA_Control;
    SerialDataListener devA_ControlListener;

    private Bridge() {

    }
    public Bridge(String comDevB_Data, String comDevA_Data, String comDevA_Control) {
        instance = this;

        devA_ControlListener = new SerialDataListener() {
        //SerialDataEvent in Pi4J doesn't support binary
        //data by default. I implemented this myself.
            public void dataReceived(SerialDataEvent event) {
                System.out.println(bytesToHex(toPrimitives(event.getBinaryData())));
            }

        };
        devB_DataListener = new SerialDataListener() {
            public void dataReceived(SerialDataEvent event) {
                byte[] data = toPrimitives(event.getBinaryData());
                instance.spDevA_Data.write(data);
                System.out.println("B -> A: " + bytesToHex(data));
            }

        };
        devA_DataListener = new SerialDataListener() {
            public void dataReceived(SerialDataEvent event) {
                byte[] data = toPrimitives(event.getBinaryData());
                instance.spDevB_Data.write(data);
                try {
                    Thread.sleep(15);
                } catch (InterruptedException ex) {
                    Logger.getLogger(Bridge.class.getName()).log(Level.SEVERE, null, ex);
                }
                System.out.println("B <- A: " + bytesToHex(data));
            }

        };

        spDevB_Data = SerialFactory.createInstance();
        spDevB_Data.addListener(devB_DataListener);

        spDevA_Data = SerialFactory.createInstance();
        spDevA_Data.addListener(devA_ControlListener);

        spDevA_Control = SerialFactory.createInstance();
        spDevA_Control.addListener(devA_DataListener);


        spDevB_Data.setMonitorInterval(40);
        spDevA_Data.setMonitorInterval(80);
        spDevA_Control.setMonitorInterval(25);

        spDevB_Data.open(comDevB_Data, baudrate);

        spDevA_Data.open(comDevA_Data, baudrate);
        spDevA_Control.open(comDevA_Control, baudrate);

    }

    public void SetupBridge() {
        spDevA_Control.write(new byte[]{(byte) 0xAA, (byte) 0x00});
        try {
            Thread.sleep(20);
        } catch (InterruptedException ex) {
            Logger.getLogger(Bridge.class.getName()).log(Level.SEVERE, null, ex);
        }
        spDevA_Control.write(new byte[]{(byte) 0xAA, (byte) 0xFF});
    }
}

1
“问题在于SERIAL A的回复出现在SERIAL B上,而SERIAL B的数据出现在SERIAL A上。” <-- 可能是个愚蠢的问题(我对硬件布局一无所知),但是……接线没问题吗? - fge
RPi有两个USB端口。每个设备连接到其中一个。所以布线没问题 :) - Rawnald Gregory Erickson
前缀会使系统复杂化,如果这是唯一的解决方法,那么可以接受。 - Rawnald Gregory Erickson
1
好的,我仍然不清楚手头的问题,但如果您使用JDK 7+,您可以在Java代码中至少使用try-with-resources吗? - fge
你写道:“通常当我发送命令时...”,你期望得到某些响应,但是“我尝试了不同的语言和串行库,但结果并非如你所愿。”那么,在什么情况下你实际上得到了“正常”的响应?或者这只是一种修辞方式来解释某个手册? - sawdust
显示剩余3条评论
1个回答

1

没有足够的声望来评论: 我猜测这可能与Linux如何枚举硬件有关。我认为,根据使用的发行版和连接顺序,您的USB设备可能会具有另一个串行路径。

您确定每次都使用相同的USB端口吗?并且正确的USB端口映射到/dev/tty了吗?

您可以通过确保为其设置udev规则来强制使HID始终具有相同的名称。一些信息 在这里


我确信这个问题与Linux中的设备枚举无关。实际上,我已经放弃了将两个设备连接到Pi的想法,因为这已经不再相关了。尽管如此,还是谢谢你的回复。 - Rawnald Gregory Erickson
你好。我只是被一条评论触发了,评论中提到在Windows上使用C#运行良好,但我不知道你在树莓派上使用的是哪个版本的C#。不客气。 - acidjunk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接