diff --git a/ch32v003evt/ch32v003.ld b/ch32v003evt/ch32v003.ld
index d25e60d0f91560c55b073fbd6d724cd75c5e615d..d3d27288f1530e420dafd11ea17d1c93401890c9 100644
--- a/ch32v003evt/ch32v003.ld
+++ b/ch32v003evt/ch32v003.ld
@@ -1,4 +1,4 @@
-ENTRY( _start )
+ENTRY( InterruptVector )
 
 __stack_size = 256;
 
diff --git a/ch32v003evt/ch32v00x_conf.h b/ch32v003evt/ch32v00x_conf.h
index 01e713230dcf913be285e77a7779acd3f49fa12b..217af40d57d30349d14b4ba0be8e2d7fcd6b10c2 100644
--- a/ch32v003evt/ch32v00x_conf.h
+++ b/ch32v003evt/ch32v00x_conf.h
@@ -31,7 +31,20 @@ extern "C" {
 int main() __attribute__((used));
 void SystemInit(void) __attribute__((used));
 
-	
+// Useful functions
+void SystemInit48HSI( void );
+
+#define UART_BAUD_RATE 115200
+#define OVER8DIV 4
+#define INTEGER_DIVIDER (((25 * (APB_CLOCK)) / ((OVER8DIV) * (UART_BAUD_RATE))))
+#define FRACTIONAL_DIVIDER ((INTEGER_DIVIDER)%100)
+#define UART_BRR ((((INTEGER_DIVIDER) / 100) << 4) | (((((FRACTIONAL_DIVIDER) * ((OVER8DIV)*2)) + 50)/100)&7))
+// Put an output debug UART on Pin D5.
+// You can write to this with printf(...) or puts(...)
+// Call with SetupUART( UART_BRR )
+void SetupUART( int uartBRR );
+
+
 /* ch32v00x_gpio.c -----------------------------------------------------------*/
 /* MASK */
 #define LSB_MASK                  ((uint16_t)0xFFFF)
diff --git a/ch32v003evt/core_riscv.h b/ch32v003evt/core_riscv.h
index 2f533c30b3761c30aa915df956874d7592f77c7c..2e4382d0b8651e0246c2a7eb5619e8809fc9e51e 100644
--- a/ch32v003evt/core_riscv.h
+++ b/ch32v003evt/core_riscv.h
@@ -511,7 +511,7 @@ static inline uint32_t __get_MEPC(void)
  *
  * @return  mepc value
  */
-void __set_MEPC(uint32_t value)
+static inline void __set_MEPC(uint32_t value)
 {
     __ASM volatile("csrw mepc, %0" : : "r"(value));
 }
@@ -532,7 +532,7 @@ static inline uint32_t __get_MCAUSE(void)
 }
 
 /*********************************************************************
- * @fn      __set_MEPC
+ * @fn      __set_MCAUSE
  *
  * @brief   Set the Machine Cause Register
  *
diff --git a/ch32v003evt/startup_ch32v003.c b/ch32v003evt/startup_ch32v003.c
index 8e486b8dd6c0e40eaf45b5025942d42cfab0fb87..c52564a2609669df550c496950e6d4c421e8f04b 100644
--- a/ch32v003evt/startup_ch32v003.c
+++ b/ch32v003evt/startup_ch32v003.c
@@ -5,12 +5,13 @@
 */
 
 #include <stdint.h>
+#include <ch32v00x.h>
 
 int main() __attribute__((used));
 void SystemInit( void ) __attribute__((used));
 
-void InterruptVector() __attribute__((naked)) __attribute((section(".init"))) __attribute__((used));
-void handle_reset() __attribute__((naked)) __attribute((section(".text.handle_reset"))) __attribute__((used));
+void InterruptVector()         __attribute__((naked)) __attribute((section(".init"))) __attribute__((used));
+void handle_reset()            __attribute__((naked)) __attribute((section(".text.handle_reset"))) __attribute__((used));
 void DefaultIRQHandler( void ) __attribute__((section(".text.vector_handler"))) __attribute__((naked)) __attribute__((used));
 
 extern uint32_t * _sbss;
@@ -27,6 +28,8 @@ void DefaultIRQHandler( void )
 	asm volatile( "1: j 1b" );
 }
 
