diff --git a/.github/add_include.py b/.github/add_include.py
index f7ae02f8fc33b0c50ad01d84ffd85401744dd1c2..4985a1d3aeab7991d74dfedb33e63164af903135 100644
--- a/.github/add_include.py
+++ b/.github/add_include.py
@@ -1,5 +1,20 @@
-from os.path import join
+import os.path
 Import("env", "projenv")
+# Rely on the fact that all examples to build_src_filter = +<EXAMPLE_THEY_WANT>
+# And figure out correct include path from that
+src_filter = str(env.subst("$SRC_FILTER"))
+# A string with e.g. "+<ch32v003fun> +<examples/blink>" 
+# rudimentary parsing is fine for CI
+src_filters = src_filter.split("+")
+example_folder = ""
+for filter in src_filters:
+   # cleanup unwanted characters
+   f = filter.replace("<", "", 1).replace(">", "", 1)
+   # starts with "examples" and looks like a path?
+   if f.startswith("examples") and ("/" in f or os.path.sep in f):
+      example_folder = f
+      break
+   
 # propagate to all construction environments
 for e in env, projenv, DefaultEnvironment():
-   e.Append(CCFLAGS=[("-I", join("examples", env.subst("$PIOENV")))])
+   e.Append(CCFLAGS=[("-I", example_folder)])
diff --git a/.github/gen_ldscript.py b/.github/gen_ldscript.py
index 48ab05a49d0bc1b854b26443583743ba40900dda..d85306969dc31d8ad8c92dad1400cbdab0ade11d 100644
--- a/.github/gen_ldscript.py
+++ b/.github/gen_ldscript.py
@@ -1,25 +1,94 @@
 from os.path import join
+import sys
 Import("env")
 
-mcu_package = 1
-target_mcu = 0
-mcu_ld = 0
-linkerscript_cmd = env.Command(
-    join("$BUILD_DIR", "ldscript.ld"),  # $TARGET
-    join(env.subst("$PROJECT_DIR"), "ch32v003fun", "ch32v003fun.ld"),  # $SOURCE
+# Returns TARGET_MCU, MCU_PACKAGE and TARGET_MCU_LD values
+# In the same way that the Makefile would do.
+def get_ld_defines(chip_name: str):
+    target_mcu: str = ""
+    mcu_package: int = 0
+    target_mcu_ld: int = 0
+    if chip_name.startswith("ch32v003"):
+        target_mcu = "CH32V003"
+        target_mcu_ld = 0
+    else:
+        mcu_package = 1
+        if chip_name.startswith("ch32v10"):
+            target_mcu = chip_name.upper()[0:len("ch32v10x")]
+            if "r8" in chip_name:
+                mcu_package = 1
+            elif "c8" in chip_name:
+                mcu_package = 1
+            elif "c6" in chip_name:
+                mcu_package = 2
+            target_mcu_ld = 1
+        elif chip_name.startswith("ch32v20"):
+            target_mcu = chip_name.upper()[0:len("ch32v20x")]
+            if "f8" in chip_name:
+                mcu_package = 1
+            elif "g8" in chip_name:
+                mcu_package = 1
+            elif "k8" in chip_name:
+                mcu_package = 1
+            elif "c8" in chip_name:
+                mcu_package = 1
+            elif "f6" in chip_name:
+                mcu_package = 2
+            elif "k6" in chip_name:
+                mcu_package = 2
+            elif "c6" in chip_name:
+                mcu_package = 2
+            elif "rb" in chip_name:
+                mcu_package = 3
+            elif "gb" in chip_name:
+                mcu_package = 3
+            elif "cb" in chip_name:
+                mcu_package = 3
+            elif "wb" in chip_name:
+                mcu_package = 3
+            target_mcu_ld = 2
+        elif chip_name.startswith("ch32v30"):
+            target_mcu = chip_name.upper()[0:len("ch32v30x")]
+            if "rc" in chip_name:
+                mcu_package = 1
+            elif "vc" in chip_name:
+                mcu_package = 1
+            elif "wc" in chip_name:
+                mcu_package = 1
+            elif "cb" in chip_name:
+                mcu_package = 2
+            elif "fb" in chip_name:
+                mcu_package = 2
+            elif "rb" in chip_name:
+                mcu_package = 2
+            target_mcu_ld = 3
+        else:
+            sys.stdout.write("Unkonwn MCU %s\n" % chip_name)
+            env.Exit(-1)
+    return (target_mcu, mcu_package, target_mcu_ld)
+
+# Retrieve MCU name from selected board info
+board = env.BoardConfig()
+chip_name = str(board.get("build.mcu", "")).lower()
+# retrieve needed macro values
+target_mcu, mcu_package, target_mcu_ld = get_ld_defines(chip_name)
+
+# Let the LD script be generated right before the .elf is built
+env.AddPreAction(
+    "$BUILD_DIR/${PROGNAME}.elf",
     env.VerboseAction(" ".join([
         "$CC",
         "-E",
         "-P",
         "-x",
         "c",
-        "-DTARGET_MCU=%d" % target_mcu,
+        "-DTARGET_MCU=%s" % target_mcu,
         "-DMCU_PACKAGE=%d" % mcu_package,
-        "-DTARGET_MCU_LD=%d" % mcu_ld,
-        "$SOURCE" 
+        "-DTARGET_MCU_LD=%d" % target_mcu_ld,
+        join("ch32v003fun", "ch32v003fun.ld"),
         ">",
-        "$TARGET"
-    ]), "Generating linkerscript $BUILD_DIR/ldscript.ld")
+        join("$BUILD_DIR", "ldscript.ld")
+    ]), "Building %s" % join("$BUILD_DIR", "ldscript.ld"))
 )
