From b705ade6a72f561c6eb3aa70e0a6218fcc0b43f4 Mon Sep 17 00:00:00 2001 From: pio <pio@server.local> Date: Thu, 20 Jul 2023 12:38:26 +0200 Subject: [PATCH] more HSE tests and custom NMI handler --- ch32v003fun/ch32v003fun.c | 4 +- examples/sysclk_config/funconfig.h | 4 +- examples/sysclk_config/readme.md | 5 ++ examples/sysclk_config/sysclk_config.c | 83 ++++++++++++++++++++++++-- 4 files changed, 89 insertions(+), 7 deletions(-) diff --git a/ch32v003fun/ch32v003fun.c b/ch32v003fun/ch32v003fun.c index afad2c0..ae83a41 100644 --- a/ch32v003fun/ch32v003fun.c +++ b/ch32v003fun/ch32v003fun.c @@ -1018,7 +1018,7 @@ void SystemInit() #endif #if defined(FUNCONF_USE_CLK_SEC) && FUNCONF_USE_CLK_SEC - #define RCC_CSS RCC_CSSON // Enable clock security system + #define RCC_CSS RCC_CSSON // Enable clock security system #else #define RCC_CSS 0 #endif @@ -1039,6 +1039,8 @@ void SystemInit() #if defined(FUNCONF_USE_HSE) && FUNCONF_USE_HSE // seems that remapping PA1_2 via AFIO is not required? + //RCC->APB2PCENR |= RCC_APB2Periph_AFIO; // enable AFIO + //AFIO->PCFR1 |= GPIO_Remap_PA1_2; // remap PA1 PA2 to XTAL 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 = RCC_PLLSRC_HSE_Mul2 | RCC_SW_HSE; // Switch to HSE and set the PLL source diff --git a/examples/sysclk_config/funconfig.h b/examples/sysclk_config/funconfig.h index e5c34d6..c47418b 100644 --- a/examples/sysclk_config/funconfig.h +++ b/examples/sysclk_config/funconfig.h @@ -1,8 +1,8 @@ #ifndef _FUNCONFIG_H #define _FUNCONFIG_H -#define FUNCONF_USE_HSE 0 // external crystal on PA1 PA2 -#define FUNCONF_USE_HSI 1 // internal 24MHz clock oscillator +#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 diff --git a/examples/sysclk_config/readme.md b/examples/sysclk_config/readme.md index 9ad74d6..5f6fb6a 100644 --- a/examples/sysclk_config/readme.md +++ b/examples/sysclk_config/readme.md @@ -53,6 +53,11 @@ MCO_OUT_HSI MCO_OUT_HSE MCO_OUT_PLL ``` +## Custom NMI interrupt handler +Uncommenting `#define USE_CUSTOM_NMI_HANDLER` enables the local custom NMI irq handler. The Non Maskabe Interrupt is triggered by the RCC Clock Security System when the HSE fails. It can be used for other tasks like clock recovery or other ways to detect the clock fail. +## PA1 PA2 Testing +PA1 and PA2 are the HSE pins. According to the datasheet the PA12_RM bit in the AFIO_PCFR1 register has to be 1 for the crystal to work. However, the tests show that the HSE is taking control over these pins no matter what the configuration is. +The example performs a few configuration changes trying to make the HSE fail and trigger the NMI interrupt. Please report if it was successful. ## 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 index eb20eb9..b9f6c91 100644 --- a/examples/sysclk_config/sysclk_config.c +++ b/examples/sysclk_config/sysclk_config.c @@ -18,6 +18,13 @@ */ // LED is on D.6 (nanoCH32 board) + +/* Uncomment this to enable an MNI interrrupt handler override + * and peform other tasks than the default Clock security system + * flag clear. + */ +//#define USE_CUSTOM_NMI_HANDLER + #define LED_PIN 6 #include <stdarg.h> @@ -55,12 +62,15 @@ typedef enum void print_sysclk_cfg(void); static inline void MCO_cfg(mco_cfg_e cfg); static inline uint8_t getMCOidx(uint32_t mco); +void xtal_pin_test(void); 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); +volatile uint8_t HSE_fail_flag = 0; // used in custom NMI handler to signal a HSE fail + // -------------------------------------------------------- static inline void MCO_cfg(mco_cfg_e cfg) { @@ -181,11 +191,17 @@ int main() // 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(); +#if defined(FUNCONF_USE_HSE) +/** + * Various test to abuse the PA1 and PA2 xtal pins and show that HSE is taking + * full control over the pins. + */ + xtal_pin_test(); +#endif + while(1) { // blink onboard led to show the mcu is running @@ -195,7 +211,14 @@ int main() 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 defined(FUNCONF_USE_HSE) + #if defined(USE_CUSTOM_NMI_HANDLER) + if (HSE_fail_flag) + { + printf("%s%s%s", msg_sep, msg_clkFail, msg_sep); + HSE_fail_flag = 0; + } + #else if (FUNCONF_USE_HSE && (RCC->CFGR0 & 0x0C) == 0x00) { printf("%s%s%s", msg_sep, msg_clkFail, msg_sep); @@ -203,6 +226,58 @@ int main() // 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 // END USE_CUSTOM_NMI_HANDLER #endif } -} \ No newline at end of file +} +#if defined(FUNCONF_USE_HSE) +void xtal_pin_test(void) +{ + printf(msg_sep); + printf("Testing if PA1 and PA2 config has any impact on HSE.\r\n"); + printf("Any positive result should trigger the HSE Fail.\r\n"); + printf("AFIO_PCFR1[PA12RM] = %d\r\n", (AFIO->PCFR1 & AFIO_PCFR1_PA12_REMAP) != 0); + printf("PA1+PA2 as PP output, write 0\r\n"); + GPIO_pinMode(GPIOv_from_PORT_PIN(GPIO_port_A, 1), GPIO_pinMode_O_pushPull, GPIO_Speed_10MHz); + GPIO_pinMode(GPIOv_from_PORT_PIN(GPIO_port_A, 2), GPIO_pinMode_O_pushPull, GPIO_Speed_10MHz); + GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_A, 1), low); + GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_A, 2), low); + Delay_Ms(100); + + printf("PA1+PA2 as PP out Mux, enable TIM1, PWM on CH2\r\n"); + GPIO_pinMode(GPIOv_from_PORT_PIN(GPIO_port_A, 1), GPIO_pinMode_O_pushPullMux, GPIO_Speed_10MHz); + GPIO_pinMode(GPIOv_from_PORT_PIN(GPIO_port_A, 2), GPIO_pinMode_O_pushPullMux, GPIO_Speed_10MHz); + RCC->APB2PCENR |= RCC_APB2Periph_TIM1; + TIM1->PSC = 0x0000; + TIM1->ATRLR = 255; + TIM1->SWEVGR |= TIM_UG; + TIM1->CCER |= TIM_CC2NE | TIM_CC2NP; + TIM1->CHCTLR2 |= TIM_OC2M_2 | TIM_OC2M_1; + TIM1->CH2CVR = 128; + TIM1->BDTR |= TIM_MOE; + TIM1->CTLR1 |= TIM_CEN; + Delay_Ms(100); + RCC->APB2PRSTR |= RCC_APB2Periph_TIM1; // reset Tim1 + RCC->APB2PRSTR &= ~RCC_APB2Periph_TIM1; + + printf("Enabling Opamp on PA1+PA2\r\n"); + EXTEN->EXTEN_CTR |= EXTEN_OPA_EN; + Delay_Ms(100); + EXTEN->EXTEN_CTR &= ~EXTEN_OPA_EN; +} + + #if defined(USE_CUSTOM_NMI_HANDLER) + /** + * @brief override the built in NMI handler to perform + * additional operation except clearing the CSS flag + * and letting the MCU run with HSI as the clock source. + * This could be ie.: try to recover + * + */ + void NMI_Handler(void) + { + RCC->INTR |= RCC_CSSC; // clear the clock security int flag + HSE_fail_flag = 1; + } + #endif // END USE_CUSTOM_NMI_HANDLER +#endif -- GitLab