CSS Animations

This post is supposed to be a beginners introduction to CSS 3 animations. There are two ways for animation in CSS 3, animations and transitions.

Differences between Animations and Transitions

This article sums it up nicely: https://www.peachpit.com/articles/article.aspx?p=2300569&seqNum=2

In general transitions are defined by a start and an end state. Animations can have an arbitrary amount or keyframes in between a start and a end state. Transitions are therefore suited for simpler use-cases, whereas animations are used when the requirement is complex.



The main use-case for animations is

For animations, a trigger is optional. A animation can start without a trigger for example right after the page loads.

Animations can be created via .css files or via the element.animate() javascript WebAPI (https://developer.mozilla.org/en-US/docs/Web/API/Element/animate).



The main use-case for transitions is highlighting items such as navigation elements on hover. Instead of instantly switching the background color of such a navigation element to a darker shade, the transition can smoothly transition the color to the darker shade which gives the page a more relaxed and more organic feel.

A transition needs a trigger to run. This trigger can be the change of a CSS property or some JavaScript.

Transitions are added to CSS classes. A transition lists the properties of the same CSS class, that should be changed in a smooth animation. A animation always need a new value for a property so that a property can be animated as it transitions from the current value to the new value. The new value is not defined by the transition, in other words is not predefined. When a property, that is defined in the list of transitions, changes either by adding or removing a css class or using javascript, then the transition is triggered and will be applied so that the value is smoothly interpolated between the current and the new value.

Transitions are controlled using the transition-properties inside a CSS class. The transition properties are: transition-propertytransition-durationtransition-timing-function and transition-delay.

A shorthand notation is available that combines all properties above into a single line: transition: <property> <duration> <timing-function> <delay>;

STM32F103C8T6 with Eclipse (OpenSTM32) under Mac OSX


The System Workbench for STM32 (SW4STM32) aka. OpenSTM32 aka. AC6 Tools is a set of plugins for the popular Eclipse IDE. There are two ways to install OpenSTM32. The first is to add the plugins to an existing installation of Eclipse, the other is to download a flavor of the Eclipse IDE from the ST homepage which has all the plugins preinstalled. This article explains what my experience was installing the preconfigured Eclipse on a MAC including debugging the program on a bluepill with openocd, the arm-none-eabi-gdb and a ST Link v2 programmer.

I used this article as a foundation for my article.


A word of advice to everybody wanting to use OpenSTM32 on a mac. Make absolutly sure that you are using the latest software packages available. It will not work with outdated software! There is absolutely no backwards compatibility. If your versions are off, the tools just do not work together. They just fail without usefull error messages!

Use the latest version of

Download and Install OpenSTM32

wget --no-check-certificate http://www.ac6-tools.com/downloads/SW4STM32/install_sw4stm32_macos_64bits-v2.9.run
chmod +x install_sw4stm32_macos_64bits-v2.9.run
Error: Unrecognized option: -d64

If you get this error:

Unrecognized option: -d64
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

The solution is to run the installer with root priviledges.

sudo ./install_sw4stm32_macos_64bits-v2.9.run -m -f
Opening the OpenSTM32
open -a /Applications/Ac6/SystemWorkbench.app
Creating a Project

Create a C project as outlined here.

In Eclipse, select File > New > Project > C Project

As Project Type: Empty Project

As Toolchain: Ac6 STM32 MCU GCC

Keep Debug and Release builds checked.

In the wizard step “Target Configuration” switch to the MCU Tab, select

Series: STM32F1

Mcu: STM32F103C8Tx

In the Firmware Configuration Dialog, choose Hardware Abstraction Layer (Cube HAL). The Standard Peripheral Library (StdPeriph) is discontinued and I was not able to get the StdPeriph working.

Error: The Project Importer fails to install

If you get this error:

'tar' failed to extract /Applications/Ac6/SystemWorkbench.app/Contents/Eclipse/plugins/fr.ac6.mcu.ide_2.9.0.201904120827/resources/project_importer/macosx/ProjectImporter.app.tar.bz2 to /Applications/Ac6/SystemWorkbench.app/Contents/Applications

the solution is to manually untar the file:

cd /Applications/Ac6/SystemWorkbench.app/Contents/Eclipse/plugins/fr.ac6.mcu.ide_2.9.0.201904120827/resources/project_importer/macosx
sudo bzip2 -d ProjectImporter.app.tar.bz2
sudo tar xopf ProjectImporter.app.tar
sudo cp -R ProjectImporter.app /Applications/Ac6/SystemWorkbench.app/Contents/Applications
Error: The compiler fails to install
Failed to extract '/Applications/Ac6/SystemWorkbench.app/Contents/Eclipse/plugins/fr.ac6.mcu.externaltools.arm-none.macos64_1.17.0.201812190825/tools/st-gnu-arm-gcc-7-2018-q2-update_gdb-5_4-2016q3-mac.tar.bz2' to '/Applications/Ac6/SystemWorkbench.app/Contents/Eclipse/plugins/fr.ac6.mcu.externaltools.arm-none.macos64_1.17.0.201812190825/tools/compiler' (tar returned '1')

The solution is to install the compiler manually:

cd /Applications/Ac6/SystemWorkbench.app/Contents/Eclipse/plugins/fr.ac6.mcu.externaltools.arm-none.macos64_1.17.0.201812190825/tools
sudo bzip2 -d st-gnu-arm-gcc-7-2018-q2-update_gdb-5_4-2016q3-mac.tar.bz2
sudo tar xopf st-gnu-arm-gcc-7-2018-q2-update_gdb-5_4-2016q3-mac.tar
sudo ln -s ./st-gnu-arm-gcc-7-2018-q2-update_gdb-5_4-2016q3-mac ./compiler
Write a small C Program

In order to test debugging you need a small application which is large enough to place some breakpoints in for testing. My example does not make any sense at all so do not question the application too much.

int main(void)
  int i = 0;

  while (1) {


    if (i == 100)
      i = 0;

  return 0;

The application should now compile in Eclipse without errors.

There are two error messages that are apparently caused by the Eclipse code scanner as explained here.

Program “arm-none-eabi-g++” not found in PATH testf7 Project Properties, C++ Preprocessor Include.../Providers, Ac6 SW4 STM32 MCU Built-in Compiler Settings options C/C++ Scanner Discovery Problem
Program “arm-none-eabi-gcc” not found in PATH testf7 Project Properties, C++ Preprocessor Include.../Providers, Ac6 SW4 STM32 MCU Built-in Compiler Settings options C/C++ Scanner Discovery Problem 

If you get these two errors, you can just select and delete them as they are bogus messages more or less and they do not cause your build to fail.

Install gcc-arm-none-eabi on macos


download and unzip the latest version of gdb

sudo cp -R /Users/<username>/Downloads/gcc-arm-none-eabi-9-2019-q4-major /usr/local/gcc_arm

In eclipse in the debug configuration change the gdb command path
Debugger-Tab > GDB command >

Install OpenOCD

brew install open-ocd

OpenOCD Configuration Script

OpenOCD is used in the OpenSTM32 build sequence to establish a connection to the bluepill. gdb will then be used to debug the application over the connection that openocd has established.

To configure how openocd establishes the connection, you have to write a configuration script. In Eclipse, debugging or running an application is done using so called run configurations.

You will create a debug run configuration and specify the path to openocd and the configuration script in the run configuration.

According to this post, there is a bug in OpenSTM32 were the openocd configuration script is not allowed to be located within the Eclipse project but in some other folder. Because of this bug, create the configuration script in some folder but not in the project folder.

I keep my configuration scripts in the folder:


Here is the script that works for me:

source [find interface/stlink-v2.cfg]
transport select "hla_swd"

# added
set BOARDNAME genericBoard

# added
# Enable debug when in low power modes
# Stop Watchdog counters when halt
# STlink Debug clock frequency
#set CLOCK_FREQ 4000
#set CLOCK_FREQ 1000
set CLOCK_FREQ 950
# We do not connect RESET line to stlink, that's why reset is disabled.
#reset_config none
reset_config none separate
source [find target/stm32f1x.cfg]
Create a Run Configuration for Debugging

Open the pull down menu on the Debug button (Button with a green bug on it) in the toolbar. Select “Debug Configurations…”. A dialog opens. Select Ac6 STM32 Debugging and then click the Document button with the small golder plus symbol on it to create a new Debug Run Configuration of the type Ac6 STM32 Debugging.

Here are the settings to make on the Debugger-Tab:

Start Debugging

A breakpoint causes the debugger to pause the microcontroller as soon as it executes the code the breakpoint was set on. You can set breakpoints everywhere which is a bit misleading as for example you can place a breakpoint on an empty line. Obviously there is no code generated for empty lines and the debugger cannot stop on an empty line. It will only stop on lines that actually translate to code that can be executed by the microcontroller. Eclipse will move breakpoints around before starting the application. If a breakpoint is not on a sensical location, Eclipse will move it to the next sensical location.

To set a breakpoint, double click left of a line number. A small blue dot appears. To get rid of a breakpoint, double click the blue dot and it disapears.

In this example, there is a breakpoint on line 25 of our example application:

Whenever i has been incremented to the value 100, the breakpoint will be hit.

Now that you have an application to debug and a breakpoint that makes sense, you can start the debugging session. To start the session, Select your Run Configuration from the Debugging drop down on the toolbar.

Eclipse will compile your application with debug symbols. It will connect to the board using openocd. It will upload the code via the openocd connection and it will start the gnu debugger gdb and connect it to the openocd server which allows gdb to debug the application on the hardware. Eclipse will switch to the debug perspective and it will break on the breakpoint.

The output during application start should be something similar to this:

Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
none separate
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
none separate
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : clock speed 950 kHz
Info : STLINK v2 JTAG v29 API v2 SWIM v7 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 3.132308
Info : STM32F103C8Tx.cpu: hardware has 6 breakpoints, 4 watchpoints

In the debugger you can look at variable values and you can single step through the code. If there are errors and you cannot figure out what the issue is by solely thinking about the issue, you can use a debugger to see what decision your code makes and you can figure out what decision making is happening and where the incorrect decisions are made. You can then fix the issues.

Do not forget to write unit tests for your code! Do not get into bad code-and-fix habits! Maybe, if the problem lends itself to it, give test-driven development a shot.

Error: error in final launch sequence

If you get the “error in final launch sequence” – error, make sure you are using the latest gcc-arm-none-eabi. If the versions of the tools do not match, errors such as this one arise.

Error: dyld: Library not loaded: requires version 3.0.0 or later, but

If you get this error message:

dyld: Library not loaded: /usr/local/opt/libusb/lib/libusb-1.0.0.dylib
  Referenced from: /usr/local/opt/libftdi/lib/libftdi1.2.dylib
  Reason: Incompatible library version: libftdi1.2.dylib requires version 3.0.0 or later, but libusb-1.0.0.dylib provides version 2.0.0

then your libusb versions are out-dated.

For me the libusb files in


are all outdated.

You can check this using the otool command:

otool -L /usr/local/opt/libusb/lib/libusb-1.0.0.dylib

  /usr/local/opt/libusb/lib/libusb-1.0.0.dylib (compatibility version 3.0.0, current version 3.0.0)
  /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
  /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
  /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1575.17.0)
  /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)