+// This makes it so that all of the interrupt handlers just alias to
+// DefaultIRQHandler unless they are individually overridden.
 void NMI_Handler( void )                 __attribute__((section(".text.vector_handler"))) __attribute((weak,alias("DefaultIRQHandler"))) __attribute__((used));
 void HardFault_Handler( void )           __attribute__((section(".text.vector_handler"))) __attribute((weak,alias("DefaultIRQHandler"))) __attribute__((used));
 void SysTick_Handler( void )             __attribute__((section(".text.vector_handler"))) __attribute((weak,alias("DefaultIRQHandler"))) __attribute__((used));
@@ -60,10 +63,10 @@ void InterruptVector()
 	asm volatile( "\n\
 	.align  2\n\
 	.option   norvc;\n\
-	j	   handle_reset\n\
+	j handle_reset\n\
 	.word   0\n\
-	.word   NMI_Handler				  /* NMI Handler */		\n\
-	.word   HardFault_Handler			/* Hard Fault Handler */ \n\
+	.word   NMI_Handler               /* NMI Handler */                    \n\
+	.word   HardFault_Handler         /* Hard Fault Handler */             \n\
 	.word   0\n\
 	.word   0\n\
 	.word   0\n\
@@ -72,34 +75,34 @@ void InterruptVector()
 	.word   0\n\
 	.word   0\n\
 	.word   0\n\
-	.word   SysTick_Handler			 /* SysTick Handler */	 \n\
+	.word   SysTick_Handler           /* SysTick Handler */                \n\
 	.word   0\n\
-	.word   SW_Handler				  /* SW Handler */		  \n\
+	.word   SW_Handler                /* SW Handler */                     \n\
 	.word   0\n\
-	/* External Interrupts */									 \n\
-	.word   WWDG_IRQHandler		 	/* Window Watchdog */				\n\
-	.word   PVD_IRQHandler		  	/* PVD through EXTI Line detect */   \n\
-	.word   FLASH_IRQHandler			/* Flash */						  \n\
-	.word   RCC_IRQHandler		  	/* RCC */							\n\
-	.word   EXTI7_0_IRQHandler	   	/* EXTI Line 7..0 */				 \n\
-	.word   AWU_IRQHandler			  /* AWU */							\n\
-	.word   DMA1_Channel1_IRQHandler   	/* DMA1 Channel 1 */				 \n\
-	.word   DMA1_Channel2_IRQHandler   	/* DMA1 Channel 2 */				 \n\
-	.word   DMA1_Channel3_IRQHandler   	/* DMA1 Channel 3 */				 \n\
-	.word   DMA1_Channel4_IRQHandler   	/* DMA1 Channel 4 */				 \n\
-	.word   DMA1_Channel5_IRQHandler   	/* DMA1 Channel 5 */				 \n\
-	.word   DMA1_Channel6_IRQHandler   	/* DMA1 Channel 6 */				 \n\
-	.word   DMA1_Channel7_IRQHandler   	/* DMA1 Channel 7 */				 \n\
-	.word   ADC1_IRQHandler		  	/* ADC1 */						   \n\
-	.word   I2C1_EV_IRQHandler		 	/* I2C1 Event */					 \n\
-	.word   I2C1_ER_IRQHandler		 	/* I2C1 Error */					 \n\
-	.word   USART1_IRQHandler		  	/* USART1 */						 \n\
-	.word   SPI1_IRQHandler				/* SPI1 */						   \n\
-	.word   TIM1_BRK_IRQHandler			/* TIM1 Break */					 \n\
-	.word   TIM1_UP_IRQHandler		 	/* TIM1 Update */					\n\
-	.word   TIM1_TRG_COM_IRQHandler		/* TIM1 Trigger and Commutation */   \n\
-	.word   TIM1_CC_IRQHandler		 	/* TIM1 Capture Compare */		   \n\
-	.word   TIM2_IRQHandler				/* TIM2 */						   \n" );
+	/* External Interrupts */                                              \n\
+	.word   WWDG_IRQHandler           /* Window Watchdog */                \n\
+	.word   PVD_IRQHandler            /* PVD through EXTI Line detect */   \n\
+	.word   FLASH_IRQHandler          /* Flash */                          \n\
+	.word   RCC_IRQHandler            /* RCC */                            \n\
+	.word   EXTI7_0_IRQHandler        /* EXTI Line 7..0 */                 \n\
+	.word   AWU_IRQHandler            /* AWU */                            \n\
+	.word   DMA1_Channel1_IRQHandler  /* DMA1 Channel 1 */                 \n\
+	.word   DMA1_Channel2_IRQHandler  /* DMA1 Channel 2 */                 \n\
+	.word   DMA1_Channel3_IRQHandler  /* DMA1 Channel 3 */                 \n\
+	.word   DMA1_Channel4_IRQHandler  /* DMA1 Channel 4 */                 \n\
+	.word   DMA1_Channel5_IRQHandler  /* DMA1 Channel 5 */                 \n\
+	.word   DMA1_Channel6_IRQHandler  /* DMA1 Channel 6 */                 \n\
+	.word   DMA1_Channel7_IRQHandler  /* DMA1 Channel 7 */                 \n\
+	.word   ADC1_IRQHandler           /* ADC1 */                           \n\
+	.word   I2C1_EV_IRQHandler        /* I2C1 Event */                     \n\
+	.word   I2C1_ER_IRQHandler        /* I2C1 Error */                     \n\
+	.word   USART1_IRQHandler         /* USART1 */                         \n\
+	.word   SPI1_IRQHandler           /* SPI1 */                           \n\
+	.word   TIM1_BRK_IRQHandler       /* TIM1 Break */                     \n\
+	.word   TIM1_UP_IRQHandler        /* TIM1 Update */                    \n\
+	.word   TIM1_TRG_COM_IRQHandler   /* TIM1 Trigger and Commutation */   \n\
+	.word   TIM1_CC_IRQHandler        /* TIM1 Capture Compare */           \n\
+	.word   TIM2_IRQHandler           /* TIM2 */                           \n");
 }
 
 
