PDA

View Full Version : Qt serial port to implement DMX



aut432
9th June 2015, 22:02
Hello,

I am trying to implement DMX protocol in Qt. Basically, Clicking a button on the screen should turn on combination of lights.

DMX works on RS 485. I am using Embedded Linux and Qt. The protocol requires to hold the line low for some time and high after that for some time again, before sending the actual frames for data.

Its like follows:

11197


I need to pull serial line down for 88uS and high after that for 8uS. I see that Qt has a function to send 0's for a specific duration.(i.e QSerialPort::sendBreak(int duration = 0) ). But I cannot find how do I hold the line high after this. I have not tested the sendBreak either.

Each Slot is like a frame of serial data, which I beleive I can use just QByteArray along with Qtserial.write function.

Just wondering ways I can achieve the custom high/low states. Any comments will be really helpful.

Thank you,
Amit

ChrisW67
10th June 2015, 22:15
I think You will be unlikely to be able to maintain the microsecond timings for bit flipping from user level code using a generic serial interface library (assuming it can even drive an EIA-485 interface) but I notice these are minimum lengths. Tight timing would usually be done with programmable hardware timers in the UART

aut432
10th July 2015, 15:24
Hello,

Thank you chris for the reply. I did get the DMX to work with 1ms timing(as the timings were minimum requirements). I used ioctl with terminos structure to get a custom baud rate of 250000. I am still interested in knowing how can I get micro second resolution in linux. Will i have to write my custom driver to do that? Or is there any way i can change linux parameters to execute instructions faster than 1ms. I know its not a Qt question. But if anyone could help me with this, it would be really awesome!

Thank you
Amit Tamboli

ChrisW67
10th July 2015, 21:12
Your CPU executes instructions on time scale way shorter than milliseconds: this is simply the result of the clock speed and nothing to do with operating system. In the "good old days" if you wanted to delay something for precisely x microseconds you could write a well crafted loop that executed NOOPs for the appropriate number of clock cycles. This approach blocked the CPU from doing other things and was very sensitive to clock speed.

These days PCs have highly programmable hardware timers that interrupt the CPU on precise time periods (see HPET or the older 8245 ). These are generally used from low-level drivers (Linux, Windows, whatever) handling the CPU interrupts and not user-level code. User code usually interfaces with the virtual device provided by the low-level driver, e.g. /dev/ttyS0. The facilities offered on embedded hardware will varying widely.

In your case the hardware timer is in the serial port, your 250000 baud rate giving 4 microsecond per bit, running independently of the CPU. The CPU only has to feed the small buffer of bytes on a less frequent basis to keep a well timed bit stream running. The serial port hardware interrupts when its buffer is empty or close to it, and OS driver provides more data if available.

aut432
13th July 2015, 14:48
Hello Chris,

Thank you for the reply. I understand that the CPU execution speed must be very high. And that the serial driver will talk with CPU to achieve the baud rate of 250000. I was wondering if I could get Qt code to execute faster than 1 ms. Because, If i write code like:

Set_High(bit);
Set_Low(bit);

It will flip the bit to low after 1ms(Checked on oscilloscope). It tells me that the Qt instructions execute every 1ms. I am new to linux and not sure why it executes so slow.

Is there a way to execute these instruction really fast as to achieve the delay of 88uS, I should have to write code like:

Set_High(bit);
Delay_uS(88);
Set_Low(bit);

Thanks,
Amit Tamboli

ChrisW67
13th July 2015, 21:34
Your CPU is executing single machine instructions very much faster than 1000 Hertz. C++ is a high level language and your code is is using Qt's high level wrapper around Linux's low-level serial port driver. Each call to Set_High() is, at the very least, executing hundreds of machine instructions to achieve this call. The CPU has to push the machine's program counter onto the stack, push the argument, call the code for the routine, push the state of any machine register that the routine might use, do whatever housekeeping your code does, push the machine state when calling the Qt code (which likely includes jump table lookups and numerous other library calls), any Qt serial class housekeeping that is needed, repeat the whole process in a call to the Linux driver, then unwind all of this as it returns. During this time your process may be preempted by another for a multitasking time slice. On top of that Qt's serial port implementation may only act when your code reaches the event loop which could be delayed by anything else your program is doing in the meantime. This why we decouple the transmission timing from the CPU with dedicated hardware, tx/rx buffers, and timers. That hardware generally takes care of start/stop signalling on its own, and you have only to worry about the payload.

You could remove Qt from the layer cake but most everything else would apply.

aut432
5th August 2015, 19:33
Hi Chris,

That was an awesome explanation! What i gather from it is that writing a function call at C++ level will not work for me. If i want that accurate timings, I might have to write a driver for linux. Is there any other way that you might know of?

Once again, Thank you for explaining it in so much detail.

-Amit