MCU外设初始化:为什么参数配置必须优先于使能
在微控制器领域,初始化参数配置阶段至关重要。此时,虽无电源驱动,但微控制器在使能信号到来前,借初始化参数配置这一精细步骤,开启关键准备进程。初始化参数配置如同物理坐标锚定、逻辑指令部署、内在秩序预设,各参数像深埋沃土的种子,坐标、朝向、深度经精密计算,为未来指令运行奠定基础。
下面以国科安芯的MCU芯片AS32A601为例,详细展示下MCU这一严格的设计特性:
1. 外设检测阶段:MCU会尝试检测外设可用性,然后才开始执行用户代码。
2. 时钟树配置:系统时钟(CK_SYS)、AHB、APB等总线时钟必须在其他外设初始化前完成配置。
为什么参数要在使能前配置?
避免电平跳变:
- GPIO复用模式下,若先使能外设再配置复用选择器,会导致短暂电平变化。
- 普通输出IO默认输出低电平,若先使能再设置高电平,会出现短暂低脉冲。
防止硬件冲突:
- 时钟使能必须在外设初始化之前,否则会导致外设无法正常工作。
- 寄存器默认值可能不符合应用需求,直接使能可能导致意外行为。
确保稳定状态:
- 外设使能前需要建立正确的时钟源、中断优先级等基础环境。
- 参数配置需要一定时间生效,立即使能可能导致功能异常。
时钟配置
- 通过阅读芯片手册,确认好项目所需外设所在时钟
- 确保时钟最先配置,再去配置外设
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | void Systemclock_Init() { //注意:此处需要开启系统总线级的时钟配置,具体外设时钟配置可在各模块初始化函数中具体开启,具体请参考时钟树或者下图注释 // 1. 使用串口时,由于串口挂在APB0总线下,需要在此处开启AXIBUS3时钟、AXI4TOAPB0时钟以及APBBUS0时钟。 // 2. 使用延时函数时,需要开启CLINT时钟 // 3. 使用eflash、qspi时,需要开启AXIBUS3时钟、AXILITEBUS2时钟
/* AXIBus3 clock operation Guide*/ AXIBUS3_CLK_ENABLE(); AXI4TOAPB0_CLK_ENABLE(); APBBUS0_CLK_ENABLE(); AXI4TOAPB1_CLK_ENABLE(); APBBUS1_CLK_ENABLE(); AXILITEBUS1_CLK_ENABLE(); AXILITEBUS2_CLK_ENABLE(); EFLASH_CLK_ENABLE(); PLIC_CLK_ENABLE(); CLINT_CLK_ENABLE();
SMU_PLLInitTypeDef SMU_PLLInitStruct; SMU_ClockInitTypeDef SMU_ClockInitStruct;
/* Set PLL parameters values */ SMU_PLLInitStruct.OscillatorType = SMU_OSCILLATORTYPE_OSC; SMU_PLLInitStruct.FIRCOscState = DISABLE; SMU_PLLInitStruct.FIRCCalibrationValue = 0x00; SMU_PLLInitStruct.PLLConfig.PLLState = ENABLE; SMU_PLLInitStruct.PLLConfig.PLLSource = SMU_PLLCLK_OSC; SMU_PLLInitStruct.PLLConfig.PLLDivR = 0x01; SMU_PLLInitStruct.PLLConfig.PLLDivQ = 0x01; SMU_PLLInitStruct.PLLConfig.PLLDivN = 0x14; SMU_PLLInitStruct.PLLConfig.PLLDivF = 0xA0; SMU_PLLInit(&SMU_PLLInitStruct);
/* Ensure that the EFLASH is consistent with the system clock */ FLASH_UnlockCtrl(); FLASH_SetCLKFreq(0xA0);
/* Set System Clock parameters values */ SMU_ClockInitStruct.SYSCLKSelect = SMU_SYSCLK_PLL; SMU_ClockInitStruct.AXI4Bus3CLKDiv = AXI4Bus3CLKDiv1; SMU_ClockInitStruct.APBBus0CLKDiv = APBBus0CLKDiv1; SMU_ClockInitStruct.APBBus1CLKDiv = APBBus1CLKDiv8; SMU_ClockInitStruct.CANX2CLKDiv = CANX2CLKDiv1;
SMU_ClockInit(&SMU_ClockInitStruct);
EFLASH_CLK_UPDATE_ENABLE(); EFLASH_CLK_UPDATE_DISABLE();
FLASH_LockCtrl();
/* Get System Clock values */ SMU_GetClocksFreq(&SMU_ClocksStruct); } |
GPIO初始化
开始GPIO对应时钟
如果是复用IO,首先要配置复用
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | void GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; /*开启GPIO所在时钟*/ GPIOD_CLK_ENABLE(); GPIOG_CLK_ENABLE(); GPIOF_CLK_ENABLE(); /* Set GPIO multiplex mapping */ GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_CAN1);//先开启复用模式 GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_CAN1);
/* GPIO Configure */ GPIO_StructInit(&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_Out_PP; GPIO_InitStructure.GPIO_OStrength = GPIO_OStrength_9mA;
GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStructure.GPIO_IType = GPIO_IPU; GPIO_InitStructure.GPIO_OStrength = GPIO_OStrength_9mA; GPIO_Init(GPIOD, &GPIO_InitStructure);
/* GPIOB Configure */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_Out_PP; GPIO_InitStructure.GPIO_OStrength = GPIO_OStrength_9mA;
GPIO_Init(GPIOG, &GPIO_InitStructure);
/* GPIOB Configure */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStructure.GPIO_IType = GPIO_IPU; GPIO_InitStructure.GPIO_OType = GPIO_Out_PP; GPIO_InitStructure.GPIO_OStrength = GPIO_OStrength_9mA;
GPIO_Init(GPIOF, &GPIO_InitStructure); } |
部分外设参数配置
Usart
- 最后使能外设
- 配置外设参数
- 配置GPIO先配置复用
- 开启GPIO和外设时钟
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | void User_Print_Init(uint32_t BaudRate) { USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; PLIC_InitTypeDef PLIC_InitStructure; /*GOPI/外设时钟使能*/ GPIOD_CLK_ENABLE(); USART0_CLK_ENABLE(); /* Set GPIO multiplex mapping */ GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_USART0); /* USART0_TX */ 开启复用模式 GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_USART0); /* USART0_RX */ /* GPIO Configure */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_Out_PP; GPIO_InitStructure.GPIO_OStrength = GPIO_OStrength_4_5mA; GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStructure.GPIO_IType = GPIO_IN_FLOATING; GPIO_InitStructure.GPIO_OStrength = GPIO_OStrength_4_5mA; GPIO_Init(GPIOD, &GPIO_InitStructure); /*防止配置冲突*/ USART_DeInit(USART0); USART_StructInit(&USART_InitStructure);
/* Initializes the USART0 */ USART_InitStructure.USART_BaudRate = BaudRate; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_InitStructure.USART_OverSampling = USART_OverSampling_16; USART_Init(USART0, &USART_InitStructure); /*配置好相关参数后,使能USART*/ USART_Cmd(USART0, ENABLE); USART_ITConfig(USART0, USART_IT_RXNE, ENABLE);
/* Configer the USART0 interrupt */ PLIC_InitStructure.PLIC_IRQChannel = USART0_IRQn; PLIC_InitStructure.PLIC_IRQPriority = 1; PLIC_InitStructure.PLIC_IRQChannelCmd = ENABLE; PLIC_Init(&PLIC_InitStructure); } |
CAN
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | void User_CANFD3_Init() { CANFD3_CLK_ENABLE(); GPIOC_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStructure; CANFD_InitTypeDef CANFD_InitStructure; PLIC_InitTypeDef PLIC_InitStructure;
/* Set GPIO multiplex mapping */ GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_CAN3); GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_CAN3);
/* GPIO Configure */ GPIO_StructInit(&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_Out_PP; GPIO_InitStructure.GPIO_OStrength = GPIO_OStrength_18mA;
GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStructure.GPIO_IType = GPIO_IPU; GPIO_InitStructure.GPIO_OStrength = GPIO_OStrength_18mA; GPIO_Init(GPIOC, &GPIO_InitStructure);
/* Initializes the CANFD1 */ /* Arbitration Phase (Nominal) Baud Rate 500KHz */ /* Data Phase Baud Rate 2MHz */ CANFD_StructInit(&CANFD_InitStructure); CANFD_InitStructure.CANFD_SRR = CANFD_SRR_RESET; CANFD_InitStructure.CANFD_APBRPR = CANFD_APBRPR_10tp; CANFD_InitStructure.CANFD_APBTR_APTS1 = CANFD_APBTR_TS1_11tp; CANFD_InitStructure.CANFD_APBTR_APTS2 = CANFD_APBTR_TS2_4tp; CANFD_InitStructure.CANFD_APBTR_APSJW = CANFD_APBTR_SJW_2tp;
CANFD_InitStructure.CANFD_DPBRPR = CANFD_DPBRPR_2tp; CANFD_InitStructure.CANFD_DPBTR_DPTS1 = CANFD_DPBTR_TS1_7tp; CANFD_InitStructure.CANFD_DPBTR_DPTS2 = CANFD_DPBTR_TS2_2tp; CANFD_InitStructure.CANFD_DPBTR_DPSJW = CANFD_DPBTR_SJW_2tp; CANFD_Init(CANFD3, &CANFD_InitStructure);
/* CANFD receive filter configure */ CANFD_FilterInit(CANFD3, TB0, 0xFFE00000, 0X62E00000);
CANFD_AutoRetransConfig(CANFD3,ENABLE); /* Enable new message received interrupt */ CANFD_ITConfig(CANFD3, CANFD_IT_ERXOK, ENABLE); /* CANFD Enable */ CANFD_Enable(CANFD3);
PLIC_StructInit(&PLIC_InitStructure);
/* Configer the CANFD1 interrupt */ PLIC_InitStructure.PLIC_IRQChannel = CANFD3_IRQn; PLIC_InitStructure.PLIC_IRQPriority = 2; PLIC_InitStructure.PLIC_IRQChannelCmd = ENABLE; PLIC_Init(&PLIC_InitStructure); CANFD_ClearITPendingBit(CANFD3, CANFD_CLEAR_ALL); } |
通过遵循"参数配置在先,外设使能在后"的原则,并采用结构化初始化流程,可以显著提高MCU系统的稳定性和可靠性。