+1 500 000 products in offer

6000 packages per day

+300 000 clients from 150 countries

Quick Buy Favourites
Cart

How to program a microcontroller? Top microcontroller programming languages

Date of publication: 25-03-2024 🕒 11 min read


Microcontrollers were initially used in devices developed by manufacturing companies and large industrial plants, because prototyping tools and compilers for writing software were very costly. In addition, the requirement to order a large series of OTP (One-Time-Programmable) microcontrollers with a Mask ROM was very expensive and paid off only in large-scale production. Subsequently, the microcontrollers got equipped with Flash memory, which significantly reduced their price and improved functionality, and this meant that they also found their way to the hands of electronics enthusiasts. At that time, it turned out that many devices are easier and cheaper to build with a microcontroller than without it.

  • Basic information about microcontrollers and programming languages
  • IDE – programming environment
  • Assembler
  • C language
  • C++ language
  • Python and MicroPython
  • Java
  • Other programming languages

Microcontrollers, microprocessors and programming languages

At the beginning of the development of the technology described here, it was difficult to say clearly what is a microcontroller and what is a microprocessor. Today, we know that the microcontroller, in addition to the processor (CPU), contains non-volatile program memory and a number of peripheral circuits that allow it to exchange data with the environment. However, at first, microcontrollers had only I/O ports and little static RAM, while the memory that contained the program code and most peripheral circuits were included as external chips. Today, we know that a microprocessor has one or more CPUs, while a microcontroller has an entire ecosystem that is open to external circuits, but even without them, it can operate properly when the power is connected. To put it simple, it can be said that a microcontroller is a tiny microcomputer in a single housing, able to perform complex tasks in our device.

Since microcontrollers first appeared in popular applications, there has been a discussion about their programming languages. The proponents and opponents of different solutions list various advantages and disadvantages of various languages, and the market subjects them to rigorous verification. Still, it is difficult not to notice that some programming languages and technical solutions that used to be popular some time ago are virtually non-existent today. The Pascal language is a perfect example here – it was once promoted as the best language for learning programming, and today hardly anyone knows and remembers about it.

In our opinion, the discussion about programming languages does not make much sense – every solution has its pros and cons, and it only depends on the skills of the programmer whether they can make the best use of the language and perform the task despite possible imperfections. Programming languages are also a bit like foreign languages. Every new language opens up a new perspective and new opportunities for the programmer. If you know many languages, you can choose the most appropriate one for a given application, instead of adjusting your application to the capabilities of the language. Let us briefly look at the most popular languages used for programming microcontrollers.

Programming environment

Modern microcontrollers have much greater capabilities than many of the older, flagship microprocessors used in personal computers. As a result, tools for creating software for microcontrollers have also improved significantly over the years. Nowadays, these are not only language compilers, but entire integrated programming environments, called IDEs. In fact, it can be stated that the popularity of a programming language depends largely on the IDE and the possibilities it offers.

For many developers, Visual Studio Code is their favourite IDE. Versions for Windows and Linux are available, so those in favour of open source software can also use it. Visual Studio Code is an editor, to which tools are attached as modules. Depending on the type of microcontroller, you can find so many options that it is not the availability of those components but choosing the right one that might prove to be a challenge. These are, for example, modules that facilitate orientation in the program code by highlighting keywords and changing their colour, modules that allow debugging (running the program) using a simulator and "in circuit" (in the microcontroller soldered on the PCB), version control and many, many more. And, most importantly, Visual Studio Code is not tied to any specific programming language, so among the modules you can find numerous compilers of different languages for different families of microcontrollers and microprocessors. You can even find a compiler and bootloader for Arduino, which are a bit more complicated to use and therefore designed for more advanced users, but they provide mechanisms that, according to many users, exceed the IDEs available from Arduino and are much more similar to what can be found in professional work.

It can be said that modern programming environments are very similar to each other. In most cases, there is no problem with "changing" from one environment to another, as it was a dozen or so years ago. The functionalities and method of operation of individual IDEs are similar, regardless of the selected microcontroller model, because they, too, differ less and less in terms of capabilities and resources (this does not apply to the number of bits per word, of course).

