High Level code on micro-controllers - Page 1
Forum section: electronics / general

author: Crazy_Engineer
author_rank: Contributor
timestamp: March 27, 2018, 01:23:38 pm
content: I learned to code in assembly and eventually moved up to C. In the last couple of years I have noticed that most IDEs support C++, and RTOS integration. With ram and flash being rather cheap now days, the overhead from the abstraction is less of a concern and I have been wondering if it is time move up. What are your thoughts on using "higher level" object oriented code and possibly a real time operating system vs plain old C?

author: nctnico
author_rank: Super Contributor
timestamp: March 27, 2018, 01:55:27 pm
content: Getting Lua going on a microcontroller is high on my wish list.

author: C
author_rank: Super Contributor
timestamp: March 27, 2018, 03:14:52 pm
content: Not all higher level abstraction costs in code space.Often the higher level is just better source checking or a different way to do same thing.If every thing is an integer then you lose the option of compiler throwing an error when you try to combine a temperature integer & something else. Checks like these costs Source space but not code space.Often a different way to do same thing can let the compiler do a better job.When you read a "C" program you see "DEFINE" used all over the place.Think of foundation of "DEFINE". Read source text and replace matching text with new text. Compiler can not check "DEFINE" only the replaced text for proper syntax.Remember that the compiler writer has to allow for all possible valid uses of "DEFINE".For example if you use "DEFINE" to work around CPU differences you have a problem. To verify your code you need to recompile for each CPU possible in the "DEFINE" to check for errors.If the higher level language allows, you could just have a list of CPU's and the compiler check all for errors.You do not need a higher level of abstraction if the language allows same result with out abstraction.Think back when each printer needed a different driver due to code sequence to do BOLD for example.You just need to swap functions based on what printer.Back in the 70's, I worked with what you would call and embedded system. Code changes happened often and happened while the program was running. Proper language can make this very easy.Think about "C", will you get an error if you do a dumb think and connect two RTOS things?Not hard to have error checking for code separation if the language defines such a thing."C" gives you great power, but makes you check for errors that are simple with proper language.Today a language should know what an event is and help with program checks.So lots of things can become something the compiler can check if the compiler knows what it is and their for what rules to use in checking.In the process you have to type/input more text but gain in better compiler checking which saves debug time and finds errors better.Think of what an abstraction is. A way of hiding needed facts from something.No need to hide something if you can state the facts.

author: free_electron
author_rank: Super Contributor
timestamp: March 27, 2018, 03:19:56 pm
content: Quote from: nctnico on March 27, 2018, 01:55:27 pmhas been done. stm32 can do it. so can esp8266 ....for many processor cores :http://www.eluaproject.net/overview/statusfor ESP8266:https://nodemcu-build.com/ive been running ESP8266 without problems

author: Crazy_Engineer
author_rank: Contributor
timestamp: March 27, 2018, 05:43:23 pm
content: Abstracting through macros is something I do often. Indeed many of my projects start off with a long list of macros that relieve me from having to know what pin an input or output is assigned to. Unfortunately the C pre-processor has its limits which I have run into when trying to make driver libraries that function across a family of chips. The driver library in question was for an LCD screen for msp430 series processors. When writing it there were several times that I thought "A class would probably be helpful here."; but I was dissuaded by the resource inefficiency horror stories that came from my peers.

author: technix
author_rank: Super Contributor
timestamp: March 27, 2018, 05:49:29 pm
content: AFAIK most RTOS is written in C. As of object oriented programmingyou can do it in plain C too. (If you really think about it the stdio.h filesystem access API is really a FILE class and its methods.)Personally I don't use a lot of C++ on microcontrollers. AFAIK smaller chips like 8051, STM8 as well as 8-bit and 16-bit PIC don't even support C++ (there was a patchset for xc16 that adds C++ support, but that was flaky AF and abandoned.) OTOH that object-oriented C is very widely used across my own code.Quote from: Crazy_Engineer on March 27, 2018, 05:43:23 pmYou can do classes and OOP in C. Ever heard of the concept "callback function pointers?"

author: Crazy_Engineer
author_rank: Contributor
timestamp: March 27, 2018, 06:11:36 pm
content: Quote from: technix on March 27, 2018, 05:49:29 pmLooks like I have some reading to do. Thanks.