For a correct libusb as outlined above, the output says that the libusb library has a compatibility version of 3.0.0 and a current version of 3.0.0. If your libusb versions deviate, install libusb using brew (brew install libusb) then check where the brew files are (brew list libusb) and then replace the outdated libs with the current libs from brew.

Install a new version of libusb using brew (brew install libusb) then create backups of the four outdated files and copy the new libusb files over

cd /Applications/STMicroelectronics/STM32Cube/STM32CubeProgrammer/STM32CubeProgrammer.app/Contents/MacOs/Drivers/FirmwareUpgrade/native/mac_x64
sudo mv libusb-1.0.0.dylib libusb-1.0.0.dylib.backup
sudo cp /usr/local/lib/libusb-1.0.0.dylib .

cd /Applications/STMicroelectronics/STM32Cube/STM32CubeProgrammer/STM32CubeProgrammer.app/Contents/MacOs/bin
sudo mv libusb-1.0.0.dylib libusb-1.0.0.dylib.backup
sudo cp /usr/local/lib/libusb-1.0.0.dylib .

cd /Applications/Ac6/SystemWorkbench.app/Contents/Eclipse/plugins/fr.ac6.mcu.externaltools.openocd.macos64_1.23.0.201904120827/tools/openocd/lib/libusb-1.0.dylib
sudo mv libusb-1.0.0.dylib libusb-1.0.0.dylib.backup
sudo mv libusb-1.0.dylib libusb-1.0.dylib.backup
sudo cp /usr/local/lib/libusb-1.0.0.dylib .
sudo cp /usr/local/lib/libusb-1.0.dylib .
Make an LED Blink