Finally, it is worth mentioning that creating programs for microcontrollers is significantly different from creating programs for PCs. This is because they have much smaller working memory resources, and their core offers a much lower performance. Modern language compilers that are available for microcontrollers allow for rather strenuous, PC-like transformations of variables, but they are not as quick as in the PC. When creating an application for a microcontroller, you must always be aware that certain actions will be performed at a lower speed – operations on floating-point numbers consume a large part of the scarce resources and are performed much slower than using the FPU available in most of the modern microprocessors.

Assembler

Those who are professionally involved in programming microcontrollers should know at least the basics of the simplest programming language, i.e. the assembler. There is a certain trap in the word "simplest", because even though individual commands carry a very simplified functionality, there are many of them, and you need to be able to compile some more complex commands to carry out sequences of simple operations. At the same time, it forces the programmer to know the device well, because there are significant differences depending on the type of microcontroller, which makes it difficult to control it. However, with a good knowledge of the assembler, you can understand how the microcontroller works and in this way make the best use of its capabilities. The assembly language will also be useful for those using other codes, for instance to make some modules or inserts in the code of higher-level languages if some functions are missing or the program needs to be optimised.

Why is it worth mastering the assembly language?

It is a fact that when writing in a high-level language, we do not have to know the assembler, and it may well happen that there will never be a need to reach for it and make "inserts", but without knowing it it is difficult to understand what is hidden, for example, under the multiplication of variables or addressing and reading those lying in the external address space of the microcontroller. It is also difficult to imagine how many operations an 8-bit core must perform if we use variable numbers with a length of, for example, 64 bits in our program, or to understand what a bit or byte is and what their meaning is in the register or memory of a microcontroller.

Assemblers are very simple programming languages. Some say that only the elite of programmers can write programs using the assembler, but it is not true. Anyone who takes the time to understand how the microcontroller core works can learn assembly programming. In fact, this (i.e. the need to understand the principles of operation of CPUs and peripheral modules) might be the main reason for which the assembler is considered difficult.

Assembler and code redundancy

The assembler is an elementary language. Each high-level language, for example C, is translated during compilation into the assembler’s commands. In addition, individual high-level language commands must communicate with each other, exchanging constants and variables in the form of calculation results or tables. This means that the author of the compiler must create certain data exchange mechanisms, as well as allocate part of the operating memory to the system stack and other mechanisms. Therefore, every high-level programming language has some code redundancy. The easier it is to use a given high-level language, the greater this redundancy becomes.

The assembler itself does not add commands that are redundant. They can only be added by the author of the program, which means that the programmer can influence the implementation time of a given process and to determine its length very precisely. This is the main reason why it is worth using assemblers when handling tasks that must be performed in real time. Sometimes, you can hear the opinion that the assembler allows you to really control the microcontroller. Indeed, the implementation times of functions or subroutines may be the shortest, and the programmer may affect the final placement of variables in memory. The assembler gives us a practical opportunity that will either be used or not, because it largely depends on the skills of the programmer.

Combining a high-level language with the assembler

The assembler is a rather challenging tool in the implementation of the user interface, which in most applications takes up at least 70% of the code. As the programming practice shows, as the programmer gains experience programming in the assembler, after some time they can perform a certain number of ready-made functions for operating displays, keyboards and various ways of signalling the device status, but they will always spend the most time on the user interface. Therefore, it is worth combining a high-level language and the assembler: the former gives you the opportunity to quickly implement and change the user interface, the latter allows you to handle tasks in real time. Complicated as it may seem, in fact, combining programming languages is easy to master, and the benefits are obvious. For the user, when entering the settings, it does not matter whether the function supporting the key reading takes up ten or fifty machine cycles – they will not notice it anyway, but for the programmer shortening the application implementation time is usually something that is worth the effort.

Sometimes, however, for various reasons, it is not possible to combine a high-level language with the assembler. The memory size of the microcontroller program may be such a reason. Any high-level language, depending on the set level of optimisation of the output code, the quality of the compiler and the task being performed, creates a binary output code with an average size that is twice as large as a functionally comparable program coded using the assembler. Of course, for typical applications, this does not matter much, because the prices of microcontrollers with different program memory sizes are very similar. However, sometimes changing the size of the memory is associated with the need to replace the microcontroller or change the supplier, and this can be a hassle.

Other disadvantages of the assembler

