Technical Article

Automatic Code Generation for Embedded Microcontrollers

October 03, 2021 by Jost Allmeling

Block diagrams are a natural way to model and simulate closed-loop controls. Before digital controls can be used in a power electronics application, a block diagram design must be translated into C code and deployed on an embedded microcontroller. Compared to hand coding, automatic code generation significantly speeds up the implementation, reduces programming errors and leads to better maintainability.

The development of a power electronic system is a multidisciplinary endeavor. It includes not only the design of the power stage, but also the development of the controls, which are often implemented on a microcontroller. Few developers are equally skilled in all disciplines to design every part of the system with the same high level of quality. Electrical engineers, like myself, are usually not professional software developers by education. During their studies, most of them have developed small programs in C or in scripting languages, but only few were involved in large software projects and have learned to follow the principles of structured programming.

In their jobs, however, electrical engineers are often assigned the task of programming embedded microcontrollers, and for good reasons: They have experience in how to control a power electronic circuit and they know the requirements of the entire system. Moreover, they have a good understanding of the microcontroller’s on-chip peripherals such as PWM generators and analog-to-digital converters (ADCs). On the downside, for a typical electrical engineer a product development is completed when the code compiles without errors, when the system behaves as expected, and when all tests are passed. Especially with today’s short time-to-market pressures, there is little intrinsic motivation to write code that can be reused and maintained over the entire lifetime of the product.

As a result, the handwritten codebase often lacks modularity, clear structure and proper documentation. For outsiders, it can therefore become difficult to figure out from the code what function it performs. Even the authors of the control code themselves may scratch their heads when revisiting their own code a few years later, wondering what they had in mind when writing it. That is why we should support electrical engineers in what they are good at, namely electronics and control design, and leave the software architecture and the implementation either to experienced software developers or to a computer program that generates the code automatically.

Modeling Controls as Block Diagrams

The most natural way to sketch control systems are block diagrams representing signal flows. They visualize the underlying control concept in a clear and understandable manner. Functional units that belong together can be encapsulated in subsystems in order to hide complexity and create a hierarchical structure. It is not surprising that most simulation software for control systems is based on block diagrams.

While the simulation software PLECS was primarily developed to accelerate the simulation of power electronic circuits, it also provides an extensive collection of signal processing blocks for the design of control systems. The PLECS library includes continuous and discrete transfer functions, discontinuous and non-linear blocks as well as elements frequently required for power electronics applications such as coordinate transformations, phase-locked loops (PLLs) and PID controllers. As an example, Figure 1 shows the block diagram of a current controller used in a solar inverter.

Figure 1. Current controller with resonant integrator and anti-reset windup

Like with similar tools, block diagrams designed in PLECS can be translated into equivalent C code. Figure 2 shows an excerpt from the code generated by PLECS for the controller in Figure 1. The generated code can be compiled and executed on different target systems, such as microcontrollers. Automatically generating control code from a block diagram model, rather than using the model merely for offline simulation and implementing the code by hand, has some significant advantages:

  • The control engineer can focus on functionality rather than its implementation.
  • An experienced embedded software developer is not needed.
  • Changes to the model are possible even late in the development process.
  • The model inherently provides clear and up-to-date documentation.
  • The model and its implementation are always in sync

I do not want to hide the fact that using the model as the sole basis for defining the control code also has some disadvantages:

  • The model must contain extra information about data types, sample rates and execution tasks.
  • Diff tools for tracking changes are still in their infancy when dealing with block diagrams.
  • The user gives up precise control over implementation details and low-level optimizations.
  • Not all of the advanced low-level functionality may be available through model components.

Despite its many benefits, automatic code generation will only be accepted if it accelerates and simplifies the development process right from the start. The time savings, however, depend very much on the toolchain that is being used. At Plexim, we have taken great efforts to make embedded code generation with PLECS and the PLECS Coder an easy and intuitive experience.

Accessing I/O Peripherals

When starting with a microcontroller project, it is usually not the implementation of the control algorithm itself that causes the biggest headache, but configuring and accessing the microcontroller’s I/O peripherals. Before you can begin to generate a PWM and acquire ADC readings inside an interrupt-driven control loop, you need to study technical manuals of more than 1000 pages. Not only do newcomers struggle with such a steep learning curve. Even if you already know the ins and outs of one microcontroller family, you may need to start almost from scratch when dealing with microcontrollers from another manufacturer. Although microcontrollers from competing manufacturers may offer similar peripherals, the naming conventions and implementation details can be quite different. This presents a major obstacle when you want to port your project to a new target microcontroller, especially if the development was made with a vendor-specific toolchain.

