Embedded C Book Recommendation - Page 1
Forum section: electronics / general

author: Stiege
author_rank: Regular Contributor
timestamp: February 11, 2013, 12:48:00 am
content: Hey all,I would class myself as an intermediate C programmer. I understand the basics well, however I want to have a more in depth understanding. Especially with regard to things like function pointers and using structs to control registers.I've ordered this book:http://www.amazon.com/gp/product/1418039594/ref=oh_details_o00_s00_i00However I'm not sure it will hit the mark by itself.Does anyone have any suggested reading?Cheers

author: smashedProton
author_rank: Frequent Contributor
timestamp: February 11, 2013, 01:07:07 am
content: I can't help except by linking you to arduino.cc/reference

author: IanB
author_rank: Super Contributor
timestamp: February 11, 2013, 01:07:55 am
content: Holy guacamole! That's an expensive book! I don't see how it's possible to justify $150 for a textbook when the Internet is full of free information these days...I would pay $150 for a book if it contains pure, timeless information, distilled and organized by an expert. There are some of those books around and I like to have them on my bookshelf. But for a tutorial guide on programming in C? It doesn't seem like good value.I'm not sure how to advise you. I'm a strong believer in learning basic theory supported by seeing how it is applied in practical cases.So for the C language, I would always recommend K&R. If you work through the book chapter by chapter and do all the exercises you will know as much about C as you need to know. It's not a big language (compare to Stroustrup's book on C++ which is many times bigger and yet is still no bigger than it needs to be).Added to the C language, you need to learn about hardware. If you want to learn about AVR, read specific technical information on that. Learn how to understand the free technical references and datasheets for those processors, and use online tutorials to help when you get stuck.

author: Stiege
author_rank: Regular Contributor
timestamp: February 11, 2013, 01:13:43 am
content: Quote from: IanB on February 11, 2013, 01:07:55 amCheers for that. I actually already understand the hardware. I've done quite a bit of C programming, things like RS-232 communication. Wrote an LCD display driver. Even a PID controller for a motor after I extracted the step response and did an optimisation in Vissim and converted the control function from continuous to the discrete domain. So I'm pretty experienced, but I want to improve further. The sad part is that whenever I buy books now it's usually for only 2-3 chapters. I own a lot of books too.So, any further guidance on where I can read about function pointers and structs in embedded design as well as other more advanced techniques?And of course, I bought the book used.

author: notsob
author_rank: Frequent Contributor
timestamp: February 11, 2013, 01:28:14 am
content: I would have bought that book 2nd hand thru goodwillexpress - they act as a front end for a lot of bookshops$52 Embedded C Programming and the Atmel Avrhttp://www.goodwillexpress.com

author: IanB
author_rank: Super Contributor
timestamp: February 11, 2013, 01:34:14 am
content: Quote from: Stiege on February 11, 2013, 01:13:43 amSorry, I'll have to let someone else chime in. Learning how to program AVR is on my to-do list (I have AdaFruit's Ice Tube clock and I need to hack the firmware to improve the time keeping), but I have not got around to it yet.Function pointers are just functions you store in a variable. Like for instance "3" is a constant, but if you store the value 3 in a variable k then you can access 3 by referring to k. Likewise, the function int f(int x) has the constant name "f", but if you store the function in a variable fp then you can access the function through the variable fp. Function pointers can be useful for things like dispatch tables where you have an array of function pointers and you branch to the desired function by indexing into the array.Structs are just a way of assigning names to locations in a region of memory offset from some base address. You can view a struct a bit like a sliding window that you move around the memory address space. The struct then allows you to access the memory locations under the window by name. Structs are especially useful like this when based off a pointer, so you have syntax like this: p->x, which means the structure member "x" that is accessed when the struct is positioned at location p in the memory map.(By the way, K&R covers function pointers and structs in detail as these are essential elements of the C language.)

author: Stiege
author_rank: Regular Contributor
timestamp: February 11, 2013, 02:03:37 am
content: Yeah, I understand what you've said, I just want it as it specifically relates to embedded programming, and in quite a bit of detail. As in, source code and examples to try. I get confused at the moment when I see multiple levels of de-referencing.Reading the internet is great, but getting the information from different places can be a bit confusing and it's easy to miss stuff. I like to back myself up by reading some more formal material from time to time.

author: AlfBaz
author_rank: Super Contributor
timestamp: February 11, 2013, 03:16:59 am
content: I think the biggest pitfalls of C in an embedded scenario are ISO divergences for the compiler you are using.The point to take from the above is once you've learnt what you need for C and you go to create your firmware on a paticular platform you need to go through the compiler documentation to see what it can and can't implement, any extra pragma's the compiler might have and (gcc in particular) any weird non-C type statements that are used to take advantage (or work around) hardware quirks for the controller you are using.These sorts of problems become more convoluted if you wish to keep the code portable between platforms.Sometimes its may be benificial to write more code and therefore use more resources, than to use things like function pointers. For example you may loose certain optimisations that can be generated by the compiler because with function pointers the compiler has no way of nowing when or if it will ever be called. Worse still if you have a crap compiler (there are plenty) it may not give a toss (and not even generate a warning) optimise the function and give you an intermitant problem that will be very hard to debug.My favorite quote (and I beleive its from K&R)Everyone knows its twice as hard to debug than it is to code, so if you are as clever as you can be when you code, how will you ever degug it?

author: TerminalJack505
author_rank: Super Contributor
timestamp: February 11, 2013, 03:41:06 am
content: Quote from: Stiege on February 11, 2013, 12:48:00 amIf you're programming for the AVR microcontrollers then I suggest you read the latest AVR libc documentation. You can download ithere, or, if you've installed Atmel's development software then you'll find it somewhere installed on your system.

