semf
Stm32

General

In the following steps we create a very simple project using the semf library. We assume you already set up a microcontroller project which uses an Eclipse based IDE. We set this up for an STM32F4 microcontroller and used STM32CUBEMX / STM32CUBEIDE for hardware initialization.

C++ Project

Convert your project to a C++ project by a right mouse click -> Convert to C++. Copy the semf library to a new 'semf' folder in the root directory of your project.

Library Linkage

Link the semf library to your project by adding the library and include path and the library itself. You can find these settings in Project -> Settings -> C/C++ General -> Paths and Symbols. Make sure you add both to the C++ language and all configurations:

  1. Go to Includes and add the semf library path.
  2. Go to Library Paths and add the semf library path.
  3. Go to Libraries and add the name of the semf library file with a „:“ in front of the name (e.g. :semf_cortex-m4_fpv4-sp-d16.a).

STM32 Systick

Setting up a new project with STM32CUBEMX / STM32CUBEIDE the systick callback is never called.

We add this callback in the stm32f4xx_it.c file.

void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
HAL_SYSTICK_Callback(); // this line has to be added
/* USER CODE END SysTick_IRQn 1 */
}

C to Cpp Bridge

maincpp.h

Create the following maincpp.h file in the same directory as the main.h.

#ifndef CPPMAIN_H_
#define CPPMAIN_H_
void cppInit(); // Board support package and application initialization.
void cppMainLoop(); // Application loop.
#endif /* CPPMAIN_H_ */

maincpp.cpp

Create the following maincpp.cpp file in the same directory as the main.c.

#include "bsp.h"
#include "app.h" // include your application
extern "C" {
/* Cpp init*/
void cppInit()
{
static msp::Msp myMsp;
static bsp::Bsp myBsp(myMsp);
static app::App myApp(myBsp);
msp.init();
}
/* Main-loop for cpp code*/
void cppMainLoop()
{
app.mainLoop();
}
}

main.c Changes

Add following code in the main.c to enter the cpp part of the project.

// ...
#include "maincpp.h"
// ...
// ...
int main(void)
{
// ...
cppInit();
while (1)
{
cppMainLoop();
// ...
}
}

Microcontroller and Board Support Package

semf makes applications hardware independent. Somewhere in the project we need to define all the hardware dependent stuff. To do that, let’s create a Msp and Bsp class.

In the following example we make the semf::app::Timer, one semf::Gpio and a semf::AnalogIn (adc) accessable from the application.

msp.h

Create the following msp.h file in the same directory as the main.h.

#ifndef MSP_H_
#define MSP_H_
#include "main.h" // For having the hardware handlers available
#include "stm32f4xx_hal.h" // For having access to the microcontroller hardware
namespace msp
{
class Msp
{
public:
Msp();
explicit Msp(const Msp& other) = delete;
~Msp() = default;
semf::Gpio& gpio1();
semf::Timer& systick1ms();
semf::AnalogIn& analogIn1();
void init();
private:
semf::Stm32F4Gpio m_gpio1;
semf::Stm32F4Systick m_systick1ms;
semf::Stm32F4AnalogIn m_analogIn1;
};
} // namespace amsp
#endif /* MSP_H_ */
Interface for using ADC (Analog to Digital Conversion) hardware in interrupt mode.
Definition: analogin.h:27
Interface class for using a GPIO pin of the microcontroller.
Definition: gpio.h:23

msp.cpp

Create the following msp.cpp file in the same directory as the main.c.

#include "msp.h"
extern ADC_HandleTypeDef hadc1;
namespace msp
{
Msp::Msp()
:m_gpio1(B1_GPIO_Port, B1_Pin),
m_analogIn1(hadc1)
{
}
semf::Gpio& Msp::gpio1()
{
return m_gpio1;
}
semf::Timer& Msp::systick1ms()
{
return semf::Stm32F4Systick::instance();
}
semf::AnalogIn& Msp::analogIn1()
{
return m_analogIn1;
}
void Msp::init()
{
// Do something ...
}
} // namespace bsp

bsp.h

Create the following bsp.h file in the same directory as the main.h.

#ifndef BSP_H_
#define BSP_H_
#include "msp.h"
namespace bsp
{
class Bsp
{
public:
explicit Bsp(msp::Msp& myMsp);
explicit Bsp(const Bsp& other) = delete;
~Bsp() = default;
semf::TimeBase& timeBase1ms();
semf::DigitalOut& digitalOut1();
void init();
private:
semf::TimeBase m_timeBase1ms;
semf::DigitalOut m_digitalOut1;
};
} // namespace bsp
#endif /* BSP_H_ */
Class for handling a digital output.
Definition: digitalout.h:22
A TimeBase is the bridge between e.g. a hardware timer (interrupt service routine) and TickReceiver o...
Definition: timebase.h:24

bsp.cpp

Create the following bsp.cpp file in the same directory as the main.c.

#include "bsp.h"
namespace bsp
{
Bsp::Bsp(msp::Msp& myMsp)
:m_timeBase1ms(myMsp.systick()),
m_digitalOut1(myMsp.gpio1)
{
}
semf::TimeBase& Bsp::timeBase1ms()
{
return m_timeBase1ms;
}
semf::DigitalOut& Bsp::digitalOut1()
{
return m_digitalOut1;
}
void Bsp::init()
{
// Do something ...
}
} // namespace bsp

Application

Now we can set up our micro application.

app.h

Create the following app.h file in the same directory as the main.h.

#ifndef APP_H_
#define APP_H_
#include "bsp.h"
namespace app
{
class App
{
public:
explicit App(bsp::Bsp& myBsp);
explicit App(const App& other) = delete;
~App() = default;
void init();
void mainLoop();
private:
void onTimeout();
SEMF_SLOT(m_onTimeoutSlot, App, *this, onTimeout);
semf::app::DigitalOut& m_digitalOut;
};
} // namespace app
#endif /* APP_H_ */
Software SoftwareTimer which can be added to a TimeBase or used singular.
Definition: softwaretimer.h:29
Class for handling a digital output.
Definition: digitalout.h:24
#define SEMF_SLOT(name, className, object, function,...)
Creates a semf slot.
Definition: slot.h:30

app.cpp

Create the following app.cpp file in the same directory as the main.c.

#include "app.h"
namespace app
{
App::App(bsp::Bsp& myBsp)
:m_swTimer(myBsp.timeBase1ms()),
m_digitalOut(myBsp.digitalOut1())
{
}
void App::init()
{
// do something
}
void App::mainLoop()
{
// do something
}
void App::onTimeout()
{
m_digitalOut.toggle();
}
} // namespace app