Figure 2. A snippet of controller C code generated by PLECS

For an efficient workflow involving automated code generation, it is not sufficient to translate only the target-independent part of the control algorithm from a block diagram to C code and import the generated code into a target-specific software project. This approach would still require the user to write and maintain glue code for addressing the target-specific peripherals, which is a manual and error-prone task. If a fully automated workflow with one-step code generation is desired, all information about the target peripherals must be included in the model. Peripherals can be represented in the model by individual blocks that behave as signal sources or sinks during a simulation. During code generation, these blocks can inject target-specific code into the project to configure the corresponding peripherals and provide data access.

Figure 3. Complete controller model for a solar inverter including trigger and task management

Target Blocks in PLECS

For selected microcontroller families commonly found in power electronic applications, Plexim offers dedicated Target Support Packages (TSPs) to be used in conjunction with PLECS and the PLECS Coder. These TSPs contain a library of target blocks that represent the various on-chip peripherals of the respective microcontroller. Target blocks can be distinguished from other control blocks by their rounded corners.

Each target block has a dialog that allows the user to configure the corresponding hardware peripheral. In case of an analog input, the user can choose the ADC unit, the input channel(s), scale, offset and acquisition time window. As each data acquisition is initiated by an event, such as a timer, the user can also specify a trigger source. Instead of setting individual bits in configuration registers, the user configures the peripherals at a more abstract level using natural language options. Although the options may vary slightly between different microcontroller families and not all hardware resources are available on every chip, the hardware abstraction provided by the target blocks makes it quite easy to port a model to another microcontroller.

Target blocks in PLECS not only provide intuitive access to digital and analog inputs or outputs. They can also combine multiple peripherals to offer more complex functionality, such as peak current control with ramp compensation and leading-edge blanking. Figure 3 shows the complete controller model for a solar inverter, including the target-specific peripheral blocks. The current controller subsystem contains the model of Figure 1.

When looking under the hood of a target block, you will notice two different implementations, as in a configurable subsystem. One implementation is text-based and contains meta code in the Lua scripting language. It is used to generate code for the on-chip peripherals according to the dialog box entries. The other implementation is a PLECS model that emulates the behavior of the hardware peripheral during an offline simulation. In the case of an ADC, the offline model consists of a triggered sample-and-hold element and a quantizer.

Code-generation Subsystem

To make use of the offline models for the I/O peripherals, the controls must be wrapped in a subsystem along with the target blocks. When a target block representing a data source, such as an ADC, is placed in a subsystem, an input terminal will be added to the subsystem block. This input can be connected to a signal source outside the control subsystem, for example to a measurement in the electrical circuit model. Likewise, a target block representing a data endpoint, such as a PWM generator, will create an output terminal at the subsystem. This output can be used as gate signals for semiconductor switches in the power circuit. Grouping the controls in a subsystem does not adversely affect the control code generation, as the PLECS Coder can be told to generate code for a single subsystem only. On the contrary, when following this approach, the control system can be developed and verified against a plant model using offline simulation before the code is deployed to a microcontroller. The fact that the exact same model can be used without modification for both offline simulation and code generation ensures that model and code are always in sync. Figure 4 shows the overall circuit model of the solar inverter, with the controller model from Figure 3 wrapped in a subsystem.\

Event-based execution of control tasks When employing microcontrollers for closed-loop control of power electronic systems, the acquisition of current and voltage measurements is typically synchronized with the PWM generation. Synchronous sampling ensures the acquisition of currents and voltages with the lowest possible switching harmonics. In most digital current control loops, a PWM generator periodically triggers an ADC for sampling the analog measurements. As soon as the ADC has finished converting the sampled quantities, it signals the presence of new digital values by triggering an interrupt. Within the interrupt service routine, the control algorithm is computed using the newly acquired values and an updated duty cycle is passed to the PWM generator. In other configurations, it may be more appropriate to let the PWM generator or an independent timer trigger the interrupt and initiate the computation of the control task.

In PLECS we have introduced a special signal type (depicted as a dashed brown line in Figure 3) to define and visualize the propagation of trigger events. To model the control loop described above, the user first needs to select under which condition the PWM generator should emit a trigger event, for example at counter underflow or overflow. The trigger output of the PWM generator is then connected to the trigger input of the ADC block to control the timing of the acquisition. To indicate that the control task computation is invoked each time after a conversion is finished, the trigger output of the ADC must finally be connected to a special “Control Task Trigger” block.