author: amyk
author_rank: Super Contributor
timestamp: February 11, 2013, 11:55:09 am
content: This is about C in general but still very useful book for the more advanced parts:http://www.amazon.com/Expert-Programming-Peter-van-Linden/dp/0131774298As AlfBaz pointed out above the nonstandard pieces are the biggest difference for C in embedded systems.

author: caroper
author_rank: Regular Contributor
timestamp: February 11, 2013, 03:40:31 pm
content: I can recommend the Smiley's Workshop series of articles By Joe Pardue from Nuts & Volts Magazine beginning in August 2008.Unfortunately his web site is undergoing changes and the old links to the articles have not yet been ported across, but if you do get a chance to look at them it is a very well written set of step by step guides that start with no required knowledge of C or Microcontrolers and is still going strong with the current issue covering the "time.c" standard library.Even as an an accomplished developer for PIC and AVR chips I find his style entertaining, stimulating and often containing hints and tips that improve my own tool kit.CheersChrisp.s.I forgot to mention he also has a book on the subject:https://store-jwm4nhbo.mybigcommerce.com/c-programming-microcontrollers-book-and-projects-kit/Another popular author from Nuts and Volts has 3 books on the subject (Vols 1-3):https://store.nutsvolts.com/product.php?productid=16727&cat=328&page=2

author: Kremmen
author_rank: Super Contributor
timestamp: February 11, 2013, 06:04:30 pm
content: Quote from: Stiege on February 11, 2013, 02:03:37 amMaybe it is just me but i find myself gravitating more and more towards C++ in my embedded projects. I do Atmel processors mostly, and in my experience already the Mega328 (the same that Arduino uses) runs C++ code with no problems whatever. Some say C++ is too heavy and involved for embedded applications but that is not my experience at all. The AVR gcc supports all common C++ OO constructs (i haven't really explored the limits) like classes, inheritance, visibility and access rules, scoping, polymorphism etc. Dynamic class instantiation works as expected with minimum effort (you have to tell the compiler how to implement 'new' and 'delete'). It may be a feature that should be used with care in an app requiring deterministic timing and behavior, but i have used it e.g. to create linked lists of actions in user programmable sequences. One such was a camera dolly controller. The movements, camera control actions and timings were all child classes inherited from a generic base action. The actions were stored in a linked list, likewise a hierarchy of classes, with persistence to the EEPROM even. All of that would have been doable with plain C but so much easier and more elegant in C++. By the way it was done with a plain vanilla Arduino, with no problems in performance or memory size.Quote from: Stiege on February 11, 2013, 01:13:43 amMove over to C++ and you don't need no steenking function pointers. The structs come free as every class is a struct and every struct is a class. All the "advanced" stuff is C++ anyway.

author: Stiege
author_rank: Regular Contributor
timestamp: February 11, 2013, 07:28:49 pm
content: Thanks for that Kremmen. I haven't done a project in C++ yet, but you might have just talked me into giving it a go.

author: firewalker
author_rank: Super Contributor
timestamp: February 11, 2013, 07:38:36 pm
content: I had a pik at he first edition of"Embedded C Programming and the Atmel AVRRichard H. Barnett, Sarah Cox, Larry O'Cull"If I can recall I didn't like the fact that it was heavily dependent on CodeVision AVR compiller/IDE.Alexander.

author: Stiege
author_rank: Regular Contributor
timestamp: February 11, 2013, 07:40:30 pm
content: Quote from: caroper on February 11, 2013, 03:40:31 pmThanks for that, should just about do it for books!

author: westfw
author_rank: Super Contributor
timestamp: February 11, 2013, 10:52:13 pm
content: QuoteDo you understand any assembly languages, or microcontroller architectures at the hardware level?I've found it very useful to understanding C (which some people have called "a high level assembler"), to be able to imagine exactly how it compiles onto the (or "a", anyway) target device.For example, a function pointer uses the indirect call mechanism; put the address of a function in a register and then do "icall" or the equivalent via that register. The advantage is that you can load that register from all sorts of useful places, like tables (character dispatch table for an editor, say) or a structure (byte write and read functions associated with a higher level 'device' structure.)Structures mapped onto io registers are particularly useful (IMO) when you have duplicated functions in the chip. For instance, on an AVR you might have up to four uarts, all of which look like:Code:typedef struct uart_t_ {volatile uint8_t ucsra; //status Avolatile uint8_t ucsrb; //status Bvolatile uint8_t ucsrc; //status Cuint8_t u_res1; // reserved (not used)uint8_t ubrrl; //bitrate lowuint8_t ubrrh; //bitrate highvolatile uint8_t data; // rx/tx data.} uart_t;So instead of writing code for each uart, you can stick a pointer into a variable (at some risk of being ugly and bug-attracting):Code:uart_senddata('a', (uart_t *) (&UCSR0A));At one point I had re-written the Arduino serial code along these lines. It got a lot smaller, and prettier.Alas, this does not seem to be the way that chip vendors like to describe their peripherals. There are some dangers; the C language definition does not tightly control how a compiler should lay out a structure. Some compilers require (non-standard) additions to prevent them from adding padding, or reordering "things" (endianness, for one.) Also, at the bit level, bitfields are not strongly defined. So code written with structures may not be very portable...

author: Stiege
author_rank: Regular Contributor
timestamp: February 12, 2013, 12:20:11 am
content: Yeah, I know assembly, had to code a multi tasking kernel in second year.Pretty much what you've written I understand and have looked at some stuff online on how to do that, what I'm after is a book with all those cool tricks in one place. Why don't you start writing?!I think I was concerned about the same things you've mentioned. Thats is, if I record the struct's bits in a certain order, is it for sure that the struct will be arranged as I've listed it (and therefore map onto the hardware properly)? I think you've answered that as "not always", and there can be extra stuff too. Interesting.

author: Harvs
author_rank: Super Contributor
timestamp: February 12, 2013, 09:24:49 pm
content: Possibly what you're after is a book of embedded C designPatternsas apposed to a language reference.I've been looking for a good one for some time, and I've picked up very valuable snippets from various embedded C programming books (such as Test Driven Development for Embedded C which has some good pointers about coding in an OO manner in C.) But I'm yet to find a very good one, it's quite frustrating really.http://www.amazon.com/Design-Patterns-Embedded-Systems-Engineering/dp/1856177076This one looked like it was going to be good, but it's full of poor editing and code errors. Which isn't much good for a programming book!

author: Kremmen
author_rank: Super Contributor
timestamp: February 12, 2013, 09:41:56 pm
content: Maybe a stupid question but referring to my earlier comment, why would you want to just simulate OO in C, when you can do it properly using C++? Especially as the compiler is readily available, at least for Atmel chips. It is not that hard once you get over the initial threshold.

author: Harvs
author_rank: Super Contributor
timestamp: February 12, 2013, 10:26:55 pm
content: Quote from: Kremmen on February 12, 2013, 09:41:56 pmI can't say that I fully understand the resource implications of using C++ vs C, and unfortunately I'm not sure that a lot of people that discuss this on forums really know either.But... traditionally the manufacturer IDEs didn't support C++ very well at all, so it became a real headache to use them with C++ (like if memory serves me correctly Atmels IDE didn't even recognize files with cpp extension as being code, but it's been quite a while since I've used it). Some manufacturers like Microchip didn't have a C++ compiler until recent (and I believe even now it's under consideration as to how much effort they'll put into supporting it in future, according on one of their employees on their forums.) So for relatively small projects (like a lot of uC projects are), it's simply much quicker to put it together in C than deal with wrapping all the libraries in extern C statements and dealing with poor IDE support.

