From 689710f986d355cb95e289052ad9c442ee92aacf Mon Sep 17 00:00:00 2001
From: cnlohr <lohr85@gmail.com>
Date: Mon, 27 Feb 2023 19:50:51 -0800
Subject: [PATCH] Trying some things out

---
 ch32v003evt/ch32v003.ld                       |   2 +-
 ch32v003evt/ch32v00x_conf.h                   |  15 +-
 ch32v003evt/core_riscv.h                      |   4 +-
 ch32v003evt/startup_ch32v003.c                | 146 +++++++++++-------
 examples/blink/blink.c                        |  23 +--
 examples/fulldemo/fulldemo.c                  |  58 +------
 examples/sandbox/sandbox.c                    |  65 +-------
 .../ws2812demo/ws2812b_dma_spi_led_driver.h   |   2 +
 examples/ws2812demo/ws2812bdemo.c             |  62 +-------
 minichlink/Makefile                           |   5 +-
 minichlink/wch_printf.c                       |  35 +++++
 11 files changed, 171 insertions(+), 246 deletions(-)
 create mode 100644 minichlink/wch_printf.c

diff --git a/ch32v003evt/ch32v003.ld b/ch32v003evt/ch32v003.ld
index d25e60d..d3d2728 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 01e7132..217af40 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 2f533c3..2e4382d 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 8e486b8..c52564a 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 b17372a..e99ce3f 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 534e977..340da89 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 d60f918..30cea30 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 8d18264..d47d8a0 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 a2d21ac..9f2d407 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 bdc97bc..5706a90 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 0000000..1d78ee8
--- /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 );
+}
-- 
GitLab