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

Hi Group,

Intro I've been programming for a long time (to long to mention!), but 99% of my programming has been for 'desktop' PCs under DOS and UNIX using C. I've rarely had to worry about interrupts, watchdogs etc. Well now I have to! The target will be a Radio with an 8-bit NEC Micro controller (non-RTOS). My initial task is to Tx/Rx data over serial comms (again, I hope in C). The data may be short (so I thought interrupts would be ok), but can also be 'streaming', which I'm guessing means the comms module can't run off interrupts? This will be one of several functions the radio will need to perform, so again I guess some overall control will need to be implemented to get all the tasks done.

The Help I would like.

1 - Recommended resources (books, websites) that will help me with the fundamentals. 2 - Recommend resources on embedded serial comms (with coding examples if possible).

TIA.

Reply to
Roger Walker
Loading thread data ...

Why no RTOS? There are very small real-time kernels that work just fine on 8-bit processors, and will simplify your life if your code needs to do more than one or two things at once.

I'm not sure what you mean by 'streaming', but using interrupts on serial data is often a very good way to go. You set up your main-body code to consume received data from a queue, and to write transmit data to another queue, then you set up your ISR to service the queues.

I learned embedded system design by osmosis, so I don't have direct experience with the books. Jack Ganssel's embedded book is a classic; one hopes that means it's worth while. Lewin Edwards has one out on becoming an embedded engineer -- I don't know if he speaks to technical issues or just the office politics peculiar to embedded software, though.

--
Tim Wescott
Wescott Design Services
 Click to see the full signature
Reply to
Tim Wescott

You might like to check out the source code for DZComm -

formatting link

This is a freeware RS232 driver for DOS and Linux. It is based on the 16550 UART and is probably much more comprehensive than you need for an 8bit micro, but you can cut out great chunks to be left with the basic buffered driver.

--
Regards,
Richard.
 Click to see the full signature
Reply to
FreeRTOS.org

Whether you can get away with polling depends on how long you will be between checking the serial peripheral in comparison the the character time.

An older book actually meant for DOS rather than embedded

"C Programmer's Guide to Serial Communications" - Joe Campbell

The problem might be finding a copy.

Robert

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

SNIP

RTOS is probably (from my very limited knowledge of this) the way forward, alas if we can 'fudge' something I think we'll have to fudge (for now at least).

One device will be transmitting (possibly) constantly (GPS device). I know I only want one of the various data strings, and that only every

1/4s max. That's is why I thought with that sort of 'streaming' data maybe I wouldn't want to tie the CPU up not only having to get all the data but having to compare it to see if it's the data needed, only to find that I possibly do not need it.

How I envisage it working (in my sequential mind!) is that I would:-

i) Receive any data sent over serial link (Interrupt driven?) (Only receive xbit(s)/byte(s) at a time to allow CPU to do other things - not sure if this means I could loose data, don't even know if an interrupt is raised for every bit received on serial line!!). ii) Compare received data to see if it's one I'm interested in. Not sure how I would call this routine. I believe data 'sentences' are terminated with a CR/LF. So perhaps raise my own interrupt from (i) to call this routine. iii) IF it is data I want store it in memory until it is overwritten. Maybe raise an interrupt to tell whatever that update data is available. Or just let the required software grab it when it needs it (but what happens if that memory location is being written to as it's being read)