Multi-tasking Environments

A single control loop is normally not sufficient for the control of inverters and drive systems. Most often you will find a cascaded structure with a fast inner current control loop and a slower outer loop for voltage or speed regulation. While the calculation of the outer loop can take some time, the calculation of the simple current control task is typically fast. However, since the current control is highly dynamic, it must be performed with a higher execution rate. In order to optimally utilize the computing power of the microcontroller, such cascaded control schemes can be implemented with preemptive rate-monotonic multitasking, where the fast current control task interrupts the computation of the slower outer control loop. The control model must be split into multiple tasks with different sample rates and interrupt priorities. In PLECS, this partitioning is performed graphically using frames that each comprise a group of blocks. Each frame references a specific task defined in the Coder settings. If your target is a multi-core processor, you can distribute individual tasks to different cores. In Figure 3, the yellow frames assign separate tasks to the maximum power point tracker (MPP) and the voltage regulator, which operate at lower sampling rates than the rest of the control system.

Figure 4. The power circuit of the solar inverter with controls wrapped in a code-generation subsystem

Blocks that are to be computed in the same task do not necessarily have to be placed together in the same frame. As multiples frames can reference the same task, the assignment of blocks to different tasks does not interfere with the logical structure of the control system. Other embedded code generation tools employ subsystems or special blocks to separate the individual tasks from each other. However, reusing such existing structures for a different purpose might be confusing. On the contrary, the task frames in PLECS let a user immediately recognize a group of blocks sharing an implementation-specific attribute.

State Charts for Supervisory Control

Procedural functionality traditionally performed by Programmable Logic Controllers (PLCs) is not easily represented with block diagrams. To model startup, dc-link pre-charge and error modes, state machines are more appropriate. State machines describe event-driven systems that move from one discrete state to another in response to discrete events. Included in the PLECS library is a state machine block providing a chart editor that lets you graphically create state machines using common concepts such as boxes for states and curved arrows for transitions, and simulate them together with the surrounding block diagram. You can feed continuous or discrete signals into a state machine block to react to external events and output discrete signals from a state machine. Actions are specified in the C programming language and can be associated with states and transitions. Thanks to built-in timer events, state machines are useful for implementing supervisory controls. The PLECS Coder will automatically insert the equivalent C code for a state machine block into the embedded project when generating code for the model.

External Mode

Once the embedded code is generated and uploaded to the target microcontroller, the so-called External Mode can be activated in PLECS to connect the program on the microcontroller with the original PLECS model.

This allows the user to observe real-time data in PLECS and tune parameters such as setpoints. When operating in External Mode, the scopes in the PLECS model are populated with live data from the microcontroller, such as depicted in Figure 5. The user determines interactively how the scope data is updated: continuously or depending on a trigger condition. Real-time data obtained from the microcontroller can be compared with stored results from offline simulations by superimposing the waveforms in a PLECS scope. Thus, the External Mode provides an intuitive way to interact with the microcontroller and to test and debug the control code. Instrumentation in External Mode of course consumes some processing power of the microcontroller and may be limited by the bandwidth of the communication interface.

Figure 5. PLECS Scope for observing real-time data from the microcontroller

Software-in-the-Loop Testing

Another powerful way to verify the control code is software-in-the-loop (SIL) testing. Instead of generating code for a specific microcontroller, the PLECS Coder can be instructed to produce independent C code for a generic target. In this case, the target blocks such as PWM and ADC will not insert code for the peripherals. In an offline simulation, the user can now choose to simulate the generated code in lieu of the original control model. In this “Codegen” simulation mode, the generated code is linked to the offline implementation of the peripherals. If the control subsystem is connected to a plant model as in Figure 4, the complete power electronic system can be simulated. Toggling between the “Normal” and “Codegen” simulation modes allows the user to identify any discrepancies between the original control model and its implementation in C code, such as the discretization of time-continuous control blocks. Further validation and verification may include a hardware-in-the-loop (HIL) simulation of the real microcontroller together with the plant model simulated on a real-time platform such as the RT Box.

Conclusion

PLECS in conjunction with the PLECS Coder lets control engineers not only model and simulate controls for power electronics, but also easily implement those controls on selected microcontrollers. This automatic code generation workflow neither requires special software development skills nor indepth knowledge about the microcontroller peripherals. The iterative development approach using a PLECS model allows a design to evolve from an initial concept to a robust implementation. The PLECS model serves both as the definition and the documentation of the control algorithm. SIL and HIL simulation can be applied to test the generated control code.