HAL库默认使用内部RC,因为它不想让编程人员接触和硬件有关的东西,因此HAL库没有使用外部晶振,但提供了相关配置函数。使用外部晶振是由后期设计工程模板的的人自己写了一个SystemClock_Config()来实现的。看到SystemClock_Config()没有带“HAL_”前缀,你就明白了,它不是来自HAL库。PY32F003F18P和STM32F334R8的HALL库,就是如此,其它CPU不知道是否是如此,为了体现HAL库的优势,HAL库不会自己否定自己,因此,它不会去写这种函数,好比写议论文一样,为了支持自己的观点,啥都不要了。举例说明如下:
1、PY32F003F18P的官方模板是这么切换时钟的,如下:
void APP_SystemClockConfig(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/* 振荡器配置 */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
/* 选择RCC振荡器为HSE */
RCC_OscInitStruct.HSIState = RCC_HSI_ON; /* 开启HSI */
RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV4; /* 4分频 */
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_8MHz;
/* 配置HSI输出时钟为8MHz */
RCC_OscInitStruct.HSEState = RCC_HSE_ON; /* 开启HSE */
RCC_OscInitStruct.HSEFreq = RCC_HSE_16_24MHz; /* HSE晶振工作频率16M~32M */
RCC_OscInitStruct.LSIState = RCC_LSI_OFF; /* 准备关闭LSI */
/* 配置振荡器 */
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/* 时钟源配置 */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1; /* 选择配置时钟 HCLK,SYSCLK,PCLK1 */
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
/* 准备选择HSE作为系统时钟 */
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; /* AHB时钟 1分频 */
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; /* APB时钟 1分频 */
/* 配置时钟源 */
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
}
void Error_Handler(void)
{
/* 无限循环 */
while (1)
{
}
}
PY32F003F18P的HAL库提供的部分函数如下:
HAL_RCC_OscConfig()
HAL_RCC_ClockConfig()
注意:没有HAL_RCCEx_PeriphCLKConfig();
下面的是工程模板提供的:
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)24000000) /*!< Value of the External oscillator in Hz */
#endif /* HSE_VALUE */
注意:PY32F003F18P没有PLL这个功能。
2、STM32F334R8的官方模板是这么切换时钟的,如下:
//函数功能:系统时钟使用HSE+PLL
void SystemClock_Config_HSE(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;//RCC_OSCILLATORTYPE_HSE=0x00000001U
RCC_OscInitStruct.HSEState = RCC_HSE_ON;//RCC_HSE_ON=0x00010000
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
//设置RCC->CFGR寄存器的PLLXTPRE=0,HSE分频器设置为1
RCC_OscInitStruct.HSIState = RCC_HSI_ON;//RCC_HSI_ON=0x00000001
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
//设置RCC->CFGR寄存器的SW=2,设置PLL作为系统时钟
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
//设置RCC->CFGR寄存器的PLLSRC=1,PLL使用HSE作为输入时钟源
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL6;
//设置RCC->CFGR寄存器的PLLMUL[3:0]=0100B,即PLL乘法因子为6
//由于PLL乘法因子为6,外部晶振频率为12MHz,因此当AHB分频器为1时,系统时钟就是72MHz
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{//设置PLL时钟进入工作状态
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
//使用PLL作为系统时钟源
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;//AHB分频器设置为1
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
//APB1分频器设置为2,则PCLK1=36MHz,TIM2CLK,TIM3CLK,TIM6CLK,TIM7CLK为72MHz
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
//APB2分频器设置为1,则PCLK2=72MHz,TIM1CLK,TIM15CLK,TIM16CLK,TIM17CLK为72MHz
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{//根据RCC_ClkInitStruct结构变量初始化系统时钟
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_HRTIM1|RCC_PERIPHCLK_USART1;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK1;
PeriphClkInit.Hrtim1ClockSelection = RCC_HRTIM1CLK_PLLCLK;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
SystemCoreClockUpdate();//根据系统时钟开关状态更新SystemCoreClock的值
}
void Error_Handler(void)
{
while(1);
}
STM32F334R8的HAL库提供的部分函数如下:
HAL_RCC_OscConfig()
HAL_RCC_ClockConfig()
HAL_RCCEx_PeriphCLKConfig()
下面是工程模板的:
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)12000000)
/*!< Value of the External oscillator in Hz */
//外部晶振12000000Hz
#endif /* HSE_VALUE */
SystemCoreClockUpdate()
3、STM32G031F6P6使用的系统时钟,其配置如下:
//函数功能:使用内部HSI+PLL实现系统时钟,得到SystemCoreClock=(16/1)*16/4=64MHz
void SystemClock_Config_HSI(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
//内部稳压为1.2 V,允许系统频率最高为64MHz
//Configure the main internal regulator output voltage
/** Initializes the CPU, AHB and APB busses clocks */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1;
//PLLM=000b,位于RCC_PLLCFGR寄存器,表示PLL输入时钟分频器的M分频值为1
//RCC_OscInitStruct.PLL.PLLN = 16;
//PLLN[6:0]=16,位于RCC_PLLCFGR寄存器,,表示PLL乘法因子为16
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV4;//PLLR division factor = 4
//PLLR[2:0]=011b,位于RCC_PLLCFGR寄存器,表示PLLRCLK输出时钟分频器值为4
//HSI_VALUE为16000000Hz
//fVCO = fPLLIN × (N / M)
//fPLLP = fVCO / P
//fPLLQ = fVCO / Q
//fPLLR = fVCO / R
//PLLM=000b,位于RCC_PLLCFGR寄存器,表示PLL输入时钟分频器的M分频值为1
//PLLN[6:0]=16,位于RCC_PLLCFGR寄存器,,表示PLL乘法因子为16
//fVCO=16000000*(16/1)=256000000Hz
//fPLLR = fPLLIN × (N / M) / R = 16000000*(16/1)/4=64000000Hz
//SystemCoreClock
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
//用来设置RCC->CFGR寄存器的SW[2:0],SW[2:0]=010b,表示使用PLLRCLK时钟作为系统时钟
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
//用来设置RCC_CFGR寄存器的HPRE[3:0],HPRE[3:0]=0xxxb,表示1分频
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
/** Initializes the peripherals clocks */
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_ADC
|RCC_PERIPHCLK_TIM1;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK1;
PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_SYSCLK;
PeriphClkInit.Tim1ClockSelection = RCC_TIM1CLKSOURCE_PLL;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
SystemCoreClockUpdate();
}
void SystemCoreClockUpdate(void)
{
uint32_t tmp;
uint32_t pllvco;
uint32_t pllr;
uint32_t pllsource;
uint32_t pllm;
uint32_t hsidiv;
/* Get SYSCLK source -------------------------------------------------------*/
switch (RCC->CFGR & RCC_CFGR_SWS)
{
case RCC_CFGR_SWS_HSE: /* HSE used as system clock */
SystemCoreClock = HSE_VALUE;
break;
case RCC_CFGR_SWS_LSI: /* LSI used as system clock */
SystemCoreClock = LSI_VALUE;
break;
case RCC_CFGR_SWS_LSE: /* LSE used as system clock */
SystemCoreClock = LSE_VALUE;
break;
case RCC_CFGR_SWS_PLL: /* PLL used as system clock */
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLLM) * PLLN
SYSCLK = PLL_VCO / PLLR
*/
pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC);
pllm = ((RCC->PLLCFGR & RCC_PLLCFGR_PLLM) >> RCC_PLLCFGR_PLLM_Pos) + 1UL;
if(pllsource == 0x03UL) /* HSE used as PLL clock source */
{
pllvco = (HSE_VALUE / pllm);
}
else /* HSI used as PLL clock source */
{
pllvco = (HSI_VALUE / pllm);
//内部RC为HSI_VALUE=16MHz,PLLM=1,则pllvco=16/1=16MHz
}
pllvco = pllvco * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos);
//内部RC为HSI_VALUE=16MHz,PLLM=1,PLLN=16,则pllvco=(16/1)*16=256MHz
pllr = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLR) >> RCC_PLLCFGR_PLLR_Pos) + 1UL);
SystemCoreClock = pllvco/pllr;
//内部RC为HSI_VALUE=16MHz,PLLM=1,PLLN=16,PLLR=4,则SystemCoreClock=(16/1)*16/4=64MHz
break;
case RCC_CFGR_SWS_HSI: /* HSI used as system clock */
default: /* HSI used as system clock */
hsidiv = (1UL << ((READ_BIT(RCC->CR, RCC_CR_HSIDIV))>> RCC_CR_HSIDIV_Pos));
SystemCoreClock = (HSI_VALUE/hsidiv);
break;
}
/* Compute HCLK clock frequency --------------------------------------------*/
/* Get HCLK prescaler */
tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> RCC_CFGR_HPRE_Pos)];
//从RCC_CFGR寄存器中读取HPRE[3:0],HPRE[3:0]=0xxxb,表示1分频
/* HCLK clock frequency */
SystemCoreClock >>= tmp; //SystemCoreClock=64000000Hz
}
STM32G031F6P6的HAL库提供的部分函数如下:
HAL_RCC_OscConfig()
HAL_RCC_ClockConfig()
HAL_RCCEx_PeriphCLKConfig()
总结:
从上面可以看出:
所有的HAL库均提供了HAL_RCC_OscConfig(); HAL_RCC_ClockConfig();HAL_RCCEx_PeriphCLKConfig();
PY32F003F18P的官方模板是24MHz;
STM32F334R8的官方模板是72MHz;
STM32G031F6P6的官方模板是64MHz;
STM32F103的官方模板是72MHz;这是众所周知的事情,不再叙述。
4、HAL是通用的吗?
HAL库无法做到互相替换,但函数名是一样的,实现的功能大体是一个意思。每个HAL库都要去了解,不是说一通百通,但大体上,是那个意思。
为什么HAL不写全,而让工程模板去实现,是因为各个厂家的CPU是不同的,为了体现兼容性强的特点,它没法去写。。所以,当有人问HALL库使用外部晶振的频率是多少,我很无语,因为这是和工程模板有关的。标准库和CPU有关,所以就不会存在这种问题。
如果没有工程模板,自己实现,太难了。千万不要举一反三,CPU变了,HAL库没法保证外部晶振的频率是一样的。