@@ -112,9 +115,7 @@ void handle_reset()
 	la gp, __global_pointer$\n\
 .option pop\n\
 	la sp, _eusrstack\n"
-
-	// Setup the interrupt vector.
-
+	// Setup the interrupt vector, processor status and INTSYSCR.
 "	li t0, 0x80\n\
 	csrw mstatus, t0\n\
 	li t0, 0x3\n\
@@ -122,32 +123,71 @@ void handle_reset()
 	la t0, InterruptVector\n\
 	ori t0, t0, 3\n\
 	csrw mtvec, t0\n"
+ );
+
+	// Careful: Use registers to prevent overwriting of self-data.
+	// This clears out BSS.
+	register uint32_t * tempout = _sbss;
+	register uint32_t * tempend = _ebss;
+	while( tempout < tempend )
+		*(tempout++) = 0;
 
-	// Clear BSS
-	// Has to be in assembly otherwise it will overwrite the stack.
-    "la t0, _sbss\n\
-    la t1, _ebss\n\
-    bgeu t0, t1, 2f\n\
-1:\n\
-    sw zero, (t0)\n\
-    addi t0, t0, 4\n\
-    bltu t0, t1, 1b\n\
-2:" );
+	// Once we get here, it should be safe to execute regular C code.
 
 	// Load data section from flash to RAM 
-	uint32_t * tempin = _data_lma;
-	uint32_t * tempout = _data_vma;
-	while( tempout != _edata )
+	register uint32_t * tempin = _data_lma;
+	tempout = _data_vma;
+	tempend = _edata;
+	while( tempout != tempend )
 		*(tempout++) = *(tempin++); 
 
-	SystemInit();
+	__set_MEPC( (uint32_t)main );
+
+	// set mepc to be main as the root app.
+	asm volatile( "mret\n" );
+}
+
+void SystemInit48HSI( void )
+{
+	// Values lifted from the EVT.  There is little to no documentation on what this does.
+	RCC->CTLR  = RCC_HSION | RCC_PLLON; 				// Use HSI, but enable PLL.
+	RCC->CFGR0 = RCC_HPRE_DIV1 | RCC_PLLSRC_HSI_Mul2;	// PLLCLK = HSI * 2 = 48 MHz; HCLK = SYSCLK = APB1
+	FLASH->ACTLR = FLASH_ACTLR_LATENCY_1;				// 1 Cycle Latency
+	RCC->INTR  = 0x009F0000;                            // Clear PLL, CSSC, HSE, HSI and LSI ready flags.
+
+	// From SetSysClockTo_48MHZ_HSI
+	while((RCC->CTLR & RCC_PLLRDY) == 0);														// Wait till PLL is ready
+	RCC->CFGR0 = ( RCC->CFGR0 & ((uint32_t)~(RCC_SW))) | (uint32_t)RCC_SW_PLL;					// Select PLL as system clock source
+	while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08);									// Wait till PLL is used as system clock source
+}
 
