From f25573b8d159f6b37f49f853da1294906387b5aa Mon Sep 17 00:00:00 2001
From: pio <pio@server.local>
Date: Wed, 19 Jul 2023 15:12:50 +0200
Subject: [PATCH] sysclk fixes and example project

---
 ch32v003fun/ch32v003fun.c              |  75 ++++++---
 ch32v003fun/ch32v003fun.h              |  12 ++
 examples/sysclk_config/Makefile        |   9 ++
 examples/sysclk_config/funconfig.h     |  11 ++
 examples/sysclk_config/readme.md       |  58 +++++++
 examples/sysclk_config/sysclk_config.c | 208 +++++++++++++++++++++++++
 6 files changed, 354 insertions(+), 19 deletions(-)
 create mode 100644 examples/sysclk_config/Makefile
 create mode 100644 examples/sysclk_config/funconfig.h
 create mode 100644 examples/sysclk_config/readme.md
 create mode 100644 examples/sysclk_config/sysclk_config.c

diff --git a/ch32v003fun/ch32v003fun.c b/ch32v003fun/ch32v003fun.c
index 12f693b..5da6a51 100644
--- a/ch32v003fun/ch32v003fun.c
+++ b/ch32v003fun/ch32v003fun.c
@@ -684,7 +684,24 @@ void DefaultIRQHandler( void )
 
 // This makes it so that all of the interrupt handlers just alias to
 // DefaultIRQHandler unless they are individually overridden.
+
+#if defined(FUNCONF_USE_CLK_SEC) && FUNCONF_USE_CLK_SEC
+/**
+ * @brief 	Non Maskabke Interrupt handler
+ * 			Invoked when the Clock Security system
+ * 			detects the failure of the HSE oscilator.
+ * 			The sys clock is switched to HSI.
+ * 			Clears the CSSF flag in RCC->INTR
+ */
+void NMI_RCC_CSS_IRQHandler( void )
+{
+	RCC->INTR |= RCC_CSSC;	// clear the clock security int flag
+}
+
+void NMI_Handler( void ) 				 __attribute__((section(".text.vector_handler"))) __attribute((weak,alias("NMI_RCC_CSS_IRQHandler"))) __attribute__((used));
+#else 
 void NMI_Handler( void )                 __attribute__((section(".text.vector_handler"))) __attribute((weak,alias("DefaultIRQHandler"))) __attribute__((used));
+#endif
 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));
 void SW_Handler( void )                  __attribute__((section(".text.vector_handler"))) __attribute((weak,alias("DefaultIRQHandler"))) __attribute__((used));
@@ -999,36 +1016,59 @@ void SystemInit()
 #else
 	#define HSEBYP 0
 #endif
+// set the correct clock source for the PLL
+#if defined(FUNCONF_USE_HSE) && FUNCONF_USE_HSE
+    #define PLL_SRC RCC_PLLSRC_HSE_Mul2
+#endif
+#if defined(FUNCONF_USE_HSI) && FUNCONF_USE_HSI
+    #define PLL_SRC RCC_PLLSRC_HSI_Mul2
+#endif
 
-	#if defined(FUNCONF_USE_PLL) && FUNCONF_USE_PLL
-		#define BASE_CFGR0 RCC_HPRE_DIV1 | RCC_PLLSRC_HSI_Mul2    // HCLK = SYSCLK = APB1 And, enable PLL
-	#else
-		#define BASE_CFGR0 RCC_HPRE_DIV1      // HCLK = SYSCLK = APB1 And, no pll.
-	#endif
+#if defined(FUNCONF_USE_PLL) && FUNCONF_USE_PLL
+    #define BASE_CFGR0 (RCC_HPRE_DIV1 | PLL_SRC)                // HCLK = SYSCLK = APB1 And, enable PLL
+#else
+    #define BASE_CFGR0 (RCC_HPRE_DIV1)     						// HCLK = SYSCLK = APB1 And, no PLL
+#endif
+
+#if defined(FUNCONF_USE_CLK_SEC) && FUNCONF_USE_CLK_SEC
+	#define RCC_CSS RCC_CSSON									// Enable clock security system
+#else
+	#define RCC_CSS 0
+#endif
+
+// HSI always ON - needed for the Debug subsystem
+#define BASE_CTLR	(((FUNCONF_HSITRIM) << 3) | RCC_HSION | HSEBYP | RCC_CSS)
+//#define BASE_CTLR	(((FUNCONF_HSITRIM) << 3) | HSEBYP | RCC_CSS)	// disable HSI in HSE modes
 
 #if defined(FUNCONF_USE_HSI) && FUNCONF_USE_HSI
 	#if defined(FUNCONF_USE_PLL) && FUNCONF_USE_PLL
 		RCC->CFGR0 = BASE_CFGR0;
