STM32F030R8T6 PCB with GD32F330R8T6 variant

I’ve build a small FW alternative for an existing PCB with a STM32F030R8T6 MCU.
I did it with:

platform = ststm32
board = nucleo_f030r8
framework = stm32cube

Now it turned out, that there’re also PCB’s with an GD32F330R8T6 in the wild.
The GD is fully pin compatible, but has a much quicker core (M4 instead M0), as well as double SRAM, …

platform = ststm32 as well as framework = stm32cube don’t support any GD device (for sure).

While searching for a possible new common code base, I found platform-gd32 and thought I could switch to framework = spl but unfortunately it turned out that it does not has support for STM32F030.

While digging deeper and deeper I stumbled over CMSIS-Arm and all the CMSIS Modules.
Well there’s everything… but… it would mean that I had to start from scratch… with damn bad documented stuff, whereas I’m spoiled by my lovely and comfortable PlatformIO.

I’ve now two main questions:
1.) Now that I saw all the CMSIS srcs, isn’t there a hacky way for me copying the GD32 CMSIS files to the correct place so that I can switch within my own header files the MCU variant? Or use a similar STM32 variant and hack the GD32 CMSIS data together?
2.) If 1.) is childish wishful thinking… which platform/framework can/shall I use so that I get a best possible common code basis?

I don’t get that part and that’s the second time I’ve heard people say that, please help me out here.

Here’s the CMSIS Pack for GD32F3x0:
And for STM32F0:

For GD32’s GPIO functions you e.g. have

/* set GPIO pin bit */
void gpio_bit_set(uint32_t gpio_periph, uint32_t pin);
/* reset GPIO pin bit */
void gpio_bit_reset(uint32_t gpio_periph, uint32_t pin);
/* write data to the specified GPIO pin */
void gpio_bit_write(uint32_t gpio_periph, uint32_t pin, bit_status bit_value);

aka, that’s framework = spl.

STM32’s HAL modules that’s in that CMSIS pack gives you

void              HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);

and the LL module gives you

__STATIC_INLINE void LL_GPIO_SetOutputPin(GPIO_TypeDef *GPIOx, uint32_t PinMask);
__STATIC_INLINE void LL_GPIO_ResetOutputPin(GPIO_TypeDef *GPIOx, uint32_t PinMask);

so they all have different API signature for even the most basic things. How does unification come from using the CMSIS-ARM packs for each microcontroller? You’d still have to compile-time switch between those one or the other API signatures, unless you create your own abstarction layer.

Well one possibility is to chose a framework = .. value that’s valid for both your GD32 and STM32 board. Right now with what’s implemented, that’s only framework = arduino. Though its more heavy on the smaller microcontroller, your STM32F030R8 with 64K flash and 8K RAM.

So you might also just want to write the thinnest possible abstraction layer possible for your hardware and your application.

Though, I wanna go back to the beginning: Do you wish to create two firmwares, that depending on the PCB that has either a STM32 or GD32 chip, you flash one of these on it? Or do you want to have one firmware binary that you can flash on both?

Quite thanks for answering!!

Well, at least for my personal statement, it’s because you’ve much more knowledge in the details and have much more experienced than me.

While I still think theoretical about it, you’re already within the sources, and thanks at this place, already clarifies that the API’s aren’t 1:1 usable (without an extra abstraction layer). This is what I worried about to recognize when doing/checking in my next step. You simply outpaced me :innocent:.
Thanks a this place for this information, I worry that it took me 2-3 hours to get to this point :see_no_evil:

Yes the common framework is what I looked for but stopped by framework = spl.
Don’t know why I went around the arduino framework. Probably because I just adapted my brain to the HAL layer and started to open my brain to go more to the lower level APIs like LL.
Well, it could be a good option! I still have 40% Flash and >50% SRAM left on the smaller STM32 variant!!

“thinnest possible” is not really my intention, but probably more the direction better solution/code. However I also tend to waste time by such decisions, instead of simply code the functionality with whatever base is existing :see_no_evil:

Yes, two FW versions, one for the GD one for ST is fully sufficient!!
In fact, my FW alternative/port is based on the code of a replacement-PCB with a Pico MCU and Pico-SDK code.
Sound chaotic but isn’t :upside_down_face:

Also can’t find support for GD32F330R8 in

platform_packages = 


You’re right, technically only GD32F330CC is existing, but the chips should be really close together. Let me just add them…

Quite thanks :pray: but don’t hurry.

BTW: Didn’t found any F33??? do I’m on the right repository?

“Close” should be enough! I only need the GPIOs, approx. 4 basic timer and 1 (better 2) UART’s :roll_eyes:

Oh right. It’s F3X0 not F30X. I was thinking of F303CC.

But we also have F350.


so it should be close ot that one. I’ll check the pin maps tomorrow. Or in fact, if I’m not mistaken, our script

should actually work when fed the datasheet data.

Quite thanks :pray: Impressive support!!!

Will also check tomorrow or the day after tomorrow!
So, take the time you need.
I already started over with arduino framework will take some time before I can give it outside for GD32 testing (I’ve only an STM32 here).

Really cool to work which such well performing environment!!!

Did some first test and wohooo, some stuff already work.

It’s a little bit complicated to test, as I don’t have a PCB with GD32F330R8 here. I’ve to build some test code and send it to one of those by whom the GD32 variant popped up.

What I can report now is the following:

  • In general my test code start
  • I’ve only used 18 GPIO’s in this first test, but I’ve problems with PC0-4, 10-12 as well as PD2.
    Problems in that way that hey aren’t known in arduino and if I stuppidly use the ones from STM32F030R8 definition, they also don’t work (Output with LED’s attached).
    PA0-1, 4-7 & 15 as well as PB0, 3 & 4 do work!! (also as output, LED’s)
  • Clock/Systick timer seem also approx. 10 times slower in real (simple delay()) than put in test FW.

