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)
{
}
}
HALL库提供的部分函数如下:
HAL_RCC_OscConfig()
HAL_RCC_ClockConfig()
下面的是工程模板提供的:
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)24000000) /*!< Value of the External oscillator in Hz */
#endif /* HSE_VALUE */
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);
}
HALL库提供的部分函数如下:
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()
总结:
从上面可以看出:
PY32F003F18P的官方模板是24MHz;
STM32F334R8的官方模板是12MHz;
3、HAL是通用的吗?
HAL库无法做到互相替换,但函数名是一样的,实现的功能大体是一个意思。每个HAL库都要去了解,不是说一通百通,但大体上,是那个意思。
为什么HAL不写全,而让工程模板去实现,是因为各个厂家的CPU是不同的,为了体现兼容性强的特点,它没法去写。。所以,当有人问HALL库使用外部晶振的频率是多少,我很无语,因为这是和工程模板有关的。标准库和CPU有关,所以就不会存在这种问题。
如果没有工程模板,自己实现,太难了。千万不要举一反三,CPU变了,HAL库没法保证外部晶振的频率是一样的。