赞
踩
目录
1.3 将配置好的值写入到对应的寄存器、初始化PLL主时钟;
1.3.1 __HAL_RCC_HSE_CONFIG(RCC_OscInitStruct->HSEState)分析:
2.3.1 PLL时钟就绪检测和__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY)宏定义的分析
本人使用的单片机stm32f407vg,代码来源stm32CubeMx。
时钟配置如下

主函数调用SystemClock_Config();,这也是RCC初始化函数;
- void SystemClock_Config(void)
- {
- RCC_OscInitTypeDef RCC_OscInitStruct = {0};
- RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
-
- /** Configure the main internal regulator output voltage
- */
- __HAL_RCC_PWR_CLK_ENABLE();
- __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
- /** Initializes the CPU, AHB and APB busses clocks
- */
- RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
- RCC_OscInitStruct.HSEState = RCC_HSE_ON;
- RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
- RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
- RCC_OscInitStruct.PLL.PLLM = 25;
- RCC_OscInitStruct.PLL.PLLN = 144;
- RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
- RCC_OscInitStruct.PLL.PLLQ = 4;
- 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_CLOCKTYPE_PCLK2;
- RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
- RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
- RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
- RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
-
- if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
- {
- Error_Handler();
- }
- }

__HAL_RCC_PWR_CLK_ENABLE();
#define __HAL_RCC_PWR_CLK_ENABLE() do { \
__IO uint32_t tmpreg = 0x00U; \
SET_BIT(RCC->APB1ENR, RCC_APB1ENR_PWREN);\
/* Delay after an RCC peripheral clock enabling */ \
tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_PWREN);\
UNUSED(tmpreg); \
} while(0U)#define SET_BIT(REG, BIT) ((REG) |= (BIT))
#define READ_BIT(REG, BIT) ((REG) & (BIT))
#define RCC_APB1ENR_PWREN_Pos (28U)
#define RCC_APB1ENR_PWREN_Msk (0x1UL<<RCC_APB1ENR_PWREN_Pos)
#define RCC_APB1ENR_PWREN RCC_APB1ENR_PWREN_Msk
把0x1左移28位,即使能电源接口时钟;
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
这个函数是调压器输出电压级别选择,选择级别1模式(默认的模式),暂时不了解有什么作用;
32的时钟树,暂时用到这些;
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;#define RCC_OSCILLATORTYPE_NONE 0x00000000U
#define RCC_OSCILLATORTYPE_HSE 0x00000001U
#define RCC_OSCILLATORTYPE_HSI 0x00000002U
#define RCC_OSCILLATORTYPE_LSE 0x00000004U
#define RCC_OSCILLATORTYPE_LSI 0x00000008U#define RCC_HSE_OFF 0x00000000U
#define RCC_HSE_ON RCC_CR_HSEON
#define RCC_HSE_BYPASS ((uint32_t)(RCC_CR_HSEBYP | RCC_CR_HSEON))分别是配置时钟源为(HSE\HSI\LSE\LSI); 选择HSE时钟状态(使能、关闭、旁路)
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 144;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;#define RCC_PLL_NONE ((uint8_t)0x00)
#define RCC_PLL_OFF ((uint8_t)0x01)
#define RCC_PLL_ON ((uint8_t)0x02)#define RCC_PLLSOURCE_HSI RCC_PLLCFGR_PLLSRC_HSI
#define RCC_PLLSOURCE_HSE RCC_PLLCFGR_PLLSRC_HSE#define RCC_PLLCFGR_PLLSRC_HSE_Pos (22U)
#define RCC_PLLCFGR_PLLSRC_HSE_Msk(0x1UL <<RCC_PLLCFGR_PLLSRC_HSE_Pos)
#define RCC_PLLCFGR_PLLSRC_HSE RCC_PLLCFGR_PLLSRC_HSE_Msk#define RCC_PLLP_DIV2 0x00000002U
#define RCC_PLLP_DIV4 0x00000004U
#define RCC_PLLP_DIV6 0x00000006U
#define RCC_PLLP_DIV8 0x00000008U分别为PLL时钟(使能、关闭);PLL时钟源选择(HSE\HSI);PLL M\N\P,Q分频器赋值;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}调用这个函数进行赋值,并通过返回值判断是否初始化成功;
代码很长,只留下HSE和PLL配置部分;
- __weak HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct)
- {
- uint32_t tickstart, pll_config;
-
- /* Check Null pointer */
- if(RCC_OscInitStruct == NULL)
- {
- return HAL_ERROR;
- }
-
- /* Check the parameters */
- assert_param(IS_RCC_OSCILLATORTYPE(RCC_OscInitStruct->OscillatorType));
- /*------------------------------- HSE Configuration ------------------------*/
- if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_HSE) == RCC_OSCILLATORTYPE_HSE)
- {
- /* Check the parameters */
- assert_param(IS_RCC_HSE(RCC_OscInitStruct->HSEState));
- /* When the HSE is used as system clock or clock source for PLL in these cases HSE will not disabled */
- if((__HAL_RCC_GET_SYSCLK_SOURCE() == RCC_CFGR_SWS_HSE) ||\
- ((__HAL_RCC_GET_SYSCLK_SOURCE() == RCC_CFGR_SWS_PLL) && ((RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) == RCC_PLLCFGR_PLLSRC_HSE)))
- {
- if((__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) != RESET) && (RCC_OscInitStruct->HSEState == RCC_HSE_OFF))
- {
- return HAL_ERROR;
- }
- }
- else
- {
- /* Set the new HSE configuration ---------------------------------------*/
- __HAL_RCC_HSE_CONFIG(RCC_OscInitStruct->HSEState);
-
- /* Check the HSE State */
- if((RCC_OscInitStruct->HSEState) != RCC_HSE_OFF)
- {
- /* Get Start Tick */
- tickstart = HAL_GetTick();
-
- /* Wait till HSE is ready */
- while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET)
- {
- if((HAL_GetTick() - tickstart ) > HSE_TIMEOUT_VALUE)
- {
- return HAL_TIMEOUT;
- }
- }
- }
- else
- {
- /* Get Start Tick */
- tickstart = HAL_GetTick();
-
- /* Wait till HSE is bypassed or disabled */
- while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) != RESET)
- {
- if((HAL_GetTick() - tickstart ) > HSE_TIMEOUT_VALUE)
- {
- return HAL_TIMEOUT;
- }
- }
- }
- }
- }
- /*-------------------------------- PLL Configuration -----------------------*/
- /* Check the parameters */
- assert_param(IS_RCC_PLL(RCC_OscInitStruct->PLL.PLLState));
- if ((RCC_OscInitStruct->PLL.PLLState) != RCC_PLL_NONE)
- {
- /* Check if the PLL is used as system clock or not */
- if(__HAL_RCC_GET_SYSCLK_SOURCE() != RCC_CFGR_SWS_PLL)
- {
- if((RCC_OscInitStruct->PLL.PLLState) == RCC_PLL_ON)
- {
- /* Check the parameters */
- assert_param(IS_RCC_PLLSOURCE(RCC_OscInitStruct->PLL.PLLSource));
- assert_param(IS_RCC_PLLM_VALUE(RCC_OscInitStruct->PLL.PLLM));
- assert_param(IS_RCC_PLLN_VALUE(RCC_OscInitStruct->PLL.PLLN));
- assert_param(IS_RCC_PLLP_VALUE(RCC_OscInitStruct->PLL.PLLP));
- assert_param(IS_RCC_PLLQ_VALUE(RCC_OscInitStruct->PLL.PLLQ));
-
- /* Disable the main PLL. */
- __HAL_RCC_PLL_DISABLE();
-
- /* Get Start Tick */
- tickstart = HAL_GetTick();
-
- /* Wait till PLL is ready */
- while(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) != RESET)
- {
- if((HAL_GetTick() - tickstart ) > PLL_TIMEOUT_VALUE)
- {
- return HAL_TIMEOUT;
- }
- }
-
- /* Configure the main PLL clock source, multiplication and division factors. */
- WRITE_REG(RCC->PLLCFGR, (RCC_OscInitStruct->PLL.PLLSource | \
- RCC_OscInitStruct->PLL.PLLM | \
- (RCC_OscInitStruct->PLL.PLLN << RCC_PLLCFGR_PLLN_Pos) | \
- (((RCC_OscInitStruct->PLL.PLLP >> 1U) - 1U) << RCC_PLLCFGR_PLLP_Pos) | \
- (RCC_OscInitStruct->PLL.PLLQ << RCC_PLLCFGR_PLLQ_Pos)));
- /* Enable the main PLL. */
- __HAL_RCC_PLL_ENABLE();
-
- /* Get Start Tick */
- tickstart = HAL_GetTick();
-
- /* Wait till PLL is ready */
- while(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET)
- {
- if((HAL_GetTick() - tickstart ) > PLL_TIMEOUT_VALUE)
- {
- return HAL_TIMEOUT;
- }
- }
- }
- else
- {
- /* Disable the main PLL. */
- __HAL_RCC_PLL_DISABLE();
-
- /* Get Start Tick */
- tickstart = HAL_GetTick();
-
- /* Wait till PLL is ready */
- while(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) != RESET)
- {
- if((HAL_GetTick() - tickstart ) > PLL_TIMEOUT_VALUE)
- {
- return HAL_TIMEOUT;
- }
- }
- }
- }
- else
- {
- /* Check if there is a request to disable the PLL used as System clock source */
- if((RCC_OscInitStruct->PLL.PLLState) == RCC_PLL_OFF)
- {
- return HAL_ERROR;
- }
- else
- {
- /* Do not return HAL_ERROR if request repeats the current configuration */
- pll_config = RCC->CFGR;
- if((READ_BIT(pll_config, RCC_PLLCFGR_PLLSRC) != RCC_OscInitStruct->PLL.PLLSource) ||
- (READ_BIT(pll_config, RCC_PLLCFGR_PLLM) != RCC_OscInitStruct->PLL.PLLM) ||
- (READ_BIT(pll_config, RCC_PLLCFGR_PLLN) != RCC_OscInitStruct->PLL.PLLN) ||
- (READ_BIT(pll_config, RCC_PLLCFGR_PLLP) != RCC_OscInitStruct->PLL.PLLP) ||
- (READ_BIT(pll_config, RCC_PLLCFGR_PLLQ) != RCC_OscInitStruct->PLL.PLLQ))
- {
- return HAL_ERROR;
- }
- }
- }
- }
- return HAL_OK;
- }

