(2015.9.23 作成)
STM32の場合RTCはペリフェラルとして内部にあるため、特に何の準備もなく使用できると思います。
ただVL Discoveryに使用されているF1シリーズはRTCの仕様が他のものと異なっており注意が必要です。詳しくはRTCのApplication note: AN3371を参照してください。F0~F4,L1まで微妙に進化し続けているので仕様が少しずつ違っています。
ここではF3Discovery基板に使用されているF303xCとNucleoF401REに使用されているF401xEで使用できるクラスを公開しています。
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 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); } }