author: technix
author_rank: Super Contributor
timestamp: March 27, 2018, 06:15:24 pm
content: Quote from: Crazy_Engineer on March 27, 2018, 06:11:36 pmFrom what I see in that book, that fellow took an approach fairly similar to the Objective-C runtime though.

author: nctnico
author_rank: Super Contributor
timestamp: March 27, 2018, 06:45:41 pm
content: Quote from: free_electron on March 27, 2018, 03:19:56 pmUnfortunately the Eluaproject needs a lot of work to get some decent platform support for NXP's LPC devices.

author: abraxa
author_rank: Frequent Contributor
timestamp: March 27, 2018, 07:44:03 pm
content: Don't forgethttps://github.com/micropython/micropython/wiki

author: legacy
author_rank: Super Contributor
timestamp: March 28, 2018, 06:34:17 am
content: @C100++/100 agree with you

author: legacy
author_rank: Super Contributor
timestamp: March 28, 2018, 06:45:58 am
content: #define pin instead of typedef enum?

author: andyturk
author_rank: Frequent Contributor
timestamp: March 28, 2018, 06:46:28 am
content: Quote from: technix on March 27, 2018, 05:49:29 pmIMO, the lack of a quality C++ compiler is a good reason to avoid a particular architecture. (if you have the choice, of course).QuoteUh huh.

author: legacy
author_rank: Super Contributor
timestamp: March 28, 2018, 06:50:06 am
content: Quote from: nctnico on March 27, 2018, 06:45:41 pmyup, and a bootstrapper

author: obiwanjacobi
author_rank: Super Contributor
timestamp: March 28, 2018, 07:39:41 am
content: I think there are certain types of MCU devices that can be considered suitable for moving the level op abstraction up a bit. But it all starts with a good compiler. I would not go with interpreted languages such a Lua (which sucks imo). Dicking around with C can do OO too is all very well when you're on a 8051 (or a small AVR) and you have no other choice, but not for larger teams with mixed skill sets.Ask yourself what parts of the code need to be close to the metal because of performance or efficiency of interfacing. The process flow, driving UI and higher order logic should be readable (and maintainable) code foremost.Personally I think (C++ 20) meta programming is a very interesting topic for the embedded domain, but here again it's about the compiler. Also a higher level of abstraction may not need a new language but merely a different mindset and design. I (usually) cringe when I see most C library API designs where to my mind all the wrong choices of separation of concerns and single responsibilities have been made. Designing good library APIs is hard.So yes, it is definitely time to move to a higher level, but not necessary time for a new language and certainly not an interpreted language (unless you aim to run user/custom scripts on the device itself).

author: technix
author_rank: Super Contributor
timestamp: March 28, 2018, 10:33:44 am
content: Quote from: obiwanjacobi on March 28, 2018, 07:39:41 amEven if I plan to run user scripts, those are always native code loaded into the RAM. Whatever language you are using, compile it down to native code and link against the symbol list of the main application please.

author: ajb
author_rank: Super Contributor
timestamp: March 28, 2018, 05:49:35 pm
content: Quote from: Crazy_Engineer on March 27, 2018, 05:43:23 pmFor cross-platform mid- or high-level libraries, using layers of functions is a better approach than macros. You write a layer of short, simple low-level functions that interface with hardware (setting modes, starting hardware transfers, handling events, etc), then a layer of functions that sit above that to handle protocol level stuff (sending/receiving via buffers, etc). Depending on the application, where you delineate the two layers may shift, but when moving to a new platform you only need to rewrite the low level functions. For simple things like basic UARTs you COULD do the lower level with macros, but functions are more versatile, and all things being equal an inlined function won't produce any extra overhead.If you wanted to be more objecty about it, you could also wrap up a whole interface as a struct containing a reference to the hardware instance and function pointers to your low-level code. Your higher-level functions would take such a struct as an argument, and then as needed extract the hardware instance and pointer to the appropriate function and pass the former to the latter (along with whatever other parameters are needed).