首先是很长一堆判断是否出错的语句,不用管;
/* Set the new HSE configuration ---------------------------------------*/
__HAL_RCC_HSE_CONFIG(RCC_OscInitStruct->HSEState);
而函数本身如下#define __HAL_RCC_HSE_CONFIG(__STATE__) \
do { if ((__STATE__) == RCC_HSE_ON)SET_BIT(RCC->CR, RCC_CR_HSEON); \
else if ((__STATE__) == RCC_HSE_BYPASS) \
{ SET_BIT(RCC->CR, RCC_CR_HSEBYP); \
SET_BIT(RCC->CR, RCC_CR_HSEON); } \ else \
{ CLEAR_BIT(RCC->CR, RCC_CR_HSEON); \
CLEAR_BIT(RCC->CR, RCC_CR_HSEBYP); } } while(0U)简单看就是判断HSE使能还是旁路。然后给RCC_CR对应位赋值;
SET_BIT(RCC->CR, RCC_CR_HSEON);
#define SET_BIT(REG, BIT) ((REG) |= (BIT))
之前初始化的语句是:
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
对应的值如下
#define RCC_CR_HSEON_Pos (16U)
#define RCC_CR_HSEON_Msk (0x1UL << RCC_CR_HSEON_Pos)
#define RCC_CR_HSEON RCC_CR_HSEON_Msk即1左移16位;
而,SET_BIT(RCC->CR, RCC_CR_HSEBYP);
#define RCC_CR_HSEBYP_Pos (18U)
#define RCC_CR_HSEBYP_Msk (0x1UL << RCC_CR_HSEBYP_Pos)
#define RCC_CR_HSEBYP RCC_CR_HSEBYP_Msk即1左移18位;
暂时没用过HSE旁路,好像是不用晶振、用外部信号输入代替晶振。
先关闭PLL时钟:
__HAL_RCC_PLL_DISABLE();
#define __HAL_RCC_PLL_DISABLE() (*(__IO uint32_t *) RCC_CR_PLLON_BB = DISABLE)
#define RCC_PLLON_BIT_NUMBER 0x18U
#define RCC_CR_PLLON_BB (PERIPH_BB_BASE + (RCC_CR_OFFSET * 32U) + (RCC_PLLON_BIT_NUMBER * 4U))
#define PERIPH_BB_BASE 0x42000000UL
/* --- CR Register --- */
#define RCC_CR_OFFSET (RCC_OFFSET + 0x00U)#define RCC_OFFSET (RCC_BASE - PERIPH_BASE)
简单点看就是在RCC_CR寄存器里。
0x18转换为10进制就是24,即bit24;
赋0就是关闭PLL;使能PLL同理
主要赋值语句
WRITE_REG(RCC->PLLCFGR, (RCC_OscInitStruct->PLL.PLLSource | \
RCC_OscInitStruct->PLL.PLLM | \
(RCC_OscInitStruct->PLL.PLLN << RCC_PLLCFGR_PLLN_Pos) | \ (((RCC_OscInitStruct->PLL.PLLP >> 1U) - 1U) << RCC_PLLCFGR_PLLP_Pos) | \
(RCC_OscInitStruct->PLL.PLLQ << RCC_PLLCFGR_PLLQ_Pos)));#define WRITE_REG(REG, VAL) ((REG) = (VAL))
首先是RCC_PLLSOURCE_HSE之前写结构体如下:
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
#define RCC_PLLSOURCE_HSE RCC_PLLCFGR_PLLSRC_HSE
#define RCC_PLLCFGR_PLLSRC_HSE_Pos (22U)
#define RCC_PLLCFGR_PLLSRC_HSE_Msk (0x1UL << RCC_PLLCFGR_PLLSRC_HSE_Pos) /*!< 0x00400000 */
#define RCC_PLLCFGR_PLLSRC_HSE RCC_PLLCFGR_PLLSRC_HSE_Msk即1左移22位,在RCC_PLLCFGR寄存器中作用选择HSE;
寄存器其他位和分频器计算如下:
#define RCC_PLLCFGR_PLLN_Pos (6U)
#define RCC_PLLP_DIV2 0x00000002U
#define RCC_PLLP_DIV4 0x00000004U
#define RCC_PLLP_DIV6 0x00000006U
#define RCC_PLLP_DIV8 0x00000008U#define RCC_PLLCFGR_PLLP_Pos (16U)
#define RCC_PLLCFGR_PLLQ_Pos (24U)
PLLM不移动,PLLN左移6位,PLLQ左移24位
PLLP分频的值右移1位、即除以2再减1,2~8分频对应00~11两位,PLLP左移16位;
最后把各个数相或,数值写到寄存器。
完毕后使能PLL时钟。
这儿使用write,是因为操作的位比较多,直接相与再写入比较方便。
时钟树如上,结构体成员赋值如下:
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;#define RCC_CLOCKTYPE_SYSCLK 0x00000001U
#define RCC_CLOCKTYPE_HCLK 0x00000002U
#define RCC_CLOCKTYPE_PCLK1 0x00000004U
#define RCC_CLOCKTYPE_PCLK2 0x00000008U#define RCC_SYSCLKSOURCE_HSI RCC_CFGR_SW_HSI
#define RCC_SYSCLKSOURCE_HSE RCC_CFGR_SW_HSE
#define RCC_SYSCLKSOURCE_PLLCLK RCC_CFGR_SW_PLL#define RCC_SYSCLK_DIV1 RCC_CFGR_HPRE_DIV1
#define RCC_SYSCLK_DIV2 RCC_CFGR_HPRE_DIV2...
#define RCC_SYSCLK_DIV512 RCC_CFGR_HPRE_DIV512#define RCC_HCLK_DIV1 RCC_CFGR_PPRE1_DIV1
#define RCC_HCLK_DIV2 RCC_CFGR_PPRE1_DIV2
...
#define RCC_HCLK_DIV16 RCC_CFGR_PPRE1_DIV16分别是要使能的外设时钟(HCLK\SYSCLK\PCLK1\PCLK2);
系统时钟源选择(HSI\HSE\PLLCLK);AHB分频器,APB1分频器,APB2分频器。
将配置好的结构体成员写到对应的寄存器内;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}调用这个函数并且判断是否出错;
函数主体如下:
- HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency)
- {
- uint32_t tickstart;
-
- /* Check Null pointer */
- if(RCC_ClkInitStruct == NULL)
- {
- return HAL_ERROR;
- }
-
- /* Check the parameters */
- assert_param(IS_RCC_CLOCKTYPE(RCC_ClkInitStruct->ClockType));
- assert_param(IS_FLASH_LATENCY(FLatency));
-
- /* To correctly read data from FLASH memory, the number of wait states (LATENCY)
- must be correctly programmed according to the frequency of the CPU clock
- (HCLK) and the supply voltage of the device. */
-
- /* Increasing the number of wait states because of higher CPU frequency */
- if(FLatency > __HAL_FLASH_GET_LATENCY())
- {
- /* Program the new number of wait states to the LATENCY bits in the FLASH_ACR register */
- __HAL_FLASH_SET_LATENCY(FLatency);
-
- /* Check that the new number of wait states is taken into account to access the Flash
- memory by reading the FLASH_ACR register */
- if(__HAL_FLASH_GET_LATENCY() != FLatency)
- {
- return HAL_ERROR;
- }
- }
-
- /*-------------------------- HCLK Configuration --------------------------*/
- if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_HCLK) == RCC_CLOCKTYPE_HCLK)
- {
- /* Set the highest APBx dividers in order to ensure that we do not go through
- a non-spec phase whatever we decrease or increase HCLK. */
- if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK1) == RCC_CLOCKTYPE_PCLK1)
- {
- MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_HCLK_DIV16);
- }
-
- if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK2) == RCC_CLOCKTYPE_PCLK2)
- {
- MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, (RCC_HCLK_DIV16 << 3));
- }
-
- assert_param(IS_RCC_HCLK(RCC_ClkInitStruct->AHBCLKDivider));
- MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_ClkInitStruct->AHBCLKDivider);
- }
-
- /*------------------------- SYSCLK Configuration ---------------------------*/
- if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_SYSCLK) == RCC_CLOCKTYPE_SYSCLK)
- {
- assert_param(IS_RCC_SYSCLKSOURCE(RCC_ClkInitStruct->SYSCLKSource));
-
- /* HSE is selected as System Clock Source */
- if(RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_HSE)
- {
- /* Check the HSE ready flag */
- if(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET)
- {
- return HAL_ERROR;
- }
- }
- /* PLL is selected as System Clock Source */
- else if((RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_PLLCLK) ||
- (RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_PLLRCLK))
- {
- /* Check the PLL ready flag */
- if(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET)
- {
- return HAL_ERROR;
- }
- }
- /* HSI is selected as System Clock Source */
- else
- {
- /* Check the HSI ready flag */
- if(__HAL_RCC_GET_FLAG(RCC_FLAG_HSIRDY) == RESET)
- {
- return HAL_ERROR;
- }
- }
-
- __HAL_RCC_SYSCLK_CONFIG(RCC_ClkInitStruct->SYSCLKSource);
-
- /* Get Start Tick */
- tickstart = HAL_GetTick();
-
- while (__HAL_RCC_GET_SYSCLK_SOURCE() != (RCC_ClkInitStruct->SYSCLKSource << RCC_CFGR_SWS_Pos))
- {
- if ((HAL_GetTick() - tickstart) > CLOCKSWITCH_TIMEOUT_VALUE)
- {
- return HAL_TIMEOUT;
- }
- }
- }
-
- /* Decreasing the number of wait states because of lower CPU frequency */
- if(FLatency < __HAL_FLASH_GET_LATENCY())
- {
- /* Program the new number of wait states to the LATENCY bits in the FLASH_ACR register */
- __HAL_FLASH_SET_LATENCY(FLatency);
-
- /* Check that the new number of wait states is taken into account to access the Flash
- memory by reading the FLASH_ACR register */
- if(__HAL_FLASH_GET_LATENCY() != FLatency)
- {
- return HAL_ERROR;
- }
- }
-
- /*-------------------------- PCLK1 Configuration ---------------------------*/
- if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK1) == RCC_CLOCKTYPE_PCLK1)
- {
- assert_param(IS_RCC_PCLK(RCC_ClkInitStruct->APB1CLKDivider));
- MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_ClkInitStruct->APB1CLKDivider);
- }
-
- /*-------------------------- PCLK2 Configuration ---------------------------*/
- if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK2) == RCC_CLOCKTYPE_PCLK2)
- {
- assert_param(IS_RCC_PCLK(RCC_ClkInitStruct->APB2CLKDivider));
- MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, ((RCC_ClkInitStruct->APB2CLKDivider) << 3U));
- }
-
- /* Update the SystemCoreClock global variable */
- SystemCoreClock = HAL_RCC_GetSysClockFreq() >> AHBPrescTable[(RCC->CFGR & RCC_CFGR_HPRE)>> RCC_CFGR_HPRE_Pos];
- /* Configure the source of time base considering new system clocks settings */
- HAL_InitTick (uwTickPrio);
-
- return HAL_OK;
- }