Making an LED Blink is described here.

For those coming from a high-level application programming background, a word of advice:

The fact that there is a function called HAL_Delay(500); or osDelay(1000); does not automatically mean that this function will work without setup code! In microcontroller programming, you have to first set up the hardware clock before you can make use of it via waiting functions such as the delay functions!

Same goes for LEDs. You have to first setup the LEDs before you can toggle them. The example given in this article shows how to perform the initial setup which is necessary to periodically make an LED blink. I will duplicate the code here for reference.

#include "stm32f1xx.h"

void SystemClock_Config(void) {

  RCC_ClkInitTypeDef clkinitstruct = { 0 };
  RCC_OscInitTypeDef oscinitstruct = { 0 };

  oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  oscinitstruct.HSEState = RCC_HSE_ON;
  oscinitstruct.HSIState = RCC_HSI_OFF;
  oscinitstruct.PLL.PLLState = RCC_PLL_ON;
  oscinitstruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  oscinitstruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&oscinitstruct) != HAL_OK) {
    while (1)

  clkinitstruct.ClockType = (RCC_CLOCKTYPE_SYSCLK |
  clkinitstruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  clkinitstruct.APB2CLKDivider = RCC_HCLK_DIV1;
  clkinitstruct.APB1CLKDivider = RCC_HCLK_DIV2;
  if (HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2) != HAL_OK) {
    while (1)

// https://www.onetransistor.eu/2017/12/program-stm32-bluepill-hal-eclipse.html
int main(void) {



  GPIO_InitTypeDef gpio;
  gpio.Mode = GPIO_MODE_OUTPUT_PP;
  gpio.Pin = GPIO_PIN_13;
  gpio.Pull = GPIO_PULLUP;

  HAL_GPIO_Init(GPIOC, &gpio);

  while (1) {
    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);

In order for this code to compile correctly, you have to add the FreeRTOS utility.

Output an Assembler Listing of the Binary

As outlined here, you can add a command to the post build step to call arm-none-eabi-objdump on your binary to disassemble it and output a listing file.

Open the preferences of your project > C / C++ Build > Settings > Tab: Build Steps > PostBuild Steps > Command.

Append to the command:

; arm-none-eabi-objdump -D "${BuildArtifactFileBaseName}.elf" > "${BuildArtifactFileBaseName}.lst"

Do not forget to prefix the semicolon! The semicolon will append a new command to the post build step.

The result is a .lst file in the debug folder that contains the assembly listing.

Test or It Did Not Happen

What if someone as a programming would strictly act by the following simple rule:

Untested code does not exist!

What I mean by ‘does not exist’ is, what if the programmer would just pretend that the untested code is not available to him. He acts as if the code literaly is not there! In other words he ignores it, when he downloads the code from his source repository. And he will not perform a code review on the untested code, even if a code review was assigned to him, because the code does not exist in his mind! The programmer would just act as if the code was not there. He could not see it, smell it, use it, modify it, compile it, ship it nor deploy it.

Beside probably getting into trouble with his boss, this would make the programmers life substantially more easy.

I assume that this programmer writes tests for all of his own code, so he is able to validate that his own code does work, in other words, his own code does in fact exist! Now I know this is not a very realistic assumption because code is often hard to test and often there are time limitations of financial limitations. Would you work for free writing tests if those tests are not paid for by the customer?

Now imagine the programmer is responsible for an application that others contribute code to. If other programmers commit errneous code, the programmer is the one who has to fix those bugs, not the one that commitet buggy features. At this point if the programmer receives untested code, he would just act as if this code did not exist. If he is assigned the code review, he will say: “Which code do you mean? I can’t perform a code review of code that does not exist! I do not see any code in this merge request you assigned to me! Please check your code again and let me know when your tests are ready!”. As a consequence the untested code won’t be merged!

If someone else is dumb enough to merge the code, the programmer could just say: “Well, the code that is supposed to implement the much requested feature X does obviously not exist, since there are no unit tests! I will have to implement feature X again! Such a shame!”. He would then take the time to write code and test the code. He is then convinced that his code works and can ship quality code to the customer.

How do you even come up with this concept of “code does not exist”? Well, think about it. If someone sends you code and there is no unit test for it, how do you know that the code works? It might be possible, that the author of the untested code did not run the code even once! It is possible, that the code does not even compile! It is possible that the code is not even a real computer programm! Parts of the code might be missing! Without a test, you actually know nothing about the characters in the text file you have received! Not only can you not trust the code, there is no proof of the functionality of the code! The code does not even exist!

I am sure I am breaking a few rules of logic here and there, but just imagine how much better your life would be, if you could just pretend untested code did not exist!

Single Board Computers (SBC)