Skip to content

Commit

Permalink
more HSE tests and custom NMI handler
Browse files Browse the repository at this point in the history
  • Loading branch information
pio committed Jul 20, 2023
1 parent 0a78122 commit b705ade
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 7 deletions.
4 changes: 3 additions & 1 deletion ch32v003fun/ch32v003fun.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions examples/sysclk_config/funconfig.h
Original file line number Diff line number Diff line change
@@ -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
Expand Down
5 changes: 5 additions & 0 deletions examples/sysclk_config/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
83 changes: 79 additions & 4 deletions examples/sysclk_config/sysclk_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
Expand All @@ -195,14 +211,73 @@ 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);
// 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 // END USE_CUSTOM_NMI_HANDLER
#endif
}
}
}
#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

0 comments on commit b705ade

Please sign in to comment.