Assemblers have one more downside. Programs coded using the assembler are very difficult to transfer to another microcontroller model, even with the same core, not to mention when the core does not differ in word length, but is different in terms of hardware design or architecture (e.g. programs written for 8051 on AVR and vice versa). Very often, this requires not only changing the structure of the program, but also a completely different approach to the issue or dividing a single instruction into a number of partial steps. What is more, the assembler is very strongly related to the device, and therefore each core has its own assembly language. They are often very similar, but still different, because the modes and ways of addressing data by the CPU, the number of bits available in the status registers, and the peripherals cooperating with the core are different.

For many hobbyists, the implementation time of the application does not matter. If something is done with passion, for oneself, then the economic aspect is less important. What counts is the challenge of finding a solution to the problem, not the time devoted to the device. However, for professionals, where the time from the idea to its implementation counts, the assembler can often be the worst choice, because the time needed to run the program and find errors can be very long. Plus, the more complex the task is, the more time is needed.

C language

A bit more than just a decade ago, the C language was extremely popular among microcontroller programmers. However, the development of technology and the availability of increasingly larger and cheaper hardware have brought its primacy to an end, turning it into the progenitor for other solutions, which will be discussed below.

As a high-level language, C does not have the two most serious drawbacks of the assembler. Firstly, programs written in the C language can be transferred, to a certain extent and under certain conditions, between microcontrollers, even ones with different architectures. Secondly, C programming does not require the programmer to have a very good knowledge of the microcontroller architecture and thanks to that it is much easier to analyse and find errors. C compilers are available on many platforms, for example running under Windows or Linux, and thus the language itself is known to a much wider group of programmers than assemblers, which requires in-depth knowledge of the equipment – learning a new dialect of the language requires only a change in habits related to the management of lean resources.

A program written using the C language is easier to read than its assembly language counterpart. This is the case, for example, because the addition of two 32-bit numbers can be included in a single line of code (including the declaration of variables), while the same operation in the assembler, if we are dealing with an 8-bit core, requires several lines of code and taking into account the carry bits. Moreover, when using the C language, the programmer gets a great tool, namely the pointers. They are not available in assemblers, and they make work much easier for a skilled programmer.

The C language syntax is much easier to understand and analyse compared to that of the assemblers, as most of it is not dependent on the type of microcontroller. Most importantly, many popular programming languages are based on C, so learning C can be an introduction to mastering and understanding other compilers. Is it definitely worth knowing the C language, or maybe C++?

C++ language

The C++ compiler is an extension of the C compiler. Both languages have a very similar syntax, but C++ includes certain extensions resulting from additional functionalities. The most important difference between C and C++ is that the former one is a procedural language, and the latter one is an object-oriented language. It should be highlighted here that it is not only a matter of the words used to define compilers, but one of the most important differences between those programming methods. C and C++ differ in the "philosophy" of programming and require different methods of organising the source code, data structures and accessing them. It is worth noting that you can use the C++ compiler to create a procedural code, i.e. the same as in C, but the C compiler does not allow the use of object-oriented methods.

At this stage, it is sufficient to know that C++ has introduced an extension of the capabilities of the C language and some mechanisms that are not available in the latter. It is also worth knowing that thanks to object-oriented programming, it is much easier to divide work within the team of programmers, so C++ will be an obvious choice for multi-level projects requiring extensive coding.

The purpose of this text is not to teach programming, but only to present a certain spectrum of available methods and solutions, and therefore we will not discuss the differences in detail. However, if we could suggest something to those taking their first steps in programming microcontrollers, it is definitely worth learning C++, because it is a very popular language, available for various platforms and operating systems, so you don’t have to be afraid that you will get stuck with a specific manufacturer or equipment.

Python and MicroPython

In recent years, the Python language has been gaining more and more popularity. Initially, it was available as one of the languages for programming personal computers, but then it has also started to be used in popular applications. MicroPython is one of the variants of Python, designed for microcontrollers. As such, it needs only minimum hardware resources.

Python is an interpreted language, just like good old Basic. This means that commands are retrieved and processed in real time, and the source code is not compiled before running. The Python variants designed for microcontrollers are optimised for operation with scarce resources, although some chips known as microcontrollers have much more to offer than early PCs, such as the IBM XT.

Python is a very versatile programming language. It is used for a variety of applications: controlling models, robots, Internet of Things or smart building devices, devices connecting via a mobile phone network or the Internet. MicroPython can also be used for programming microcomputers based on ESP8266, ESP32, popular Arduino, Raspberry Pi, Raspberry Pi Pico, and others. Due to the fact that it is an interpreter, not a compiler, it can interact with a microcontroller. This feature was available in older models of personal computers programmed with Basic. It was enough to enter the command using the keyboard, and it was executed immediately. The same interaction is provided by the MicroPython interpreter, which makes it easier to test the application and the environment cooperating with it. MicroPython also has powerful support in the form of many ready-made libraries and extensions that make it easier to perform various tasks, from sensor support to communication with peripheral devices.