author: Yansi
author_rank: Super Contributor
timestamp: March 28, 2018, 06:19:32 pm
content: Don't you rather need a quad core ARM written in Java to blink the LED?I do not have nothing against high level code, any kind of a complex application will certainly need one. However LUA, Java, Python, etc is not a high level code, that is highly resource hungry garbage, that should be kept out of them, even out of any PC, especially Java. I hate that junk!

author: SiliconWizard
author_rank: Super Contributor
timestamp: March 28, 2018, 06:24:53 pm
content: Quote from: Yansi on March 28, 2018, 06:19:32 pmQuote from: Yansi on March 28, 2018, 06:19:32 pmDitto.A lot of people, especially in the purely software world, confuse high-level languages with the existence of a monster (and unavoidable) runtime accompanied with a lazy memory model.And if you look closely, what a lot of those people are after are actually those gigantic runtimes and libraries, not necessarily the language constructs themselves.

author: obiwanjacobi
author_rank: Super Contributor
timestamp: March 28, 2018, 07:34:14 pm
content: Quote from: Yansi on March 28, 2018, 06:19:32 pmYes! Because the Quad ARM costs less than the other less capable parts you might consider and I am done in 5 seconds because of the higher abstraction level. My solution will costs a tenth of your custom optimized one.I am exaggerating to make a point here, but the question is: does it matter it is not super lean when the price is significantly lower. Software development hours are not cheap. You could buy a lot of hardware for that. Of course there are specs that may make you to choose otherwise (power, performance etc) but still, the hardware is capable.

author: tggzzz
author_rank: Super Contributor
timestamp: March 28, 2018, 07:35:47 pm
content: Quote from: SiliconWizard on March 28, 2018, 06:24:53 pmYes indeed; just so.