-env.Depends("$BUILD_DIR/${PROGNAME}.elf", linkerscript_cmd)
+# Already put in the right path for the to-be-generated file
 env.Replace(LDSCRIPT_PATH=join("$BUILD_DIR", "ldscript.ld"))
\ No newline at end of file
diff --git a/ch32v003fun/generated_ch32v003.ld b/ch32v003fun/generated_ch32v003.ld
deleted file mode 100644
index ea85db74cd908176c9bf19d840d16801e3bd860b..0000000000000000000000000000000000000000
--- a/ch32v003fun/generated_ch32v003.ld
+++ /dev/null
@@ -1,115 +0,0 @@
-ENTRY( InterruptVector )
-MEMORY
-{
- FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 16K
- RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 2K
-}
-SECTIONS
-{
-    .init :
-    {
-      _sinit = .;
-      . = ALIGN(4);
-      KEEP(*(SORT_NONE(.init)))
-      . = ALIGN(4);
-      _einit = .;
-    } >FLASH AT>FLASH
-    .text :
-    {
-      . = ALIGN(4);
-      *(.text)
-      *(.text.*)
-      *(.rodata)
-      *(.rodata*)
-      *(.gnu.linkonce.t.*)
-      . = ALIGN(4);
-    } >FLASH AT>FLASH
-    .fini :
-    {
-      KEEP(*(SORT_NONE(.fini)))
-      . = ALIGN(4);
-    } >FLASH AT>FLASH
-    PROVIDE( _etext = . );
-    PROVIDE( _eitcm = . );
-    .preinit_array :
-    {
-      PROVIDE_HIDDEN (__preinit_array_start = .);
-      KEEP (*(.preinit_array))
-      PROVIDE_HIDDEN (__preinit_array_end = .);
-    } >FLASH AT>FLASH
-    .init_array :
-    {
-      PROVIDE_HIDDEN (__init_array_start = .);
-      KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
-      KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
-      PROVIDE_HIDDEN (__init_array_end = .);
-    } >FLASH AT>FLASH
-    .fini_array :
-    {
-      PROVIDE_HIDDEN (__fini_array_start = .);
-      KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
-      KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
-      PROVIDE_HIDDEN (__fini_array_end = .);
-    } >FLASH AT>FLASH
-    .ctors :
-    {
-      KEEP (*crtbegin.o(.ctors))
-      KEEP (*crtbegin?.o(.ctors))
-      KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
-      KEEP (*(SORT(.ctors.*)))
-      KEEP (*(.ctors))
-    } >FLASH AT>FLASH
-    .dtors :
-    {
-      KEEP (*crtbegin.o(.dtors))
-      KEEP (*crtbegin?.o(.dtors))
-      KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
-      KEEP (*(SORT(.dtors.*)))
-      KEEP (*(.dtors))
-    } >FLASH AT>FLASH
-    .dalign :
-    {
-      . = ALIGN(4);
-      PROVIDE(_data_vma = .);
-    } >RAM AT>FLASH
-    .dlalign :
-    {
-      . = ALIGN(4);
-      PROVIDE(_data_lma = .);
-    } >FLASH AT>FLASH
-    .data :
-    {
-      . = ALIGN(4);
-      *(.gnu.linkonce.r.*)
-      *(.data .data.*)
-      *(.gnu.linkonce.d.*)
-      . = ALIGN(8);
-      PROVIDE( __global_pointer$ = . + 0x800 );
-      *(.sdata .sdata.*)
-      *(.sdata2*)
-      *(.gnu.linkonce.s.*)
-      . = ALIGN(8);
-      *(.srodata.cst16)
-      *(.srodata.cst8)
-      *(.srodata.cst4)
-      *(.srodata.cst2)
-      *(.srodata .srodata.*)
-      . = ALIGN(4);
-      PROVIDE( _edata = .);
-    } >RAM AT>FLASH
-    .bss :
-    {
-      . = ALIGN(4);
-      PROVIDE( _sbss = .);
-      *(.sbss*)
-      *(.gnu.linkonce.sb.*)
-      *(.bss*)
-      *(.gnu.linkonce.b.*)
-      *(COMMON*)
-      . = ALIGN(4);
-      PROVIDE( _ebss = .);
-    } >RAM AT>FLASH
-    PROVIDE( _end = _ebss);
- PROVIDE( end = . );
- PROVIDE( _eusrstack = ORIGIN(RAM) + LENGTH(RAM));
-}
diff --git a/platformio.ini b/platformio.ini
index 5af4840aebd10ad436dfae769ce9a892b9f04ddc..1e673083b80273c57509c5269f34e29b5b3d42a7 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -5,12 +5,7 @@ include_dir = .
 
 [env]
 platform = https://github.com/Community-PIO-CH32V/platform-ch32v.git
