今回はRTCについてお話します。
投稿時の開発環境を記しておきます。
PC:Windows10 OS
IDE: STM32CubeIDE Version1.3.0
Configurator: STM32CubeMX Version5.6.0
Board: STM32Nucleo-F401RE
RTCとは?
RTC は Real Time Clock の略で時計の機能です。
電池をつなぐ
せっかくなので、電池をつないでみて電源を落とした時の動作も確認してみます。
とは言え回路は簡易的なものですませます。
今回の回路は電源が 入っている・いない にかかわらず電池を消耗してしまう実験的なものになっています。
実用的なものにするには通常の電源と電池の電源を切り替えて VBAT に供給するような回路が必要になります。
電池は公称3V系のものに電池ホルダーをつけて線を引き出したものを用意しておきます。
電源を落とした時の処理も確認しておきたいので、下図の SB45 をはずしておきます。
SB45には 0Ω の抵抗がついているので、それをはずします。
なくさないようにとっておいてください。
C29 1uF のコンデンサがあるので、電池がなくても、これでわずかな時間はVBATがバックアップされます。
はっきりと VBAT=0V の状態をつくりたい場合には C29 をはずす方法も考えられますね。
そして VBAT/VLCD に電池の + 端子を接続し、 GNDに電池の – 端子を接続します。
こちらでは CN7の33番ピンに電池の + 端子をつなぎ、CN7の19番ピンに電池の – 端子を接続しました。
プロジェクトを作成する
IDEを起動し、File- New – STM32 Project を選択し、Target Selection ウィンドウが出たら Board Selector タブを選択し Boards List から NUCLEO-F401RE を選択し Next ボタンを押します。
Project 名に F401RTcHAL と入力し、Finishボタンを押します。
Initialize all peripherals with their default Mode ? と聞いてくるので Yesを押します。
This kind of project is associated with the STM32CubeMx perspective. Do you want to open this perspective now ? と聞いてくるので Yesを押します。
コードジェネレーターでRTCを設定する
Pinout & Configuration – Categories – Timers – RTC を選択し、
RTC Mode and Configuration の Mode の Activate Clock Sourceにチェックを入れます。
Clock Configuration – RTC Clock Mux で LSE を選択します。
なぜ LSE を選択するのか説明しておきます。
電源が落ちた状態でも VBAT に規定以上の電圧があれば LSE が動作するので、電源が落ちた状態でも RTC は計時することができるためです。
LSE を選択しないと、VBATでバックアップすることができても電源が落ちている間にRTCが計時できないために、その間の時刻が止まってしまいます。
もちろん OSC32_IN (OSC32_OUT) に水晶を接続する必要があります。
(STM32Nucleo-F401REボードには水晶が実装されています)
Project Explorer で F401RTcHAL を選択し、Project – Build Project でビルドしておきます。
コーディングする
main()の少し手前に以下のコードを書きます。
これはRTCの時刻をリアルタイムで確認するためのデバッグ用のコードです。
このprintf()デバッグについては こちら の記事を参考に設定を行ってください。
/* USER CODE BEGIN 0 */
#include <stdio.h>
int __io_putchar(uint8_t ch)
{
return ITM_SendChar(ch);
}
/* USER CODE END 0 */
while()ループの付近に以下のコードを書きます。
/* USER CODE BEGIN WHILE */
RTC_TimeTypeDef sTime;
RTC_DateTypeDef sDate;
while (1)
{
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
printf("20%02d.%02d.%02d %02d:%02d:%02d\r\n", sDate.Year, sDate.Month, sDate.Date, sTime.Hours, sTime.Minutes, sTime.Seconds);
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
コードジェネレーターが出力する MX_RTC_Init()を以下のように書き換えます。
static void MX_RTC_Init(void)
{
/* USER CODE BEGIN RTC_Init 0 */
/* USER CODE END RTC_Init 0 */
/* USER CODE BEGIN RTC_Init 1 */
/* USER CODE END RTC_Init 1 */
/** Initialize RTC Only
*/
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 127;
hrtc.Init.SynchPrediv = 255;
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN RTC_Init 2 */
#define MAGIC_NO 0x12a5
if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0) != MAGIC_NO)
{
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
sTime.Hours = 1;
sTime.Minutes = 0;
sTime.Seconds = 0;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
sDate.WeekDay = RTC_WEEKDAY_WEDNESDAY;
sDate.Month = RTC_MONTH_JANUARY;
sDate.Date = 1;
sDate.Year = 20;
if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, MAGIC_NO);
}
/* USER CODE END RTC_Init 2 */
}
ビルドして実行する
Run – Resume します。
実行前に一度、プログラムが停止したところで、SWV ITM Data Console の〇部分を押してprintf()出力を有効にしておきます。
そして Run します。
一番初めだけは、2020.01.01 01:00:00 から計時を始めます。
電池でバックアップできている間は電源を落とした状態でもRTCは計時します。
コードの概要
MX_RTC_Init()の中
HAL_RTC_Init()の後、HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0)でバックアップレジスタの内容を読みます。
バックアップレジスタの値が正しければその後の処理は行いません。
バックアップレジスタに書く値(MAGIC_NO)は意味のないもので、適当に決めました。
読んだ値が正しくない場合に、VBATに電源が供給できなかったと判断して日時の初期化を行います。
このサンプルでは 2020.01.01 01:00:00 に設定しています。
西暦の上2桁は手抜きで20固定にしています。
バックアップレジスタは複数あるので、この値も保存しておいて読み出すという方法もありますね。
後は while()ループで1秒毎に時刻を読み、printf()出力しています。
いかがでしたか?うまく計時できたでしょうか?
コメントを書く