-		RCC->CTLR  = RCC_HSION | RCC_PLLON | ((FUNCONF_HSITRIM) << 3); // Use HSI, but enable PLL.
+		RCC->CTLR  = BASE_CTLR | RCC_HSION | RCC_PLLON; 			// Use HSI, enable PLL.
 	#else
-		RCC->CFGR0 = BASE_CFGR0;                                // PLLCLK = HCLK = SYSCLK = APB1
-		RCC->CTLR  = RCC_HSION | ((FUNCONF_HSITRIM) << 3);      // Use HSI, Only.
+		RCC->CFGR0 = BASE_CFGR0;                               		// PLLCLK = HCLK = SYSCLK = APB1
+		RCC->CTLR  = BASE_CTLR | RCC_HSION;     					 // Use HSI, Only.
 	#endif
 #endif
 
 #if defined(FUNCONF_USE_HSE) && FUNCONF_USE_HSE
 
-	RCC->CTLR  = RCC_HSION | RCC_HSEON | RCC_PLLON | HSEBYP;       // Keep HSI and PLL on just in case, while turning on HSE
-
-	// Values lifted from the EVT.  There is little to no documentation on what this does.
-	while(!(RCC->CTLR&RCC_HSERDY));
+    RCC->CFGR0 = RCC_HPRE_DIV1; 
+    RCC->CTLR  = BASE_CTLR | RCC_HSION;								// start with HSI first, no PLL
+    RCC->APB2PCENR |= RCC_APB2Periph_AFIO;							// enable AFIO
+    AFIO->PCFR1 |= GPIO_Remap_PA1_2;								// remap PA1 PA2 to XTAL
 
 	#if defined(FUNCONF_USE_PLL) && FUNCONF_USE_PLL
-		RCC->CFGR0 = BASE_CFGR0 | RCC_SW_HSE;
-		RCC->CTLR  = RCC_HSEON | RCC_PLLON | HSEBYP;                    // Turn off HSI.
+        RCC->CTLR  = BASE_CTLR | RCC_HSION | RCC_HSEON;				// Keep HSI and PLL on just in case, while turning on HSE
+        while(!(RCC->CTLR&RCC_HSERDY));								// wait untill the HSE is ready
+        RCC->CFGR0 = BASE_CFGR0 | RCC_SW_HSE;						// switch the clock to HSE
+        while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x04);	// Wait till HSE is used as system clock source
+		RCC->CTLR  = BASE_CTLR | RCC_HSEON | RCC_PLLON ;			// enable PLL (switch off HSI - optional)
 	#else
-		RCC->CFGR0 = BASE_CFGR0 | RCC_SW_HSE;
-		RCC->CTLR  = RCC_HSEON | HSEBYP;                                // Turn off PLL and HSI.
+        RCC->CTLR  = BASE_CTLR | RCC_HSION | RCC_HSEON ;			// Keep HSI on while turning on HSE
+        while(!(RCC->CTLR&RCC_HSERDY));   							// Wait till HSE is ready
+        RCC->CFGR0 = BASE_CFGR0 | RCC_SW_HSE;						// Switch to HSE
+		while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x04);	// Wait till HSE is used as system clock source
+		RCC->CTLR  = BASE_CTLR | RCC_HSEON;							// (switch off HSI - optional)
 	#endif
 #endif
 
@@ -1038,11 +1078,9 @@ void SystemInit()
 	FLASH->ACTLR = FLASH_ACTLR_LATENCY_0;                   // +0 Cycle Latency
 #endif
 
-
 	RCC->INTR  = 0x009F0000;                               // Clear PLL, CSSC, HSE, HSI and LSI ready flags.
 
 #if defined(FUNCONF_USE_PLL) && FUNCONF_USE_PLL
