ISR and serial port

Hello ^^

I am writing code to handle a device sending loads of data through serial port. I have to manage emptying the serial buffer at least every 13 milliseconds or so.
I am using interrupts to know when the data is ready, and so I am planning to use the Interrupt Service Routine to empty the buffer as quickly as possible and store the data somewhere.

My problems are:

  • the ISR’s stack is limited to 200 bytes or so, so I can’t use this space to store my data.
  • the read function won’t allow me to use a volatile variable.

I have little experience with real time systems so there must be a way but I can’t think of one :-/

Has any of you encountered this kind of problem before ?
Any help appreciated ^^

Can’t you just use the standard serial port?

The ISR can access data through a global variable, doesn’t need to be on the stack.

Are you talking about read() or your own read. I can’t figure out why you would want to pass a volatile pointer to read(). By the way you can use volatile with proper case, but the read function will not use it as such. I don’t see the connetion between ISR and read().

Or is it that you plan on using read() in the ISR? You can’t do that. Read the documentation most C calls cannot be using in ISR.

OK so it was a stupid question sorry ^^
I was trying to use read in the ISR :-/

So let’s say: “any way to get the serial buffer copied in the ISR ?”

I don’t beleive there is such a thing as a stupid question!

I can’t make sense of your question. I will try to guess what you are trying to do.

You are using an interrupt to know when the data is ready. What is the source of that interrupt? The serial chip? What to you mean by “get the serial buffer copied in the ISR”, where to you want to copy it from and to where?

Basicaly if you want to read data from a serial port you do.

fd open("/dev/ser1" );
read( fd, buffer, size of data )
close().

The serial driver will handle all the interrupt of the serial port for you

If you want to start the read operation upong generation of a interrupt (lets’ say a timer interrupt). You can setup the ISR to send an event to a thread and then have the thread perform the read().

OK this is exactly what I am doing now, but when the CPU load is high, even with boosting the thread’s priority, I’m missing data sometimes. I was trying to prevent this by having the ISR itself reading the serial buffer.

So then I guess there is no way to have the ISR reading it.
And this is only up to code optimization and hoping the CPU load will never get too high, is it ?

It is very unlikely the serial driver is missing data since it’s interrupt driven. The serial driver should be able to buffer 2k of data. So unless a higher priority interrupt is taking long time to complete or some other process is disabling interrupt, your problem moslty cause by the design of your software.

Even at 115kb,in 13 milliseconds you get only 130 bytes which is way below the 2k buffer (which you can increase via command line argument to devc-ser8250).

There must be some problem with the design, but without any imore detail I can’t make any suggestion.

I guess the first question is, why do you need to write your own device driver? Is it a special serial chip that you don’t have a QSSL driver for it? Or is it read/write to a QSSL driver is too slow for you?

A standard devc-ser8250 driver, is driven by ISR. It also uaually enable FIFO so it can reduce interrupts. A driver will have seperated IO buffers, by default, 2K each. You can use the -I/-O option to adjust this number if you reaaly ran into a buffer underrun problem.

As Mario pointed out, the feeling here is your problem is not on interrupt, but something else. If that’s the case, writing your own device driver may not help.

Does your reading thread have enough higher priority? Does the reading thread priority floating ? Are you sure it is the hardware missed the incomming characters, or the driver actually pick up the character by does not deliever to your application on time?

Arg sorry I posted a reply already but it seems that it got scrapped. Likely timed out.
For some reason openQNX tends to be very slow over there :-/

So here’s the point:
I’m not using RS232 but RS422 @ 500000bauds
I receive interrupts on the LPT port, not serial. When I receive one, I trigger my interrupt reading thread, and get the data cleared out to some variable in memory.

The fact is I receive bunches of data each 13ms, and I have to get valid data frames out of it. If I cannot empty my buffer fast enough, I’m starting to miss some data and get invalid frames.
Right now my thread is running with 50 priority.
It seems that having other processes running cause the data frames to be invalid sometime, so I believe this is due to my thread missing some data.

My idea was to extract the data in the ISR so that it would be highest possible priority. But from what you tell me I believe I can’t do that :slight_smile:

You helped me a lot already so thank you very much ^^

I beg you Peng, tell us what exactly you are doing, even post some code if you can. You are giving so little information that you are making it impossible to help you. As you said yourself you don’t appear to have lots of experience with this stuff. So please help us get inside your head so we can tell you about some misconception your might have.

By the way RS422 doesn’t make any difference as long as the serial chip is 8250 compatible. However the 500000 baud is a weird one. At that rate you have 50000 interrupt seconds which is a lot depending on your processor speed. That’s one interrupt evey 20us. Of course if you specify the depth of the fifo to the serial driver you can reduce that a lot (xtang mentionned it but you didn’t give any feedback to us about that). If you are not specifying the fifo depth, it mean any other interrupt that last longer then 20us (like maybe your own ISR for the parallel port) will result in character being lost

