在MRS的默认配置中,在main函数执行之前,就已经执行了时钟的初始化配置程序,这部分程序被放在了一个名为system_ch32v00x.c的文件中,这个文件默认被加载到MRS的User目录下(可双击打开它)。在该文件中,最重要的一个函数就是SystemInit,它负责系统的初始化工作,其代码如下所示。
void SystemInit (void) { 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; SetSysClock(); }
在末尾调用了一个设置系统时钟的函数SetSysClock,其代码如下。
static void SetSysClock(void) { #ifdef SYSCLK_FREQ_8MHz_HSI SetSysClockTo_8MHz_HSI(); #elif defined SYSCLK_FREQ_24MHZ_HSI SetSysClockTo_24MHZ_HSI(); #elif defined SYSCLK_FREQ_48MHZ_HSI SetSysClockTo_48MHZ_HSI(); #elif defined SYSCLK_FREQ_8MHz_HSE SetSysClockTo_8MHz_HSE(); #elif defined SYSCLK_FREQ_24MHz_HSE SetSysClockTo_24MHz_HSE(); #elif defined SYSCLK_FREQ_48MHz_HSE SetSysClockTo_48MHz_HSE(); #endif }
从上可看出,该函数主要依据宏定义SYSCLK_FREQ的值来决定使用哪一个配置函数,并没有具体内容。这里不妨选取最后一个函数SetSysClockTo_48MHz_HSE(即使用外部晶振的48MHz主频模式)来展开讨论,其代码如下所示。
static void SetSysClockTo_48MHz_HSE(void) { __IO uint32_t StartUpCounter = 0, HSEStatus = 0; /* Close PA0-PA1 GPIO function */ RCC->APB2PCENR |= RCC_AFIOEN; AFIO->PCFR1 |= (1<<15); RCC->CTLR |= ((uint32_t)RCC_HSEON); /* Wait till HSE is ready and if Time out is reached exit */ do { HSEStatus = RCC->CTLR & RCC_HSERDY; StartUpCounter++; } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); if ((RCC->CTLR & RCC_HSERDY) != RESET) { HSEStatus = (uint32_t)0x01; } else { HSEStatus = (uint32_t)0x00; } if (HSEStatus == (uint32_t)0x01) { /* Flash 0 wait state */ FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY); FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_1; /* HCLK = SYSCLK = APB1 */ RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; /* PLL configuration: PLLCLK = HSE * 2 = 48 MHz */ RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC)); RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE_Mul2); /* Enable PLL */ RCC->CTLR |= RCC_PLLON; /* Wait till PLL is ready */ while((RCC->CTLR & RCC_PLLRDY) == 0) { } /* Select PLL as system clock source */ RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW)); RCC->CFGR0 |= (uint32_t)RCC_SW_PLL; /* Wait till PLL is used as system clock source */ while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08) { } } else { /* * If HSE fails to start-up, the application will have wrong clock * configuration. User can add here some code to deal with this error */ } }
以上就是MRS中系统初始化的具体流程,看起来是不是有些绕(仅为了方便开发者),能不能自己实现一个精简的初始化过程呢?答案是肯定的。以下就一个是非常精简的初始化代码实例。
void SetSysClockTo_48MHz_HSE(void) { RCC->CFGR0 = 0; //AHB时钟不分频 RCC->APB2PCENR |= 0x00000001; //使能I/O辅助功能模块时钟 AFIO->PCFR1 |= (1<<15); //引脚PA1&PA2重映射位,改为接外部晶振引脚 RCC->CTLR |= 0x00010000; //使能外部振荡器 while(!(RCC->CTLR & 0x00020000)); //等待外部晶振稳定 FLASH->ACTLR &= 0x00000003; FLASH->ACTLR |= 0x00000001; //设置FLASH等待 RCC->CFGR0 |= 0x00010000; //选择外部晶振 RCC->CTLR |= 0x01000000; //开启PLL while(!(RCC->CTLR & 0x02000000)); //等待PLL稳定 RCC->CFGR0 &= ~0x00000003; RCC->CFGR0 |= 0x00000002; //选择PLL作为系统时钟 }
上面的函数可直接放在main.c文件中,提供给main函数调用。为此,还可以移出默认在User目录下的系统配置文件system_ch32v00x.c及其头文件system_ch32v00x.h,只留下一个main.c文件,同时把头文件ch32v00x.h中的“#include <system_ch32v00x.h>”一句注释掉,让开发环境做到极简。但是,文件移出后编译会报错,原因是缺失了系统初始化函数SystemInit。如何解决?有两种方法。其一,把上面的函数名称改为“void SystemInit (void)”,让系统在复位后直接执行初始化,这就不需要在main函数中来调用它了。其二,更改系统在复位后的程序入口,直接执行main函数,这需要修改系统启动文件startup_ch32v00x.S。启动文件是用汇编写成的,下面是它的具体内容。
.section .init, "ax", @progbits .globl _start .align 2 _start: .option norvc; j handle_reset .word 0 .word NMI_Handler /* NMI Handler */ .word HardFault_Handler /* Hard Fault Handler */ .word 0 .word 0 .word 0 .word 0 .word 0 .word 0 .word 0 .word 0 .word SysTick_Handler /* SysTick Handler */ .word 0 .word SW_Handler /* SW Handler */ .word 0 /* External Interrupts */ .word WWDG_IRQHandler /* Window Watchdog */ .word PVD_IRQHandler /* PVD through EXTI Line detect */ .word FLASH_IRQHandler /* Flash */ .word RCC_IRQHandler /* RCC */ .word EXTI7_0_IRQHandler /* EXTI Line 7..0 */ .word AWU_IRQHandler /* AWU */ .word DMA1_Channel1_IRQHandler /* DMA1 Channel 1 */ .word DMA1_Channel2_IRQHandler /* DMA1 Channel 2 */ .word DMA1_Channel3_IRQHandler /* DMA1 Channel 3 */ .word DMA1_Channel4_IRQHandler /* DMA1 Channel 4 */ .word DMA1_Channel5_IRQHandler /* DMA1 Channel 5 */ .word DMA1_Channel6_IRQHandler /* DMA1 Channel 6 */ .word DMA1_Channel7_IRQHandler /* DMA1 Channel 7 */ .word ADC1_IRQHandler /* ADC1 */ .word I2C1_EV_IRQHandler /* I2C1 Event */ .word I2C1_ER_IRQHandler /* I2C1 Error */ .word USART1_IRQHandler /* USART1 */ .word SPI1_IRQHandler /* SPI1 */ .word TIM1_BRK_IRQHandler /* TIM1 Break */ .word TIM1_UP_IRQHandler /* TIM1 Update */ .word TIM1_TRG_COM_IRQHandler /* TIM1 Trigger and Commutation */ .word TIM1_CC_IRQHandler /* TIM1 Capture Compare */ .word TIM2_IRQHandler /* TIM2 */ .option rvc; .section .text.vector_handler, "ax", @progbits .weak NMI_Handler .weak HardFault_Handler .weak SysTick_Handler .weak SW_Handler .weak WWDG_IRQHandler .weak PVD_IRQHandler .weak FLASH_IRQHandler .weak RCC_IRQHandler .weak EXTI7_0_IRQHandler .weak AWU_IRQHandler .weak DMA1_Channel1_IRQHandler .weak DMA1_Channel2_IRQHandler .weak DMA1_Channel3_IRQHandler .weak DMA1_Channel4_IRQHandler .weak DMA1_Channel5_IRQHandler .weak DMA1_Channel6_IRQHandler .weak DMA1_Channel7_IRQHandler .weak ADC1_IRQHandler .weak I2C1_EV_IRQHandler .weak I2C1_ER_IRQHandler .weak USART1_IRQHandler .weak SPI1_IRQHandler .weak TIM1_BRK_IRQHandler .weak TIM1_UP_IRQHandler .weak TIM1_TRG_COM_IRQHandler .weak TIM1_CC_IRQHandler .weak TIM2_IRQHandler NMI_Handler: 1: j 1b HardFault_Handler: 1: j 1b SysTick_Handler: 1: j 1b SW_Handler: 1: j 1b WWDG_IRQHandler: 1: j 1b PVD_IRQHandler: 1: j 1b FLASH_IRQHandler: 1: j 1b RCC_IRQHandler: 1: j 1b EXTI7_0_IRQHandler: 1: j 1b AWU_IRQHandler: 1: j 1b DMA1_Channel1_IRQHandler: 1: j 1b DMA1_Channel2_IRQHandler: 1: j 1b DMA1_Channel3_IRQHandler: 1: j 1b DMA1_Channel4_IRQHandler: 1: j 1b DMA1_Channel5_IRQHandler: 1: j 1b DMA1_Channel6_IRQHandler: 1: j 1b DMA1_Channel7_IRQHandler: 1: j 1b ADC1_IRQHandler: 1: j 1b I2C1_EV_IRQHandler: 1: j 1b I2C1_ER_IRQHandler: 1: j 1b USART1_IRQHandler: 1: j 1b SPI1_IRQHandler: 1: j 1b TIM1_BRK_IRQHandler: 1: j 1b TIM1_UP_IRQHandler: 1: j 1b TIM1_TRG_COM_IRQHandler: 1: j 1b TIM1_CC_IRQHandler: 1: j 1b TIM2_IRQHandler: 1: j 1b .section .text.handle_reset, "ax", @progbits .weak handle_reset .align 1 handle_reset: .option push .option norelax la gp, __global_pointer$ .option pop 1: la sp, _eusrstack 2: /* Load data section from flash to RAM */ la a0, _data_lma la a1, _data_vma la a2, _edata bgeu a1, a2, 2f 1: lw t0, (a0) sw t0, (a1) addi a0, a0, 4 addi a1, a1, 4 bltu a1, a2, 1b 2: /* clear bss section */ la a0, _sbss la a1, _ebss bgeu a0, a1, 2f 1: sw zero, (a0) addi a0, a0, 4 bltu a0, a1, 1b 2: li t0, 0x80 csrw mstatus, t0 li t0, 0x3 csrw 0x804, t0 la t0, _start ori t0, t0, 3 csrw mtvec, t0 jal SystemInit la t0, main csrw mepc, t0 mret
在系统复位后,程序指针从handle_reset的地方开始执行。这里只需要把下面的“jal SystemInit”一句(倒数第4行处)注释掉就可以了,不让程序指针跳转到SystemInit函数去,而是直接跳转到main函数去执行。
标签:初始化,word,CH32V003,weak,MRS,IRQHandler,DMA1,1b,RCC From: https://www.cnblogs.com/fxzq/p/17400177.html