C 'desktop' programmer needs advice on how to code embedded C on a micro controller.

You could also poll on output and just use interrupt on recieve. I've done that before. Or use a unbuffered send routine where the transmit interrupt uses the send string directly rather than storing it an intermediate buffer. That does mean you can't reuse a buffer until send is done.

IE something like

send("string"); send(buffer); change(buffer); send(buffer);

Will give odd results, you'd need something more like

send("string"); send(buffer); wait_for_buffer_sent(); change(buffer); send(buffer);

Basically you move the buffer handling and sizing up to the main program for transmit. Could be more memory efficient with less blocking but it's less transparent.

Robert

--
Posted via a free Usenet account from http://www.teranews.com
Reply to
Robert Adsett
Loading thread data ...

David did not post any of the IRQ code here, but it should include an overflow check.

You should also check the overflow flag from the IRQ routine and if you are not certain of the adequacy of the buffer size, you may want to include a variable to store the max buffer space used.

Maybe this is a nit, but depending on the details of the message, you may need to recognize that you have not found a match and need to read to the end of the message before starting to scan for the next start of message.

That is an important point. Mainly due to code size limitations in embedded micros, you often have to work without the standard libraries. Instead of printf, you will need to use a simple itoa function.

I worked on a device that used a GPS module this way. The guys writing the code had to convert from NMEA lat/long to GMRS which is similar to UTM. They decided to use canned code that used 16 kB of library code in a 64 kB MCU! The result is that the total code was over 32 kB and we had a requirement to leave 40% free. So then they were afraid to add even simple stuff like ADC code to check the power supply voltages for selftest.

They also did everything in the interrupt code so that the idle loop was just that, a loop doing *nothing*! We also had to minimize our power consumption and my plan was to put the MCU in a low power state when it was idling. I thought the "idle" loop would make this very useful, but they didn't want to do that!

Reply to
rickman

The point is valid, when modifying a shared variable, you need to make sure it can not be read when in an invalid state such as updating two parts separately. But, this does not always require disabling the interrupt. The code for each end of the queue needs to know the value for the other end of the queue. But it does not have to know the absolute most current value. So instead of incrementing a multiple cell value in place, you can just copy to another variable (like a register), increment it and copy it back. But you do have to make sure the copy back *is* an atomic operation. Even on 8 bit MCUs, a 16 bit write is often a single instruction.

Reply to
rickman

  1. A single instruction read/write operation is not a guarantee of the atomic access. It depends.
  2. The good practice is assume that every interruptable access to any shared data has to be done atomic explicitly. The convenient way for doing that is using the template in the declaration of the variable.

Vladimir Vassilevsky

DSP and Mixed Signal Design Consultant

formatting link

Reply to
Vladimir Vassilevsky

I thought I had made a reply, but I don't see it so I will rewrite it.

You actually have three choices, no buffer, a circular buffer and a double (or triple) buffer. The double buffer is also called a PingPong buffer because you switch them back and forth like a ping pong game. Rather than managing pointers chasing around a circle, the interrupt routine can recognize the end of a message and hand off the buffer to the non-interrupt code. For the double buffer, the non- interrupt code must be done with the other buffer before the interrupt code switches buffers, so for some applications where there may be short term mismatches be the rate of data out and data in, a third buffer is added. Otherwise both approaches work the same.

The circular buffer does not swap the buffers around, but you have to manage pointers into the buffer and potentially do modulo math on them. This is only needed if you have to calculate the current length of the buffer. If you are just adding or subtracting one element at a time, the modulo math is just the IF statement shown in David Kelly's post.

No buffer eliminates both the buffer and the shared variables that must be managed in the other two approaches. But it places much higher requirements on the processing speed of the code to parse the buffer, essentially it becomes n character receive times unless you have clear dead times between messages like you seem to. So you may not need to use a buffer at all or can use a single buffer with a flag from the interrupt routine to indicate when it is full.

Heck, to debug your parsing code, you can even work without an interrupt routine at all, reading the messages from a string or directly reading the UART.

