[JAVA] Lezen van seriële poort op RPI met timeout.

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • Steve04
  • Registratie: September 2011
  • Laatst online: 06-08 07:48
Hallo,

Momenteel ben ik bezig met een toepassing waarbij een Raspberry Pi communiceert met 8 microcontrollers via een half-duplex RS-485 verbinding, zie figuur.

Afbeeldingslocatie: http://users.telenet.be/stevevandenbussche/Afbeeldingen/rs485.png

De RPI (master) verstuurd een commando waarbij de geadresseerde microcontroller (slave) een antwoord terugstuurt. Zowel het commando als de response hebben een vaste lengte van 20 bytes. De code voor de RPI is geschreven in Java en maakt gebruik van de PI4J library.

Het volgende stukje code verstuurd een commando en leest de response
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public Future<byte[]> startTransaction(byte[] frameOut){
        
    Future<byte[]> futureResult = executor.submit(new Callable<byte[]>() {

        @Override
        public byte[] call() throws Exception {
                
            // put RS-485 transceiver in transmit mode
            txdenPin.high();
                
            // send command frame
            serialPort.write(frameOut);
            serialPort.flush();
                
            // put RS-485 transceiver in receive mode
            txdenPin.low();
                
            // read response frame
            return serialPort.read(frameOut.length);
        }       
    });
        
    return futureResult;    
}


Ter verduidelijking "serialPort" is een object van de klasse Serial in de PI4J library en "txdenPin" is de IO-pin die bepaald of de RS485 transceiver chip (ST1480) als transmitter of als receiver wordt gebruikt. De opdracht wordt doorgegeven aan een single thread executor omdat de read method wacht tot er data binnenkomt.

Aan de hand van de teruggegeven Future kan de response gelezen wordt inclusief het detecteren van een timeout:
Java:
1
2
3
4
5
6
7
8
9
try{
    response = futureResult.get(1000, TimeUnit.MILLISECONDS);
}catch(InterruptedException ex){
            
}catch(ExecutionException ex){
            
}catch(TimeoutException ex){
    // HANDLE TIMEOUT
}


Met een timeout van 1000ms loopt het af en toe fout terwijl er wel degelijk een response wordt teruggestuurd en dit ruim binnen de ingestelde timeout. Dit heb ik geverifieerd met een logic analyzer. Als ik de timeout vrij hoog kies bv. 5000ms werkt het perfect. Ik vroeg mij dus af wat de oorzaak hiervan zou kunnen zijn ?

Steve

Alle reacties


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 27-09 13:03
Je systeem is niet een realtime ding, dus in feite mag je em niets kwalijk nemen.

Wat ik me wel afvraag is wat er eigenlijk gebeurt als future timeout je read() call afbreekt (als het dat al doet). Is het niet beter om een non-blocking read te doen en zelf de timeout te implementeren, of een read call te doen met een timeout (als die er is)?

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • Steve04
  • Registratie: September 2011
  • Laatst online: 06-08 07:48
Ik ben uiteindelijk afgestapt van de Serial implementatie van Pi4J en heb gebruik gemaakt van de native methods en heb zelf de buffering gedaan. De asynchrone leesopdracht zie er nu zo uit:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
protected Future<?> readTask(byte[] dst) {
        
        Future<?> future = executor.submit(new Runnable() {
            
        @Override
        public void run() {
                
            while(true) {
                    
                if(rxBuffer.available() >= dst.length) {
                        
                    // up to dst.length bytes available
                    // => transfer data to destination and end task
                        
                    rxBuffer.dequeue(dst);
                    return;
                }else{
                        
                    // data currently not available
                    // => sleep a while and end the task when interrupted
                        
                    try{
                        Thread.sleep(50);
                    } catch(InterruptedException e) {
                        return;
                    }
                }
            }
        }
    });
        
    return future;
}


Als er een timeout is kan de achterliggende taak toch gestopt worden aan de hand van de cancel method van de Future. Deze zet de interrupt vlag van de uitvoerende thread hoog, de sleep wordt onderbroken en taak kan via de InterruptedException beëindigd worden.

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// start a new read task
Future<?> taskHandle = readTask(dst);
        
// wait for the task to complete
try {
    taskHandle.get(rxTimeout, TimeUnit.MILLISECONDS);
} catch(Exception e) {      
    throw new TimeoutException("receive operation failed; timeout");
} finally {
                        
    // cancel the task if it's still running
    if(!taskHandle.isDone()) {
                
        // interrupt the executing task, which will cause it to stop.
        taskHandle.cancel(true);
    }
}


Om de communicatie op betrouwbaarheid te testen heb ik een testprogramma gemaakt die continue berichten uitwisselt met de nodes op de bus. Tot mijn ongenoegen ging het steeds na minder dan 5000 transacties fout. De oorzaak was de GPIO-pin die de mode van RS-485 chip instelt: een logisch '1' is transmit en een logische '0' is receive. Een stukje pseudo code die het principe aangeeft hoe een bericht wordt verzonden:

code:
1
2
3
pin.high()
transmit()
pin.low()


Via mijn logic analyzer heb ik kunnen nagaan dat er redelijk wat vertraging zit op het laag maken van die pin,
soms zaten er uitschieters bij van >400ms. Uiteraard is het onmogelijk om data te ontvangen terwijl deze pin hoog is en dat wat dus de oorzaak van de fout.

Hoe heb ik het opgelost, ik ben vertrokken vanaf https://minibianpi.wordpress.com/, een minimale image van raspbian. Vervolgens heb ik enkel het nodige toegevoegd (was een serieus werkje) en de meest recente Java SE geïnstalleerd. Momenteel loopt de test opnieuw en zit ik boven de 100.000 uitgewisselde berichten. Ook de pulsduur van het signaal is vrij constant en ook geen grote uitschieters meer.

Acties:
  • +1 Henk 'm!

  • Radiant
  • Registratie: Juli 2003
  • Niet online

Radiant

Certified MS Bob Administrator

Dan heb je nu geluk, maar als je systeem complexer wordt of de scheduler een andere bui heeft zou het kunnen dat je alsnog tegen timing issues aan gaat lopen. Het zal nooit real-time worden, daarvoor moet je in de kernel aan de slag of een kant-en-klare 485-oplossing gebruiken van bijvoorbeeld FTDI of een circuitje bouwen dat de TX-enable voor je afschakelt zodra de UART even stil blijft.

[ Voor 11% gewijzigd door Radiant op 25-01-2017 17:56 ]


Acties:
  • 0 Henk 'm!

  • Steve04
  • Registratie: September 2011
  • Laatst online: 06-08 07:48
Hoe heb ik daar nog niet aan gedacht, ik heb het zelfs liggen 8)7

Afbeeldingslocatie: http://users.telenet.be/stevevandenbussche/Afbeeldingen/usb_to_rs485.png

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 27-09 13:03
Overigens kun je een Linux taak wel soft-realtime krijgen door de preempt-rt patches op de kernel los te laten ( zie https://wiki.linuxfoundation.org/realtime/start )
Is ook wel wat (leuk) werk trouwens :)

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.

Pagina: 1