author: IanB
author_rank: Super Contributor
timestamp: February 12, 2013, 10:35:44 pm
content: Quote from: Kremmen on February 12, 2013, 09:41:56 pmI think one possible reason is that C is almost entirely transparent as a language. There is essentially no code that is automatically generated by the compiler, so you have very precise control over the code that is generated. What you see is what you get.With C++ it is somewhat different. Now the compiler may generate code automatically (default constructors, default destructors, operator overloading, etc.) and now you need deep knowledge of what the compiler may do behind the scenes. If you trust the compiler and know what to expect, all is fine, but there are more pitfalls in C++ for the unwary.

author: Stiege
author_rank: Regular Contributor
timestamp: February 13, 2013, 04:32:58 am
content: Quote from: Harvs on February 12, 2013, 09:24:49 pmI think you're exactly right. I am after a book of design patterns. Thanks heaps!

author: Stiege
author_rank: Regular Contributor
timestamp: February 13, 2013, 04:39:47 am
content: Quote from: IanB on February 12, 2013, 10:35:44 pmYeah I think this is probably my attraction to it. To me it's as low level as you can be while still having a relative degree of portability. I think learning about how to code in C to be like using C++ will also be beneficial for me to really understand what's going on behind the scenes. As I'm an EE and not a software guru, I pretty much see that as my job, even if the software guru's can do a lot more complicated things with less code using C++.

author: Otatiaro
author_rank: Regular Contributor
timestamp: February 14, 2013, 07:18:04 pm
content: Hello,Quote from: Stiege on February 12, 2013, 12:20:11 amYou coded a multi tasking kernel without using function pointers ?I coded one this week and, well, I don't know how it's possible without function pointers.Anyway ... function pointers are just that ... a function has a body, which is somewhere is memory, and the function pointer is the address of the entry point of the function's body.You define the type of the pointer (with parameters types and return type) so that the compiler knows how to call it and get back the return value (this process depends on the processor ... typically first few parameters are in registers, then they are placed on the stack - this is why you don't want to have more than 3 parameters for a function, otherwise use a structure and pass structure's pointer - , and returned value is in register).Structures are kind of arrays but with different elements in it (and the compiler calculates the offset for you).It's nice to have a good knowledge of the platform you are working on, I see C (and even more C++) as a way to write assembler more efficiently, but I don't want the compiler to take decisions on his own, I want to drive it to get the result I want.Plus you'll need good knowledge of all this to debug efficiently.I'm now mainly working on ARM Cortex, it is a modern architecture and there is a lot to learn from how it works.Thomas.

author: Stiege
author_rank: Regular Contributor
timestamp: February 14, 2013, 07:38:00 pm
content: I coded it in assembly, there was a semester worth of notes to help me do it.It would take me quite some time to figure out how to do it in C, it's something I'd like to know more about though.I understand quite well what a structure, and what a function pointer is. I was after a book that talked about how to use them effectively, along with other more advance techniques.

