STM32のクロック
出典: Wikimura
PICやH8と異なり、STM32はクロックの管理が結構細かくできるようです。この辺をブラックボックスとしておくと、思わぬところで壁にぶち当たりそうなので、詳しく調べてみます。
目次 |
クロック設定
STM32はコア、バス、ペリフェラルのクロックを細かく設定できるそうです。これらはレジスタを直接操作して行うには少々難しそうです。幸い、STM32F10x Standard Peripheral Libraryではシステム初期化関数のテンプレートが用意されており、その中でクロック初期化コードが記述されています。
このクロック初期化は各部の周波数をどのように設定してくれているかを見ていきます。
起動時に使うSystemInit関数
サンプルプログラムでは、main関数の最初で必ずSystemInit関数を呼び出しています。 この関数は、CMSISフォルダのsystem_stm32f10x.cで定義されています。 CMSISのドキュメントによれば、system_stm32f10x.cはテンプレートファイルであり、クロックや外部バスに関する設定を行い、システムを初期化する関数を記述するためのファイルとのことです。
SystemInit関数では(詳しいことは分かりませんが)基本的な設定と、クロック設定を行っているようです。 クロック設定はSetSysClock関数で行われています。
void SystemInit( void)
{
/* Reset the RCC clock configuration to the default reset state(for debug purpose) */
/* Set HSION bit */
RCC->CR |= (uint32_t)0x00000001;
中略
/* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
/* Configure the Flash Latency cycles and enable prefetch buffer */
SetSysClock();
}
クロック設定関数を呼び出すSetSysClock関数
SetSysClock関数では、以下のように周波数ごとにマクロ定義でスイッチするように作られています。
static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
SetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHz
SetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHz
SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
SetSysClockTo56();
#elif defined SYSCLK_FREQ_72MHz
SetSysClockTo72();
#endif
}
デフォルトでは72MHzになるようマクロ定義されていました。
ただし重要な注意があります。 テンプレートファイルであるsystem_stm32f10x.cに書かれた周波数設定関数は、 8MHzの外部発振子(器)が使われているとき正しく動作するということです(High/Med/Low-densityの場合)。 これ以外の周波数の場合は、自分で書きなおさなくてはなりません。
幸いにして、Olimex社のSTM32-H103およびSTM32-P103ボードでは、8MHz水晶が使われています。 そのため、デフォルトのままで72MHzがシステムクロックになります。
/*!< Uncomment the line corresponding to the desired System clock (SYSCLK)
frequency (after reset the HSI is used as SYSCLK source)
IMPORTANT NOTE:
==============
1. After each device reset the HSI is used as System clock source.
2. Please make sure that the selected System clock doesn't exceed your device's
maximum frequency.
3. If none of the define below is enabled, the HSI is used as System clock
source.
↓注目
4. The System clock configuration functions provided within this file assume that:
- For Low, Medium and High density devices an external 8MHz crystal is
used to drive the System clock.
- For Connectivity line devices an external 25MHz crystal is used to drive
the System clock.
If you are using different crystal you have to adapt those functions accordingly.
*/
/* #define SYSCLK_FREQ_HSE HSE_Value */
/* #define SYSCLK_FREQ_24MHz 24000000 */
/* #define SYSCLK_FREQ_36MHz 36000000 */
/* #define SYSCLK_FREQ_48MHz 48000000 */
/* #define SYSCLK_FREQ_56MHz 56000000 */
#define SYSCLK_FREQ_72MHz 72000000
システムクロックを72MHzにするSetSysClockTo72関数
SetSysClock関数から呼び出される関数の一つがこの関数です。 この関数は、外部発振子(器)の8MHzを逓倍し、システムクロックを72MHzにしてくれます。
それだけでなく、バスやペリフェラルの周波数も設定してくれます。 各部の周波数に関する定数が以下のように定義されています。 デフォルトの状態では、各部の周波数が以下になります。
const uint32_t SystemFrequency = SYSCLK_FREQ_72MHz; /*!< System Clock Frequency (Core Clock) */ const uint32_t SystemFrequency_SysClk = SYSCLK_FREQ_72MHz; /*!< System clock */ const uint32_t SystemFrequency_AHBClk = SYSCLK_FREQ_72MHz; /*!< AHB System bus speed */ const uint32_t SystemFrequency_APB1Clk = (SYSCLK_FREQ_72MHz/2); /*!< APB Peripheral bus 1 (low) speed */ const uint32_t SystemFrequency_APB2Clk = SYSCLK_FREQ_72MHz; /*!< APB Peripheral bus 2 (high) speed */
SetSysClockTo72の中身
STM32ではリセット後は内部8MHz発振回路(HSI)の作るクロックで動作します。 また、外部発信回路(HSE)が正常に動作することが確認できるレジスタが存在します。
この関数ではまず、発振が確認できるか、設定した時間が経過するまで待機するようです。 設定した時間が経過しても発振していなければ、無限ループで止まるようになっています。 (この関数はテンプレートのため、ユーザ定義の動作を書き加えることが可能です)
外部発振が正常に動作しているとわかったら、プリスケーラの設定を行っています。 最後にPLLを有効化し、PLLの正常動作が完了するのを待機します。
これらが終了した時点で関数を抜け、通常動作に移行します。 (テンプレートがないとここでまずつまづきそう...)
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
/* Enable HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSEStartUp_TimeOut));
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
} else {
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* Enable Prefetch Buffer */
FLASH->ACR |= FLASH_ACR_PRFTBE;
/* Flash 2 wait state */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
/* Enable PLL */
RCC->CR |= RCC_CR_PLLON;
/* Wait till PLL is ready */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
} else {
/* If HSE fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
/* Go to infinite loop */
while (1)
{
}
}
}
各部へ供給されるクロック
各モジュールへ供給されるクロックはどこで決まるかは、クロックツリーを見ると分かります。 各モジュールへのクロックはANDが付いており、停止可能になっています(Gated Clock)。
クロックツリー
リファレンスマニュアルにSTM32のクロックツリーが載っています。 ペリフェラル各部へ供給されるクロックを把握できます。
SetSysClockTo72を使ってシステムクロック(SYSCLK)を72MHzにすると、AHB(システムバス)クロックが72MHz、APB2(ペリフェラルバス)が72MHz、APB1(ペリフェラルバス)が32MHzとなります。
各部のクロック
外部8MHz発振子(器)から72MHzを作った時の各部のクロックは以下のようになります。 デフォルトのSetSysClockTo72の設定のままの状態でのクロックですので、設定が異なれば変わります。
タイマクロックについて
注意しなくてはならないのがタイマクロックです。 タイマ1および8のクロックは、APB2プリスケーラの値によって変化します。APB2プリスケーラの分周比が1の場合、タイマへのクロックはAPB2へのクロックと同じになります。1以外の場合はAPB2クロックの倍になります。
タイマ2~7のクロックは、APB1プリスケーラの値によって変化します。こちらもタイマ1および8と同様、APB1プリスケーラの分周比が1の場合、APB1へのクロックと同じになります。それ以外はAPB1クロックの倍になります。
ADCクロックについて
ADCクロックも注意が必要です。このクロックは最大14MHzと決まっているそうです。ここで取り扱っている設定では、AHBクロックは72MHzです。ADCクロックはこれをADCプリスケーラで2,4,6,8分周することで生成します。
分周比が2,4,6,8のとき、ADC周波数はそれぞれ32,18,12,9MHzとなります。最大が14MHzなので、分周比2と4は選択できません。
まとめ
- Standard Peripheral Libraryで提供されるsystem_stm32f10x.cにシステム初期化関数とクロック初期化関数が定義されている
- このファイルはテンプレートとして提供される
- クロック初期化関数は数種用意されており、8MHzの外部発振子(器)を使用した時に正しく動作するよう定義されている
- 異なる周波数の場合は自分で設定しなおすこと
- PLL起動に失敗した時の挙動を定義できる
- 外部に8MHz発振子(器)を接続し、デフォルトのSetSysClockTo72を実行した時の各部の周波数
- SYSCLK: 72MHz
- AHB: 72MHz
- APB2: 72MHz
- APB1: 36MHz
- タイマ周波数は所属するペリフェラルバス(APBx)プリスケーラ値によって変化する
- ADC周波数の上限は14MHz
コメント
- タイマ割り込みを使う場合、割り込み周期が計算できないとどうしようもありません。H8と異なり、設定次第ではタイマのクロックはコアのクロックと異なることもあり得ます。十分に注意する必要があることが分かりました。