And I'm not even thinking about what I have to do to with the data now that I've got it (that's the next stage!!!)

I think this is why I need to understand alot more about the basics of embedded coding!!!!

Thanks for your help!

Reply to
Roger Walker

"Initial" task? This begs a question that you should ask yourself before going on: what is the actual "final" task?

You need to at least roughly know the whole scale of the project before you can go into details of designing individual aspects of it.

Actually, it often means you *must* run the comms off interrupts, because the rest of the program will be so busy doing other things that you can't afford polling for new data on the serial ports. You'll typically need some circular buffering between the serial ports and the main application.

Polling only really works well if the program does not much else than handle communication. If it's supposed to also get some "real" work done on the side, polling tends to turn into an excessive burden.

It doesn't directly match your choice of CPU (who made that for you, anyway?), but I consider Michael J. Pont's book "Patterns for Time-Triggered Embedded Systems" a good first choice. It starts all the way down at the basics (what's a port pin, and how should I use it), and goes quite a long way from there. Applying what you find in this book to this non-8051 micro might even provide extra didactic value.

Reply to
Hans-Bernhard Bröker

A lightweight operating system (not necessarily real-time) or a foreground/background design would both work fine here.

The typical microcontroller UART doesn't have a built-in FIFO so you'll be interrupted as each character (not bit) is received. Have your serial ISR toss each character into a lightweight queue and the foreground process can retrieve them as and when it needs.

NMEA data is very structured, so deciding if the sentence you've just received is one that you want isn't that hard.

Set a sentinel or double-buffer the stored data.

Reply to
Rich Webb

I understand and agree with you! Alas this is another 'get it working quick' and the basic software (hacked from previous versions code developed for previous hardware - not by me) has just been rammed in! The initial task (for me) of getting some serial comms is going to be another 'ramming' exercise. Then maybe a design can be put together!

Don't know. Could have been someone technial, could have been the accountant!

Thanks for your help.

Reply to
Roger Walker

Depends on the speed of the UART.

2400 bauds is 4 mSec between bytes. Can get a lot done in 4 mSec.

RTOS, we don't need no stink'n RTOS.

What's in the design spec ??

donald

Reply to
Donald

I guess you could consider it a one-level FIFO, since you typically have until the end of the reception of the next character to remove the character from the register (after that, data is lost).

Best regards, Spehro Pefhany

--
"it's the network..."                          "The Journey is the reward"
speff@interlog.com             Info for manufacturers: http://www.trexon.com
 Click to see the full signature
Reply to
Spehro Pefhany

Tim,

Do you think that the message queue is the essential part of OS ? It provides the uniform interface to the events, however it is a significant complication.

Likewise. The necessity is the mother of knowledge.

BTW, I am done with my RTOS core, and currently doing the misc. services. The primary intention was to build a lightweight OS with a SENSIBLE and uniform API. The targets are up from the medium class

8-bitters and up to the systems with less then 16M of RAM. The interface language is C++. So I would appreciate any thoughts of what should be included as the services at the core level.

Vladimir Vassilevsky

DSP and Mixed Signal Design Consultant

formatting link

Reply to
Vladimir Vassilevsky

It isn't essential, but it can be pretty darn convenient. I have certainly seen message queues overused, in applications that used an all-queue intertask communication scheme where most of the communication could, and should, have been handled by loading atomic data types and raising semaphores. Since I still bear the scars from that particular experience, I can be pretty strident about minimizing the use of queues

-- but there are times when they are essential.

I _do_ think that semaphores (or events, or flags, or whatever you want to call them) are essential. I also think that if you leave out message queues, you will find your users making them.

-- snip --

--
Tim Wescott
Wescott Design Services
 Click to see the full signature
Reply to
Tim Wescott

I congratulate you on being willing to do without the RTOS or any other formal OS. The majority coming from your background first demand an OS. Based on your description of the minimal task to be performed I fully agree that a formal OS is wasteful. In the end you will have some form of order imposed on your code either way, I just think you will get there quicker without.

This is what I would do, and have done similar many times:

Use interrupts to receive the incoming data. Put the data in a small circular buffer about twice the size of the anticipated sentence of data. Don't attempt to process or interpret the data during IRQ processing.

Outside of IRQ look directly in the serial receive buffer for the GPS NEMA sentences you are interested in. I like to use 2 indexes into circular buffers, named "head" and "tail". Food goes in the head and out the tail, that's how I keep them straight. Only advance head in IRQ, only advance tail outside of IRQ. If head == tail then nothing is in the buffer.

Avoid doing anything but the barest minimum inside of IRQ time. Copy the received character to buffer, advance the index and wrap it if necessary, tickle the UART bits if necessary. Return.

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.

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

// 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; }

}

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.
Reply to
David Kelly

It's really almost the reverse of that. If data arrives in small predictable chunks, and the program is idling inbetween, it's usually OK to poll for it. But when streams of variable size are arriving, perhaps while you are doing something else, let the interrupt receive the characters, and put them into a (usually circular) buffer. That way, the stream can run ahead of the program without losing characters (as long as the buffer is big enough).

Paul Burke

Reply to
Paul Burke

SNIP

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?

Reply to
Roger Walker

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

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.

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

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

[...] Ok, it is better to have it.

Certainly. The things like critical sections, mutexes and mailboxes are there.

Here is another question:

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.

Vladimir Vassilevsky

DSP and Mixed Signal Design Consultant

formatting link

Reply to
Vladimir Vassilevsky

I have never, in nearly 20 years of embedded work, written software that killed threads or uninstalled interrupt handlers.

So I think you're safe leaving it out, at least for version 1.0.

--
Tim Wescott
Wescott Design Services
 Click to see the full signature
Reply to
Tim Wescott

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.

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.

Reply to
David Kelly

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