ホームホーム

STM32 の RTC で時計を作る

はじめに

STM32 マイコン(STM32F334R8)に内蔵されている RTC(リアルタイム・クロック)を使って時計を作ってみます。

システム構成

マイコンボードは STMicroelectronics 社の NUCLEO-F334R8 を使いました。 時刻の表示には「有機 EL ディスプレイ(OLED) SO1602A を使う」で紹介した有機 EL ディスプレイ SO1602A を使いました。

システム構成
川内 康雄 (著)

プロジェクトの作成

STM32CubeMX を使ってプロジェクトを作ります。

  1. "File" メニューの "New Project" を選択します。
  2. "Board Selector" で,使用するボードを選択します。

RTC の設定

STM32CubeMX のメイン画面,Pinout タブの Configuration → Peripherals → RTC で設定を行います。

  1. "Activate Clock Source" にチェックを入れます。
  2. "Activate Calendar" にチェックを入れます。
  3. "WakeUp" のドロップダウンリストで "Internal WakeUp" を選択します。

RTC のクロックソースの設定

今回使用したマイコンボード NUCLEO-F334R8 には RTC 用の 32.768 kHz 水晶発振子が搭載されているので,これを使うようにします。

  1. STM32CubeMX のメイン画面で "Clock Configurration" タブを開きます。
  2. ”RTC Clock Mux" で "LSE" を選択します。

割り込みの設定

1 秒ごとに割り込みを発生させるための設定を行います。

  1. STM32CubeMX のメイン画面で "Configurration" タブを開きます。
  2. ”Control" の "RTC" ボタンを押します。
  3. "RTC Configuration" 画面で,"Parameter Settings" タブを選択します。
  4. "Wake Up Clock" で "1 Hz" を選択します。
  5. "Wake Up Counter" に "0" を入力します。
  6. "NVIC Settings" タブを開きます。
  7. "RTC wake-up interrupt through EXTI line 20" の "Enabled" にチェックを入れます。

以上で,1 秒ごとに割り込みがかかるようになりますが,HAL_Delay 関数を使うときの問題を回避するため,"RTC wake-up" 割り込みの優先順位を下げておきます。

  1. STM32CubeMX のメイン画面で "Configurration" タブを開きます。
  2. ”Control" の "NVIC" ボタンを押します。
  3. "NVIC Configuration" 画面で,"NVIC" タブを選択します。
  4. "RTC wake-up interrupt through EXTI line 20" の "Preemption Priolity" を "Time base System tick timer" のそれより大きい数値にします。

以上で,STM32CubeMX での設定は終わりです。"Project" メニューの "Generate Code" でコードを生成し,お好みの統合開発環境で,コードを追加していきます。ちなみに私は,Atllic の TrueStudio を使っています。

コールバック関数の追加

STM32CubeMX で生成されたコードを読んでみます。 RTC wake-up 割り込みが発生すると,RTC_WKUP_IRQHandler 関数が呼ばれます。 そこから,HAL_RTCEx_WakeUpTimerIRQHandler が呼ばれます。 その中で,HAL_RTCEx_WakeUpTimerEventCallback が呼ばれます。

HAL_RTCEx_WakeUpTimerEventCallback は __weak 属性で定義されているので,この関数を再定義して処理するのが良さそうです。

void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
    // ここに 1 秒ごとの処理を書く
}

時刻の取得と表示

時刻の取得は HAL_RTC_GetTime 関数で行なえます。

ただし,HAL_RTC_GetTime を読んだあとは,(日付が必要なくても),HAL_RTC_GetDate を呼ばないといけないようです。そうしないと処理が止まります。

ソースコードは以下のようになります。

void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
    RTC_DateTypeDef date;
    RTC_TimeTypeDef time;

    HAL_RTC_GetTime(hrtc, &time, RTC_FORMAT_BCD);
    HAL_RTC_GetDate(hrtc, &date, RTC_FORMAT_BCD);

    OLED_WriteInstraction(0x02);     // Return Home
    PrintTime(&time);
}

上で使っている PrintTime 関数は以下のように定義しました。

// 1 文字表示
void PutChar(int c)
{
    OLED_WriteData(c);
}

// BCD(2 進化 10 進数) の表示
void PrintBCD(uint8_t n)
{
    PutChar(((n >> 4) & 0xf) + '0');
    PutChar((n & 0xf) + '0');
}

// 時刻の表示
void PrintTime(RTC_TimeTypeDef *time)
{
    PrintBCD(time->Hours);
    PutChar(':');
    PrintBCD(time->Minutes);
    PutChar(':');
    PrintBCD(time->Seconds);
}

OLED_WriteData,OLED_WriteInstraction 関数は,「有機 EL ディスプレイ(OLED) SO1602A を使う」で定義したものです。

時刻の設定

以上で 1 秒ごとに(起動からの)時刻を表示できるようになりました。

実際に時計として使うには,時刻や日付を設定する仕組みが必要です。時刻の設定は HAL_RTC_SetTime 関数,日付の設定は HAL_RTC_SetDate 関数で行なえます。

川内 康雄 (著)