-	// set mepc to be main as the root interrupt.
-asm volatile( "\n\
-	la t0, main\n\
-	csrw mepc, t0\n\
-	mret\n" );
+void SetupUART( int uartBRR )
+{
+	// Enable GPIOD and UART.
+	RCC->APB2PCENR |= RCC_APB2Periph_GPIOD | RCC_APB2Periph_USART1;
+
+	// Push-Pull, 10MHz Output, GPIO D5, with AutoFunction
+	GPIOD->CFGLR &= ~(0xf<<(4*5));
+	GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*5);
+	
+	// 115200, 8n1.  Note if you don't specify a mode, UART remains off even when UE_Set.
+	USART1->CTLR1 = USART_WordLength_8b | USART_Parity_No | USART_Mode_Tx;
+	USART1->CTLR2 = USART_StopBits_1;
+	USART1->CTLR3 = USART_HardwareFlowControl_None;
+
+	USART1->BRR = uartBRR;
+	USART1->CTLR1 |= CTLR1_UE_Set;
 }
 
 
+// For debug writing to the UART.
+int _write(int fd, char *buf, int size)
+{
+	for(int i = 0; i < size; i++){
+	    while( !(USART1->STATR & USART_FLAG_TC));
+	    USART1->DATAR = *buf++;
+	}
+	return size;
+}
+
 
diff --git a/examples/blink/blink.c b/examples/blink/blink.c
index b17372a6d6694539a626c20f5f74d2c2a4e7be14..e99ce3f3e8bf5141451be8caf9249266c6c8267a 100644
--- a/examples/blink/blink.c
+++ b/examples/blink/blink.c
@@ -6,29 +6,10 @@
 
 #define APB_CLOCK SYSTEM_CORE_CLOCK
 
