This article will provide some basic ideas on programming microcontrollers (MCUs) in contrast to programming “full size” computers like PCs etc. We will talk about the requirements to substitute certain well known functions like printf() etc. by our own code, will talk about the special settings for using variables, memory issues, what “ports” and how they can be accessed etc. etc.
Also the special bit shifting techniques to access certain pins in the MCU will be demonstrated as well as the use of the preprocessor in the compiler system.
From large to small – Entering the world of microcontrollers
When coming from another programming language (Java, Delphi or whatsoever) from whatever computer you have previously programmed into the world of microcontrollers (aka “MCUs”, microcontroller units) and “bare metal” C as programming technique there are some things that will be very different.
The first decision you have to make is if you want to program with an operating system (like Embedded Linux or RTOS etc.) in the background or if you would like to program “bare metal” which means “hand coding” all the necessary code by your own.
“Bare metal” means to run a software directly on the hardware level without the support of any operating system. Using this style we program a microcontroller directly with a number of code lines of C code, as it was traditionally made in electronics. This is for people who are very familiar with the hardware, e. g. the registers and ports of the MCU. Thus another word for “bare metal” is “register oriented programming”. It is the closest approach to hardware we can think of.
When using an operating system like Embedded Linux our applications are running using a system framework, which acts as an operating system (OS). Quite comparable to the OS on our PC. The kernel is the core of the operating system controlling almost everything in the system. This is especially true for all input/output operations. So, every interaction between hardware and software runs through the framework of the OS.
What are the main differences to PC programming for example when programming “bare metal” for MCUs?
For programmers who have created software using other languages and environments there are some things that might be new. For example: There are no “printf()“, “writeln()” etc. functions on an MCU. When you want to display a numerical or alphanumerical value on an LCD for example you have to create your own code to do this. Some examples can be found in the LCD-section of my Github repository (section “03_”) for demonstration of this technique. As an MCU “bare metal” software designer you have to step down the stairway to the very first or second step.
The same is true for getting data into the MCU. You don’t work with a keyboard like with a PC where you can read the keyboard’s code each time a key is pressed. In an MCU you just read ports or other type of data coming from the bus lines (SPI, I²C) for example.
Before you can use these ports, you have to know that ports in an MCU can be used as input or output ports. Configuration for AVRs for example is explained in section “01” and “02” in my Github repo. “Output” means you can set a pin (or a whole port or parts of it) as “hi” or “low” during runtime by software. When reading a port, you can do the same vice versa by using certain functions telling you the voltage present to a certain pin. That’s the way how you get data into your MCU. With an MCU you deal with voltages, signals (a special representation of voltages), bus line data etc.
For checking data in the MCU and sometimes “debugging” purposes microcontrollers have USART (universal synchronous/asynchronous receiver transmitter) interfaces that are RS232 compatible. This makes it easier because you can re-direct applicarion’s output to a serial terminal on your PC.
BTW: Forget about OOP (object oriented programming) in “bare metal”. Even if there certain OOP related languages or onsets exist for MCUs, I have personally never used them. Maybe you will. Or not.
These programming onsets produce some overhead and experience has shown that there are no real advantages for using OOP in small microcontrollers. Neither from the point of view concerning run-time efficiency nor from from the coding process itself. The same might be true when using operating systems like Embedded Linux (as mentioned before) or other RTOS (real time operating systems) in small MCUs. The only advantage may be that you can have access to predefined code in the form of predefined functions.
Summary: If you produce programs with let’s say 50,000 to 100,000 code lines maximum you can get well on with the procedural onset. This means you have to do lots of coding on your own but fortunately the vast majority of code can be reused if you want to program “bare metal”. The topic “OOP vs. procedural style“, by the way, is subject of endless discussions…
Limited memory space must be kept an eye on
When you are using large arrays in a program for a PC or another comparable machine size does not matter. There is always sufficient memory in RAM or on your harddisk. In MCUs this is different. Memory is limited to some 100kB of so called FLASH memory for program code. The same is true for RAM which can take dynamic data. Thus large arrays of long long integers (64 bit wide) will make you run out of memory soon. This means you have to think very carefully about your types of variables, your array sizes etc.
Use integer (16 bits wide) instead of long (32 bits wire) use char (8 bits) instead of integer whenever possible etc.. You have to think much more carefully about data structures in an MCU than on a large computer memory model.