author: Kremmen
author_rank: Super Contributor
timestamp: February 14, 2013, 09:11:10 pm
content: Quote from: Harvs on February 12, 2013, 10:26:55 pmWhile that may be true, could it be a case of what was good for our fathers is good enough for us as well? It is not like C++ is this newfangled thing - i first wrote C++ in the 80s although not for embedded systems... I have found the resource implications to be all but nonexistent. In fact, i'm not sure that it won't be the other way around in many cases. It is less than trivial to simulate certain common C++ constructs consistently better than the C++ compiler can. Same argument as with asm vs C.QuoteYou have a valid point. I understood the OP was involved with AVR devices that by default (Atmel's default that is) are programmed using AVR-gcc that has no issues with C++ support. It is also true that not all versions of AVR Studio support C++, but at least Studio 5 does, and i think 6 as well. Since they are freely available from the Atmel website, why not use them.Quote from: IanB on February 12, 2013, 10:35:44 pmCertainly, C++ does do things behind the screen. I haven't seen that as a problem, because the things it does without asking you, have to do with the OO language constructs, not with anything the actual code does as side effects (the actual things you want accomplished). Runtime construction and destruction only apply to entities that go in and out of scope and if you don't want that it is easily avoided by using static instances only of the entities. Rules easily learned. As to operator overloading, that never happens by accident so until you know how to do it, you will be safe. And should a library use overloaded operators - why worry, it will work? The things an embedded programmer should be wary about are heedless usage of dynamic memory that makes the program non-deterministic, and class introspection or runtime type identification that might have unforeseen impact to performance. There may be some other similar features, less relevant to embedded applications.What i am trying to say that while knowing every item of compiler behavior to the last detail doesn't hurt, it is not necessary. Practically all of that is done to enable the OO constructs and behavior and it does not impact the end result of the code. Having said that one should understand at least in a general way the implications of some coding decisions and practices to the code size and execution speed. But again, not that big a deal and once learned, there is no going back. The 20/80 rule surely applies here as well: just learn to use 20% of C++ features and you get 80% of the benefits. For me it has been a good deal.Maybe the one difference that hits you unless you know to look for it, is the difference in calling conventions between C and C++. In order to support polymorphism, C++ just cannot use the same convention that C uses and that necessitates the extern "C" pragmas that some find cumbersome. I haven't actually tried it but i am almost sure the linker will catch that error.I find in your logic some of the same argument that exists between asm and C. The asm guys want to maintain microscopic control over every bit of code without realizing the ultimate futility of it - or that is how i see it. Asm has its place in the few cases where an operation is so fully machine architecture dependent that no higher level expression will do, or where a clever optimization in a tight loop exists and cannot be expressed in other ways. I freely admit that careless usage of C++ features may lead to trouble but that is not because there is anything wrong with C++, any more than C is broken because it lets you shoot yourself in the foot with sloppy pointer arithmetic. Different tools, different rules.Edit: typos.

author: JuiceKing
author_rank: Regular Contributor
timestamp: February 14, 2013, 10:10:47 pm
content: Quote from: IanB on February 12, 2013, 10:35:44 pmAgreed. For several years I did embedded system development (languages and graphics in laser printers) and we used C and not C++ for this reason.We used Gnu compilers wherever we could because they could always be debugged in house if we had to.For the work I did, the embedded system aspect had more to do with the challenges of debugging remote systems than programming them in special ways. You had to be aware of what memory would be in ROM and what things were performance-critical, but that was no big deal.I will say that endianess was a PITA gotcha.

author: JuiceKing
author_rank: Regular Contributor
timestamp: February 14, 2013, 10:26:10 pm
content: Quote from: Stiege on February 14, 2013, 07:38:00 pmIn addition to Deep C Secrets that was already mentioned, I recommend Tannenbaum's book on the design of Unix. It's the one that got Linus Torvolds started. No single book taught me more about advanced design concepts. Inspired and inspiring.

author: andyturk
author_rank: Frequent Contributor
timestamp: February 14, 2013, 10:55:35 pm
content: Quote from: Harvs on February 12, 2013, 10:26:55 pmIf you've turned off exceptions, rtti and don't pull in the standard libraries, there should be basically no difference at all between C++ and straight C. If you're using inline functions, references and other constructs that the compiler knows about, you may even get a better result from C++.Classes with virtual functions are fairly cheap too. C++ will put the code pointers in Flash/ROM. The only RAM overhead will be a pointer per instance to where its vtable lives.Some people have concerns about C++ templates, but I use them all the time for embedded projects (along with multiple inheritance).

author: andersm
author_rank: Super Contributor
timestamp: February 14, 2013, 11:32:17 pm
content: Quote from: Otatiaro on February 14, 2013, 07:18:04 pmI've been usingTNKernelfor a few projects, porting it to new platforms, and literally the only place where function pointers are used is when specifying the task entry point.Quote from: andyturk on February 14, 2013, 10:55:35 pmA lot of the bad reputation of templates stems from old compilers that weren't able to eliminate multiple instantiations of the same template from the final executable, but that hasn't been an issue for a long, long time.IMO, the biggest overhead of C++ is on the part of the programmer. Without learning proper design techniques you don't get much out of using it, but the learning curve is quite steep. Doesn't help that the language evolves at such a rapid pace.

author: IanB
author_rank: Super Contributor
timestamp: February 15, 2013, 01:24:47 am
content: Quote from: Kremmen on February 14, 2013, 09:11:10 pmWell OK, but don't assume I like to write in C. I was just offering a reason that might be argued.I code in C++ quite happily, and have not written any pure C in ages. I actually find being forced to code in C a pain in the neck.But, and I say this carefully, I don't code on micros. If I did, and the application made sense, I might consider whether C was a good choice. One should not be dogmatic about such things, but should choose the tools to fit the need.

author: IanB
author_rank: Super Contributor
timestamp: February 15, 2013, 01:35:46 am
content: Quote from: Stiege on February 14, 2013, 07:38:00 pmI'm afraid, perhaps, that this is where books end and where learning starts. At some point you have to put the books down and start putting the work in to understand things for yourself. Nobody ever graduates from being a student to being a professional until they can do that.If you know how to do things in assembly, you also know how to do them in C. There is nothing new to learn, it's just a different way of abstracting the same ideas.If you think you know what a structure is, and what a function pointer is, and if you think you know how to code things in assembly that require those concepts, then either you already know what you need to know, or you have not yet learned the underlying theory adequately.I would say you should go back over the material and learn it again, and again, until you really understand it. Because if you did understand it you would instinctively know how to do it in C and you would not think you need books to help you.The last cry of a drowning man is "Please give me a book that tells me how to swim!" You have to stop looking to books and start learning to swim.

author: AlfBaz
author_rank: Super Contributor
timestamp: February 15, 2013, 03:18:49 am
content: Quote from: IanB on February 15, 2013, 01:35:46 amKnowing and understanding the concept in asm is one thing but to suggest that this can easily be transferred to another language is a little harsh, namely due to the syntacticle hurdles of the new language and it's implementation.Below are some examples of function pointer declarations to illustrate how confusing it can getI got them from "Programming with C" by GottfriedCode:int (*p)(char * a); //p is a pointer to a function that accepts an argumant which is a pointer to a character and returns an integer quantityint (*p(char *a)); //p is a function that accepts an argument which is a pointer to a character and returns a pointer to a 10 element integer arrayint *(*p)(char *a); //p is a 10 element array of pointers to functions, each function accepts an argument which is a pointer to a character and returns a pointer to an integer quantity

author: IanB
author_rank: Super Contributor
timestamp: February 15, 2013, 04:25:03 am
content: Quote from: AlfBaz on February 15, 2013, 03:18:49 amYes of course, the syntax is horrifying. But then you need a book on the C language like K&R, rather than a book that "talks about how function pointers are used in embedded programming". If you want to program in C and you can't take a decent stab at the exercises in K&R you will be screwed.

author: Kremmen
author_rank: Super Contributor
timestamp: February 15, 2013, 07:15:12 am
content: Quote from: IanB on February 15, 2013, 01:24:47 amFair enough and i fully agree. I am just arguing the one side of this question, but that is not to dismiss the other side as worthless. It is just my personal experience that if one were to pick one language, C++ would be a worthy choice. If you don't want to use (some of) its features then they won't interfere.Quoting Wikipedia:In The Design and Evolution of C++ (1994), Bjarne Stroustrup describes some rules that he used for the design of C++:C++ is designed to be a statically typed, general-purpose language that is as efficient and portable as CC++ is designed to directly and comprehensively support multiple programming styles (procedural programming, data abstraction, object-oriented programming, and generic programming)C++ is designed to give the programmer choice, even if this makes it possible for the programmer to choose incorrectlyC++ is designed to be compatible with C as much as possible, therefore providing a smooth transition from CC++ avoids features that are platform specific or not general purposeC++ does not incur overhead for features that are not used (the "zero-overhead principle")C++ is designed to function without a sophisticated programming environmentI have found it to be so.Quote from: AlfBaz on February 15, 2013, 03:18:49 amThis is where the terseness of C really kind of works against itself. While the basic pointer constructs should be understood by anyone calling him/herself a C programmer, it is also true that the more involved declarations are a shortcut to painful bugs. They are simply to be avoided. Again, arguing for C++, this function pointing thing is almost completely eliminated by the OO mechanisms of the language. Sure, it happens in the background but handled by the compiler without you having to pay any attention to the nuts and bolts of it.

author: Otatiaro
author_rank: Regular Contributor
timestamp: February 15, 2013, 08:20:19 am
content: Hello,Quote from: andersm on February 14, 2013, 11:32:17 pmYep, that's the main use I was thinking about ... it's not everywhere, but how can you do a multi tasking kernel without a way to express a task entry point?I totally agree with IanB ... continue reading books/PDFs for inspiration, but the only way to really learn is to dive in it.Once you start doing it, you'll know what to look for in the books (I always keep Yiu's book on Cortex-M3 with me, just in case, but most of the time a google search will lead you to the info you want).Keep in mind thousands (if not millions) of programmers already faced the problems you are facing, and someone probably found a solution for each of them (but trying to find the solution by yourself at first is a good exercice).This is what makes programming so fun. Problems => solutions.Thomas.