-void SystemInit(void)
-{
-	// Values lifted from the EVT.  There is little to no documentation on what this does.
-	RCC->CTLR |= (uint32_t)0x00000001;
-	RCC->CFGR0 &= (uint32_t)0xFCFF0000;
-	RCC->CTLR &= (uint32_t)0xFEF6FFFF;
-	RCC->CTLR &= (uint32_t)0xFFFBFFFF;
-	RCC->CFGR0 &= (uint32_t)0xFFFEFFFF;
-	RCC->INTR = 0x009F0000;
-
-	// From SetSysClockTo_48MHZ_HSI
-	// This is some dark stuff.  But, I copy-pasted it and it seems towork.
-	FLASH->ACTLR = (FLASH->ACTLR & ((uint32_t)~FLASH_ACTLR_LATENCY)) | FLASH_ACTLR_LATENCY_1; 	// Flash 0 wait state
-	RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; 														// HCLK = SYSCLK = APB1
-	RCC->CFGR0 = ( RCC->CFGR0 & ((uint32_t)~(RCC_PLLSRC)) ) | (uint32_t)(RCC_PLLSRC_HSI_Mul2); 	// PLL configuration: PLLCLK = HSI * 2 = 48 MHz
-	RCC->CTLR |= RCC_PLLON; 																	// Enable PLL
-	while((RCC->CTLR & RCC_PLLRDY) == 0);														// Wait till PLL is ready
-	RCC->CFGR0 = ( RCC->CFGR0 & ((uint32_t)~(RCC_SW))) | (uint32_t)RCC_SW_PLL;					// Select PLL as system clock source
-	while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08);									// Wait till PLL is used as system clock source
-}
-
 int main()
 {
+	SystemInit48HSI();
+
 	// Enable GPIOD.
 	RCC->APB2PCENR |= RCC_APB2Periph_GPIOD;
 
diff --git a/examples/fulldemo/fulldemo.c b/examples/fulldemo/fulldemo.c
index 534e97793a13d1153754865ad41aab018be16fef..340da898fbe9fae936d9966bc72d90ca98a5dc51 100644
--- a/examples/fulldemo/fulldemo.c
+++ b/examples/fulldemo/fulldemo.c
@@ -4,66 +4,16 @@
 
 // Could be defined here, or in the processor defines.
 #define SYSTEM_CORE_CLOCK 48000000
+#define APB_CLOCK SYSTEM_CORE_CLOCK
 
 #include "ch32v00x.h"
 #include <stdio.h>
 
-#define APB_CLOCK SYSTEM_CORE_CLOCK
-
-// For debug writing to the UART.
-int _write(int fd, char *buf, int size)
-{
-    for(int i = 0; i < size; i++){
-        while( !(USART1->STATR & USART_FLAG_TC));
-        USART1->DATAR = *buf++;
-    }
-    return size;
-}
-
-void SystemInit(void)
-{
-	// Values lifted from the EVT.  There is little to no documentation on what this does.
-	RCC->CTLR |= (uint32_t)0x00000001;
-	RCC->CFGR0 &= (uint32_t)0xFCFF0000;
-	RCC->CTLR &= (uint32_t)0xFEF6FFFF;
-	RCC->CTLR &= (uint32_t)0xFFFBFFFF;
-	RCC->CFGR0 &= (uint32_t)0xFFFEFFFF;
-	RCC->INTR = 0x009F0000;
-
-	// From SetSysClockTo_48MHZ_HSI
-	// This is some dark stuff.  But, I copy-pasted it and it seems towork.
-	FLASH->ACTLR = (FLASH->ACTLR & ((uint32_t)~FLASH_ACTLR_LATENCY)) | FLASH_ACTLR_LATENCY_1; 	// Flash 0 wait state
-	RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; 														// HCLK = SYSCLK = APB1
-	RCC->CFGR0 = ( RCC->CFGR0 & ((uint32_t)~(RCC_PLLSRC)) ) | (uint32_t)(RCC_PLLSRC_HSI_Mul2); 	// PLL configuration: PLLCLK = HSI * 2 = 48 MHz
-	RCC->CTLR |= RCC_PLLON; 																	// Enable PLL
-	while((RCC->CTLR & RCC_PLLRDY) == 0);														// Wait till PLL is ready
-	RCC->CFGR0 = ( RCC->CFGR0 & ((uint32_t)~(RCC_SW))) | (uint32_t)RCC_SW_PLL;					// Select PLL as system clock source
-	while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08);									// Wait till PLL is used as system clock source
-
-	// Once clock is up and running, we can enable the UART for other debugging.
-
-	// Enable GPIOD and UART.
-	RCC->APB2PCENR |= RCC_APB2Periph_GPIOD | RCC_APB2Periph_USART1;
-
-	// Push-Pull, 10MHz Output, GPIO D5, with AutoFunction
-	GPIOD->CFGLR &= ~(0xf<<(4*5));
-	GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*5);
-	
-	// 115200, 8n1.  Note if you don't specify a mode, UART remains off even when UE_Set.
-	USART1->CTLR1 = USART_WordLength_8b | USART_Parity_No | USART_Mode_Tx;
-	USART1->CTLR2 = USART_StopBits_1;
-	USART1->CTLR3 = USART_HardwareFlowControl_None;
-
-	#define UART_BAUD_RATE 115200
-	#define OVER8DIV 4
-	#define INTEGER_DIVIDER (((25 * (APB_CLOCK)) / (OVER8DIV * (UART_BAUD_RATE))))
-	#define FRACTIONAL_DIVIDER ((INTEGER_DIVIDER)%100)
-	USART1->BRR = ((INTEGER_DIVIDER / 100) << 4) | ((((FRACTIONAL_DIVIDER * (OVER8DIV*2)) + 50)/100)&7);
-	USART1->CTLR1 |= CTLR1_UE_Set;
-}
-
 int main()
 {
+	SystemInit48HSI();
+	SetupUART( UART_BRR );
+
 	// Enable GPIOD.
 	RCC->APB2PCENR |= RCC_APB2Periph_GPIOD;
 
diff --git a/examples/sandbox/sandbox.c b/examples/sandbox/sandbox.c
index d60f918eb2387c057ca9136b5c8fa60ce1d383c1..30cea301f513dddec128748b53b1d00bc704be7e 100644
--- a/examples/sandbox/sandbox.c
+++ b/examples/sandbox/sandbox.c
@@ -1,68 +1,18 @@
 // Could be defined here, or in the processor defines.
 #define SYSTEM_CORE_CLOCK 48000000
+#define APB_CLOCK SYSTEM_CORE_CLOCK
 
 #include "ch32v00x.h"
 #include <stdio.h>
 #include <string.h>
 
-#define APB_CLOCK SYSTEM_CORE_CLOCK
-
 // Working on WS2812 driving.
 
-// For debug writing to the UART.
-int _write(int fd, char *buf, int size)
-{
-    for(int i = 0; i < size; i++){
-        while( !(USART1->STATR & USART_FLAG_TC));
-        USART1->DATAR = *buf++;
-    }
-    return size;
-}
-
-
-void SystemInit(void)
-{
-	// Values lifted from the EVT.  There is little to no documentation on what this does.
-	RCC->CTLR |= (uint32_t)0x00000001;
-	RCC->CFGR0 &= (uint32_t)0xFCFF0000;
-	RCC->CTLR &= (uint32_t)0xFEF6FFFF;
-	RCC->CTLR &= (uint32_t)0xFFFBFFFF;
-	RCC->CFGR0 &= (uint32_t)0xFFFEFFFF;
-	RCC->INTR = 0x009F0000;
-
-	// From SetSysClockTo_48MHZ_HSI
-	// This is some dark stuff.  But, I copy-pasted it and it seems towork.
-	FLASH->ACTLR = (FLASH->ACTLR & ((uint32_t)~FLASH_ACTLR_LATENCY)) | FLASH_ACTLR_LATENCY_1; 	// Flash 0 wait state
-	RCC->CFGR0 = ( RCC->CFGR0 & ((uint32_t)~(RCC_PLLSRC)) ) | (uint32_t)(RCC_PLLSRC_HSI_Mul2); 	// PLL configuration: PLLCLK = HSI * 2 = 48 MHz
-	RCC->CTLR |= RCC_PLLON; 																	// Enable PLL
-	while((RCC->CTLR & RCC_PLLRDY) == 0);														// Wait till PLL is ready
-	RCC->CFGR0 = ( RCC->CFGR0 & ((uint32_t)~(RCC_SW))) | (uint32_t)RCC_SW_PLL;					// Select PLL as system clock source
-	while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08);									// Wait till PLL is used as system clock source
-
-	// Once clock is up and running, we can enable the UART for other debugging.
-
-	// Enable GPIOD and UART.
-	RCC->APB2PCENR |= RCC_APB2Periph_GPIOD | RCC_APB2Periph_USART1;
-
-	// Push-Pull, 10MHz Output, GPIO D5, with AutoFunction
-	GPIOD->CFGLR &= ~(0xf<<(4*5));
-	GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*5);
-	
-	// 115200, 8n1.  Note if you don't specify a mode, UART remains off even when UE_Set.
-	USART1->CTLR1 = USART_WordLength_8b | USART_Parity_No | USART_Mode_Tx;
-	USART1->CTLR2 = USART_StopBits_1;
-	USART1->CTLR3 = USART_HardwareFlowControl_None;
-
-	#define UART_BAUD_RATE 115200
-	#define OVER8DIV 4
-	#define INTEGER_DIVIDER (((25 * (APB_CLOCK)) / (OVER8DIV * (UART_BAUD_RATE))))
-	#define FRACTIONAL_DIVIDER ((INTEGER_DIVIDER)%100)
-	USART1->BRR = ((INTEGER_DIVIDER / 100) << 4) | ((((FRACTIONAL_DIVIDER * (OVER8DIV*2)) + 50)/100)&7);
-	USART1->CTLR1 |= CTLR1_UE_Set;
-}
-
 int main()
 {
+	SystemInit48HSI();
+	SetupUART( UART_BRR );
+
 	int k;
 
 	// Enable GPIOD (for debugging)
@@ -72,11 +22,12 @@ int main()
 	GPIOD->BSHR = 1; // Turn on GPIOD0
 	GPIOD->BSHR = 1<<16; // Turn off GPIOD0
 
-	printf( "CFG: %08x\n", SPI1->CTLR1 );
-
 	while(1)
 	{
-		Delay_Ms( 100 );
+		Delay_Ms( 10 );
+		uint32_t val = *(uint32_t*)0xe0000000;
+		*(uint32_t*)0xe0000000 = 0xaabbccdd;
+		printf( "0xe0000000: %08x\n", val);
 	}
 }
 
