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

Do you have a question? Post it now! No Registration Necessary

Translate This Thread From English to

Threaded View
Re: C 'desktop' programmer needs advice on how to code embedded C on a micro controller.

SNIP
Quoted text here. Click to load it

Thanks Paul, another question, if I may impose further, to all the
people that recommended circular buffers. They do seem popular, and
please excuse my ignorance, but isn't there a big problem that the
routine that uses the circular buffer could get corrupted data if it
(buffer)  isn't big enough?  And as memory constraint seem to be at a
premium  in micro controllers you don't want 'over kill', therefore
make it any bigger than it needs to be. Now this is probably where
(again) my lack of embedded programming experience trips me up. If the
routine that uses data from the serial buffer doesn't get run
frequently enough it can miss ever getting the data it requires?

Ok I think I'll put it in plain English!
What are  the downsides (if any) about circular buffers? What do I
need to _watch out_ for?

Re: C 'desktop' programmer needs advice on how to code embedded C on a micro controller.
says...
Quoted text here. Click to load it

Yes, but less of a probability than with no buffer.

Quoted text here. Click to load it

Yes, you have to size the buffer so it's large enough to store the
expected peak while being emptied at the main tasks rate.  If the peak
rate can be sustained forever and the main task can only empty at a
lower rate then you are obviously in trouble.

Quoted text here. Click to load it

The buffer handling does add extra overhead but unless your processing
overhead is really small and you are not doing anything else it buffers
you from the immediate requirements of the serial port.  You can be busy
for several character times doing something and get to the serial data
at a time of your choosing rather than being forced to stop what you are
doing and check the serial port right now.

Robert

--

Re: C 'desktop' programmer needs advice on how to code embedded C on a micro controller.
Quoted text here. Click to load it

The buffer only needs to be big enough to hold the incoming while you
are off somewhere else for some reason or another. You could possibly
poll the UART and process one character at a time. As your project gets
more complex this becomes harder and harder to do without losing
incoming data.

What I suggested earlier is that you allocate about twice as much to the
buffer as the GPS sentence you are expecting. Then the code example
parsed it one character at a time making absolutely no use of the deep
buffer. But once you know the buffer contains a complete desired
sentence you could grab it out of the buffer for other uses. I didn't
expand the example for that, or to handle a variable in the input sentence.

Quoted text here. Click to load it

Don't modify the tail index in IRQ, don't modify the head index outside
of IRQ. With that simple rule there is no need to protect either
variable by disabling IRQs. Remember the head index can change at any
time. In my example code the head should have been declared volatile.

Good C compilers will lift a copy of a memory variable and use it from
register until the register is needed for other variables. The volatile
modifier says the value must be lifted from memory for each use because
its subject to change. This isn't a problem in the example code I
provided because head was only used once in each function but if one had
an infinite (or very long) loop it would be very important. For example:

    //  wait for input character
    while( uib.buf.hd == uib.buf.tl )
        ;

Generally I find a large output buffer of more use than the input
buffer, especially if communicating text to a human interface. One
wishes to output a sentence either one must be able to break it up
character at a time so it can be sent from the main loop, or provide a
buffer big enough to throw it at the UART TX IRQ as one big chunk.

Probably best not to try to send it out the UART from the same buffer
used for input. Then again the TX IRQ should send at least as fast as
the RX IRQ could fill so the two should not overlap.

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

Quoted text here. Click to load it


Also, the changes to the tail pointer must be 'atomic'.

(I haven't read this whole thread, so you may have mentioned that.)

If you have an 8-bit processor modifying a 16-bit tail pointer a byte
at a time, then if an interrupt can slide in between a) incrementing
the low-byte from 0xff to 0x00, and b) incrementing the high byte with
the carry, then you will have a bug which is VERY hard to find, but
WILL bite you at the most inopportune time.

Disabling interrupts for the few cycles required to increment the tail
pointer, then immediately re-enabling them is acceptable for somethign
slow like a UART.  (There are also ways to do it without disabling
interrupts by having the tail pointer be more elaborate than a simple
pointer value.)

--
David M. Palmer   snipped-for-privacy@email.com (formerly @clark.net, @ematic.com)

Re: C 'desktop' programmer needs advice on how to code embedded C on a micro controller.
Quoted text here. Click to load it

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.



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



Quoted text here. Click to load it

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 <ATOMIC> template in the declaration of the
variable.

Vladimir Vassilevsky

DSP and Mixed Signal Design Consultant

http://www.abvolt.com




Re: C 'desktop' programmer needs advice on how to code embedded C on a micro controller.
Quoted text here. Click to load it

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

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


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

Quoted text here. Click to load it

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.

Quoted text here. Click to load it

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

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




Quoted text here. Click to load it

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

Quoted text here. Click to load it

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

http://www.abvolt.com




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


Quoted text here. Click to load it


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   snipped-for-privacy@email.com (formerly @clark.net, @ematic.com)

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

Quoted text here. Click to load it

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
We've slightly trimmed the long signature. Click to see the full one.
Re: C 'desktop' programmer needs advice on how to code embedded C on a micro controller.
Quoted text here. Click to load it

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?



Re: C 'desktop' programmer needs advice on how to code embedded C on a micro controller.
Quoted text here. Click to load it

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.

Re: C 'desktop' programmer needs advice on how to code embedded C on a micro controller.
Quoted text here. Click to load it

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.



Re: C 'desktop' programmer needs advice on how to code embedded C on a micro controller.
Quoted text here. Click to load it

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.

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

Quoted text here. Click to load it

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   snipped-for-privacy@email.com (formerly @clark.net, @ematic.com)

Re: C 'desktop' programmer needs advice on how to code embedded C on a micro controller.
Quoted text here. Click to load it

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.

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

Quoted text here. Click to load it

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

Quoted text here. Click to load it

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'.

Quoted text here. Click to load it

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   snipped-for-privacy@email.com (formerly @clark.net, @ematic.com)

Re: C 'desktop' programmer needs advice on how to code embedded C on a micro controller.
Quoted text here. Click to load it

How important a buffer overflow is and what you can do about it are
entirely application dependant.  In one application you might not want
to use valuable CPU time to check the buffer, in another it may be
critical to know if the buffer is corrupt or even to prevent it by
dropping data and telling the app that it happened.


Quoted text here. Click to load it
"Flags might help find it."

BINGO!!!  Even if buffer checking is not appropriate for your app in
the field, it can be a valuable resource for debugging.


Quoted text here. Click to load it

Not sure what that means, but the important thing is to know if data
has been lost so that something can be done about it even if that is
to not trust the results produced from the data or turning on a red
light and shutting down!


Re: C 'desktop' programmer needs advice on how to code embedded C on a micro controller.
says...
Quoted text here. Click to load it

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

--

Site Timeline