QSerialPort sets DTR to high upon closing port; won't emit readyReady when DTR is otherwise held high.



  • Hi there,

    I'm writing a software downloader for the Parallax Propeller microcontroller, which is a microcontroller whose download protocol is designed to work over the serial port. One thing that makes this possible is the fact that the DTR signal is tied to the reset line with a single shot circuit.

    I wrote the initial application under Linux and it worked great. Now I'm testing on windows, and everything works exactly the same, download works great, it's all great, except now when the application is closed, QSerialPort sends another DTR signal (circled in the image).
    http://snag.gy/jCmTU.jpg

    I was able to isolate the behavior to simply opening the port, twiddling the DTR signal, then closing the port, and I discovered what the issue is. My original application sets DTR to true, then false for the remainder of the program. When the program closes, Windows appears to set DTR back to true again, which means whatever code I just downloaded to the chip is lost.

    The following code is really all it would take to reproduce this. This results in an extra reset when the port closes.

        QSerialPort serial;
        serial.open(QSerialPort::ReadWrite);
        serial.setDataTerminalReady(true);
        serial.setDataTerminalReady(false);
        serial.close();
    

    This one does not produce a reset at the end.

        QSerialPort serial;
        serial.open(QSerialPort::ReadWrite);
        serial.setDataTerminalReady(false);
        serial.setDataTerminalReady(true);
        serial.close();
    

    However, this second one produces another issue.

    With this one small change, my downloader times out every single time and receives no data at all in the read buffer at all, even though the chip itself makes it all the way through the entire handshake. There's no reason why this change should mess things up so badly. I've been starting at the download on the logic analyzer and the entire process is basically identical on both. The only difference is one resets at the end, and the other never seems to receive any data from the chip. Am I right in thinking that if DTR is held high, readyRead() doesn't get sent?

    Why does the Windows version of QSerialPort send DTR at the end? And why would holding DTR high instead result in readyRead() never being received?

    If this is some issue with Windows configuration or Windows wanting DTR to be held high all the time, is there any way to reconfigure that? Or work around it? There are other Propeller loaders around that don't use QSerialPort, and they don't have this reset problem when the port closes.

    This issue is a stumbling block that is preventing me from making a release of a much larger application. Any help is greatly appreciated.

    I'm using the pre-built MinGW Qt 5.4 on Windows 8.1 64-bit.

    My full software is here. It's not terribly long but you would need a Propeller chip to use it.
    https://github.com/parallaxinc/PropellerManager



  • This post is deleted!


    1. QSerialPort does nothing with DTR when opening or closing. Exception - if a queried DCB structure already has the "currentDcb.fDtrControl == DTR_CONTROL_HANDSHAKE" condition. In this case QSerialPort will reset it to DTR_CONTROL_DISABLE, because QSerialPort do not support DTR_CONTROL_HANDSHAKE.
    1. QSerialPort has QSerialPort::settingsRestoredOnClose property which by default is true. So, when closing, QSerialPort will try to reset the port state to it was as before opening (an original DCB or termios structure will be written back) . By other words: if your port had the DTR in true before opening, then QSerialPort will set this to true when closing too (and vice-versa).

    You can set "settingsRestoredOnClose" to false before opening of port.



  • Wow! This is perfect! That was exactly what I needed and my loader works now!!

    But this leads to a more troubling issue now.

    I felt so dumb thinking that I somehow overlooked this property though, as I had read through the QSerialPort docs up one side and down the other trying to figure this out, but now it makes sense why I didn't find it.

    http://doc.qt.io/qt-5/qserialport-obsolete.html#settingsRestoredOnClose-prop =(

    What's the deal? How can they mark this as obsolete and relegate it to some back corner of the documentation when it's the only way my application can possibly work?

    Now I have a much larger worry that my loader is going to stop working and there will be no way to fix it if I ever upgrade to the latest version of Qt 7! Is there any path to knock on developer's doors and say "PLEASE KEEP THIS FEATURE FOREVER DON'T DEPRECATE PLEASE PLEASE PLEASE"?

    Thanks for your help. :)



  • What's the deal?
    How can they mark this as obsolete and relegate it to some back corner of the documentation when it's the only way my application can possibly work?

    Yes, in future it will be deprecated (for Qt6, I guess). Instead, QSerialPort never won't restore settings when closing. Also when opening, QSerialPort will drop the state to some initial (e.g. when DTR/RTS is in low, always).

    PS: So, for your case it will be irrelevant.



  • Let me give you a little more background on why this is an issue.

    Here is a screen capture of the entire download protocol. The DTR line in this configuration is tied to a circuit that pulses the hardware reset when a DTR transition is received. This reset marks the beginning of the download process.

    As you can see, there is only one reset at the beginning, and at the end, the microcontroller starts running the program that was just downloaded. One of the compelling features of the Propeller microcontroller is the ability to download code to the device's RAM and run it directly, speeding up testing considerably.

    Naturally, if code is loaded into RAM and the board is reset, this code is lost, but this is exactly the situation that the auto-restore is creating, because it's forcing an extra reset after code download is complete.

    The problem I am having is that the library can't guarantee what the serial port settings were prior to using QSerialPort. So on my Linux machine, my code runs fine, because it sets DTR to high, then to low, and when the program closes, DTR is low by default so everything is great.

    But on Windows, DTR defaults to high, so when my command-line application finishes downloading code with DTR set to false, and closes the serial port, QSerialPort puts DTR back to high, which transitions DTR sending another reset, defeating the purpose of downloading to RAM.

    I know what you're thinking; just go from low to high on Windows. Well, two problems:

    • This is the other issue I reported that having DTR set high seems to result in readyRead() not ever being triggered. Any thoughts?
    • I also now have platform-specific behavior when the whole reason to use Qt is to avoid that.

    This download protocol has been around for nearly ten years, and there is another similar protocol that has been around for almost twenty. The number of boards that rely on this DTR behavior likely number in the hundreds of thousands at this point, so I can't change the protocol.

    I am working on a new download manager to support these boards, and I was very thrilled to see that Qt had its own serial library. There are not a lot of C++ serial libraries out there, let alone good ones, and QSerialPort is a breath of fresh air and very well done.

    However, if the library won't guarantee that the DTR behavior is well-defined, it breaks one of the core features of the application I'm using it for. By well-defined, I mean not resetting the serial port to whatever arbitrary values it had before I opened the port.

    I understand that what I am using serial for is not the most likely use case. However, removing it limits what I will be able to accomplish with this library.

    So why is this feature being removed with nothing to replace it? I added that one line, settingsRestoredOnClose(false), to my constructor, and now my application works flawlessly on Windows, and I can keep on developing my application.

    What am I supposed to do if this is removed?



  • What am I supposed to do if this is removed?

    Nothing. As I wrote above, this is in planns (for Qt6) that QSerialPort will be always initialize DTR/RTS to an default initial state (e.g. to low) when opening, and no not restore nothing when closing. In this case, after closing you got a same state as after opening (with no DTR triggering back). So, this "deprecating" just should to simplify behavior in future (when we knows and can relies on initial state after opening).



  • It's the not initial state that matters in this case, but the final state. Is there a way to determine what the initial conditions were so that my code can account for it and use the reset in a way that won't cause a transition?



  • I'm running into this exact issue. I have an application connecting to an external programmable chip via the serial port. When the Qt application terminates it resets the device. I wasn't having this problem in C#.

    Would it be possible to have a setting on the port object to not touch DTR at all? I've tried everything mentioned here and mine is resetting on app exit regardless of any settings I apply.



  • use QSerialPort::settingsRestoredOnClose(false)



  • I tried that - has no effect. I'm enumerating all serial ports, looking for a connected external device. When I open the port, I do this:

    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        if( info.portName() == QString("COM3") ) continue;
        if( info.portName() == QString("COM4") ) continue;
    
    	if( quit ) return;
    
        serial->close();  // close any previously open port
        serial->setPortName(info.portName());
        serial->setBaudRate( QSerialPort::Baud115200 );
    	serial->setFlowControl( QSerialPort::NoFlowControl );
    	serial->setParity( QSerialPort::NoParity );
    	serial->setStopBits( QSerialPort::OneStop );
    	serial->setReadBufferSize(8192);
    	serial->setSettingsRestoredOnClose(false);
    
    	if( !serial->open(QIODevice::ReadWrite) || !serial->isOpen() ) {
            continue;
        }
        serial->setDataTerminalReady(true);  // tried high, low, and neither
    

    I have tried setting DTR after opening the port (tried setting both high and low, and not at all) and that has no effect either. Any suggestions? Every time I connect to this board it resets. I don't have this issue on Mac, and I wasn't having it with C# either.





  • I looked at the Qt source for the serial port, and it's relatively simple, so it was pretty clear the issue wasn't there. I did more digging and found that the reset signal is inverted on the device I'm using (maybe it normally is - I'm not sure). In any case, me setting the DTR line true was causing the reset. I had tried setting it false as well, but it's possible I did that without having the "restoreOnClose" setting cleared.

    Regardless, thank you for pointing me in the right direction.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.