author: westfw
author_rank: Super Contributor
timestamp: February 15, 2013, 10:03:56 am
content: QuoteWell, aside from there being no C++ runtime library for avr-g++, so you can only use those aspects of the language not dependent on such a runtime without writing the appropriate functions yourself...QuoteYes, that would be confusing. Don't do it. Typedef's are your friends. Do something like:Code:typedef int (*fp_type)(char *) ;fp_type func_table;We're also not above tricks like:Code:#define tty_puts(t, s) ((*t->functable)(s))Which allows you to do "tty_puts(mytty, "this is a test");" without ever knowing that tables of function pointers are involved.C's syntax can be obscure, but there is usually no need to put up with it more than once. (after which it starts to look rather like c++, which would have a tty.puts() method that was essentially similar. Probably.)

author: ecat
author_rank: Frequent Contributor
timestamp: February 15, 2013, 10:11:11 am
content: Quote from: andersm on February 14, 2013, 11:32:17 pmI always love the idea of function pointers but hardly ever find a use for them apart from saving a few cycles over what would be a case statement.For task switching I always go for the swap stack pointer and return approach. You may need a little assembly code to manipulate the stack pointer but it's a simple mechanism if the hardware is software interrupt friendly and if not almost all C compilers support setjmp.

author: Kremmen
author_rank: Super Contributor
timestamp: February 15, 2013, 10:28:15 am
content: Quote from: westfw on February 15, 2013, 10:03:56 amHeaven forbid.... It would be a disaster if dragging that luggage was mandatory. The limitations caused by lack of C++ runtime are minor, it tends to contain exctly those features that you specifically don't want to try in an embedded environment. The few useful things you either have to rip from the library if you can/know how, or else re-implement (or grab from the chip mfg frameworks in case of Atmel). But yes, i was referring specifically to the compiler itself. A full featured C++ environment is too much for most embedded systems, and also unnecessary.