author: technix
author_rank: Super Contributor
timestamp: March 28, 2018, 08:51:31 pm
content: Just an information drop: there is this thing called gnustep-libobjc2-baremetal. Yes this is about that odd language Objective-C,Objective-C (yes the old Apple language before Swift,) especially the more modern Swift-compatible-Objective-C, has every feature Java has, but it does not rely on a huge runtime library for everything since it got rid of that virtual machine (uses native code instead) and the garbage collector (uses reference counting instead, with static code analysis used to insert 99.9% of the actual reference counting code at compile time through ARC.) The compiler also enforces a fairly rigid naming convention and class contract, throw tantrums if you deviate even a little from it. The mandatory runtime library itself, libobjc2, is no larger than libstdc++, and for the much larger but non mandatory "class library" Foundation.framework you can cherry pick classes and algorithms you actually need (or allow the linker to throw away unused classes and methods) to reduce code size significantly.On a language feature level Swift is equivalent to Objective-C but with a more streamlined and concise syntax (since Objective-C have to maintain compatibility to C, being a strict superset of it.) However there is no ready to use libswift mandatory runtime library (Swift's version of libobjc2) that suits embedded development.

author: technix
author_rank: Super Contributor
timestamp: March 28, 2018, 08:59:53 pm
content: Quote from: obiwanjacobi on March 28, 2018, 07:34:14 pmNow your final product costs 40x more than your competitor's to make. Maybe they have to code on a lower level of abstraction for a week, but due to the vastly simpler hardware and the sheer volume they can churn out so much more products per unit cost the R&D cost per product is next to nothing. This, and overworkng the engineers to death with their creativity suppressed and punished, is how Chinese manufacturers can pump out so many cheap items.

author: Crazy_Engineer
author_rank: Contributor
timestamp: March 29, 2018, 12:20:23 pm
content: Quote from: obiwanjacobi on March 28, 2018, 07:39:41 amInteresting points. For me, I notice that my code becomes difficult to maintain once the program gets beyond a certain length; one of the main reasons I was inquiring about other languages. It may be I need to more carefully plan, compartmentalize, and document my longer projects. Speaking of which, does anyone know of any good resources on planning out embedded coding projects?

author: tggzzz
author_rank: Super Contributor
timestamp: March 29, 2018, 01:24:22 pm
content: Quote from: Crazy_Engineer on March 29, 2018, 12:20:23 pmThe best resources and tools are brainpower, good taste, appropriate design patterns, time, and refactoring. Purchased tools can onlysupportthose, they can't ensure success.If you don't already know about it, strongly consider event-driven programming in conjunction with explicit FSM design patterns (i.e. not horrific ad-hoc deeply nested case statements!). Then you can cleanly separate generation of events from what reaction occurs as a result of an event. Test each event generation and reaction in isolation, then when working combine them. Keep a log of events and states, so that when (not if) things don't work as you expected, you can figure out how you got to that point!Harel statecharts are a good starting point, a.k.a. UML state diagrams.

author: andyturk
author_rank: Frequent Contributor
timestamp: March 29, 2018, 02:03:56 pm
content: Quote from: tggzzz on March 29, 2018, 01:24:22 pmOr maybe any kind of diagram, because pictures speak to a different part of your brain.I was messing around with graphviz once and fed it some grep output from our codebase that found all the events, and who was sending which message to whom. This was an event-driven system that never had a top-down design, it just grew over the years. Graphviz/dot will automatically "optimize" a diagram to minimize arcs that cross each other, and the result put a particular subsystem right in the middle with arrows coming and going like a star burst.None of us had seen this kind of information before, but we all realized that the module in the middle had been more tricky to work with than other parts of the system. The picture confirmed what some of us felt about the code and provided a great starting point to a conversation about architecture.

author: nctnico
author_rank: Super Contributor
timestamp: March 29, 2018, 02:13:12 pm
content: Quote from: obiwanjacobi on March 28, 2018, 07:39:41 amFYI: Lua isn't interpreted perse. You can also distribute/run it as bytecode. AFAIK the Eluaproject is setup this way so you don't need to have the Lua compiler in the flash. The VM which executes the bytecode is enough.

author: tggzzz
author_rank: Super Contributor
timestamp: March 29, 2018, 02:17:11 pm
content: Quote from: andyturk on March 29, 2018, 02:03:56 pmAll sensible, and very valid.I'm not a UML/Harel zealot, but it does have the advantages thatit is well documented and there are many examples around. That means less writing on our partit encourages a sound way ofthinkingabout,structuring, anddecompomsingsystemsbeforerushing into implementation

author: SiliconWizard
author_rank: Super Contributor
timestamp: March 29, 2018, 02:19:07 pm
content: Indeed.Never assume that any specific tools or languages, no matter how hype they look, will make up for a bad architectural design (or even lack thereof).If anything, they will probably make things worse if there is no solid underlying architecture.And all you need for this is some brain, really.

author: NorthGuy
author_rank: Super Contributor
timestamp: March 29, 2018, 03:07:46 pm
content: Quote from: Yansi on March 28, 2018, 06:19:32 pmYou're living in the past. The quad core ARM doesn't cut it any more. Today, if you want to blink a LED, you need an FPGA:http://www.instructables.com/id/CmodA7-7-segment-Stopwatch/

author: C
author_rank: Super Contributor
timestamp: March 29, 2018, 03:27:51 pm
content: I think a lot are looking at problem the wrong way.You have the statement about runtime library.Often pushed off to the side as a separate thing to be used by compiler or language.Some do not like parts of PCODE, while some do.The same with runtime.One problem here is that you are thinking only ONE.Internal to compiler or interpreter it is not hard to have a bunch of switches if you plan ahead.A good example is the cross compiler. A bad one only does one instruction set. A little better one lets you specify which of a few instruction sets.Think of what you really want.As technix stated,One builds with more powerful CPU.One builds with very limited CPU.Build is a very good word and IDEA.Stop compiling and start building. A computer using your knowledge can build many. If it works on building at same time it can easily find where the source code breaks for the one of many.A good part of making something work on many is having the hooks to adapt to the many.Think of what you want, If the CPU has a multiply instruction, you want to use it if it's better. If not then you need a multiply function. You just need a table that specifies which CPU the CPU uses. In the process you have added or removed a part of the runtime. What you need to change is all the black boxes. You need to specify CPU instructions or language instructions.PCODEFor a comparison, think of the old Z80. With interpreted byte code the instruction is a byte. You gain some speed by changing to a CALL at a cost of 3 bytes.Then you have how much each code is doing. Is it checking for errors or not.If you are thinking my way here you again want choice. You want that new source code to be really checked for errors at byte code level and when good back off to called or machine code versions.This is not something new. It is much easer for the compiler writer if he can get you to think the only good thing is machine code.That old Z80 has a good foundation of instructions. It can work with 64-bit or larger math, it just takes time. Again this saves the compiler writer a little time. But think of how much time? The old PCode that first Pascal's used could be foundation for larger math.By not giving the programmer the option of larger math, the compiler writer removed all the complaints of it being slow.Think the better choice is for you the programmer have a choice with out having to jump through the hoops of an added library. With it built in you get better error checking then added library.So break it down some.You do not care how large the source is unless you are building on something small.The only part that matters is the final code size that goes to machine.If you have studied it or just think of what you are using to read this, there are ways to swap program code in and out of ram.In the 1980's, I was programming 68000's using Pascal. I often turned on the HP system programming switch for a small area of the program and could load new code into the running program. Was not that hard to do and only a few of pascal error checks needed to change.Notice I said Change. This Pascal already had the capability of treating a procedure or function call as a pointer with all the error checking in place. Was a minor change to allow dynamic modules.If you open you eyes, you might find that the full Oberon operating system is very small, yet has a character based GUI.One of the things as a programmer you have to learn is how to input your source. Oberon actually has less rules then Pascal and does better checking of code.Need to wrap your mind some around the internals of a language some.In C you have a function. The body of the function is the same as the two possible bodies of an IF statement. The difference is the before body starts and when body ends.Your C program is actually a function.The pascal that I used back then had many options.You had the stardard three for pascal of Program, Function & Procedure and more.One added was MODULE.You could build modules. The SYSTEM module was actually the interface to all built in types.Here the compiler writers pulled a trick or were smart.The normal pascal compiler started with compiling the Program function. Here this compiler started with the System module and then the Program function.Think of this, the normal code generator that you used actually built all the internals needed for the compiler to function.Change the System Module from ___ to Z80 and you had a Z80 compiler. Modify the System Module some and you had a special version for a specific Z80 system.Again not not radical or new. Think you will find that the DotNet languages do the same.So with the right foundation that is actually simpler, you get choice and more power.Procedures and functions could work with the many different interfaces that other used. This pascal could use functions written in C.When you needed to get to assembly it could generate the procedure or function interface to assembly.Think of the compiler writer, This would make quick changes or additions to compiler much faster & also help the programmer.So by putting together what has been done properly, the compiler can build or extend it's self. Can use new base types while sometimes needing help with the code generation.So I think the question is the card before the horse type of question.You should be asking what editor actually understands the language you are using and can generate code. Not as a step in the editor but when you exit a block of code.You write a function and when you move from function you get all error messages and code shortly from a background process.This editor should let you have many information screens. When working with a module or class what is visable to other modules or classes is different from internal.I say screen as all the pop-up windows are not good for fast programming.When you get to this level then it is you, compiler writer, language creator all working together. You now have a choice.Need to keep in mind also that the programming syntax is not the only syntax you need to work with.When you add a debug statement trying to display a variable the build system should know to use the binary_to_Human function and if this does not exist ask for it.If you are working with XML you have to binary to/from XML function.Think of the parts the controller is interfacing with. You should be able to quickly add that table of bit positions you find in the data sheet. Less chance of error if you are just matching tables.One bit could have many names. The data sheet name and your programs name for example.A chip becomes one of the many views that you have to work from/with.So with a good foundation you can build up and build down.At any point you could right click on a variable and change or add to some of it's information. A name change would only effect this one variable and not the many that have the same name. Remember that when you have many classes, modules and others, the short name could match many places.Looks like the current trend is hiding more and more things. Hiding is good at preventing errors but more choice gives you power. Think of all the different ways to point to something.Having a choice can be as simple as entering your choice.The build system can be smart.By using an exposed NEW to create storage for a variable you can gain the easy location setting of where. Here you are just changing a value in the AST for the code generator to use. It's that simple at the low level.Think of the stepsSource code to AST and you get source code format errors.Error checking of AST and you get language errors.Code gen used AST and you get Not possible in Code errors.Not having a good AST actually increases code size and errors.Having access to AST from source code lets your source get fancier and safer. A size of function is just one example.Sorry for the Book but you need to keep in mind that each little broken thing is the base of something huge later.