STM32F4-Discovery/Nucleo-F401RE 用FM音源プログラム -- TGSTM32F4 (15)

STM32 の内蔵モジュールを利用するためのライブラリとして、これまで STMicroelectroics 社は「StdPeriph_Driver」を提供してきましたが、現在は後継の「STM32Cube」(総称) に移行が始まっており、「StdPeriph_Driver」は新規開発には推奨されないものとなっています。
「STM32Cube」は、GUI で周辺モジュールのコンフィギュレーションを行い、初期化用の C コードを生成する「STM32CubeMX」と、F4xx や F2xx などのファミリ別のライブラリ (F4xx 用は「STM32CubeF4」) とで構成されています。
現在のところ、F4 用と F2 用のライブラリが提供されていて、F1 用や F0 用はまだ提供されていないようです。
(2014/06/26 および 06/30 追記: 2014/06/18 付けで F3, F2, F0 用のライブラリ (V1.0.0) がリリースされたようです。 F4 用は V1.3.0 がリリースされました。)
Nucleo 用のサンプル・プログラム集 (stsw-32143.zip) には Nucleo-F401RE 用のサンプル・プログラムは含まれず、STM32CubeF4 (stm32cubef4.zip) パッケージの中に、STM32F4-Discovery 用のサンプルなどと一緒に Nucleo-F401RE 用のサンプル・プログラムが含まれています。
STM32F4-Discovery 用 FM 音源プログラム TGSTM32F4 を Nucleo-F401RE 用に移植するにあたっては、まずは、現状の StdPeriph_Driver を使っているプログラムの修正により実現したいと思っています。
その後に STM32CubeF4 を使用する形に書き換えたいと思います。
STM32CubeMX には、GUI でクロック関係を設定するクロック・コンフィギュレータが含まれています。
外部クロック HSE = 12.096 MHz と選んで設定した場合の画面を下に示します。 (図をクリックすると拡大します)
(2014 年 6 月 30 日追記: I2SPLL の設定値 (PLLI2SR) が誤っていたのを訂正し、図を差し替えました。)

STM32CubeMX_scr0_small.jpg

外部クロック周波数と希望の CPU クロック周波数とを入れると PLL 設定値を自動計算してくれるなら便利なのですが、実際はそうではなく、PLL の設定値をドロップ・ダウン・リストの中から手動で選ぶと、結果の CPU クロック周波数を計算してくれて、範囲外なら「赤色」で表示して警告するものです。
コンフィギュレーションとして、

  • I2S クロックを 48 kHz サンプリングに適した値に選び、
  • MCO1 からシステム PLL クロック周波数を 4 分周したものを出力
  • MCO2 から I2S PLL クロック周波数を 5 分周したものを出力

するように設定した場合に生成される初期化プログラムのクロック設定部の C ソースを下に示します。
(2014 年 6 月 30 日追記: I2SPLL の設定値 (PLLI2SR) が誤っていたのを訂正し、リストを差し替えました。)

/** System Clock Configuration
*/
void SystemClock_Config(void)
{

  RCC_ClkInitTypeDef RCC_ClkInitStruct;
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
  RCC_OscInitTypeDef RCC_OscInitStruct;

  __PWR_CLK_ENABLE();

  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);

  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 = 9;
  RCC_OscInitStruct.PLL.PLLN = 250;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  HAL_RCC_OscConfig(&RCC_OscInitStruct);

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);

  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S|RCC_PERIPHCLK_RTC;
  PeriphClkInitStruct.PLLI2S.PLLI2SN = 192;
  PeriphClkInitStruct.PLLI2S.PLLI2SR = 3;
  PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_HSE_DIV21;
  HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);

  HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_PLLCLK, RCC_MCODIV_4);

  HAL_RCC_MCOConfig(RCC_MCO2, RCC_MCO2SOURCE_PLLI2SCLK, RCC_MCODIV_5);

  HAL_RCC_EnableCSS();

}

MCO1 / MCO2 から出力されるクロック周波数を実測し、期待値と一致する結果が得られたことで、実際にこのプログラムで正しく設定されていることが確認できました。