So, I’m promising!! We/you’re on the right track and I hope you find some time to check some when the next week(s).

Quite thanks pushing me onto the right track :pray:

BTW: Is there a trick to get Semihosting running with arduino framework? Did it in the same way as with stm32cube framework. It compiles well and also debug console logs that semihosting is enabled.

It does work with SPL but there’s not yet the option to enable it in Arduino (setting linker flags basically).

The GD32F330xx support was added in

after expanding the auto-generator script. As gd32-arduino-blinky shows in the more general case, you can compile for your GD32F330R8(T6) by e.g. using the platformio.ini

platform =
platform_packages = 
monitor_speed = 115200

board = genericGD32F330R8
framework = arduino

Since the adddition to platform-gd32 is new, you will also have to issue an update to the platform if you previously installed it, using e.g. a CLI and

pio pkg update -g -p gd32

Important note: The clock selection is hardcoded for now with the standard GD32 SPL code. This code expects the GD32 chip to have a 8MHz quartz crystal connected it on the respective OSC pins.

If this is not the case on your PCB, the firmware won’t boot and you will have to comment and uncomment the right option in that file. (C:\Users\<user>\.platformio\packages\framework-arduinogd32\system\...), e.g., with the internal RC 8MHz → 84MHz PLL option.

Doesn’t matter! I’m half done without any need for simple printf()'s. :wink:
It’s terrible slow anyway and often trapped me into timing issues :innocent:

Awesome support!!! Quite thanks a lot!!! :pray:
Will send out a new test-FW to my tester tomorrow and hope to get feedback soon :heart_eyes:

Also thanks a lot for the detailed INI and update description which will save me a lot time!

Woww :flushed: important info!! Will check… have images here, but I’m pretty sure it doesn’t has an external crystal!! So I’ll do the Mod then!

One more :pray: :heart_eyes:

Probably another topic but will start here, cause I think it has something todo with this topic.

Did the updates yesterday, but via PIO → Home → Libraries → Updates.
Just there I saw that there where a couple of Libs which where also update capable.
Updated not all but i.e. “stmstm32” cause I’m currently working with it.

Added your mentioned GD32 env settings (except the clock mod), changed my FW for the next external test. Looked good, compiled fine.
(but prob. worth to mention: During compile I recognized that he did some other updates CMSIS and two or three more)

Thought: Test with your avail STM32F030 to be sure that there’re no failures.

:flushed: Didn’t booted anymore. Checked my previous local commits which worked. Also do not boot anymore.

Went to bed cause thought it would be a stupid late-evening failure.

Today started fresh a couple of hours ago, but don’t get ststm32/arduino running anymore on my STM32F030.

I shot me in some way into my PIO environment.

I even don’t get arduino-blink sample running anymore on my STM32 :flushed:!!

If I switch to ststm32/stm32cube it runs at first shot.

Any idea what happened?

Well you can roll-back the used STSTM32 platform version any time per released versions and documentation but I don’t see a reason why it wouldn’t be running anymore at the latest version.

Can you confirm there’s no crystal oscillator on the board? What exact platformio.ini are you using for the STM32 project? If you have multiple projects in your VSCode workspace, did you make sure to use the project environment switcher to select the right one?

Hi Max and quite thanks for answering on a public-holiday day!!

Already tried a couple of older versions as well as a couple their dependencies… but without success :frowning:

Yes. Found no crystal on PCB and the relevant pins (2-6?) look not connected.

I’m down to blinky :joy: :see_no_evil::

platform = ststm32
framework = arduino

board = nucleo_f030r8
upload_flags = 
upload_command = openocd $UPLOAD_FLAGS -c "init; reset halt; stm32f0x unlock 0; reset run" -c "program {$SOURCE} verify reset; shutdown;"

debug_tool = stlink
debug_build_flags = -O0 -g -ggdb
debug_server = 
  reset_config none separate

BTW: Debugging doesn’t break in main.

Upload work fine but also tried already via st-flash without success.

Sure :crazy_face:, I’ve > 20 env:'s in this workspace. I know what you mean but I already checked if the binary get compiled where it should, as well as if it has the right timestamp, flashed by hand, …

Shall I try with a new/empty workspace?! :roll_eyes:

But that’s not good. WIth board configuration it will try to and use this clock init code

which expects a CLK input signal as HSE (from the on-board ST-Link found on the real Nucleo board). Else it will just hang up in Error_Handler().

There’s also a disco_f030r8 configuration and that tries to boot from HSI:

So can you just change the environment to use board = disco_f030r8 instead?

Yippi, you’re once more my hero of the months :star_struck: :kissing_heart:

But, please explain why I had no issues with the clock during the last week?!

Because my nucleo selection was wrong at all?! and during the last days/weeks an update occured which popped up my bad nucleo selection?

Mysterious – literally no idea besides voodoo. Unless you updated from an absolutely, half a decade old version that had a different implementation for the Nucleo board (using HSI).

No, just started a couple of weeks before with ST. But I’m fine with the voodoo explanation.

In between I received one of the GD32 boards for testing/development and can report that it work/boot!!

Have some issues with HardwareTimer, in special attaching the callback to GD32 (STM32 work as expected), but will test first with a stripped down variant, blinky or the like and will open a new topic when I don’t succeed.

Quite thanks :pray: for the awesome support!

It’s totally possible that we have a bug in the ArduinoCore-GD32 or the automated pin-definitions (which includes the PWM capable pins and timers).

A reference example I’d expect to work would be

with a PWM-capable pin coming from the list here.

No PWM, no external interrupt, only a simple HardwareTimer callback like: