By default, statechart models in YAKINDU Statechart Tools (now itemis CREATE) are platform-agnostic. There are no assumptions made about any target platform. Thus the code is platform-independent, and the generated C or C++ code can be used on any microcontroller.
However, it requires some amount of manual glue code. With the “Deep C and C++” integration, which is available in the Professional Edition of YAKINDU Statechart Tools (now itemis CREATE), things get more specific. Definitions of C or C++ types, variables, and functions can directly be used within statechart models. In some cases it makes much sense to become even more specific and to consider the concrete, present microcontroller platform. This would save manual work and speed up the development cycles.
On one hand, platform-independent development has big advantages, because the software development tool does not need to support each and every microcontroller specifically. Thus the developer can choose freely the hardware platform that is most suitable for his application. Another big advantage is that you can switch to another target platform without changing your statechart model. The generated code is platform-independent – you only have to revise your hardware drivers.
On the other hand, platform-specific support for individual microcontrollers might be the better choice for an efficient and faster development process. This is the case if you want to develop very close to the hardware without using any hardware abstractions. SCT µConnect can accelerate your rapid-prototype development. This can be achieved with platform-specific code generators, provided by SCT µConnect. In the course of this YAKINDU Labs project, we developed specific code generators that are tuned to handle interrupts. Source code for interrupt service routines and interrupt handling are fully generated. Additionally, you get interfaces to control them. Another feature will be the support of our SW timer.
Developing embedded systems is technically sophisticated and can get very complex.
The picture above represents our goal: We want to provide platform support for your microcontroller. An important part of this is to provide the state machine with input events and interrupts. Communication protocols like I2C, SPI, or UART will also be integrated. Output events can be controlled via deep C integration or by annotated events. Additionally, software components, like the sc_timer_service, can be managed and adjusted within YAKINDU Statechart Tools (now itemis CREATE).
These capabilities can be very useful to speed up your development process. For example, imagine an embedded system requiring the following functionalities: Sensor data must be read, and actuators must be controlled with a high accuracy. Measurement data must be gathered, stored, analyzed and transmitted. Additionally, the microcontroller needs to communicate with peripheral hardware or other systems over I2C, SPI, or UART. And while executing all these tasks, power consumption must be as low as possible. All requirements should be managed and scheduled.
Our first goal is to generically support interrupts, e.g. timer, ADC, WDT, external events, communication, SW timer, and so on. We are working on a robust software architecture that handles interrupt and exception handling generically, in order to create a higher software abstraction level. This will enable handling different microcontrollers all in the same way, and it will accelerate your development process, especially if you are doing rapid prototyping. Switching the target platform is still easily possible.
In the end, we want to support your platform’s features directly in YSCT.
For a first prototype, we’ve started with these four microcontrollers:
As mentioned before, we want to react to interrupts directly within the code generated by YAKINDU Statechart Tools (now itemis CREATE). Our goal is that, whenever an interrupt occurs, it immediately causes an appropriate input event to be raised in the state machine. In order to achieve that, we need to combine interrupts with a statechart’s input event. In the end we will have a generated interface that can be used for handling interrupts in the generated code.
We’ve started supporting the following interrupts:
You can define your target platform by using annotations. I’ll be showing below this by the example of Arduino UNO. Within a statechart’s definition section, it is possible to annotate an input event with an interrupt vector. If one of these interrupts occurs, the input event will automatically be raised. Annotated input event can still be used as usual in the statechart.
Let’s begin with a simple sample project: two buttons (external interrupts), one timer and the ADC (internal temperature sensor). You can see the annotated events in the definition section.
After initialisation, each time an interrupt of TIMER1_COMPA occurs, the onboard LED will get switched on or off, respectively. If button1 is pressed, an INT0 interrupt occurs, and the ADC will be enabled. After finishing temperature sampling, the ADC will raise an interrupt, which will also lead to the adc input event. Then the ADC value will be read. After that, the second button can be pressed to restart the LED blinking sequence and the waiting for a button 1 press.
SCT µConnect will generate three additional files. The main functionality is contained in InterruptHandler.c. The code in this file raises the input event after an interrupt has occurred. Here’s an excerpt from the generated source code, including the generated interrupt service routines:
void example_InterruptHandler(Example* handle){
switch (im.vector_flag){
case ADC_vect_num: {
exampleIface_raise_adc(handle);
im.vector_flag = 0;
break;
}
case INT0_vect_num: {
exampleIface_raise_button1(handle);
im.vector_flag = 0;
break;
}
default: {break;}
}
}
ISR (ADC_vect){
im.vector_flag = ADC_vect_num;
example_ADC_vect_num_ISR(&im);
}
ISR (INT0_vect){
im.vector_flag = INT0_vect_num;
example_INT0_vect_num_ISR(&im);
}
You can use the vect_num_ISR(&im) functions to implement any specific code, which can additionally be executed within the ISR.
Your main loop may look like this:
Example sc;
void setup()
{
example_init(&sc);
example_enter(&sc);
set_sleep_mode(SLEEP_MODE_IDLE);
sleep_enable();
}
void loop()
{
example_InterruptHandler(&sc);
example_runCycle(&sc);
interrupts();
sleep_cpu();
noInterrupts();
}
With this generated library, you only have to call one function within in your main loop – the InterruptHandler. Within the InterruptHandler, events will be raised when interrupts occur. Additionally, the interrupt service routines are generated. They store the values of the vector within a flag. Interrupts are generally disabled. They are enabled when the microcontroller is set to a sleep mode. This is shown in the following figure.
Thus only one interrupt will be handled in a single cycle. Other interrupt flags will be automatically queued within the microcontroller, and the interrupt with the highest priority will be handled first. In this way, each interrupt is connected to a specific input event and can be used directly in the state machine. You don’t have to define the connection to the state machine any more. The only thing you still need to do are the initialisation and enabling the interrupts. The rest is done by the generated code.