author: Stiege
author_rank: Regular Contributor
timestamp: February 15, 2013, 11:08:16 pm
content: Quote from: ecat on February 15, 2013, 10:11:11 amYeah that's how we did the multi tasking kernel in 2nd semester. Would like to have a better idea of how it works on a microcontroller and using C though.

author: Kremmen
author_rank: Super Contributor
timestamp: February 16, 2013, 07:40:45 am
content: A true context switch AFAIK is possible only by saving the machine state and restoring another saved state. So stack switching it is. Some processors support this in the hardware with multiple register sets but usually a full register save/restore is needed. Cannot be done in C. This is by design since C is machine architecture agnostic on the source code level. If it wasn't it wouldn't be very portable either. This is one of the few places where assembly is actually needed.For a good look into how this happens "in C", take a look at FreeRTOS. It is freely downloadable fromhttp://www.freertos.org/. Attached the MCU specific FreeRTOS files for an Atmel UC3C. As the name says, exception.S contains all HW exception and interrupt handler vectoring, port.c and portmacro.h contain the architecture specific implementations for task switching and similar. Especially check the supervisor calls "portSAVE_CONTEXT_SCALL()" and "portRESTORS_CONTEXT_SCALL()" in portmacro.h. That is wherethe context is switched, as the function names say.The FreeRTOS package contains a large number of ports for different processors. The 8-bit ones are simpler so maybe the mechanism is clearer in one of those, but all of them work the same.

author: ecat
author_rank: Frequent Contributor
timestamp: February 16, 2013, 09:00:53 am
content: Quote from: Stiege on February 15, 2013, 11:08:16 pmAs Kremmen says in the above post, stack/context/jump buffer switching is hardware dependent making a generic example somewhat tricky. Indeed, be it interrupts, setjumps or even a full RTOS you should always look at the assembly output generated from some test code to ensure your chosen mechanism does what you think it should do.In general: a full RTOS should be able to deal with most eventualities and will document any grey areas; setjump structures should be sufficient to restore a fully operational software context - the code should run but interrupt states and hardware interactions may not be catered for; interrupts are a law unto themselves - some compilers save full context information, some just instruction and stack pointers.Just for fun here is quite possibly the most convoluted example code you have ever seen. Using function pointers and a task switching mechanism to flash an imaginary LED and provide an always on by button press override. Supplied untested and without warrantyCode:#include <stdio.h>//------ your hardware I/O definitions here ---------------------------------////bool ButtonPressed( void ) {return false; // return result of button scan here}void SetLEDState( bool state ) {if( state ) {// LED on}else {// LED off}}//---------------------------------------------------------------//------ Task primitives ----------------------------------------------------typedef void (*TTask)( void );#define TskMaxTasks 8//// list of tasks that are ready to runTTask TaskReadyList;//// A little counter/timerint TaskScanCount;void InitTaskSystem( void ) {for( int i = 0; i < TskMaxTasks; ++i ) {TaskReadyList = NULL;}TaskScanCount = 0;}//// Add pTask to next available space in list//void AddTask( TTask pTask ) {int i;i = 0;while( (i < TskMaxTasks) && ( TaskReadyList != NULL ) ) {++i;}if( i < TskMaxTasks ) {TaskReadyList = pTask;}else {// error, no space in list}}//// Remove pTask from the list and move remaining tasks up to fill the gap//void DeleteTask( TTask pTask ) {int i;i = 0;while( (i < TskMaxTasks) && ( TaskReadyList != pTask ) ) {++i;}if( i < TskMaxTasks ) {// pTask was found...TaskReadyList = NULL;// shuffle up to fill the gaps++i;while( (i < TskMaxTasks) && ( TaskReadyList != NULL ) ) {TaskReadyList[i - 1] = TaskReadyList;++i;}}else {// pTask not found in list so nothing to do}}//---------------------------------------------------------------//------ Tasks -------------------------------------------------------------//// forward declarations//void TskButtonScan( void );void TskWaitOnTimer( void );void TskWaitOffTimer( void );void TskLED_ON( void );void TskLED_OFF( void );//// process button press//void TskButtonScan( void ) {if( ButtonPressed ) {// this will run repeatedly while button is pressed// remove all LED related tasksDeleteTask( TskWaitOnTimer );DeleteTask( TskWaitOffTimer );DeleteTask( TskLED_ON ); // yes, remove On task here to avoid multiple entriesDeleteTask( TskLED_OFF );// force on taskAddTask( TskLED_ON ); // add led on task to list}else {// nothing to do}}//// Await 'timer' then turn LED on//void TskWaitOnTimer( void ) {if( TaskScanCount > 10000 ) {DeleteTask( TskWaitOnTimer ); // remove self from listAddTask( TskLED_ON ); // add led on task to list}}//// Await 'timer' then turn LED on//void TskWaitOffTimer( void ) {if( TaskScanCount > 10000 ) {DeleteTask( TskWaitOffTimer ); // remove self from listAddTask( TskLED_OFF ); // add led off task to list}}void TskLED_ON( void ) {SetLEDState( true );TaskScanCount = 0;DeleteTask( TskLED_ON ); // remove self from listAddTask( TskWaitOffTimer ); // add wait off task to list}void TskLED_OFF( void ) {SetLEDState( false );TaskScanCount = 0;DeleteTask( TskLED_OFF ); // remove self from listAddTask( TskWaitOnTimer ); // add wait on task to list}//---------------------------------------------------------------void FlashTestMain( void ) {int tskIdx;InitTaskSystem();AddTask( TskButtonScan );AddTask( TskWaitOnTimer );tskIdx = 0;while( true ) {// scan through the ready list running all tasks in sequence//if( TaskReadyList[ tskIdx ] != NULL ) {TaskReadyList[ tskIdx ]();++tskIdx;if( tskIdx >= TskMaxTasks )tskIdx = 0;}else {tskIdx = 0;}// inc scan count on each full scan of the listif( tskIdx == 0 ) {++TaskScanCount;}}}