Here is what I understand from your post. You have hook an ISR to the parrallel port, the ISR sends an even to a thread and that thread is reading from the serial port. You are not telling us how you are reading from the serial port, are you using a read or are you reading from the serial chip directly. If you are using read() (which you should) I don’t understand how you can be missing data. Have you measure time between the ISR to make sure they are really comming at every 13ms? How long the the ISR of the parallel port.

The 500000 baud rate is not standard, are you really sure it’s 5000000.

That sounds like Profibus or DataHighway. Yes, they indeed do operate at that speed which will be why Peng is not using an 8250 based controller. In fact, it looks to me like he’s talking to a serial controller that is connected to the parallel port.

Peng:
If you could provide us with the hardware docs or just a webpage link for the product you are using that would be a big advantage.

OK. I assume you are writing your own driver. Are you using “InerruptAttach()” to attach an ISR, and then trigger your reading thread? Or are you using “InterruptAttachEvent()” directly?

You want to extract data in a real ISR (That is, the function you hook up with InterruptAttach()). You want to store datas in a pre-allocated buffers, be it 2K/4K or 8K. You only signal your reading thread (or whoever is going to operate on the frame) once your see a frame character.

Try to use a seperated interrupt other then LPT if possiable, or if you can’t, try to slay devc-par so no one is sharing interrupt with you.

If the above still not faster enough, the only chance is to enable the FIFO (if the chip supports it). That will reduce interrupt rate as Mario point out.


char ibuf[8 * 1024];
int    ibuf_idx;

struct sigevent *my_isr()
{
       /* looking hardwaqre for the input character(s) */

       /* copy input character(s) into ibuf[ibuf_idx] */

       /* if there is a fram character received, signal reader */
}

int main()
{
       InterruptAttach(..., my_isr, ...);

       for (;;) {
             InterruptWait();
       }
}

Sorry i had no time to come there recently.

I did not copy code over there because it’s an embedded system and so it’s not so easy to have it copied out ;-)

The device I’m using is a SICK laser sending data frames on a Quatech RS422 PCI serial adapter. It also send sync pulses which I get through a regular serial port, every 13ms, which mean a new data frame has been sent. I’m not using devc-par, this one is disabled.

Atm I have an InterruptAttachEvent getting the sync signal from the serial port, which trigger a thread with priority lvl50 reading the serial buffer (size default 2048) using the read command.

The problem is that the high priority doesn’t seem to help the thread much :-/
When there’s just the program running everything comes out smoothly, but if I start charging the CPU, say by starting another stupid thread just doing idle things, then I will start failing to get correct frames for some reason.

I tried to have an InterruptAttach with a self-designed ISR, but I didn’t manage to have this one reading all the data without the CPU crashing.

The reading operation is basically a simple read(DEVICE, buffer, 2048), and then I need the main program to be able to extract data from the buffer.

I hope this helps enough.
If it doesn’t, I’m afraid I can’t do much more for now :-/

Thanks.

Are you sure the thread gets trigger every 13 ms (what if there is a harware problem and the serial port doesnt see the sync pulse). Can you explain what this sync pulses consists of. If youve let the RS-422 port be handled by the serial driver, unless you have prevented the serial driver from also accessing the regular serial port, doing an InterruptAttachEvent on the IRQ is not a good idea. One reason is that interrupt will stay masked until the thread unmask it, this could mean lost of data because the serial driver will not get it’s interrupt. It`s even worst if you try to use a real ISR with InterruptAttach because both the driver and your software will access the serial port, not good.

How to you trigger the thread. If its via pulse the priority of the thread is irrelevent, its the priority of the pulse that matters.

Here is one much simpler way to do it.

ser = open( “/dev/ser1”…);
Qser = open( “/dev/ser2”,… );

for(;:wink:
{
read( ser, sync, 1 ); // wait for sync
read( QSwer, frame, …); // get frame
process_frame();
}

That being said it appears to me you don’t even need to way for the sync, just wait for the frame (unless the frame is build in such a way that it’s hard to figure out where is the beginning and the end)

Also note that if you do a read( DEVICE, buffer, 2048 ) and you have not open the serial port with the NON_BLOCKING flag the read call will NOT return unless there is 2048 byte in the buffer.

Furtermore asking for 2048 byte when the buffer of the serial driver is also 2048 is not a good idea, because it could mean the driver could miss some bytes, you are not leaving any room for the serial driver to breath ;-)

The reason I need the pulse sync is just that I need to know what data arrived at what time ;-)
This pulse is coming through the parallel port, and the parallel port driver is disabled (and it IS coming every 13ms).

I’ll try playing with the serial buffer size see if it changes something, thx ^^