if(FLatency > __HAL_FLASH_GET_LATENCY())
{
/* Program the new number of wait states to the LATENCY bits in the FLASH_ACR register */
__HAL_FLASH_SET_LATENCY(FLatency);/* Check that the new number of wait states is taken into account to access the Flash
memory by reading the FLASH_ACR register */
if(__HAL_FLASH_GET_LATENCY() != FLatency)
{
return HAL_ERROR;
}
}初步看是判断等待周期的,
#define FLASH_LATENCY_2 FLASH_ACR_LATENCY_2WS
/*!< FLASH Two Latency cycles */
#define FLASH_ACR_LATENCY_2WS 0x00000002U
#define __HAL_FLASH_GET_LATENCY() (READ_BIT((FLASH->ACR), FLASH_ACR_LATENCY))
#define __HAL_FLASH_SET_LATENCY(__LATENCY__) (*(__IO uint8_t *)ACR_BYTE0_ADDRESS = (uint8_t)(__LATENCY__))
#define ACR_BYTE0_ADDRESS 0x40023C00U
我们主要操作RCC_CFGR寄存器,所以在切换时钟源时要插入1、2个等待周期。
所以做法是调用函数__HAL_FLASH_GET_LATENCY()
读取FLASH_ACR寄存器LATENCY位。当等待周期小于2时要进行处理。
方法就是给LATENCY地址写值,写的值就是FLASH_LATENCY_2。
至于为什么没有用很多基地址组合,因为FLASH下的寄存器很重要,能自主读写的很少。
/*-------------------------- HCLK Configuration --------------------------*/
if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_HCLK) == RCC_CLOCKTYPE_HCLK)
{
/* Set the highest APBx dividers in order to ensure that we do not go through
a non-spec phase whatever we decrease or increase HCLK. */
if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK1) == RCC_CLOCKTYPE_PCLK1)
{
MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_HCLK_DIV16);
}if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK2) == RCC_CLOCKTYPE_PCLK2)
{
MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, (RCC_HCLK_DIV16 << 3));
}assert_param(IS_RCC_HCLK(RCC_ClkInitStruct->AHBCLKDivider));
MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_ClkInitStruct->AHBCLKDivider);
}之前结构体HCLK和其他三个相或,所以这儿的if判断都要进入里面;
后面两个if语句都是判断欲开启的外设时钟有没有PCLK1、PCLK2,都有,然后赋值16分频,具体的后面详解;
这部分主要是
MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_ClkInitStruct->AHBCLKDivider);
#define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))
这儿括号很多,一开始看错了,看成前面的寄存器和后面一堆相与,怎么也不对,其实是寄存器和中间那部分相与,再和后面那部分相或;这个宏定义作用是给这个寄存器写数值,后面一堆按位运算的作用是只写给想修改的位,其他的保持不变。
READ_REG(REG)就是读RCC_CFGR寄存器的值;
#define RCC_CFGR_HPRE_Pos (4U)
#define RCC_CFGR_HPRE_Msk (0xFUL << RCC_CFGR_HPRE_Pos)/*!< 0x000000F0 */
#define RCC_CFGR_HPRE RCC_CFGR_HPRE_MskRCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
#define RCC_SYSCLK_DIV1 RCC_CFGR_HPRE_DIV1
#define RCC_CFGR_HPRE_DIV1 0x00000000U
#define RCC_CFGR_HPRE_DIV2 0x00000080U
#define RCC_CFGR_HPRE_DIV4 0x00000090U
#define RCC_CFGR_HPRE_DIV8 0x000000A0U
...
#define RCC_CFGR_HPRE_DIV512 0x000000F0U这个宏定义的逻辑就是寄存器本来的值和(0x000000f0取反)也就是0xffffff0f相与,得到的值是bit[5:8]清零,其他位不变,这个值再和分频的值相或,0的位置变为想写入的值。
if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_SYSCLK) == RCC_CLOCKTYPE_SYSCLK)
{
assert_param(IS_RCC_SYSCLKSOURCE(RCC_ClkInitStruct->SYSCLKSource));
/* PLL is selected as System Clock Source */
else if((RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_PLLCLK) ||
(RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_PLLRCLK))
{
/* Check the PLL ready flag */
if(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET)
{
return HAL_ERROR;
}
}__HAL_RCC_SYSCLK_CONFIG(RCC_ClkInitStruct->SYSCLKSource);
/* Get Start Tick */
tickstart = HAL_GetTick();while (__HAL_RCC_GET_SYSCLK_SOURCE() != (RCC_ClkInitStruct->SYSCLKSource << RCC_CFGR_SWS_Pos))
{
if ((HAL_GetTick() - tickstart) > CLOCKSWITCH_TIMEOUT_VALUE)
{
return HAL_TIMEOUT;
}
}
}/* Decreasing the number of wait states because of lower CPU frequency */
if(FLatency < __HAL_FLASH_GET_LATENCY())
{
/* Program the new number of wait states to the LATENCY bits in the FLASH_ACR register */
__HAL_FLASH_SET_LATENCY(FLatency);/* Check that the new number of wait states is taken into account to access the Flash
memory by reading the FLASH_ACR register */
if(__HAL_FLASH_GET_LATENCY() != FLatency)
{
return HAL_ERROR;
}
}代码很长,去掉一部分HSE、HSI作为时钟源的代码原理和PLL的一致。
首先是时钟就绪标志位的检测,就比如我要PLL作为主时钟的时钟源。那么在开启主时钟之前必须要保证PLL是打开的,软件通过CR寄存器下PLLRDY判断。
通过函数
if(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET)
{
return HAL_ERROR;
}#define __HAL_RCC_GET_FLAG(__FLAG__) (((((((__FLAG__) >> 5U) == 1U)? RCC->CR :((((__FLAG__) >> 5U) == 2U) ? RCC->BDCR :((((__FLAG__) >> 5U) == 3U)? RCC->CSR :RCC->CIR))) & (1U << ((__FLAG__) & RCC_FLAG_MASK)))!= 0U)? 1U : 0U)
这个宏定义很长,下面是__FLAG__可以选的值/* Flags in the CR register */
#define RCC_FLAG_HSIRDY ((uint8_t)0x21)
#define RCC_FLAG_HSERDY ((uint8_t)0x31)
#define RCC_FLAG_PLLRDY ((uint8_t)0x39)
#define RCC_FLAG_PLLI2SRDY ((uint8_t)0x3B)/* Flags in the BDCR register */
#define RCC_FLAG_LSERDY ((uint8_t)0x41)/* Flags in the CSR register */
#define RCC_FLAG_LSIRDY ((uint8_t)0x61)
#define RCC_FLAG_BORRST ((uint8_t)0x79)
#define RCC_FLAG_PINRST ((uint8_t)0x7A)
#define RCC_FLAG_PORRST ((uint8_t)0x7B)
#define RCC_FLAG_SFTRST ((uint8_t)0x7C)
#define RCC_FLAG_IWDGRST ((uint8_t)0x7D)
#define RCC_FLAG_WWDGRST ((uint8_t)0x7E)
#define RCC_FLAG_LPWRRST ((uint8_t)0x7F)#define RCC_FLAG_MASK ((uint8_t)0x1FU)
将宏定义拆开来看:
if((((((__FLAG__) >> 5U) == 1U)? RCC->CR :((((__FLAG__) >> 5U) == 2U) ? RCC->BDCR :((((__FLAG__) >> 5U) == 3U)? RCC->CSR :RCC->CIR))) & (1U << ((__FLAG__) & RCC_FLAG_MASK)))!= 0U) return 1;
else return 0;
把((__FLAG__) >> 5U) 看成 b;就是
if(b == 1)
{
if(RCC_CR & (1U << ((__FLAG__) & RCC_FLAG_MASK)) != 0u)
return 1;
else
return 0;
}
else if (b == 2)
{
if(RCC_BDCR & (1U << ((__FLAG__) & RCC_FLAG_MASK)) != 0u)
return 1;
else
return 0;
}
...
#define RCC_FLAG_PLLRDY ((uint8_t)0x39)
对于PLL标准位,0x39右移5位等于1,0x39和0x1f相与,也就是取后5位,得到0x19;1左移0x19位是1左移25位,也就是PLLRDY位,其他位都是0。再和RCC_CR相与,就是取第25位是0,还是1.
当是1时,也就是PLL开启时得到的结果不等于0;当未硬件置一时,0和1相与也是0,得到的结果就是0;
__HAL_RCC_SYSCLK_CONFIG(RCC_ClkInitStruct->SYSCLKSource);
#define __HAL_RCC_SYSCLK_CONFIG(__RCC_SYSCLKSOURCE__) MODIFY_REG(RCC->CFGR, RCC_CFGR_SW, (__RCC_SYSCLKSOURCE__))
#define RCC_CFGR_SW_Pos (0U)
#define RCC_CFGR_SW_Msk (0x3UL << RCC_CFGR_SW_Pos)#define RCC_CFGR_SW RCC_CFGR_SW_Msk /*!< 0x00000003 */
#define RCC_SYSCLKSOURCE_PLLCLK RCC_CFGR_SW_PLL
#define RCC_CFGR_SW_HSI 0x00000000U
#define RCC_CFGR_SW_HSE 0x00000001U
#define RCC_CFGR_SW_PLL 0x00000002U
函数逻辑和之前一样,给最后两位赋值,其他位保持不变。
if(FLatency < __HAL_FLASH_GET_LATENCY())
{
/* Program the new number of wait states to the LATENCY bits in the FLASH_ACR register */
__HAL_FLASH_SET_LATENCY(FLatency);/* Check that the new number of wait states is taken into account to access the Flash
memory by reading the FLASH_ACR register */
if(__HAL_FLASH_GET_LATENCY() != FLatency)
{
return HAL_ERROR;
}
}这部分是减少等待周期;大于2时,写入到FLASH_ACR_LATENCY,和之前一样。
/*-------------------------- PCLK1 Configuration ---------------------------*/
if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK1) == RCC_CLOCKTYPE_PCLK1)
{
assert_param(IS_RCC_PCLK(RCC_ClkInitStruct->APB1CLKDivider));
MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_ClkInitStruct->APB1CLKDivider);
}/*-------------------------- PCLK2 Configuration ---------------------------*/
if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK2) == RCC_CLOCKTYPE_PCLK2)
{
assert_param(IS_RCC_PCLK(RCC_ClkInitStruct->APB2CLKDivider));
MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2,((RCC_ClkInitStruct->APB2CLKDivider) << 3U));
}RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
#define RCC_CFGR_PPRE1_Pos (10U)
#define RCC_CFGR_PPRE1_Msk (0x7UL << RCC_CFGR_PPRE1_Pos)/*!< 0x00001C00 */
#define RCC_CFGR_PPRE1 RCC_CFGR_PPRE1_Msk#define RCC_CFGR_PPRE2_Pos (13U)
#define RCC_CFGR_PPRE2_Msk (0x7UL << RCC_CFGR_PPRE2_Pos)/*!< 0x0000E000 */
#define RCC_CFGR_PPRE2 RCC_CFGR_PPRE2_Msk#define RCC_HCLK_DIV1 RCC_CFGR_PPRE1_DIV1
#define RCC_HCLK_DIV2 RCC_CFGR_PPRE1_DIV2
...
#define RCC_HCLK_DIV16 RCC_CFGR_PPRE1_DIV16#define RCC_CFGR_PPRE1_DIV1 0x00000000U
#define RCC_CFGR_PPRE1_DIV2 0x00001000U
...
#define RCC_CFGR_PPRE1_DIV16 0x00001C00U分别是给RCC_CFGR,bit[10:12]和bit[13:15]写入数据,数据内容是分频值。
对于PCLK2分频值用到PPRE1的值,所以要左移三位。
在这儿可以直接用PPER2的分频值,但不知道为什么没有用;
#define RCC_CFGR_PPRE2_DIV1 0x00000000U
#define RCC_CFGR_PPRE2_DIV2 0x00008000U
...
#define RCC_CFGR_PPRE2_DIV16 0x0000E000U
/* Update the SystemCoreClock global variable */
SystemCoreClock = HAL_RCC_GetSysClockFreq() >> AHBPrescTable[(RCC->CFGR & RCC_CFGR_HPRE)>> RCC_CFGR_HPRE_Pos];/* Configure the source of time base considering new system clocks settings */
HAL_InitTick (uwTickPrio);翻译下来的意思是:更新SystemCoreClock全局变量;
鉴于新的系统时钟设置来配置时基。
这儿涉及到hal库,目前还不太懂,希望有大佬补充。
时钟的配置最核心的还是看懂时钟树,知道时钟源怎么选,时钟这个路怎么走的,到哪个外设。至于配置完全可以利用软件的便利,节省大量的工作,对于函数的分析其实是笔者较真了。但是一点点梳理下来,还是学习到很多函数的写法,宏定义的运用。同时,以后可能面对的时钟配置远超过CubeMX软件使用的复杂度,掌握配置时钟整体的思路至关重要。
参考资料:stm32f4xx中文参考手册
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。