-; or genericCH32V003A4M6 or whatever, see platform link
-board = ch32v003f4p6_evt_r0
 monitor_speed = 115200
-; all environments use ch32v003 fun as base, no more ch32v003evt
-extends = fun_base
-; use a source filter to only build certain folders / files in later environments
 
 ; for examples that use ch32v003fun as their base
 [fun_base]
@@ -19,137 +14,236 @@ build_flags = -flto -Ich32v003fun -I/usr/include/newlib -lgcc -Iextralibs
 ; generate linkerscript on the fly
 extra_scripts = 
   .github/add_include.py
-  .github/gen_ldscript.py
+  pre:.github/gen_ldscript.py
 build_src_filter = +<ch32v003fun>
 
+[fun_base_003]
+extends = fun_base
+; or genericCH32V003A4M6 or whatever, see platform link
+board = ch32v003f4p6_evt_r0
+
+[fun_base_103]
+extends = fun_base
+build_flags = ${fun_base.build_flags} -DCH32V10x
+board = genericCH32V103C8T6
+
+[fun_base_203]
+extends = fun_base
+build_flags = ${fun_base.build_flags} -DCH32V20x
+board = genericCH32V203C8T6
+
+[fun_base_307]
+extends = fun_base
+build_flags = ${fun_base.build_flags} -DCH32V30x
+board = ch32v307_evt
+
 ; If creating a new example:
 ; 1. Add new [env:name]