diff --git a/examples/ws2812demo/ws2812b_dma_spi_led_driver.h b/examples/ws2812demo/ws2812b_dma_spi_led_driver.h
index 8d1826403fcea9feb8688265bbe2053df6d2f2f8..d47d8a0090217cac540fe680878284d0bfe60ad4 100644
--- a/examples/ws2812demo/ws2812b_dma_spi_led_driver.h
+++ b/examples/ws2812demo/ws2812b_dma_spi_led_driver.h
@@ -2,6 +2,8 @@
    I may write another version of this to use DMA to timer ports, but, the SPI port can be used
    to generate outputs very efficiently. So, for now, SPI Port.  Additionally, it uses FAR less
    internal bus resources than to do the same thing with timers.
+   
+   For the CH32V003 this means output will be on PORTC Pin 6
 
    Copyright 2023 <>< Charles Lohr, under the MIT-x11 or NewBSD License, you choose!
 
diff --git a/examples/ws2812demo/ws2812bdemo.c b/examples/ws2812demo/ws2812bdemo.c
index a2d21acb220bd76e096d919c22f8213e3898b5a7..9f2d407e895ec50f13beafbd7a0d588269c8bb6b 100644
--- a/examples/ws2812demo/ws2812bdemo.c
+++ b/examples/ws2812demo/ws2812bdemo.c
@@ -1,5 +1,6 @@
 // Could be defined here, or in the processor defines.
 #define SYSTEM_CORE_CLOCK 48000000