Certainly using a buffer of some sort is not really hard and would be a good educational exercise if this is a hobby project. BTW, what is the goal of your project? I have some interest in designing a GPS receiver. I find the commercial units have a number of limitations for what I use them for. The GPS modules are fairly inexpensive at around $100 in single quantities down to $25 in production quantities. I think you can even buy GPS receivers that plug into the SDIO port on a PDA. Once you have a computing platform and a GPS receiver, the rest is "just" software!

So what are you building?

Reply to
rickman

I have not seen any embedded MCUs that are interruptable other than at instruction boundaries. Do you know of any?

Is a standard C feature? I have never heard of this. I couldn't find anything on this in a Google search.

Reply to
rickman

Likewise. However I can imagine the situations where it would be handy. Let's leave it for now.

We are at version 3.0 already. At 1.0, it was a dumb time slicing. At

2.0, it was still a timer based scheduling, but more intelligent. Version 3.0 is an event driven RTOS with stack and interrupt management.

Vladimir Vassilevsky

DSP and Mixed Signal Design Consultant

formatting link

Reply to
Vladimir Vassilevsky

The C166 has an interruptible divide instruction, I believe. (And this causes problems, because there are associated divide state registers which must be saved/restored after a context switch).

I don't know if there are any with interruptible load/store instructions.

I had thought there was a C99 "signal" type that is guaranteed atomic... but I can't see much about it anywhere, now.

--

John Devereux
Reply to
John Devereux

It is not unusual for the MCUs with cache and DMA. Like ADI BlackFin, for example.

This is a feature of our programming style. This is also one of the good reasons for using C++.

Vladimir Vassilevsky

DSP and Mixed Signal Design Consultant

formatting link

Reply to
Vladimir Vassilevsky

And sometimes it's not. (e.g. the AVR doesn't have a 16-bit store instruction.)

And sometimes the compiler is too smart for its own good. Hypothetically:

register int16 foo=memloc; foo++; memloc = foo;

gets optimized to memloc++;

which gets optimized to incr memloc_lo; skip_if_no_carry; incr memloc_hi;

which usually saves a memory fetch and store.

--
David M. Palmer  dmpalmer@email.com (formerly @clark.net, @ematic.com)
Reply to
David M. Palmer

No, the compiler is not too smart for its own good. It generated correct code.

That's a case of the programmer not being smart enough.

if memloc was declared volatile as it should have been, the compiler would have generated the code the programmer wanted.

--
Grant Edwards                   grante             Yow!  Just imagine you're
                                  at               entering a state-of-the-art
 Click to see the full signature
Reply to
Grant Edwards

So what do you do if there is an overflow? Its useful to have a flag stored somewhere noting the situation occurred but the situation is rare that one can do anything about it in fielded system.

Agree that is useful debugging information and one should do it but not much one can do about it after a system has shipped.

I'm thinking he doesn't expect to understand everything coming from the GPS and only wants particular message(s). So an "I didn't understand that" flag might not be of any use.

I have seen systems coded like that. There are some schools of thought saying that is The One Right Way to do "embedded" and in doing so it becomes a Real Time System. The thought is that the system timing is now deterministic. I disagree and spent a lot of time disagreeing with management as to whether a legacy system actually worked as advertised. It did not because it was often deaf to new instructions immediately after receiving an instruction. It tried to keep time by counting interrupts, and the time had to be fudged with a correction factor because a command issued during interrupt almost always ran long and missed the next 10 or so interrupts.

Reply to
David Kelly

No, it doesn't, not with the rules I layed out. The tail only changes outside of interrupt and is never used inside interrupt. There is no need for it to be atomic.

The head changes inside of interrupt and never changed outside. So it too is protected but probably should be volatile. The test was for equality between head and tail. If an interrupt occurred during the test and head was partially changed the equality test would still be valid. Might not indicate a new character is present this time but thats no loss, it will be picked up the next time. If head was changing and not equal to tail then there must be a new character present no matter the head value used for comparison was not perfectly accurate.

In olden days of m68k pointers were very efficient ways of managing buffers. In modern times I find an index is never any worse in speed or efficiency over a pointer. Especially if the buffer location is hard coded at link time.

Reply to
David Kelly

