EmbeddedRelated.com
Forums

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

Started by Roger Walker April 6, 2007
In article <28b79$4618394d$d8ba94f9$19383@KNOLOGY.NET>, David Kelly 
says...
> 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.
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
On Apr 6, 11:17 pm, David Kelly <n...@yahoo.com> wrote:
> Do your main "task" outside of IRQ. Do not "block" waiting for an event, > if there is no character available incoming do not loop waiting for it, > go off and check other things such as user input.
David did not post any of the IRQ code here, but it should include an overflow check.
> main() > { > init_hw(); > > for(;;) { > uart_input_task(); > user_output_task(); > misc_junk(); > etc(); > } > > } > > Then in uart_input_task() check for an incoming character, but don't > dawdle long. Something like this: > > struct { > uint8_t hd = 0, tl = 0; // head and tail > uint8_t buf[SIZEOF_BUF]; // 32 is a nice number > > } uib; // uart input buffer > > const uint8_t match_string = "Hello, World\n"; > > void uart_input_task(void) { > static uint8_t state = 0; > uint8_t c; > > if( uib.hd == uib.tl ) // test for data in buf > return(); // nothing to do
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.
> // There is a slick way to do the following, but this is easier > // to read and works for all SIZEOF_BUF that fit in hd or tl. > c = uib.buf[uib.tl++]; // fetch character > if( uib.tl >= SIZEOF_BUF ) // wrap if needed > uib.tl = 0; > > if( c != match_string[state] ) { > state = 0; // start over next time > return; > } > > state++; // compare next char next time > > // see if we have our match? > if( match_string[state] == 0 ) { > found_our_match(); > state = 0; > } > }
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.
> What you can do is if you are looking for "Hello, World!\n" from the GPS > you can start by comparing incoming characters for 'H', call this "state > 0, looking for H". Once you find H advance to state 1, etc, until you > reach the end of your match string which is when you know you have found > the match. This is much better than using libc string compare routines. > Also notice it does not use getchar(), printf(), or any other library > routines.
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!
On Apr 7, 10:52 pm, "David M. Palmer" <dmpal...@email.com> wrote:
> In article <28b79$4618394d$d8ba94f9$19...@KNOLOGY.NET>, David Kelly > > <n...@yahoo.com> wrote: > > 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. > > 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.)
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.

rickman wrote:

> 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.
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
On Apr 7, 9:14 am, Roger Walker <n...@nil.com> wrote:
> 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?
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?
On Apr 8, 10:04 am, Vladimir Vassilevsky <antispam_bo...@hotmail.com>
wrote:
> rickman wrote: > > 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. > > 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.
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.

Tim Wescott wrote:


>> Is it necessary to have a run time mechanism for releasing a resource? >> Such as killing a thread or uninstaling an interrupt handler? It is a >> big and complicated part with many potential dangers. It seems to be >> unnecessary for the embedded OS. >> > I have never, in nearly 20 years of embedded work, written software that > killed threads or uninstalled interrupt handlers.
Likewise. However I can imagine the situations where it would be handy. Let's leave it for now.
> So I think you're safe leaving it out, at least for version 1.0.
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 http://www.abvolt.com
"rickman" <gnuarm@gmail.com> writes:

> On Apr 8, 10:04 am, Vladimir Vassilevsky <antispam_bo...@hotmail.com> > wrote: >> rickman wrote: >> > 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. >> >> 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. > > I have not seen any embedded MCUs that are interruptable other than at > instruction boundaries. Do you know of any?
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.
> Is <ATOMIC> a standard C feature? I have never heard of this. I > couldn't find anything on this in a Google search.
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

rickman wrote:


>>>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. >> >>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. > > > I have not seen any embedded MCUs that are interruptable other than at > instruction boundaries. Do you know of any?
It is not unusual for the MCUs with cache and DMA. Like ADI BlackFin, for example.
> Is <ATOMIC> a standard C feature? I have never heard of this. I > couldn't find anything on this in a Google search.
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
In article <1176040035.163208.234450@n59g2000hsh.googlegroups.com>,
rickman <gnuarm@gmail.com> wrote:

> On Apr 7, 10:52 pm, "David M. Palmer" <dmpal...@email.com> wrote:
> > Also, the changes to the tail pointer must be 'atomic'.
> 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.
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)