+#define APB_CLOCK SYSTEM_CORE_CLOCK
 
 #include "ch32v00x.h"
 #include <stdio.h>
@@ -9,62 +10,8 @@
 
 #include "ws2812b_dma_spi_led_driver.h"
 
-#define APB_CLOCK SYSTEM_CORE_CLOCK
-
 #include "color_utilities.h"
 
-// For debug writing to the UART.
-int _write(int fd, char *buf, int size)
-{
-    for(int i = 0; i < size; i++){
-        while( !(USART1->STATR & USART_FLAG_TC));
-        USART1->DATAR = *buf++;
-    }
-    return size;
-}
-
-
-void SystemInit(void)
-{
-	// Values lifted from the EVT.  There is little to no documentation on what this does.
-	RCC->CTLR |= (uint32_t)0x00000001;
-	RCC->CFGR0 &= (uint32_t)0xFCFF0000;
-	RCC->CTLR &= (uint32_t)0xFEF6FFFF;
-	RCC->CTLR &= (uint32_t)0xFFFBFFFF;
-	RCC->CFGR0 &= (uint32_t)0xFFFEFFFF;
-	RCC->INTR = 0x009F0000;
-
-	// From SetSysClockTo_48MHZ_HSI
-	// This is some dark stuff.  But, I copy-pasted it and it seems towork.
-	FLASH->ACTLR = (FLASH->ACTLR & ((uint32_t)~FLASH_ACTLR_LATENCY)) | FLASH_ACTLR_LATENCY_1; 	// Flash 0 wait state
-	RCC->CFGR0 = ( RCC->CFGR0 & ((uint32_t)~(RCC_PLLSRC)) ) | (uint32_t)(RCC_PLLSRC_HSI_Mul2); 	// PLL configuration: PLLCLK = HSI * 2 = 48 MHz
-	RCC->CTLR |= RCC_PLLON; 																	// Enable PLL
-	while((RCC->CTLR & RCC_PLLRDY) == 0);														// Wait till PLL is ready
-	RCC->CFGR0 = ( RCC->CFGR0 & ((uint32_t)~(RCC_SW))) | (uint32_t)RCC_SW_PLL;					// Select PLL as system clock source
-	while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08);									// Wait till PLL is used as system clock source
-
-	// Once clock is up and running, we can enable the UART for other debugging.
-
-	// Enable GPIOD and UART.
-	RCC->APB2PCENR |= RCC_APB2Periph_GPIOD | RCC_APB2Periph_USART1;
-
-	// Push-Pull, 10MHz Output, GPIO D5, with AutoFunction
-	GPIOD->CFGLR &= ~(0xf<<(4*5));
-	GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*5);
-	
-	// 115200, 8n1.  Note if you don't specify a mode, UART remains off even when UE_Set.
-	USART1->CTLR1 = USART_WordLength_8b | USART_Parity_No | USART_Mode_Tx;
-	USART1->CTLR2 = USART_StopBits_1;
-	USART1->CTLR3 = USART_HardwareFlowControl_None;
-
-	#define UART_BAUD_RATE 115200
-	#define OVER8DIV 4
-	#define INTEGER_DIVIDER (((25 * (APB_CLOCK)) / (OVER8DIV * (UART_BAUD_RATE))))
-	#define FRACTIONAL_DIVIDER ((INTEGER_DIVIDER)%100)
-	USART1->BRR = ((INTEGER_DIVIDER / 100) << 4) | ((((FRACTIONAL_DIVIDER * (OVER8DIV*2)) + 50)/100)&7);
-	USART1->CTLR1 |= CTLR1_UE_Set;
-}
-
 #define NR_LEDS 191
 
 uint16_t phases[NR_LEDS];