author: lapm
author_rank: Frequent Contributor
timestamp: February 16, 2013, 11:15:25 pm
content: I would say its not the C thats needs learning, its about limitations of compiler you use and actual embedded platforms requirements. Different microcontrollers and platforms theya re embedded can do different things..Arduino mentioned earlier in this thread is nice example of combination of compiler and platform.There is others as well. For example small device c compiler (SDCC), dont know how popular that one is...

author: AlfBaz
author_rank: Super Contributor
timestamp: February 17, 2013, 02:47:19 am
content: In C and C++ we have setjmp and longjmp which can be used for context switching of sorts. I don't understand C++'s implementation of code at the assembly level like I do with C where I've studied the calling concentions and stack implementations of several compilers accross a variety of MCU's, some with and some without hardware stacks.I have had very little to do with arm processors so their documentaion may vary but the majority of MCU's usually stipulate that their instruction set and architecture have been designed to support the C language. I don't think I have ever seen them mention C++.Another thing that may have changed since I looked at it was a consortium of companies and institutes that were laying down the ground work for implementing C++ on embeded systems. C++ afficionado's had concerns that the limited level of C++ implemtation on embeded systems made it not worthwhile to use C++. I beleive this is one of the reasons it has taken so long for C++ to get any kind of strangle hold in the embeded world.Higher level's of abstraction via tools such as C++, RTOS's and arduino type gadgets become crutches for a generation of embeded programmers who then espouse the virtues of these tools simply because they are fucked without them.This is not to say these tools don't have their place but you start seeing the missuse of them in commercial products. Do you really need to have linux running in the background for a DVD player? NO!Unfortunately there are no standardised API stacks for the various functionalities we may wish to implement in our devices and the closest we come to these thisngs is to use OS's where these features are present and well understood. This vastly reduces development cycles but we end up wasting the bandwidth of fast processors to cater for the ever growing population of software designers who have abstracted themselves far away from the hardware.One example of where I have seen people struggle and suggest C++ as a solution is in exception handling. C++ has try, catch and throw. Most C compilers simply place a vector at the exception address that jumps to a function that simply loops. If you have a debugger attached you can then interigate the hardware registers to see the address of where the exception occured. Then you have the often difficult task of trying to map that address to C code. If you have ever seen C source interlaced with the generated assembly you will see that a simple C statement may have many lines (and therefore instruction addresses) of assembly gerenarated for that statement.It becomes even harder to do anything about an exception in code, short of logging the exception for later inspection and rebooting.Setjmp saves the environment at the place it was called in a predefined environment buffer and longjmp restores this environment and returns to the setjmp statement.Being a stubborn C supporter I created a simple macro definition that allowed you to use nested try, catch macro's in a c environment#define ctry if(!(error_code = setjmp(exception_env[current_env++])))In code you would use it like thisctry{ ... dangerous code...}On the first run through setjmp returns 0 which evaluates to true (due to the ! operator) allowing the "dangerous code" to excecute. The environment is stored in an array of jmp_buf's allowing nesting.If an exception occurs in the "dangerous code" section the MCU jumps to a hardware vector that the compiler has placed a generic exception routine that generally just loops. We overide the definition of this generic exception routine with our own. In here we obtain the exception code and address from the hardware reg's that contain this info, set a global flag and longjmp back to the setjmp statement with the exception code.As the setjmp func is within the if() statement it now evaluates to false, thereby skipping the "dangerous code" section.Immediately proceeding the ctry macro we have the ccatch macro#define ccatch --current_env; if(!(--catchflag))In code its used like thisccatch{ recovery code can use the variable error_code to decide what to do}What the ccatch macro does is set the environment array index to the previous jmp_buf element (incase it was nested) and evaluates the catch flag and resets it at the same time.The attached files were meant for PIC32

author: westfw
author_rank: Super Contributor
timestamp: February 17, 2013, 05:33:05 am
content: QuoteNot really.Code:// switch task context from old to newvoid switch(task_t *newconext, task_t *oldcontext){// Save any high-level stateasm (";; save low level state on stack.\n"";; mov sp, oldcontext->sp ; (save stack pointer)\n"// --- this is the middle. ---";; move newcontext->sp, sp ; (get the new stack)\n"";; restore low-level state from stack\n");// restore high-level statereturn; // to new context}The hardest thing is likely to be accessing the c-structure from assembler for saving/restoring the stack pointers.Since C calling conventions generally save SOME context, you may be able to minimize the assembler parts of the code (but you get to decide whether it's really worth it.)This does assume a single stack, sort of (if there are separate data and return stacks, one of them is "low level context" that you'd save on the other.)At "the middle" you can call your "scheduler" or do other things, or you can simply handle the scheduler as a separate context.On small microcontrollers, the ratio of "context size" to "total ram" gets to be pretty large. An ATmega328 AVR has 32 registers, plus the stack pointer, plus the status bits, plus the program counter (at least.) And only 2k of RAM...

author: Kremmen
author_rank: Super Contributor
timestamp: February 17, 2013, 07:36:20 am
content: Quote from: westfw on February 17, 2013, 05:33:05 amTrue, and don't forget the actual stacks you need for each active task. But still a 328 runs FreeRTOS with some 5 tasks with no problems whatever. If that is not enough then maybe one has selected the wrong chip...Actually, it is not as bad as it sound, because outside of the task context, there are only the static variables. So effectively all of the RAM not allocated to statics should be available to the task contexts.