That is exactly the point. If the system crashes, you need to know why. If the overflow is just a matter of a data hiccup, then you can indicate this by some means so the system does not use corrupted data. If it is likely to be fatal to the operation, then you can trap it and halt the system in a way that you can tell what caused it. Regardless, the last thing would would want is for the system to run on without even knowing that there was a problem.

Better would be a size variable that tracks the max usage. Then in the debug stages of your project you can tell if you have sized it with adequate headroom.

But that is the point, you need to iron out any wrinkles before the system ships. Even after, there are often ways of reporting errors that can make it back to the vendor. My router has any number of software flaws and it would have been nice it they had a way to report the nature of the flaw. I expect most of them are memory leaks and they could be traced and fixed. But that particular market uses a "disposable" mindset and bugs are fixed by selling new units. Many embedded developers can't use that method since the customer goes away if you have too many bugs.

I'm not saying the rest of the messager needs to be parsed, but depending on the particulars of the message format, it may not work well to start looking for the start of a message in the middle of the last one. There may be false hits that way. But I don't know the details of the message format, so I can't tell which works. But looking for the CR/LF or whatever terminator is used is sure to work.

Using interrupts that way *can* work, but you have to understand it in detail to make sure. Then it can be harder to modify as the requirements change or grow or new features are added. By keeping the int handlers short and using a properly structured control loop, nearly any combination of timings can be handled and it can be much easier to modify.

Reply to
rickman

Which means you need to inspect the assembly produced and, if required, use inline assembly. If this is not a match for your processor, then clearly you can't use it. But if your processor has a

16 bit, atomic write, then you should be good to go. Using interrupt disables and enables is not very portable and has no advantages over this method. But there is more than one way to skin a cat... my cat suddenly left the room, I wonder why?
Reply to
rickman

No. As stated in the original conditions the tail *index* is never modified during IRQ. In fact it is never used in IRQ. So there is no atomic question.

Reply to
David Kelly

The above analysis is of the approach you have presented and it appears to be correct. If you want to have error checking, then I think some of the operations have to be atomic or non-interruptable.

Reply to
rickman

For the receiving IRQ, the head index (where the incoming data is being stored) is compared to the tail index (where the unprocessed data is stored) to prevent the new data from overwriting unprocessed old data, or at least to notice when it happens. The tail index is modified by the non-interrupt thread as it processes the data.

If the write to the tail pointer is in the middle of changing when the interrupt occurs, the data can be silently corrupted.

--
David M. Palmer  dmpalmer@email.com (formerly @clark.net, @ematic.com)
Reply to
David M. Palmer

Back to my original statement, that tail is never used in IRQ.

Wrote earlier in this thread that it is interesting to know when one has overflowed a buffer but that there isn't much one can do to recover in the field. Being a circular buffer an entire buffer full of data is lost but its not the same sort of thing as a buffer overflow security violation.

If one does not notice the data loss from an overflowed buffer then one might claim their application is robust and error tolerant. If the overflow causes your device to barf then one doesn't need debugging flags to indicate something isn't working. Flags might help find it.

Have seen others to go much effort to calculate the length of data in the buffer. Beyond debugging I have seen little need. If there is data waiting it needs to be processed no matter how much is waiting.

Reply to
David Kelly

The tail is used in the IRQ, by comparing it to the head, to prevent the data from being silently corrupted.

If you have GPS NMEA strings coming in, and it is corrupted by the buffer overflow, then either you have a problem or you weren't doing anything important with the GPS information. If you have a flag that indicates that the data was truncated, then you can mitigate by, e.g. extrapolating your new position based on previous data until you can get back to the valid data. If you don't, then you apply hard left rudder because your aircraft carrier thinks it is 3000 feet over Des Moines. (Yes there should be sanity checking. One good sanity check is whether your shrink says that you're insane.)

It's useful for a pacemaker to know the difference between 'v-fib, time to crank up the defibrillator' and 'data error--log the fault, schedule maintenance, and wait for the next beat'.

Or it needs to mode switch to decrease the data rate. Or it needs to flush the stale data and get current.

--
David M. Palmer  dmpalmer@email.com (formerly @clark.net, @ematic.com)
Reply to
David M. Palmer

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.