@@ -87,16 +34,16 @@ uint32_t WS2812BLEDCallback( int ledno )
 int main()
 {
 	int k;
+	SystemInit48HSI();
 
 	// Enable GPIOD (for debugging)
 	RCC->APB2PCENR |= RCC_APB2Periph_GPIOD;
 	GPIOD->CFGLR &= ~(0xf<<(4*0));
 	GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*0);
 
+	GPIOD->BSHR = 1;	 // Turn on GPIOD0
 	WS2812BDMAInit( );
 
-	printf( "CFG: %08x\n", SPI1->CTLR1 );
-
 	frameno = 0;
 
 	for( k = 0; k < NR_LEDS; k++ ) phases[k] = k<<8;
@@ -106,8 +53,11 @@ int main()
 
 	while(1)
 	{
+	
+		GPIOD->BSHR = 1;	 // Turn on GPIOD0
 		// Wait for LEDs to totally finish.
 		Delay_Ms( 12 );
+		GPIOD->BSHR = 1<<16; // Turn it off
 
 		while( WS2812BLEDInUse );
 
diff --git a/minichlink/Makefile b/minichlink/Makefile
index bdc97bc760d8e1d5d020f7a5d86632d88a97a296..5706a90ef536e4f396db4b089916757a2145103f 100644
--- a/minichlink/Makefile
+++ b/minichlink/Makefile
@@ -1,4 +1,4 @@
-TOOLS:=wch_erase wch_reset wch_write_simple minichlink
+TOOLS:=wch_erase wch_reset wch_write_simple wch_printf minichlink
 
 all : $(TOOLS)
 
@@ -9,6 +9,8 @@ wch_erase : wch_erase.c
 	gcc -o $@ $^ $(LDFLAGS) $(CFLAGS)
 wch_reset : wch_reset.c
 	gcc -o $@ $^ $(LDFLAGS) $(CFLAGS)
+wch_printf : wch_printf.c
+	gcc -o $@ $^ $(LDFLAGS) $(CFLAGS)
 wch_write_simple : wch_write_simple.c
 	gcc -o $@ $^ $(LDFLAGS) $(CFLAGS)
 minichlink : minichlink.c
@@ -16,6 +18,7 @@ minichlink : minichlink.c
 
 install_udev_rules :
 	echo "SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"1a86\", ATTRS{idProduct}==\"8010\", GROUP=\"plugdev\", MODE=\"0666\"" > /etc/udev/rules.d/99-qch-LinkE.rules
+	service udev restart
 
 clean :
 	rm -rf $(TOOLS)
diff --git a/minichlink/wch_printf.c b/minichlink/wch_printf.c
new file mode 100644
index 0000000000000000000000000000000000000000..1d78ee8befd5eb0d9ea5deb226dabfee5615beec
--- /dev/null
+++ b/minichlink/wch_printf.c
@@ -0,0 +1,35 @@
+#include <stdio.h>
+#include "wch_link_base.h"
+
+// TESTED
+
+int main()
+{
+	libusb_device_handle * devh = wch_link_base_setup();
+
+	// Issue reset
+//	wch_link_command( devh, "\x81\x0b\x01\x01", 4, 0, 0, 0 );
+	// Why does db[1] = 6 appear to be some sort of query?
+	// Also 0x0b appears to be a query.  But it wrecks up the chip.
+	// db[1] = 0xd DOES WRITE TO 0xe0000000.  But is it predictable?
+	// DO NOT 0x0f!
+	unsigned char databuff[11] = { 0x81, 0x0d, 0x08, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+	unsigned char rbuff[1024];
+	int i, j;
+	for( i = 1; i < 20; i++ )
+	for( j = 1; j < 20; j++ )
+	{
+		databuff[2] = i;
+		databuff[3] = j;
+		int transferred;
+		wch_link_command( devh, databuff, 11, &transferred, rbuff, 1024 );
+		int k;
+		printf( "%d, %d: %d: ", i, j, transferred );
+		for( k = 0; k < transferred; k++ ) printf( "%02x ", rbuff[k] );
+		printf( "\n" );
+		usleep(10000);
+	}
+
+	// Close out.
+	wch_link_command( devh, "\x81\x0d\x01\xff", 4, 0, 0, 0 );
+}