-; 2. Add build_src_filter with fun base files + example folder (+ extra libraries if used) for source files
-; 3. Add additional build flags as needed (see uartdemo)
-; 4. Switch to new environment in VSCode bottom taskbar (https://docs.platformio.org/en/latest/integration/ide/vscode.html#project-tasks)
+; 2. Set the extends to the fun_base_{003, 103, 203, 307} as needed
+; 3. Add build_src_filter with fun base files + example folder (+ extra libraries if used) for source files
+; 4. Add additional build flags as needed (see uartdemo)
+; 5. Switch to new environment in VSCode bottom taskbar (https://docs.platformio.org/en/latest/integration/ide/vscode.html#project-tasks)
 [env:adc_dma_opamp]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/adc_dma_opamp>
 
 [env:adc_fixed_fs]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/adc_fixed_fs>
 
 [env:adc_polled]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/adc_polled>
 
 [env:blink]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/blink>
 
+[env:blink_raw]
+extends = fun_base_003
+build_src_filter = ${fun_base.build_src_filter} +<examples/blink_raw>
+
 [env:bootload]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/bootload>
 
 [env:cap_touch_adc]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/cap_touch_adc>
 
 [env:cap_touch_exti]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/cap_touch_exti>
 
+[env:color_lcd]
+extends = fun_base_003
+build_src_filter = ${fun_base.build_src_filter} +<examples/color_lcd>
+
 [env:cpp_virtual_methods]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/cpp_virtual_methods>
 
 [env:debugprintfdemo]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/debugprintfdemo>
 
 [env:direct_gpio]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/direct_gpio>
 
 [env:dma_gpio]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/dma_gpio>
 
 [env:external_crystal]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/external_crystal>
 
 [env:exti_pin_change_isr]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/exti_pin_change_isr>
 
 [env:flashtest]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/flashtest>
 
 [env:GPIO]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/GPIO>
 
+[env:hsitrim]
+extends = fun_base_003
+build_src_filter = ${fun_base.build_src_filter} +<examples/hsitrim>
+
 [env:i2c_oled]
-build_src_filter = ${fun_base.build_src_filter}  +<examples/i2c_oled>
+extends = fun_base_003
+build_src_filter = ${fun_base.build_src_filter} +<examples/i2c_oled>
 
 [env:i2c_slave]
-build_src_filter = ${fun_base.build_src_filter}  +<examples/i2c_slave>
+extends = fun_base_003
+build_src_filter = ${fun_base.build_src_filter} +<examples/i2c_slave>
 
 [env:input_capture]
-build_src_filter = ${fun_base.build_src_filter}  +<examples/input_capture>
+extends = fun_base_003
+build_src_filter = ${fun_base.build_src_filter} +<examples/input_capture>
 
 [env:iwdg]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/iwdg>
 
-[env:hsitrim]
-build_src_filter = ${fun_base.build_src_filter}  +<examples/hsitrim>
-
 [env:MCOtest]
-build_src_filter = ${fun_base.build_src_filter}  +<examples/MCOtest>
+extends = fun_base_003
+build_src_filter = ${fun_base.build_src_filter} +<examples/MCOtest>
 
 [env:optionbytes]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/optionbytes>
 
 [env:optiondata]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/optiondata>
 
 [env:run_from_ram]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/run_from_ram>
 
 [env:self_modify_code]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/self_modify_code>
 
 [env:spi_24L01_rx]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/spi_24L01_rx>
 
 [env:spi_24L01_tx]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/spi_24L01_tx>
 
 [env:spi_dac]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/spi_dac>
 
+[env:spi_max7219]
+extends = fun_base_003
+build_src_filter = ${fun_base.build_src_filter} +<examples/spi_max7219>
+
 [env:spi_oled]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/spi_oled>
 
 [env:standby_autowake]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/standby_autowake>
 
 [env:standby_btn]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/standby_btn>
 
 [env:struct_direct_gpio]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/struct_direct_gpio>
 
 [env:struct_gpio]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/struct_gpio>
 
 [env:sysclk_config]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/sysclk_config>
 
 [env:systick_irq]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/systick_irq>
 
 [env:template]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/template>
 
 [env:tim1_pwm]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/tim1_pwm>
 
 [env:tim2_encoder]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/tim2_encoder>
 
 [env:tim2_pwm]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/tim2_pwm>
 
 [env:tim2_pwm_remap]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/tim2_pwm_remap>
 
 [env:uartdemo]
+extends = fun_base_003
 build_flags = ${fun_base.build_flags} -DSTDOUT_UART
 build_src_filter = ${fun_base.build_src_filter} +<examples/uartdemo>
 
 [env:ws2812bdemo]
+extends = fun_base_003
 build_src_filter = ${fun_base.build_src_filter} +<examples/ws2812bdemo>
+
+[env:v10x_blink]
+extends = fun_base_103
+build_src_filter = ${fun_base.build_src_filter} +<examples_v10x/blink>
+
+[env:v20x_blink]
+extends = fun_base_203
+build_src_filter = ${fun_base.build_src_filter} +<examples_v20x/blink>
+
+[env:v20x_debugprintfdemo]
+extends = fun_base_203
+build_src_filter = ${fun_base.build_src_filter} +<examples_v20x/debugprintfdemo>
+
+[env:v20x_mcotest]
+extends = fun_base_203
+build_src_filter = ${fun_base.build_src_filter} +<examples_v20x/mcotest>
+
+[env:v20x_spidmatest]
+extends = fun_base_203
+build_src_filter = ${fun_base.build_src_filter} +<examples_v20x/spidmatest>
+
+[env:v30x_blink]
+extends = fun_base_307
+build_src_filter = ${fun_base.build_src_filter} +<examples_v30x/blink>