前回の講義では、H7クロックツリーの基本的な概念について説明しました。次に、HALライブラリとCubeMxを使用して構成します。
繰り返しになりますが、この記事には時間がかかります。RCCの初期化プロセスを理解したい場合は、よくお読みください。最初は理解できないかもしれませんが、注意深く読むと必ず何かが得られます。
プロセスを開始する
H7の起動プロセスについて話しましょう。
システムの電源がオンになってリセットされ、スタートアップファイルstartup_stm32h743xx.sを入力して、このファイルのリセット割り込みサービスルーチンを実行します。
- ファイルsystem_stm32h7xx.cにあるリセット割り込みサービスルーチンで関数SystemInitを実行します。
- その後、スタートアップファイル__mainが呼び出され、最後にmain関数に入ります。
main関数を入力して、ユーザーアプリケーションプログラミングを開始します。プログラムを実行する前に、次のものが必要です。
- MPUの初期化には、ライブラリファイルstm32h7xx_hal_cortex.cおよびstm32h7xx_hal_cortex.hが必要です。
- キャッシュの初期化にはcore_cm7.hファイルが必要です。
- Systickティックタイマーを初期化するためのHALライブラリ初期化関数HAL_Initには、ファイルstm32h7xx_hal.cが必要です。
- システムクロックの初期化には、ライブラリファイルstm32h7xx_hal_rcc.cが必要です
HalライブラリのSystemInit関数はクロックに対してのみ実行することに注意してください。RCCクロック構成をデフォルトのリセット値にリセットし(HSIはデフォルトで有効になっています)、他の構成は実行しないため、クロックの初期化は次のようにする必要があります。ユーザーが構成します。
今日私たちが気にしているのは、システムクロックの初期化の最後のステップです
上記の4つのステップでは、HAL_Initを実行した後も、システムは内部高速クロックHSIを使用しています。H7の場合、HSIの主周波数は64MHzです。
クロック構成を変更する
それでは、時計の構成を変更する方法を見てみましょう。
ステップ1:
ボードの実際の水晶発振器サイズと一致するようにstm32h7xx_hal_conf.hファイルのHSE_VALUE構成のサイズを構成します。
私が使用する外部水晶は25Mhzなので、構成は25000000です。
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
#endif /* HSE_VALUE */
ステップ2:次に、クロックを初期化します。主なものは、2つの構造パラメーターと2つの関数です。
RCC_ClkInitTypeDef RCC_ClkInitStruct; // 配置时钟源相关参数
RCC_OscInitTypeDef RCC_OscInitStruct; //配置系统时钟源及各个外设分频系数
HAL_RCC_OscConfig() //配置时钟源相关参数
HAL_RCC_ClockConfig() //配置系统时钟源及各个外设分频系数
HAL_RCC_OscConfig()
HAL_RCC_OscConfig()、この関数はHALライブラリキーヘッダーファイルstm32h7xx_hal_rcc.hで宣言され、ファイルstm32h7xx_hal_rcc.cで定義されています。まず、関数宣言を見てみましょう。
__weak HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct);
RCC_OscInitTypeDef構造体パラメーターは1つだけです。この構造体を見てみましょう。
typedef struct
{
uint32_t OscillatorType; //需要选择配置的振荡器类型
uint32_t HSEState; //HSE 状态
uint32_t LSEState; //LSE 状态
uint32_t HSIState; //HIS 状态
uint32_t HSICalibrationValue; //HIS 校准值
uint32_t LSIState; //LSI 状态
uint32_t HSI48State //HSI48 的状态
uint32_t CSIState; //CSI 状态
uint32_t CSICalibrationValue; //CSI 校准值
RCC_PLLInitTypeDef PLL; //PLL 配置
}RCC_OscInitTypeDef;
1つ目は、オシレーターのタイプを選択することです。たとえば、HSEをオンにする場合は、OscillatorTypeの値をRCC_OSCILLATORTYPE_HSEに設定し、次にHSEStateの値をRCC_HSE_ONに設定してHSEをオンにします。他のクロックソースHSI、LSI、およびLSEの場合、設定方法は同様です。
最後に、RCC_PLLInitTypeDef PLL;//PLL構成があります
これは、フェーズロックループを構成するためのものです。構造パラメータは次のとおりです。
typedef struct
{
uint32_t PLLState; //PLL 状态
uint32_t PLLSource; //PLL 时钟源
uint32_t PLLM; //PLL 分频系数 M
uint32_t PLLN; //PLL 倍频系数 N
uint32_t PLLP; //PLL 分频系数 P
uint32_t PLLQ; //PLL 分频系数 Q
uint32_t PLLR; //PLL 分频系数 R
uint32_t PLLRGE; //PLL1 时钟输入范围
uint32_t PLLVCOSEL; //PLL1 时钟输出范围
uint32_t PLLFRACN; //PLL1 VCO 乘数因子的小数部分
}RCC_PLLInitTypeDef;
ここでの周波数分割係数はすべてPLLフェーズロックループによって制御されます。私たちがしなければならないのは、特定の式を理解することだけです。
- Fvco:VCO周波数
- Fsys:システムクロック周波数。これは、PLL1のp分割出力クロック周波数でもあります。pll1_p_ck
- Fpllq:PLL1のq分割出力クロック周波数pll1_q_ck
- ref1_ck:PLL入力クロック周波数。HSI、CSI、HSEなどが可能です。
- plln:PLL1周波数増倍率(PLL周波数増倍)、値の範囲:4〜512。
- pllm:PLL1プリスケール係数(PLLに入る前の周波数分割)、値の範囲:2〜63。
- pllp:周波数分割後のシステムクロックとして使用されるPLL1(PLL後の周波数分割)のp周波数分割係数、値の範囲:2〜128。(2の倍数である必要があります)
- pllq:q PLL1の周波数分割係数(PLL後の周波数分割)、値の範囲:1〜128。
VCO频率: Fvco= ref1_ck*(plln/pllm);
pll1_p_ck频率: Fsys=Fvco/pllp= ref1_ck*(plln/(pllm*pllp));
pll1_q_ck频率: Fpllq=Fvco/pllq= ref1_ck*(plln/(pllm*pllq));
具体的な計算式は次のとおりです。これは前の記事でも説明されています。
HAL_RCC_ClockConfig()
次に、次のように宣言されているHAL_RCC_ClockConfig()関数を確認します。
HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct,
uint32_t FLatency);
2つのパラメータがあります。最初のエントリパラメータRCC_ClkInitStructは、構造RCC_ClkInitTypeDefのポインタタイプであり
、SYSCLKクロックソースとSYSCLK、AHB、APB1、APB2、APB3、およびAPB4の周波数分割係数を設定するために使用され
ます。2番目のエントリパラメータFLatencyは、FLASH遅延を設定するために使用されます
stm32h7xx_hal_rcc.hで宣言されたRCC_ClkInitTypeDefの特定の場所を見てみましょう。
typedef struct
{
uint32_t ClockType; /*!< 要配置的时钟。
该参数可以是@ref RCC_System_Clock_Type 的值 */
uint32_t SYSCLKSource; /*!< 用作系统时钟的时钟源(SYSCLKS)*/
uint32_t SYSCLKDivider; /*!< 系统时钟分频 */
uint32_t AHBCLKDivider; /*!< AHB分频 */
uint32_t APB3CLKDivider; /*!APB3分频*/
uint32_t APB1CLKDivider; /*!APB1分频*/
uint32_t APB2CLKDivider; /*!APB2分频*/
uint32_t APB4CLKDivider; /*!APB4分频*/
}RCC_ClkInitTypeDef;
最初のパラメーターClockType構成は、SYSCLK、HCLK、D1PCLK1(PCLK3)、PCLK1、PCLK2、およびD3PCLK1(PCLK4)の6つのクロックを構成することを示しています。
2番目のパラメーターSYSCLKSource構成は、システムクロックソースをPLLとして選択します。
3番目のパラメーターSYSCLKDividerは、SYSCLK周波数分割係数を構成し
ます。4番目のパラメーターAHBCLKDividerは、AHB周波数分割係数
を構成します。5番目のパラメーターAPB1CLKDividerは、APB1周波数分割係数を構成します。6番目のパラメーターAPB2CLKDividerは、APB2周波数分割係数
を構成します。係数
。7番目のパラメーターAPB3CLKDividerは、APB3周波数分割係数を
構成します。8番目のパラメーターAPB4CLKDividerは、 APB4周波数分割係数を構成します。
これについては、以下で詳しく説明します
次に、2番目のパラメーターを確認します。FLatencyはFLASH遅延と電圧レギュレーターVOSを設定するために使用されます
次に、フラッシュの読み取り操作を知る必要があります
フラッシュ読み取り操作は、毎回フラッシュデータを読み取ることであり、その後の通常のデータ送信を確実にするために、一定時間遅延する必要があります。
VOS:STM32H7では、システムフラッシュは(電力制御レジスタ内の)電圧レギュレータ出力電圧レベル選択(VOS)の影響を受けます。
最高のフラッシュ読み取り速度を得るには、VOSレベルを1に設定してから、待機カウントを4に設定して、最高のフラッシュ読み取り速度を得る必要があります。ST公式ルーチンは4つの待機状態を使用します
。STM32H7xxリファレンスマニュアルページ112
最終的なコードは次のとおりです。
注釈付き
/*
*********************************************************************************************************
* 函 数 名: SystemClock_Config
* 功能说明: 初始化系统时钟
* System Clock source = PLL (HSE)
* SYSCLK(Hz) = 400000000 (CPU Clock)
* HCLK(Hz) = 200000000 (AXI and AHBs Clock)
* AHB Prescaler = 2
* D1 APB3 Prescaler = 2 (APB3 Clock 100MHz)
* D2 APB1 Prescaler = 2 (APB1 Clock 100MHz)
* D2 APB2 Prescaler = 2 (APB2 Clock 100MHz)
* D3 APB4 Prescaler = 2 (APB4 Clock 100MHz)
* HSE Frequency(Hz) = 25000000
* VDD(V) = 3.3
* Flash Latency(WS) = 4
* 形 参:
* plln:PLL1倍频系数(PLL倍频),取值范围:4~512.
* pllm:PLL1预分频系数(进PLL之前的分频),取值范围:2~63.
* pllp:PLL1的p分频系数(PLL之后的分频),分频后作为系统时钟,取值范围:2~128.(且必须是2的倍数)
* pllq:PLL1的q分频系数(PLL之后的分频),取值范围:1~128.
*
*
* Fvco:VCO频率
* Fsys:系统时钟频率,也是PLL1的p分频输出时钟频率 pll1_p_ck
* Fpllq:PLL1的q分频输出时钟频率 pll1_q_ck
* ref1_ck: PLL输入时钟频率,可以是HSI,CSI,HSE等.
*
*
* plln:PLL1倍频系数(PLL倍频),取值范围:4~512.
* pllm:PLL1预分频系数(进PLL之前的分频),取值范围:2~63.
* pllp:PLL1的p分频系数(PLL之后的分频),分频后作为系统时钟,取值范围:2~128.(且必须是2的倍数)
* pllq:PLL1的q分频系数(PLL之后的分频),取值范围:1~128.
*
*
*
* VCO频率: Fvco= ref1_ck*(plln/pllm);
* pll1_p_ck频率: Fsys=Fvco/pllp= ref1_ck*(plln/(pllm*pllp));
* pll1_q_ck频率: Fpllq=Fvco/pllq= ref1_ck*(plln/(pllm*pllq));
*
* CPU频率(rcc_c_ck)=pll1_p_ck频率=400Mhz
* rcc_aclk=rcc_hclk3=200Mhz
* AHB1/2/3/4(rcc_hclk1/2/3/4)=200Mhz
* APB1/2/3/4(rcc_pclk1/2/3/4)=100Mhz
* FMC时钟频率=pll2_r_ck=((25/25)*512/2)=256Mhz
*
*
* 外部晶振为25M的时候,推荐值:plln=160,pllm=5,pllp=2,pllq=2.
* 得到:Fvco=25*(160/5)=800Mhz
* CPU频率Fsys= pll1_p_ck频率=800/2=400Mhz
* pll1_q_ck频率=800/2=400Mhz
* 返 回 值: 无
*********************************************************************************************************
*/
void SystemClock_Config(u32 plln,u32 pllm,u32 pllp,u32 pllq)
{
HAL_StatusTypeDef ret=HAL_OK;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitTypeDef RCC_OscInitStruct;
/*使能供电配置更新 */
MODIFY_REG(PWR->CR3,PWR_CR3_SCUEN, 0);
/*
1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
详情看参考手册的Table 12的表格。
2、这里选择使用VOS1,电压范围1.15V - 1.26V。
*/
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
while ((PWR->D3CR & (PWR_D3CR_VOSRDY)) != PWR_D3CR_VOSRDY) {
}
/* 使能HSE,并选择HSE作为PLL时钟源 */
RCC_OscInitStruct.OscillatorType=RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState=RCC_HSE_ON;
RCC_OscInitStruct.HSIState=RCC_HSI_OFF;
RCC_OscInitStruct.CSIState=RCC_CSI_OFF;
RCC_OscInitStruct.PLL.PLLState=RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource=RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLN=plln;
RCC_OscInitStruct.PLL.PLLM=pllm;
RCC_OscInitStruct.PLL.PLLP=pllp;
RCC_OscInitStruct.PLL.PLLQ=pllq;
RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
ret=HAL_RCC_OscConfig(&RCC_OscInitStruct);
if(ret!=HAL_OK) while(1);
/*
选择PLL的输出作为系统时钟
配置RCC_CLOCKTYPE_SYSCLK系统时钟
配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线
AHB 分频系数为 2,故其频率为HCLK=SYSCLK/2=200MHz。
APB1 分频系数为 2,故其频率为 PCLK1=HCLK/2=100MHz。
APB2分频系数为 2,故其频率为 PCLK2=HCLK/2=200/2=100MHz,
APB3 分频系数为 2,故其频率PCLK3=HCLK/2=200/2=100MHz,
APB4 的分频系数为 2,故其频率 PLCK4=HCLK/2=200/2=100MHz
*/
RCC_ClkInitStruct.ClockType=(RCC_CLOCKTYPE_SYSCLK|\
RCC_CLOCKTYPE_HCLK |\
RCC_CLOCKTYPE_D1PCLK1 |\
RCC_CLOCKTYPE_PCLK1 |\
RCC_CLOCKTYPE_PCLK2 |\
RCC_CLOCKTYPE_D3PCLK1);
RCC_ClkInitStruct.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.SYSCLKDivider=RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider=RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB1CLKDivider=RCC_APB1_DIV2;
RCC_ClkInitStruct.APB2CLKDivider=RCC_APB2_DIV2;
RCC_ClkInitStruct.APB3CLKDivider=RCC_APB3_DIV2;
RCC_ClkInitStruct.APB4CLKDivider=RCC_APB4_DIV2;
/* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
ret=HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
if(ret!=HAL_OK) while(1);
/* 使用IO的高速模式,要使能IO补偿,即调用下面三个函数
(1)使能CSI clock
(2)使能SYSCFG clock
(3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
*/
__HAL_RCC_CSI_ENABLE() ;
__HAL_RCC_SYSCFG_CLK_ENABLE() ;
HAL_EnableCompensationCell();
}
主な関数呼び出し:
SystemClock_Config(160,5,2,4); //设置时钟400Mhz