STM32F103C8T6 ARM STM32 Minimum System Development Board

September 9, 2020

libmaple -- an i2c maple-ectomy

I have sucessfully run the i2c demo supplied with libmaple, building it inside the expected libmaple framework. It ran, albeit with some weird issues.

There seems to be no C++ upper layer to the i2c driver, so perhaps it will be possible to pull the driver out of the libmaple framework and make a simple bare metal demo out of it.

Away we go

Chopping the top layer out of the main.cpp file is easy enough, then the game becomes chasing various include files. These tend to be in two places:
libmaple/include/libmaple
libmaple/libmaple/stm32f1/include/series
User code tends to include files from the first directory as "libmaple/nvic.h" for example, and that file tends to include the files from the second directory as "series/nvic.h".

I began at first renaming files and putting them all in the same directory as my sources. I gave files from the first directory names like "maple_nvic.h". And I gave files from the second directory names like "maple_stm32f1_nvic.h". But then I came to my senses and abandoned all of this.

It makes for much less trouble to copy the include files wholesale, which I did as follows:

mkdir libmaple
mkdir libmaple/series
cp /u1/Projects/STM32/libmaple/libmaple/include/libmaple/*.h libmaple
cp /u1/Projects/STM32/libmaple/libmaple/stm32f1/include/series/*.h libmaple/series
Then I added these switches to my compiler options:
-I. -Ilibmaple -DMCU_STM32F103C8
As a side note, I will mention that the maple_mini defines -DMCU_STM32F103CB. i.e. "CB" instead of "C8". What difference does this make?

After doing this, trying to build my demo gives the following missing externs:

dac.c:(.text+0xc0): undefined reference to `I2C2'
dac.c:(.text+0x11a): undefined reference to `i2c_master_enable'
dac.c:(.text+0xe): undefined reference to `i2c_master_xfer'

I copy the C driver for i2c as follows:

cp /u1/Projects/STM32/libmaple/libmaple/i2c.c maple_i2c.c
cp /u1/Projects/STM32/libmaple/libmaple/i2c_private.h maple_i2c_private.h
cp /u1/Projects/STM32/libmaple/libmaple/stm32f1/i2c.c maple_stm32f103_i2c.c
After this, trying to build reveals the various interconnections with other parts of the libmaple system:
maple_i2c.c:(.text+0x1ae): undefined reference to `_fail'
maple_i2c.c:(.text+0x3c0): undefined reference to `systick_uptime_millis'
maple_i2c.c:(.text+0x94): undefined reference to `rcc_reset_dev'
maple_i2c.c:(.text+0xa0): undefined reference to `rcc_clk_enable'
maple_i2c.c:(.text+0x170): undefined reference to `afio_remap'
maple_stm32f103_i2c.c:(.text+0x102): undefined reference to `nvic_irq_set_priority'
maple_stm32f103_i2c.c:(.text+0x14): undefined reference to `gpio_set_mode'
maple_stm32f103_i2c.o:(.data+0x10): undefined reference to `gpiob'

_fail

This is a function in libmaple/util.c that handles failed assertions. The ASSERT macro is in libmaple/include/libmaple/util.h. It can be turned off based on the definition of DEBUG_LEVEL. All this is pretty fancy.

systick_uptime_millis

This is pretty much what you might think. It is a 32 bit counter being maintained by the systick timer, which must be configured to tick at 1000 Hz. The systick ISR also has a hook for a user callback. The i2c driver uses it to timeout a wait for a device state change in i2c_master_xfer(). It is accessed via a macro systick_uptime().

rcc_reset and rcc_clk

These do what you expect, manipulating RCC registers to reset the device or to gate on the clock.

nvic_irq_set_priority

The NVIC has 4 bits for interrupt priority, meaning you can select one of 16 priority levels.

afio_remap

This is in stm32f1/gpio.c and is particularly interesting.

gpio_set_mode and gpiob

This is also in stm32f1/gpio.c. The set_mode routine does what you might expect. The symbol gpiob is a global structure with a handful of data about the port. A pointer to this is set in the i2c_dev structure.
Feedback? Questions? Drop me a line!

Tom's Computer Info / tom@mmto.org