Five Considerations for Microcontroller Embedded Programming

In the process of microcontroller programming, if a designer can master multiple programming languages at the same time, then this designer is definitely a very talented individual. However, mastering assembly, C, and C++ simultaneously is quite difficult, and many beginners encounter numerous obstacles while learning just one of these languages, leading to frustration. This article specifically organizes the opinions of engineers with many years of experience in embedded programming into a guide that provides valuable insights for embedded programming. Friends interested in this topic, come take a look.

In microcontroller embedded programming, the two most challenging aspects are interrupts and memory management (MM). The reason some people find them not difficult is that in most cases, chip manufacturers have already written them directly. However, if the designer works for the chip manufacturer, they must write the configuration files themselves.

The difficulty of these two aspects arises from the need to write in assembly or C-like languages, which are lower-level. Interrupts can be categorized into external and internal interrupts. External interrupts have two implementation modes: hardware interrupt mode and software interrupt mode, which are relatively simple and belong to the application layer. In contrast, internal interrupts are much more complex, mainly occurring due to restarts, bus errors, overflows, checksum errors, etc. Many software developers do not write corresponding interrupt service routines because they are too difficult and generally not needed. However, if they do occur, they can lead to fatal errors. Therefore, from the perspective of overall system robustness, it is necessary to have corresponding ISRs, which is also recommended by experts from Freescale. Below, we will discuss the considerations that should be taken into account in embedded programming.

Delay

Embedded programming often involves hardware operations, such as ADC, turning on or off a current source, which require time. Therefore, when issuing these commands, reading the register values immediately will not yield the desired results, and it may be difficult to identify the cause. Sometimes the required delay is quite long, reaching the millisecond level, while in general, microsecond levels are sufficient, depending on the clock frequency of each chip, not just the MCU’s bus clock frequency.

Variables

Generally, if the scope and lifecycle of a variable are very clear, it should be defined relatively, such as using const, static, etc. This approach reduces the likelihood of errors. It is not advisable to define all variables as global variables, as management becomes cumbersome, and if an error occurs, the damage can be significant. The same applies to functions; global variables and common functions must be declared to minimize errors during calls. Some compilers do not report errors for undeclared functions, but will issue warnings about implicit type conversions when called. This point requires particular caution.

Macro Definitions

During the programming process, specific numbers should be defined as macros whenever possible. This practice has the advantage of being intuitive and easier to maintain later. Otherwise, after a long time, one might forget what that number represents. Macro definitions do not impose any burden on the program since they are replaced during compilation, so they can be widely used. It is worth mentioning that macro definitions are not limited to constants; they can also define functions. Since they are directly replaced, this avoids stack operations and improves program execution efficiency. However, this also increases the code size, so simple functions are generally used. Another drawback is that during the replacement process, parameter types are not checked, which increases security risks. The solution to this problem is to use a feature called inline functions, which inherit the advantages of macro definitions while compensating for their shortcomings. This is the best choice, but it belongs to the C++ domain and has a certain level of difficulty; interested readers can refer to relevant materials.

Floating Point Operations

Most low-end microcontrollers do not support floating-point operations, so they are rarely used in practice. To reduce costs, floating-point operation modules are generally removed, which raises the question: what if floating-point operations are needed? Observant readers may notice that even microcontrollers without floating-point operations can perform calculations using float or double data types during simulation and debugging, and the results are quite accurate. This is because the compiler automatically calls library functions to implement this, usually through iterative methods, which results in very slow execution efficiency, so this method is not recommended. The typical approach is to use fixed-point methods to solve this problem. For example, in a 32-bit data, one can assume the lowest 8 bits are the decimal part, and then perform bit-shifting calculations similar to integer operations. This method is quite complex but can achieve high precision. Another method is to directly scale by powers of ten for integer calculations to obtain approximate values. Therefore, to avoid unnecessary complications, floating-point operations should always be avoided whenever possible.

Watchdog

Taking a triple watchdog as an example, watchdog 1 checks the clock frequency, watchdog 2 monitors a small section of code, which must be fed within a relatively short time, generally required to be fed once between 250us and 650us. Watchdog 3 monitors a larger section of code, requiring to be fed within a longer time, usually within 100ms. All three conditions must be met simultaneously, which requires a clear understanding of the code execution process; otherwise, it may lead to watchdog errors and restarts.

It is important to emphasize that in the process of microcontroller embedded programming, the quality of the program is often determined by the details. Whether a program is written in detail and flexibly is directly proportional to the accumulation of knowledge and practical experience over time. Although programming can be a very tedious and even boring process, the joy of success can make everyone believe that this effort is worthwhile.

Leave a Comment