MicroPython includes many standard modules, but there are not as many of them as in the standard Python implementation. The full version of this language has a huge ecosystem of libraries and modules, which allows developers to use its rich functionality and tools. Also, some advanced dynamic features may be limited or unavailable in MicroPython.

Java

Like Python, Java has been adapted from traditional personal computers and web applications. In the world of such computers, you can find both Java interpreters and compilers. For the needs of microcontrollers, the Java ME (Java Micro Edition) language was developed, which is a variant of Java adapted to work with more limited resources than in the case of a personal computer or server.

Java ME is a subset of the Java SE environment designed specifically for embedded and mobile devices with limited resources, such as mobile phones and mobile devices. Java ME includes some of the Java SE functionalities, but also provides new APIs specific to embedded devices, such as Bluetooth connectivity, location services, sensor APIs, and more. However, due to the limitations of these devices, Java ME has some limitations, too, when compared to Java SE. For example, Java ME has a smaller API set than Java SE, and it does not support the full Java language specification.

Java is a universal language that can be used for various applications, including programming microcontrollers and single-board computers (SBCs). Java is popular in applications for embedded computers due to the ease of migration between platforms, reliability, functional flexibility and easy code modification. The Java ecosystem provides many tools, libraries, and frameworks that help developers create powerful and reliable embedded system applications. Naturally, Java ME is a little more modest than Java SE. Nevertheless, it provides a lightweight and efficient runtime environment, optimised for small, limited resources of devices.

Other programming languages – for whom?

Those who program microcontrollers know well that in addition to the programming languages listed above, there are many more available. Unfortunately, due to the technical progress and the development of applications that require connecting with the use of various interfaces, languages modelled on Basic have almost completely died out. Basic is very easy to learn, some of its varieties have many great, ready-to-use functions, but currently its applications do not seem to go beyond a hobby or education in primary schools. Python, C, C++ and Java are definitely the most popular languages among developers, and it does not seem that this situation will change in the near future. Once, programming methods based on flowcharts seemed to be the future, but, just like Basic, they ended up in schools, where they are used to teach programming, but they are no longer used when creating professional applications.

To understand why this happens, you need to separate two worlds from each other. One of them is the world of professional applications. Developing them requires the work of a team, and even if it seems to us that we have written the program ourselves, there is a huge number of function libraries underneath, supported by the object-oriented or modular structure of our code. Nowadays, when time to market counts, no one creates, for example, libraries to support FLASH memory, if the latter was made available by the manufacturer of the device. Thus, a professional programmer is in fact condemned to teamwork, even if they do not know the members of their team. They also need object-oriented programming to replace methods that are inconvenient for them in a given library, add variables or classes, etc. The other world comprises applications created for satisfaction, for pleasure, for fun. No one is running out of time here, they are often created only to achieve a certain result. A hobbyist can afford to create libraries on their own and no one will count the hours and money. Of course, a hobbyist can also work in a team or use ready-made solutions, but this is not a priority. Thanks to that, a hobbyist can use less popular tools, create programs in Basic, Pascal and other niche programming languages today.

The next revolution in microcontroller programming will probably be caused by technologies related to artificial intelligence. Perhaps the role of a programmer (like that of a present-day graphic designer, who has turned to control algorithms that generate images), will be to accurately describe the functionality of the program, and the appropriate AI algorithm will generate the code and compile it. For now, it is still the thing from the future and a topic for a separate article, but some hobbyists already use artificial intelligence to create simple code.

Transfer Multisort Elektronik (TME) is one of the world’s largest global distributors of electronic components, electrotechnical parts, workshop equipment, and industrial automation. The catalog includes over 1,500,000 products from 1,300 leading manufacturers. TME’s modern logistics centers in Łódź and Rzgów (Poland), with a combined area of over 40,000 m², ship nearly 6,000 packages daily to customers in more than 150 countries.

TME also invests in the development of knowledge and skills of young engineers and electronics enthusiasts through the TME Education project, and supports the tech community by organizing the TechMasterEvent series, promoting innovation and experience exchange.