author: andersm
author_rank: Super Contributor
timestamp: February 17, 2013, 08:11:15 am
content: Quote from: AlfBaz on February 17, 2013, 02:47:19 amEverybody claims that, in practice there's almost always some areas where the architecture doesn't work very well with the underlying model of C. Of the ones I've used, Cortex-M comes closest. And while it's probably possible to design an architecture with C++ in mind, it doesn't require anything more than plain C.

author: westfw
author_rank: Super Contributor
timestamp: February 17, 2013, 09:56:18 am
content: QuoteThat's because C and C++ require pretty much the same thing, at the bottom levels. I'm not sure what you'd add to an architecture to support C++, beyond what you'd already do to support C. (ok, there were a couple of pretty spectacular failures, like theIntel iAPX432, that tried to support things like data structures and garbage collection in the MMU hardware.)As "andersm" says, there are a lot of cpus that claim to be "designed to support C", just like there used to be a lot of cpus that claimed to be "pdp11-like." Sometimes it's more true than other times. Usually it means a RAM-based stack that can support stack frames, and multiple index registers (and addressing modes that include auto-increment and decrement.) And let's not forget byte-addressability; implementing C on a 36-bit word-addressable cpu is ... frustrating.

author: amyk
author_rank: Super Contributor
timestamp: February 17, 2013, 12:47:04 pm
content: Quote from: Kremmen on February 15, 2013, 07:15:12 amThe trick to reading C declarations is that they're not read left-to-right, but in precedence order like when you read 5+4*(2-1); not memorising the precedence is what will really confuse you.http://eli.thegreenplace.net/2008/07/18/reading-c-type-declarations/

author: Kremmen
author_rank: Super Contributor
timestamp: February 17, 2013, 01:24:42 pm
content: Quote from: AlfBaz on February 17, 2013, 02:47:19 amStraglehold... hmm. Why make it a conflict when it doesn't need to be? I am a C++ "aficionado" but don't confuse those who like to use it with those who try to create standards and "enforce" them. I for one don't give a rat's ass what some C++ formalizers say about its use in embedded systems. To squeeze every single C++ language feature into an embedded implementation is an exercise in futility and counterproductivity.QuoteNot so fast. I understand that the vast majority of embedded programmers are not computer scientists, nor do they need to be. But it also seems to be true that the vast majority is sadly clueless about the first principles of the practices of software construction. What you write above is more support for this view. True, one should not needlessly complicate a solution, but at the same time one should be aware what "complicating" means in each context.Abstraction, far from being a crutch is a method to bring the essential into the focus. Without education/training in the use of the tools of abstraction among other things, one can get sidetracked or lost. That does not mean the tools are faulty. As a tool, high level languages have been created expressly for the purpose of freeing the implementors from wading in the low level dross that can be solved mechanically by those tools.My background is in what used to be called "programming in the large" meaning that there are several teams implementing the code at different locations and times. Today the projects i run are written in Java, C# and numerous scripting languages by various project and support teams. They are not embedded in nature but the point is that the requirements are abstracted as far as the tools allow. It would be an anathema to sit at the IDE and start designing the system, because that way you will never get anywhere before the task overwhelms you. Maybe you don't need to create a set of enterprise architecture level diagrams for a lowly embedded gizmo, and maybe you don't need Linux either to run its code (then again, maybe you do!), but analyzing the problem and then applying the proper tools and methods based on that analysis always works. Curiously, it seems to be the case that productivity and quality have a strong correlation to the level of abstraction and layering achieved in the problem analysis and tool selection. So it would seem that rather than crutches, the high level tools are the bicycle. You can always run, but you get there faster and with less pain if you hop on the bicycle. True, if you concentrate on blindly pedaling and forget steering, your nose will bleed more when you finally crash. But handling that part is called competence and it should be achieved whatever the tools, there is no shorcut.QuotePartly agree. Hardware however is cheap, human labor is expensive. You don't need an OS for every little thing and there is a limit under which it really makes no sense. Still, why actively avoid using one if there is no real penalty? A lightweight RTOS costs but little resources, and the benefit from standardizing can easily outweigh the cost associated with using one.I am not suggesting that understanding the hardware solution of an embedded system is meaningless, not by a long shot. I am just saying that letting that level limit your view and understanding is not the path to optimal software construction.QuoteThe concept of exception handling in C++ does not really resemble what you write above. It is a quite big concept with a lot of implications to the environment. In fact big enough to discard right away in any but the absolutely biggest embedded cases. And maybe not even there. The potential for stack recursion would in a bad case rapidly eat the resources of your average MCU.QuoteFair enough but C++ exception handling is something different. For starters there is exception catching in an inheritance tree that is difficult (not to mention pointless) to simulate in C.But see for yourself. I think the abstracts of IEEE papers are available without login, so why not checkhttp://ieeexplore.ieee.org/search/searchresult.jsp?action=search&sortType=&rowsPerPage=25&searchField=Search_All&matchBoolean=true&queryText=((embedded)%20AND%20C.PLS..PLS.)and see that in fact C++ is and has been widely used for a long time in embedded systems, with good success.

author: Otatiaro
author_rank: Regular Contributor
timestamp: February 17, 2013, 07:40:52 pm
content: Hello,Quote from: ecat on February 15, 2013, 10:11:11 amWell, guess what is in the stack ?To change tasks you just manipulates pointers (even if they are code pointers so kind of functors), but when you start a task, you most proably need to use the functor (the first address you'll put in the stack to branch to it on interrupt return).Thomas.