マイコンで時刻を扱いたいときに便利なのがRTC(リアルタイムクロック)です。
今回は、定番のRTCモジュール「DS1307」を使用し、ATtiny202で時刻表示を行うプログラムを作成しました。
しかも今回は、
- Wireライブラリ不使用
- 自作ソフトI2C(SoftI2C_VPORT)ダウンロード使いかたはこちらのブログで紹介しています。
- 自作UART(SimpleUART)
という構成で、プログラム容量は約1410バイトに収まりました。
「小容量マイコンでもRTCを使いたい!」という方に参考になる内容になっています。
使用した環境
マイコン:ATtiny202
RTC:DS1307(I2C接続)アマゾンで数年前に購入しました。

通信:
- ソフトI2C(自作)
- UART(自作)
接続図
下図のように接続しました。

DS1307の基本動作
DS1307はI2Cで通信し、内部レジスタに時刻情報を保持しています。
| アドレス | 内容 |
|---|---|
| 0x00 | 秒 |
| 0x01 | 分 |
| 0x02 | 時 |
| 0x03 | 曜日 |
| 0x04 | 日 |
| 0x05 | 月 |
| 0x06 | 年 |
ポイントはここです👇
読み出す前にレジスタアドレスを指定する必要がある
重要ポイント(ハマりどころ)
i2c.start();
i2c.write(DS1307_ADDR << 1); // 書き込みモード
i2c.write(0x00); // 秒レジスタ
i2c.stop();これをやらないと、
正しく時刻が取得できません
理由はシンプルで、
どこから読むか」を指定していないためです。
DS1307は内部ポインタを持っているので、最初に 0x00(秒レジスタ)を指定する必要があります。
プログラム全文
#include <SimpleUART.h>
#include <string.h>
#include <SoftI2C_VPORT.h>
#define DS1307_ADDR 0x68
SoftI2C_VPORT i2c(1, 2); // SDA=PA1, SCL=PA2
// Initialize with 16 bytes buffer
SimpleUART<16> myUART;
// 英語曜日(1=日曜)
const char* weekStr[7] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
uint8_t bcdToDec(uint8_t val) {
return (val >> 4) * 10 + (val & 0x0F);
}
uint8_t decToBcd(uint8_t val) {
return ((val / 10) << 4) | (val % 10);
}
void rtc_write_time(uint8_t sec, uint8_t min, uint8_t hour,
uint8_t day, uint8_t date, uint8_t month, uint8_t year) {
i2c.start();
// 書き込みモード
if (!i2c.write(DS1307_ADDR << 1)) {
myUART.println("ADDR NACK (WRITE)");
i2c.stop();
return;
}
i2c.write(0x00);
i2c.write(decToBcd(sec) & 0x7F);
i2c.write(decToBcd(min));
i2c.write(decToBcd(hour));
i2c.write(decToBcd(day));
i2c.write(decToBcd(date));
i2c.write(decToBcd(month));
i2c.write(decToBcd(year));
i2c.stop();
delay(5);
}
void rtc_read(uint8_t *buf) {
i2c.start();
i2c.write(DS1307_ADDR << 1); // 書き込みモード
i2c.write(0x00); // 秒レジスタ
i2c.stop();
i2c.start();
i2c.write((DS1307_ADDR << 1) | 1); //読み込みモード
for (uint8_t i = 0; i < 7; i++) {
buf[i] = i2c.read(i < 6); //7バイト読みこみ終わりにする
}
i2c.stop();
}
void setup() {
myUART.init(9600);
rtc_write_time( // 最初に一度だけ実行して時刻日時を設定します
20, // 秒
44, // 分
13, // 時
6, // 曜日(例:土曜=6)
24, // 日
4, // 月
26 // 年(2026 → 26)
);
}
void loop() {
uint8_t buf[7];
rtc_read(buf);
uint8_t sec = bcdToDec(buf[0] & 0x7F);
uint8_t min = bcdToDec(buf[1]);
uint8_t hour = bcdToDec(buf[2]);
uint8_t wday = bcdToDec(buf[3]); // 曜日
uint8_t day = bcdToDec(buf[4]);
uint8_t month = bcdToDec(buf[5]);
uint8_t year = bcdToDec(buf[6]);
myUART.print("20");
myUART.print(year);
myUART.print("/");
myUART.print(month);
myUART.print("/");
myUART.print(day);
myUART.print(" (");
// 曜日表示
if (wday >= 1 && wday <= 7) {
myUART.print(weekStr[wday - 1]);
} else {
myUART.print("???");
}
myUART.print(") ");
myUART.print(":");
myUART.print(hour);
myUART.print(":");
myUART.print(min);
myUART.print(":");
myUART.println(sec);
delay(1000);
}プログラムの解説
■ BCD変換
DS1307はBCD形式なので変換が必要です。
uint8_t bcdToDec(uint8_t val) {
return (val >> 4) * 10 + (val & 0x0F);
}
uint8_t decToBcd(uint8_t val) {
return ((val / 10) << 4) | (val % 10);
}時刻の書き込み
myUART.init(9600);
rtc_write_time(
55, // 秒
58, // 分
7, // 時
1, // 曜日(例:土曜=6)
13, // 日
4, // 月
26 // 年(2026 → 26)
);
}👉 初回だけ実行すればOKです
※コメントアウトして使うのがポイント
時刻の読み出し
void rtc_read(uint8_t *buf) {
i2c.start();
i2c.write(DS1307_ADDR << 1); // 書き込みモード
i2c.write(0x00); // 秒レジスタ
i2c.stop();
i2c.start();
i2c.write((DS1307_ADDR << 1) | 1); //読み込みモード
for (uint8_t i = 0; i < 7; i++) {
buf[i] = i2c.read(i < 6); //7バイト読みこみ終わりにする
}
i2c.stop();
}流れはこうです:
- 読み取り開始位置(0x00)を指定
- 再スタート
- 読み込みモードで7バイト取得
UART表示
myUART.print("20");
myUART.print(year);実行結果
2026/4/24 (Fri) :13:45:48
2026/4/24 (Fri) :13:45:49
2026/4/24 (Fri) :13:45:50このように1秒ごとに時刻が表示されます。
RTCの電池バックアップについて
DS1307は充電式 LIR2032 ボタン電池を接続することで、
電源OFFでも時刻を保持できます
今回の環境でも問題なく動作しました。
今後の拡張(次回予告)
次はさらに実用的にしていきます👇
- PCからUART経由で時刻設定
- 自動時刻入力
- コマンド操作対応
「マイコン単体で時計設定できる仕組み」を作る予定です
まとめ
今回の記事では、ATtiny202を使ってDS1307 RTCを動かし、
時刻表示を行う方法を紹介しました。
ポイントをまとめると👇
- 自作I2CでもDS1307は十分動作する
- 読み込み前に「0x00指定」が必須
- プログラムは約1410バイトと超軽量
- 電池バックアップで時刻保持もOK
小容量マイコンでもここまでできるので、省メモリ設計の参考にもなると思います。



