For their STM32Fxxx series of Cortex-based MCU, STM had multiple releases of its software libraries. From the original StdPeriphLib 1.0, 1.5, to HAL, and then LL (Low Level driver), these libraries have been nothing more than thin layers on top of accessing the ST’s I/O peripherals. For example, instead of writing:

SPI1->CR1 |= SPI_CR_PE;

You write:

LL_SPI_Enable(SPI1);

Hardly earth-shattering capabilities.

ST then released a GUI tool called ST CubeMX, which generates HAL or LL code based on the features a user visually selects. This has improved the usability, but CubeMX is really slow. Moreover, it still does not provide an API that uses the I/O peripheral as a higher level of abstraction. For example, writing a byte using the SPI still involves individual steps of accessing the low level I/O registers.

ImageCraft’s JumpStart API (JSAPI) is designed with a different set of assumptions: a) the library should be efficiently written, b) the library should expose the “power features” of the MCU as much as possible, and c) the library should provide a functional model of the peripherals, rather than simply just supplying different names to access the I/O registers. (See http://imagecraft.com/technologies/jumpstart-api for further details.)

Smart.IO is a hardware/software technology that lets embedded engineers create app UI without app or wireless coding. To use it, the embedded firmware is built with the “Host Interface Layer”, a set of functions that translate the API into data streams between the host MCU and the Smart.IO hardware, which are connected using the SPI bus. ImageCraft provides reference source code for the Host Interface Layer in Standard C form. To use a different MCU or compiler from the reference ports, the user only needs to modify the hardware-specific portion of the code.

The original Host Interface Layer was written using JumpStart API. Recently, we ported the code to use ST’s LL driver, to use as a reference for using Smart.IO with different hardware or software platforms. This blog post compares using JSAPI vs the ST LL library in this scenario. The LL driver code is generated using ST CubeMX V4.25.0, Cube V1.0.

For the following Smart.IO Host Interface Code example, the target MCU is the STM32F411RE using the ST-Nucleo-411RE. (The example can be modified easily to run on other STM32Fxxx series.)

Briefly, the tasks are to port the code that:
  • Initializes the system clock
  • Initializes the hardware modules
  • Writes interface routines for communication between the systems

The hardware components that need to be initialized are:
  • Port C, pin 7, used as Smart.IO reset signal
  • Port A, pin 9, used as interrupt pin to the host MCU
  • Port B, pin 6, used as chip select for the SPI interface
  • SPI unit for data communication between the host MCU and the Smart.IO module
  • USART2 for using the ST-Nucleo virtual com port for debug messages and I/O

NOTE: JumpStart API is documented in browseable Doxygen format. See https://imagecraft.com/documentation/jumpstart-c-documentation

System Clock Setup
ST Cortex-based MCUs have elaborate “clock trees”, generating different clock speeds for different peripheral buses. Without some kind of easy-to-use API or GUI tools, it’s quite difficult to get things right.

JumpStart API:
jsapi_clock.SetSystemClock(16, 0, false, 84, 5);

This call specifies that the HSI clock is 16 MHz, the HSE Bypass is not used (the third argument), the PLL target speed is 84 (MHz), and the number of wait states is 5.

ST code:
SystemClock_Config();

The CubeMX GUI does the heavy lifting here, and generates the needed low-level code. Note that while you can modify the generated code, if you need to change the parameters - for example, using the external clock HSE instead of HSI - you will have to change the GUI selections and regenerate the code. As CubeMX is quite bloated and slow, these sorts of changes are tedious and slow.

With JSAPI, changing to use HSE involves changing the call to something like:
jsapi_clock.SetSystemClock(0, 8, false, 84, 5);

So, JSAPI is just as simple to use, AND also allows easy modifications.

GPIO Port C and Port B Setup

JumpStart API:
portc.MakeOutput(7, OSPEED_LOW);

PORTB pin 6 initialization is done as part of the SPI setup (see next blog post.)

ST code:
/* PORTC.7 --> SMART.IO RESET */
GPIO_InitStruct.Pin = LL_GPIO_PIN_7;
GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(GPIOC, &GPIO_InitStruct);

/* PORTB.6 --> SPI nCS */
GPIO_InitStruct.Pin = LL_GPIO_PIN_6;
GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);

Here you see a fundamental design difference between the two approaches: JSAPI uses function calls, sometimes multiples of them, to set the attributes of a hardware abstraction object. For example, the JSAPI_GPIO::MakeOutput function declares that the pin of the port is an output GPIO pin, and also declares other attributes such as I/O pin change frequency, push/pull property etc. are done using different API calls. Like other JSAPI functions, JSAPI_GPIO::MakeOutput sets the defaults that are common to the base function, so often no additional call is needed. For example, an output is set to push/pull (rather than the less common open drain), with no pull up or down internal resistor, and low speed.

Contrast these with the ST philosophy: an initialization structure (“GPIO_InitStruct_Typedef”) is created solely to store initialization parameters. The user has to familiarize themself with this otherwise useless structure, and also needs to initialize the fields themselves.

Note also ST's use of names instead of constants. While this arguably is a good programming practice, the name hides too much detail. For example, is LL_GPIO_PIN_7 a pin number, i.e. 7, or is it a bitmask for bit number 7, i.e. 0x80? This is important to know, because later on, if your firmware needs to work with the low level I/O register directly, using LL_GPIO_PIN_7 incorrectly would cause problems.

This should give you a flavor of the differences between JumpStart API and the ST LL library. In the next blog post, we will talk about how to set up for EXTI and more.