-	// From SetSysClockTo_48MHZ_HSI
 	while((RCC->CTLR & RCC_PLLRDY) == 0);                       // Wait till PLL is ready
 	RCC->CFGR0 = BASE_CFGR0 | 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
@@ -1054,7 +1092,6 @@ void SystemInit()
 #if defined( FUNCONF_USE_DEBUGPRINTF ) && FUNCONF_USE_DEBUGPRINTF
 	SetupDebugPrintf();
 #endif
-
 }
 
 // C++ Support
diff --git a/ch32v003fun/ch32v003fun.h b/ch32v003fun/ch32v003fun.h
index c8b7f99..3e07c24 100644
--- a/ch32v003fun/ch32v003fun.h
+++ b/ch32v003fun/ch32v003fun.h
@@ -14,6 +14,7 @@
 #define FUNCONF_HSITRIM 0x10            // Use factory calibration on HSI Trim.
 #define FUNCONF_SYSTEM_CORE_CLOCK  48000000  // Computed Clock in Hz.
 #define FUNCONF_HSE_BYPASS 0            // Use HSE Bypass feature (for oscillator input)
+#define FUNCONF_USE_CLK_SEC	1			// Use clock security system, enabled by default
 #define FUNCONF_USE_DEBUGPRINTF 1
 #define FUNCONF_USE_UARTPRINTF  0
 #define FUNCONF_SYSTICK_USE_HCLK 0      // Should systick be at 48 MHz or 6MHz?
@@ -34,6 +35,9 @@
 	#define FUNCONF_DEBUGPRINTF_TIMEOUT 160000
 #endif
 
+#if defined(FUNCONF_USE_HSI) && defined(FUNCONF_USE_HSE) && FUNCONF_USE_HSI && FUNCONF_USE_HSE
+       #error FUNCONF_USE_HSI and FUNCONF_USE_HSE cannot both be set
+#endif
 
 #if !defined( FUNCONF_USE_HSI ) && !defined( FUNCONF_USE_HSE )
 	#define FUNCONF_USE_HSI 1 // Default to use HSI
@@ -44,6 +48,10 @@
 	#define FUNCONF_USE_PLL 1 // Default to use PLL
 #endif
 
+#if !defined( FUNCONF_USE_CLK_SEC )
+	#define FUNCONF_USE_CLK_SEC  1// use clock security system by default
+#endif	
+
 #ifndef HSE_VALUE
 	#define HSE_VALUE                 (24000000) // Value of the External oscillator in Hz, default
 #endif
