RTC (STM32)

(2015.9.23 作成)

 STM32の場合RTCはペリフェラルとして内部にあるため、特に何の準備もなく使用できると思います。

 ただVL Discoveryに使用されているF1シリーズはRTCの仕様が他のものと異なっており注意が必要です。詳しくはRTCのApplication note: AN3371を参照してください。F0~F4,L1まで微妙に進化し続けているので仕様が少しずつ違っています。

 ここではF3Discovery基板に使用されているF303xCとNucleoF401REに使用されているF401xEで使用できるクラスを公開しています。

RTC クロックについて

 STM32ではRTCペリフェラルに供給出来るクロック(RTC CLK)は3種類あり、このどれかに設定する必要があります。


特徴
LSI

マイコン内部で生成するクロック。

付加部品がなく使用できるので最も安価だが精度に難あり。

(F303xCの場合40±10kHz, F401xEの場合32±15kHz)

VDDが切れれば停止する。

LSE

外部の水晶振動子32.768kHzで生成。

VDDが切れてもVbatに電力供給がされていればOK。

このため電源を切ってもボタン電池だけでカレンダー機能を維持する使い方が出来る。

HSE

外部の水晶振動子(8MHz)を分周して使用。

精度が高いのでDiscovery/NucleoではLSEがなければこれを使うべき

VDDが切れれば停止する。

 もっとも望ましいものはLSEの使用だと思いますが、Discovery/Nucleo基板ではLSEが必ずしも載っていません。管理人の手元にある基板で確認したところ外部水晶振動子の搭載有無は以下のような状態でした。


HSE LSE
VL Discovery
F0 Discovery ×

×

F3 Discovery ×

×

Nucleo (MB1136 C-02) ×

Nucleo (MB1136 C-01) ×

×

 Nucleo基板の場合は基板に貼られているシールによって仕様の違いを見分けることが出来るようです。このような状況ですが、いずれの基板でもHSE/LSEをユーザーが追加できるようになっているので必要に応じて追加するようにすればよいと思います。

 LSEを選択する際にはSTマイクロさんのApplication Note AN2867: Oscillator design guideが参考になるかもしれません。

 またHSEが取り付けられていない基板が多いですが、ST-Link用のマイコンには水晶が取り付けられていると思います。デフォルトでこのクロックをバイパスするように設定されているので、これを利用するのがもっとも簡便だと思います。

RTC Clockの設定

 RTC CLK関連のブロック図をまとめると以下のようになります。

 上で説明したように3種類のクロックソースのどれかから入力されたクロックを /x, Asynchronous, Synchronousプリスケーラで調整してCalendarブロックへ1Hzが供給されるように設定します。

 実際に1Hzへ調整されたかはCalibration Outputで確認できます。ここで1Hzまたは512Hz出力という名前でOutputの出力元を選択するのですが、これは32.768kHzのLSEが取り付けられたときにこの出力になるという意味で実際にこの周波数で出力されるとは限りません。F3DiscoとNucleoF401の場合は以下の通りの出力になります。



LSE F3 Discovery Nucleo F401RE
HSE LSI HSE LSI
Base Clk 32.768kHz 8MHz 40kHz 8MHz 32kHz
/x - 32 - 25 -
PREDIV_A 127 124 124 127 127
PREDIV_S 255 1999 319 2499 249
fck_apre 256Hz 2kHz 320Hz 2.5kHz 250Hz
fck_spre 1Hz 1Hz 1Hz 1Hz 1Hz
512Hz Out 512Hz 4.098kHz 655.7Hz 5kHz
500Hz
1Hz Out 1Hz 7.81Hz 1.25Hz 9.765kHz 0.976Hz

関数リファレンス

 STM32版のRTCクラスではBaseクラスの関数に加え、以下の関数が利用可能です。実際のコードはこちらからダウンロード可能です。

コンストラクタ

プロトタイプ Rtc(const Rtc_ClockSource &ClockSource);
戻り値

無し

引数

クロックソースを指定。以下のどれか

Rtc_ClockSource_LSI

Rtc_ClockSource_LSE

Rtc_ClockSource_HSE

備考

Calibration Outputからクロックを出力する

プロトタイプ void OutputEnable(const uint32_t &OutputFrequency)const
戻り値

無し

引数

Calibration Outputのソースを指定。以下のどちらか

RTC_CALIBOUTPUT_1HZ

RTC_CALIBOUTPUT_512HZ

備考

サンプルコード

 以下のコードをNucleoF401REに書き込むとキャラクタ液晶に日時を表示することが出来ます。キャラクタ液晶はこれを利用しました。

#include "DKS_Util_NucleoF401.h"
#include "DKS_RTC_NucleoF401.h"
#include "DKS_GPIO_NucleoF401.h"
#include "DKS_Wait_NucleoF401.h"
#include "DKS_HD44780.h"

int main(void)
{
        DKS::InitSystem();

        DKS::DigitalOut rs(GPIOB,GPIO_PIN_5);
        DKS::DigitalOut e(GPIOB,GPIO_PIN_4);
        DKS::DigitalOut db4(GPIOB,GPIO_PIN_10);
        DKS::DigitalOut db5(GPIOA,GPIO_PIN_8);
        DKS::DigitalOut db6(GPIOA,GPIO_PIN_9);
        DKS::DigitalOut db7(GPIOC,GPIO_PIN_7);
        DKS::Wait w(TIM9);
        DKS::HD44780 lcd(&rs, &e, &db4, &db5, &db6, &db7, &w);

        DKS::Rtc rtc(DKS::Rtc_ClockSource_LSI);
        rtc.OutputEnable(RTC_CALIBOUTPUT_512HZ);
        struct tm t;
        if (!rtc.isConfigured)
        {
                t.tm_year = 2015-1900;
                t.tm_mon = 9;
                t.tm_mday = 6;
                t.tm_wday = 0;  //Sunday
                t.tm_hour = 13;
                t.tm_min = 15;
                t.tm_sec = 30;
                rtc.settime(t);
        }

        while(1)
        {
                rtc.time(t);
                lcd.setCursor(0, 0);
                lcd.printf("%0.2d-%0.2d-%0.2d", 1900 + t.tm_year, t.tm_mon, t.tm_mday);
                lcd.setCursor(0, 1);
                lcd.printf("%0.2d:%0.2d:%0.2d",t.tm_hour, t.tm_min, t.tm_sec);
        }
}