@@ -5074,6 +5082,10 @@ extern "C" {
 #ifndef __ASSEMBLER__
 void handle_reset()            __attribute__((naked)) __attribute((section(".text.handle_reset"))) __attribute__((used));
 void DefaultIRQHandler( void ) __attribute__((section(".text.vector_handler"))) __attribute__((naked)) __attribute__((used));
+// used to clear the CSS flag in case of clock fail switch
+#if defined(FUNCONF_USE_CLK_SEC) && FUNCONF_USE_CLK_SEC
+	void NMI_RCC_CSS_IRQHandler( void ) __attribute__((section(".text.vector_handler"))) __attribute__((naked)) __attribute__((used));
+#endif
 #endif
 
 // For debug writing to the debug interface.
diff --git a/examples/sysclk_config/Makefile b/examples/sysclk_config/Makefile
new file mode 100644
index 0000000..106c4b2
--- /dev/null
+++ b/examples/sysclk_config/Makefile
@@ -0,0 +1,9 @@
+all : flash
+
+TARGET:=sysclk_config
+
+include ../../ch32v003fun/ch32v003fun.mk
+
+flash : cv_flash
+clean : cv_clean
+
diff --git a/examples/sysclk_config/funconfig.h b/examples/sysclk_config/funconfig.h
new file mode 100644
index 0000000..177c7ae
--- /dev/null
+++ b/examples/sysclk_config/funconfig.h
@@ -0,0 +1,11 @@
+#ifndef _FUNCONFIG_H
+#define _FUNCONFIG_H
+
+#define FUNCONF_USE_HSE 1  			// external crystal on PA1 PA2
+#define FUNCONF_USE_HSI 0    		// internal 24MHz clock oscillator
+#define FUNCONF_USE_PLL 1 			// use PLL x2
+#define FUNCONF_HSE_BYPASS 0 		// bypass the HSE when using an external clock source
+									// requires enabled HSE
+#define CH32V003        1
+
+#endif // _FUNCONFIG_Hma
diff --git a/examples/sysclk_config/readme.md b/examples/sysclk_config/readme.md
new file mode 100644
index 0000000..e764ffc
--- /dev/null
+++ b/examples/sysclk_config/readme.md
@@ -0,0 +1,58 @@
+# System Clock configuration example  
+This project demonstrates various system clock configurations for the CH32V003 MCU and shows a way to unbrick the MCU in case the debug system is not responding.  
+### SysClk configuration
+There are two main sources for the internal clock:  
+1. HSI - internal 24MHz oscillator
+2. HSE - external crystal or clock source  
+
+HSE bypass has to be set if an external clock generator is used - supplying the clock signal  to the OSCI/PA1 pin.
+
+These two sources can feed the PLL (x2) and once the PLL is stable it can be used as 3rd sysclk source.  
+
+Configuring the clock happens in the `funconfig.h` file:  
+```
+#define FUNCONF_USE_HSE 1  			// external crystal on PA1 PA2
+#define FUNCONF_USE_HSI 0    		// internal 24MHz clock oscillator
+#define FUNCONF_USE_PLL 1 			// use PLL x2
+#define FUNCONF_HSE_BYPASS 0 		// bypass the HSE when using an external clock source
+									// requires enabled HSE
+#define CH32V003        1
+```
+In the above example the MCU is clocked from the onboard 24MHz crystal multiplied x2 by the PLL resulting in a 48MHz system clock.  
+Use `make flash monitor` to compile/upload the firmware and open the debug printf terminal. It should print out all the details about the current clock configuration:  
+```
+System Clock Configuration Example
+====================================
+HSI Enabled
+HSI trim = 16
+HSI cal = 36
+HSE Enabled
+PLL Enabled
+PLL Source = HSE
+Clk Security Enabled
+HSE bypass Disabled
+Sysclk source = PLL 
+MCO output: Disabled
+```  
+## More details about the clock system  
+According to the [debug system datasheet](https://github.com/openwch/ch32v003/blob/main/RISC-V%20QingKeV2%20Microprocessor%20Debug%20Manual.pdf) for the CH32V003, the HSI is required to access the chip via SWIO pin. The examples provided by WCH confirm it, in their code configuring the HSE modes does not switch the HSI off, it's left always on. This poses a problem for low power projects when all non required subsystems should be turned off. The real measured difference between the HSE only and with HSI running in background was about 800µA.  
+Once the HSI is disabled, the power cycled, the programmer no longer can access the chip via SWIO pin, resulting in error mesages and known "bricking". 
+"Unbricking" using the available programmers and software in most cases works. However. i've encountered situations when only using a combination of WCHLinkE and the WCHLinkUtility software on Windows was able to revive the MCU.  
+As a countermeasure the CSS (Clock Security System) is enabled and allows emergency HSI restore action. The CSS monitors the HSE and if it fails, the HSI is turned on back by the hardware and set as the sysclock source. This reenables the debug channel.  
+A way to reliable "unbrick" the MCU if the HSI has been disabled is to **make the HSE fail by shorting the OSCI/PA1 pin to GND**. Once done, the MCU will run on half the clock rate if the PLL was enabled. HSI will be active and the used can access the chip using usual methods.  
+## MCO - clock output  
+MCU clock output (pin PC4) can be configure using the following funtion:
+
+`MCO_cfg(cfg);`  
+where cfg value is one from the list below:
+```
+MCO_DISABLED 
+MCO_OUT_SYSCLK
+MCO_OUT_HSI	
+MCO_OUT_HSE	
+MCO_OUT_PLL	
+```  
+## Reuse printf for UART  
+Project also shows how to reuse the printf implementation for both channels: via debug line and UART. Experimenting with clock sources might end up with debug channel disabled, UART being the only way to get out the messages.  
+
+
diff --git a/examples/sysclk_config/sysclk_config.c b/examples/sysclk_config/sysclk_config.c
new file mode 100644
index 0000000..eb20eb9
--- /dev/null
+++ b/examples/sysclk_config/sysclk_config.c
@@ -0,0 +1,208 @@
+/**
+ * @file sysclk_config.c
+ * @author Piotr Zapart
+ * @brief Testing various sysclock configurations for the ch32v003
+ * 			LED Pin definition for the NanoCH32V003 board
+ * 			Try different conbinations in the funconfig.h file, ie:
+ * 
+ * 				#define FUNCONF_USE_HSI 1    
+ *				#define FUNCONF_USE_PLL 1       
+ *				#define CH32V003        1
+ * 
+ * 				#define FUNCONF_USE_HSE 1    
+ *				#define FUNCONF_USE_PLL 0       
+ *				#define CH32V003        1
+ * 
+ * @version 1.0
+ * @date 2023-07-19
+ */
+
+// LED is on D.6 (nanoCH32 board)
+#define LED_PIN     6				
+
+#include <stdarg.h>
+#include "ch32v003fun.h"
+#include <stdio.h>
+#include "ch32v003_GPIO_branchless.h"
+
+const char msg_sep[] = "====================================\r\n";
+const char msg_title[] = "System Clock Configuration Example\r\n";
+const char msg_en[] = "Enabled";
+const char msg_dis[] = "Disabled";
+const char *msg_clkSrc[] = {"HSI ", "HSE" , "PLL "};
+const char *msg_mco[] = {"Disabled", "SYSCLK", "HSI 24MHz", "HSE", "PLL"};
+const char msg_clkFail[] = "HSE Fail, switched to HSI, no PLL\r\n";
+
+typedef enum
+{
+	SYSCLK_HSI, 
+	SYSCLK_HSI_PLL,
+	SYSCLK_HSE,
+	SYSCLK_HSE_PLL,
+	SYSCLK_EXTCLK,
+	SYSCLK_EXTCLK_PLL
+}rcc_sysclk_status_e;
+
+typedef enum
+{
+	MCO_DISABLED 		= 0,
+	MCO_OUT_SYSCLK 		= (4<<24),
+	MCO_OUT_HSI			= (5<<24),
+	MCO_OUT_HSE			= (6<<24),
+	MCO_OUT_PLL			= (7<<24)
+}mco_cfg_e;
+
+void print_sysclk_cfg(void);
+static inline void MCO_cfg(mco_cfg_e cfg);
+static inline uint8_t getMCOidx(uint32_t mco);
+void UART_setup(int uartBRR);
+int UART_write(int fd, const char *buf, int size);
+static int UART_puts(char *s, int len, void *buf);
+int UART_printf(const char* format, ...);		
+int mini_vpprintf(int (*puts)(char* s, int len, void* buf), void* buf, const char *fmt, va_list va);
+
+// --------------------------------------------------------
+static inline void MCO_cfg(mco_cfg_e cfg)
+{
+	RCC->CFGR0 &= ~RCC_CFGR0_MCO;
+	RCC->CFGR0 |=  cfg & RCC_CFGR0_MCO;
+}
+// --------------------------------------------------------
+static inline uint8_t getMCOidx(uint32_t mco)
+{
+	mco >>= 24;
+	return (mco ? mco-3 : mco);
+}
+// --------------------------------------------------------
+/**
+ * @brief prints out al the clock related details on both
+ * 		channels: debug printf and UART in cas the HSI is
+ * 		disabled and the debug channel can no longer work.
+ */
+void print_sysclk_cfg(void)
+{
+	uint32_t ctlr = RCC->CTLR;
+	uint32_t cfgr0 = RCC->CFGR0;
+	const char *msg_out;
+	uint32_t tmp;
+
+	printf("%s", msg_sep); UART_printf("%s", msg_sep);
+	printf("%s", msg_title); UART_printf("%s", msg_title);
+	printf("%s", msg_sep); UART_printf("%s", msg_sep);
+	// HSI state
+	msg_out = ctlr & RCC_HSION ? msg_en : msg_dis;
+	printf("HSI %s\r\n", msg_out); 
+	UART_printf("HSI %s\r\n", msg_out);
+	// HSI trim value
+	tmp = (ctlr & RCC_HSITRIM)>>3;
+	printf("HSI trim = %ld\r\n", tmp); 
+	UART_printf("HSI trim = %ld\r\n",tmp); 
+	// HSI callibration value
+	tmp = (ctlr & RCC_HSICAL)>>8;
+	printf("HSI cal = %ld\r\n",tmp); 
+	UART_printf("HSI cal = %ld\r\n",tmp);
+	// HSE state
+	msg_out = ctlr & RCC_HSEON ? msg_en : msg_dis;
+	printf("HSE %s\r\n", msg_out); 
+	UART_printf("HSE %s\r\n", msg_out);
+	// PLL state
+	msg_out = ctlr & RCC_PLLON ? msg_en : msg_dis;
+	printf("PLL %s\r\n", msg_out); 
+	UART_printf("PLL %s\r\n", msg_out);
+	// PLL Source
+	msg_out = msg_clkSrc[(cfgr0 & (1<<16))>>16];
+	printf("PLL Source = %s\r\n", msg_out); 
+	UART_printf("PLL Source = %s\r\n", msg_out);
+	// Clock Security system
+	msg_out = ctlr & RCC_CSSON ? msg_en : msg_dis;
+	printf("Clk Security %s\r\n", msg_out);
+	UART_printf("Clk Security %s\r\n", msg_out);
+	// HSE bypass
+	msg_out = ctlr & RCC_HSEBYP ? msg_en : msg_dis;
+	printf("HSE bypass %s\r\n", msg_out);
+	UART_printf("HSE bypass %s\r\n", msg_out);
+	// Sysclock source
+	msg_out = msg_clkSrc[(cfgr0 & 0x0C)>>2];
+	printf("Sysclk source = %s\r\n", msg_out);
+	UART_printf("Sysclk source = %s\r\n", msg_out);
+	// MCO setting
+	msg_out = msg_mco[getMCOidx(cfgr0 & RCC_CFGR0_MCO)];
+	printf("MCO output: %s\r\n", msg_out);	
+	UART_printf("MCO output: %s\r\n", msg_out);	
+}
+// --------------------------------------------------------
+void UART_setup( 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);
+	
+	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;
+}
+// --------------------------------------------------------
+int UART_write(int fd, const char *buf, int size)
+{
+	for(int i = 0; i < size; i++){
+	    while( !(USART1->STATR & USART_FLAG_TC));
+	    USART1->DATAR = *buf++;
+	}
+	return size;
+}
+// --------------------------------------------------------
+static int UART_puts(char *s, int len, void *buf)
+{
+	UART_write( 0, s, len );
+	return len;
+}
+// --------------------------------------------------------
+int UART_printf(const char* format, ...)
+{
+	va_list args;
+	va_start( args, format );
+	int ret_status = mini_vpprintf(UART_puts, 0, format, args);
+	va_end( args );
+	return ret_status;
+}
+// --------------------------------------------------------
+int main()
+{	
+	SystemInit();                                        
+	UART_setup(UART_BRR);
+	GPIO_port_enable(GPIO_port_D);
+    GPIO_pinMode(GPIOv_from_PORT_PIN(GPIO_port_D, LED_PIN), GPIO_pinMode_O_pushPull, GPIO_Speed_10MHz);
+	GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_D, LED_PIN), low);	
+	// needed for MCO output
+    GPIO_port_enable(GPIO_port_C);
+    GPIO_pinMode(GPIOv_from_PORT_PIN(GPIO_port_C, 4), GPIO_pinMode_O_pushPullMux, GPIO_Speed_50MHz);
+	
+	MCO_cfg(MCO_OUT_SYSCLK);
+
+	print_sysclk_cfg();
+
+	while(1)
+    {
+		// blink onboard led to show the mcu is running
+        GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_D, LED_PIN), high);
+        Delay_Ms(100);
+        GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_D, LED_PIN), low);
+        Delay_Ms(100);
+		// detect clock failsafe, if HSE was enabled but the current source is HSI/NoPLL -
+		// the clock security system did it's job.
+#if defined(FUNCONF_USE_HSE)		
+		if (FUNCONF_USE_HSE && (RCC->CFGR0 & 0x0C) == 0x00)
+		{
+			printf("%s%s%s", msg_sep, msg_clkFail, msg_sep);
+			// UART, depending on the crystal value and PLL setting, most likely will
+			// have bad baudrate setting unless the original clock was HSE only, 24MHz, No PLL
+			//UART_printf("%s%s%s", msg_sep, msg_clkFail, msg_sep);
+		}
+#endif		
+    }
+}
\ No newline at end of file
-- 
GitLab