HOME | Raspberry Pi | ビジネス書籍紹介 | 2026-01-04 (Sun) Today's Access : 155 Total : 1260853. Since 10 Sep. 2019

 画像処理 第3回リアルタイムクロック
2020.06.28掲載/2020.10.02更新

YouTubeでポイントを説明しています。画像をクリックすると再生できます。

画像処理第2回では、ArduCamカメラモジュールを使って撮影し、画像をSDカードに保存しました。 この際、画像の保存ファイル名に連番を付けていましたが、第3回ではリアルタイムクロックを用いることで、 撮影時刻をファイル名に使えるようにします。

■更新情報
2020.10.02 サンプルのソースコードを修正しました。
本文掲載のDS1302リアルタイムクロックモジュールで使われているDS1302チップでは正常に動作したのですが、 単体で別のDS1302チップを購入して試したところ、時刻情報の取得が正常に行われませんでした。
標準の、shiftIn(), shiftOut()関数を使用せずに、レジスタを操作することで対応しました。 また、バースト転送 getDataBurst() にも対応しました。
修正ソースコードを文末に掲載しています。

■開発環境

パソコンから、TeraTeamでラズパイにSSH接続、ラズパイでビルドしたコードを、Arduino に転送します。
ラズパイのディストリビューションは、Raspbian Stretch、開発環境に platformio を使用しています。
→ Ref.Arduino開発環境構築 PlatformIO

■DS1302リアルタイムクロックモジュール

基板の構成はとてもシンプルで、DS1302リアルタイムクロックICと32.768kHz水晶発振子、CR2032用電池ホルダーで構成されています。

このモジュールをそのまま自作カメラの基板上に配置するのは美的ではないので、分解しちゃいました。


■クリスタル(水晶発振子)32.768kHz
時計などに使用している32.768kHzのクリスタルです。
・負荷容量:12.5pF
・直列抵抗:50kΩmax
・並列容量:0.9pFtyp
・動作温度範囲:-10~+60℃
・許容偏差:±20ppm(±20x10-6)

■CR2032リチウム電池ホルダー
CR2032はプログラムテスト用に使用します。 最終的には、電気二重層コンデンサを用います。

リード線をはんだ付けして、ショートしないように先端にはメスピンヘッダーを取り付けました。


■DS1302 リアルタイムクロックIC
トリクルチャージャ機能
2100年までうるう年を補正
TTL互換(VCC = 5V)
完全動作電圧:2.0V~5.5V
消費電流:300nA以下(2.0V時)
【データシート】 【DS1302概要】

インターフェイスは、CE(Chip Enable),I/O(データ入出力),SCLK(同期用クロック)の3線が使われています。
■配線
Arduino -  DS1302  - CRYSTAL/CR2032
5V - [1]Vcc2 Power-Supply   
   [2]X1  - Crystal 32.768kHz
   [3]X2  - Crystal 32.768kHz
GND - [4]GND  - CR2032(-)
D2 - [5]CE    
D3 - [6]I/O    
D4 - [7]SCLK   
   [8]Vcc1 Battery Backup - CR2032(+)
Arduino側のD2~4は任意に変更可能です。



■テスト用プログラムの作成

$ mkdir RTC
$ cd RTC
$ platformio init -b nanoatmega328

$ vi src/ds1302.ino
#define PIN_CE   2
#define PIN_IO   3
#define PIN_SCLK 4

char clockTime[15]; // 撮影時刻用文字列

void setup() {
	pinMode(PIN_SCLK,OUTPUT);
	pinMode(PIN_CE,  OUTPUT);
	Serial.begin(115200);
	adjustClock();
}

void loop(){
	getDateTime();
	Serial.println(clockTime);
	delay(1000);
}
各種ピンのモード設定、シリアル通信設定を行い、setup()関数内の、adjustClock()で時刻を設定しています。
loop()処理では、1秒間隔でRTCからgetDateTime()関数で時刻を取得して、シリアル出力しています。

プログラムの詳細に移ります。
DS1302のデータシートをもとに説明していきます。


DS1302との時刻の読み書きには上記、レジスタアドレスを指定して、BCD値によるデータの受け渡しを行います。

BCD:二進化十進数
二進化十進数(BCD、Binary-coded decimal)とは、コンピュータにおける数値の表現方式の一つで、十進法の1桁を、0から9までを表す二進法の4桁で表したものです。


データ転送のタイミングは、CE(Chip Enable)をHIGHにすることで行います。

プログラムを起動した際の、DS1302への時刻の設定の部分です。
byte dec2bcd(int x) {
  return ((x/10)<<4)+(x%10);
}

void writeDS1302(int adr, int v) {
	digitalWrite(PIN_SCLK,LOW);
	digitalWrite(PIN_CE, HIGH);
	shiftOut(PIN_IO, PIN_SCLK, LSBFIRST, adr);
	shiftOut(PIN_IO, PIN_SCLK, LSBFIRST, v);
	digitalWrite(PIN_CE,  LOW);
}

void adjustClock() {
	char checkTime[15];
	int yy=20,mm=6,dd=25,hh=18,ii=35,ss=0;
	sprintf(checkTime,"20%02d%02d%02d%02d%02d%02d",yy,mm,dd,hh,ii,ss);
	getDateTime();
	if (strcmp(checkTime,clockTime)>0) {
		writeDS1302(0x80, dec2bcd(ss));
		writeDS1302(0x82, dec2bcd(ii));
		writeDS1302(0x84, dec2bcd(hh));
		writeDS1302(0x86, dec2bcd(dd));
		writeDS1302(0x88, dec2bcd(mm));
		writeDS1302(0x8C, dec2bcd(yy));
	}
}
Arduinoにはオートリセット機能があり、プログラムを書き込むときやシリアル通信を開始したときにもリセットされます。
リセットされるたびに、時刻設定されると時間が戻ってしまうので、それを防ぎます。
プログラム書き込み時に時間合わせに設定する時刻(checkTime)と、getDateTime()で取得したDS1302側(clockTime)の時刻を比較して、 時刻合わせに使用する時刻のほうが新しい場合のみ、時刻を書き換えるようにしています。

次に、DS1302から時刻を取得する部分です。
int readDS1302(int adr) {
	int bcd;
	digitalWrite(PIN_SCLK,LOW);
	digitalWrite(PIN_CE,  HIGH);
	shiftOut(PIN_IO, PIN_SCLK, LSBFIRST, adr);
	bcd = shiftIn(PIN_IO, PIN_SCLK, LSBFIRST);
	digitalWrite(PIN_CE, LOW);
	return (bcd&0x0f)+(10*(bcd>>4));
}

void getDateTime(){
	int yy,mm,dd,hh,ii,ss,ck;
	while(1) {
		ss = readDS1302(0x81);
		ii = readDS1302(0x83);
		hh = readDS1302(0x85);
		dd = readDS1302(0x87);
		mm = readDS1302(0x89);
		yy = readDS1302(0x8d);
		ck = readDS1302(0x81);
		if (ck>=ss) break;
	}
	sprintf(clockTime,"20%02d%02d%02d%02d%02d%02d",yy,mm,dd,hh,ii,ss);
}
while(1)ループ内では、最初に秒を取得して、分、時、日、月、年を取得し、最後に再度、秒の情報を取得しています。 最初と最後で秒が等しいか、最後に取得した秒が最初の秒より大きい場合にループを抜けます。
これは、極短時間での処理なので、ほとんど発生しないとは思いますが、59秒から0秒へ移る際の対応です。
例えば、2020年12月31日23時59分59秒に時刻を取得しようとしたとき、、秒の取得から始めて、年を取得する直前で 1秒進んでしまった場合、2021年に移り、結果は2021年12月31日23時59分59秒となり、1年間の誤差が発生します。
この制御では、59秒から0秒に移った際には、再度、時刻を取得しなおしています。

それでは、Arduino Nano と Raspberry Pi Zero W をUSB接続して実行してみます。
$ platformio run -t upload
$ platformio device list
/dev/ttyUSB0
------------
Hardware ID: USB VID:PID=1A86:7523 LOCATION=1-1
Description: USB Serial

/dev/ttyAMA0
------------
Hardware ID: 20201000.serial
Description: ttyAMA0

$ platformio device monitor -p /dev/ttyUSB0 -b 115200
Looking for advanced Serial Monitor with UI? Check http://bit.ly/pio-advanced-monitor
--- Miniterm on /dev/ttyUSB0 115200,8,N,1 ---
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
20200625183514
20200625183515
20200625183516
20200625183517
......

■トリクルチャージ設定
DS1302にはトリクルチャージの機能があります。 トリクルチャージは、二次電池の自然放電を補うために、主電源からの電力供給を受けているときには、微小電流によりバックアップ用電池を充電する機能です。
CR2032電池は充電できないので、この機能を利用するには、充電可能なLIR2032を使用するか、今回実装する電気二重層コンデンサーを用いる方法があります。

電気二重層コンデンサー1F 5.5V
・容量:1F
・耐圧:5.5V
・内部抵抗:30Ω(1kHz)
・外形:Φ21.5mmx9.5mm
・足ピッチ:約5mm
・重量:約7g

電気二重層コンデンサは小型大容量で電圧保持特性に優れています。 メモリや内部時計ICのバックアップ電源として有効です。 適度な内部抵抗を持ち、特別な回路なしで簡単に使えます。 耐圧が5V以上あり、一般的デジタル回路に簡単に使えます。

ボタン電池CR2032を電気二重層コンデンサに交換しました。


データシートをみてみましょう。

初期状態(Initial power-on state)では、RS(BIT0&1)は0で、トリクルチャージ機能は無効になっています。
トリクルチャージする際には、DS1302の内部抵抗とダイオードを選択することができます。

ダイオード1本(電圧降下0.7V)と2KΩの抵抗の組み合わせを選択した場合を考えてみます。
電気二重層コンデンサの容量を大雑把に計算すると
電荷:1F x 5V = 5C = 5(A x s) = 5 / (60 x 60) Ah = 0.00139Ah = 1.39mAh.
になります。
トリクルチャージにより、流れる電流は最大で
Imax = (5.0V - diode drop) / R1 = (5.0V - 0.7V) / 2KΩ = 2.2mA
となり、容量1F(1.39mA)の電気二重層コンデンサーであれば、短時間に充電されてしまいます。

電気二重層コンデンサーの有効な容量を1mAとした場合、DS1302の消費電流は300nA以下(2.0V時)なので
1mAh/(300nA*24h)=138.8(日)
主電源をOFFにしていても、二次電池からの電力供給で、RTCは100日程度は動作していると思われます。

トリクルチャージの機能を有効にします。(1 Diode, 2KΩの場合)
void adjustClock() {
	char checkTime[15];
	int yy=20,mm=6,dd=26,hh=20,ii=15,ss=0;
	sprintf(checkTime,"20%02d%02d%02d%02d%02d%02d",yy,mm,dd,hh,ii,ss);
	getDateTime();
	if (strcmp(checkTime,clockTime)>0) {
		writeDS1302(0x80, dec2bcd(ss));
		writeDS1302(0x82, dec2bcd(ii));
		writeDS1302(0x84, dec2bcd(hh));
		writeDS1302(0x86, dec2bcd(dd));
		writeDS1302(0x88, dec2bcd(mm));
		writeDS1302(0x8C, dec2bcd(yy));

		writeDS1302(0x90, 0xA5);
	}
}

これで、写真撮影、画像保存、時刻取得の機能はできました。残っているのは主電源周りになります。

続きは画像処理第4回に掲載します。


2020.10.02 修正ソースコード
#define PIN_CE   2
#define PIN_IO   3
#define PIN_SCLK 4

const uint8_t Sun = 1;
const uint8_t Mon = 2;
const uint8_t Tue = 3;
const uint8_t Wed = 4;
const uint8_t Thr = 5;
const uint8_t Fri = 6;
const uint8_t Sat = 7;

static char clockTime[13]; // 時刻文字列 yymmddhhiiss
static char editTime[20];  // 時刻表示用文字列
static char shortTime[7];  // 撮影用文字列

// 整数値をBCDに変換
byte dec2bcd(int x) {
  return ((x/10)<<4)+(x%10);
}

uint8_t bcd2dec(const uint8_t bcd) {
	return (10*((bcd&0xF0)>>4)+(bcd&0x0F));
}

uint8_t shiftInEx() {
	uint8_t input_value=0;
	uint8_t bit=0;
	pinMode(PIN_IO,INPUT);
	for (int i=0; i<8; ++i) {
		digitalWrite(PIN_SCLK,HIGH);
		delayMicroseconds(1);
		digitalWrite(PIN_SCLK,LOW);
		delayMicroseconds(1);

		bit = digitalRead(PIN_IO);
		input_value |= (bit<<i); // Bits are read LSB first.
	}
	return input_value;
}

void shiftOutEx(const uint8_t value) {
	bool pinModeFlag = false;
	pinMode(PIN_IO, OUTPUT);
	for (int i=0;i<8;++i) {
		digitalWrite(PIN_IO, (value>>i)&1);
		delayMicroseconds(1);
		digitalWrite(PIN_SCLK,HIGH);
		delayMicroseconds(1);

		// We're about to read data -- ensure the pin is back in input mode
		// before the clock is lowered
		if (i==7) {
			if (value==0xBF) pinModeFlag=true; // clock Burst Read
			if ((value>=0x80)&&(value<=0x8D)&&(value%2)) pinModeFlag=true;
		}
		if (pinModeFlag) {
			pinMode(PIN_IO,INPUT);
		} else {
			digitalWrite(PIN_SCLK,LOW);
			delayMicroseconds(1);
		}
	}
}

uint8_t readReg(const uint8_t reg) {
	digitalWrite(PIN_SCLK,LOW);
	digitalWrite(PIN_CE,HIGH);
	delayMicroseconds(4);
	shiftOutEx(reg);
	uint8_t dec = bcd2dec(shiftInEx());
	digitalWrite(PIN_CE, LOW);
	delayMicroseconds(4);
	return dec;
}

void writeReg(const uint8_t reg, uint8_t value) {
	digitalWrite(PIN_SCLK,LOW);
	digitalWrite(PIN_CE,HIGH);
	delayMicroseconds(4);
	shiftOutEx(reg);
	shiftOutEx(value);
	digitalWrite(PIN_CE, LOW);
	delayMicroseconds(4);
}

// Initialize a new chip by turning off write protection.
// These method needn't always be called.
void writeProtect() {
	writeReg(0x8E,0x00);
}

// トリクルチャージ設定(1ダイオード,2kΩ)
void trickleCharge() {
	writeReg(0x90,0xA5);
}

void getDateBurst() {
	digitalWrite(PIN_SCLK,LOW);
	digitalWrite(PIN_CE,HIGH);
	delayMicroseconds(4);
	shiftOutEx(0xBF); //clock Burst Read
	uint8_t ss  = bcd2dec(shiftInEx() & 0x7F);
	uint8_t ii  = bcd2dec(shiftInEx());
	uint8_t hh  = bcd2dec(shiftInEx());
	uint8_t dd  = bcd2dec(shiftInEx());
	uint8_t mm  = bcd2dec(shiftInEx());
	uint8_t day = bcd2dec(shiftInEx());
	uint8_t yy  = bcd2dec(shiftInEx());
	digitalWrite(PIN_CE, LOW);
	delayMicroseconds(4);
	sprintf(clockTime,"%02d%02d%02d%02d%02d%02d",yy,mm,dd,hh,ii,ss);
	sprintf(editTime, "20%02d-%02d-%02d %02d:%02d:%02d",yy,mm,dd,hh,ii,ss);
	sprintf(shortTime,"%02d%02d%02d",yy,mm,dd);
}

void getDate() {
	uint8_t yy,day,mm,dd,hh,ii,ss,ck;
	while(1) {
		ss  = readReg(0x81);
		ii  = readReg(0x83);
		hh  = readReg(0x85);
		dd  = readReg(0x87);
		mm  = readReg(0x89);
		day = readReg(0x8A);
		yy  = readReg(0x8D);
		ck  = readReg(0x81);
		// 59秒→0秒対策
		if (ck>=ss) break;
	}
	sprintf(clockTime,"%02d%02d%02d%02d%02d%02d",yy,mm,dd,hh,ii,ss);
	sprintf(editTime, "20%02d-%02d-%02d %02d:%02d:%02d",yy,mm,dd,hh,ii,ss);
	sprintf(shortTime,"%02d%02d%02d",yy,mm,dd);
}

// 時刻を設定する
void adjustClock() {
	char updateTime[13];
	int yy=20,mm=10,dd=2,hh=8,ii=58,ss=00,day=Fri;
	sprintf(updateTime,"%02d%02d%02d%02d%02d%02d",yy,mm,dd,hh,ii,ss);
	getDateBurst();
	// シリアル通信を開始の、Arduinoオートリセット機能対応
	if (strcmp(updateTime,clockTime)>0) {
		writeProtect();
		digitalWrite(PIN_SCLK,LOW);
		digitalWrite(PIN_CE,HIGH);
		delayMicroseconds(4);
		shiftOutEx(0xBE); //clock Burst Write
		shiftOutEx(dec2bcd(ss));
		shiftOutEx(dec2bcd(ii));
		shiftOutEx(dec2bcd(hh));
		shiftOutEx(dec2bcd(dd));
		shiftOutEx(dec2bcd(mm));
		shiftOutEx(dec2bcd(day));
		shiftOutEx(dec2bcd(yy));
		//shiftOutEx(0x00); // Write protecton register
		digitalWrite(PIN_CE, LOW);
		delayMicroseconds(4);

		trickleCharge();
	}
}

void setup() {

	digitalWrite(PIN_CE, LOW);
	pinMode(PIN_CE, OUTPUT);
	pinMode(PIN_IO, INPUT);
	digitalWrite(PIN_SCLK, LOW);
	pinMode(PIN_SCLK,OUTPUT);

	Serial.begin(115200);

	adjustClock();
}

void loop(){
	//getDateBurst();
	getDate();
	Serial.println(editTime);
	delay(1000);
}

■参考文献
リアルタイムクロック DS1302 をArduinoで動かしてみる
https://github.com/msparks/arduino-ds1302

Raspberry Pi(ラズベリー パイ)は、ARMプロセッサを搭載したシングルボードコンピュータ。イギリスのラズベリーパイ財団によって開発されている。
2020.05.28 画像処理 第1回トイカメラ
2020.06.09 画像処理 第2回カメラモジュール制御
2020.06.28 画像処理 第3回リアルタイムクロック
2020.07.08 画像処理 第4回電源回路
2020.10.27 画像処理 第5回自作デジカメ初号機完成
2020.11.10 画像処理 第6回ドーナツデジカメ
2021.05.16 画像処理・基本変換
2021.07.10 M5Stackアプリの移植
2022.04.04 減色処理 雑談
2022.04.18 減色処理 均等量子化法とK平均法
2022.05.04 減色処理 グレースケール・二値化
2022.05.18 減色処理 二値化画像印刷
2022.08.12 京セラ feelH” Treva カメラ
2022.10.15 デジカメ弐号機 1.仕様変更
2022.10.21 デジカメ弐号機 2.SDカードとRTC
2022.11.04 デジカメ弐号機 3.SPI DISPLAY
2022.11.18 デジカメ弐号機 4.ストリーミング
2022.12.02 デジカメ弐号機 5.機能統合
2022.12.17 デジカメ弐号機 6.完成
2023.05.15 アナログ風ゲージ
2023.06.01 ADS1115デジタル電圧計
2023.10.01 立体視(ステレオグラム)
2023.10.16 漢字フォントの表示と拡張
2024.04.08 自作デジカメ参号機・雑談
2024.08.24 シリアルカメラ
2025.07.23 C3.jsによるグラフ描画
2025.09.09 東芝デジタルカメラユニット DMR-C1
2025.10.10 AQM1248A小型液晶ボード
2025.10.11 Monochrome OLED
2025.12.31 キャラクターディスプレイで遊ぶ

たいていのことは100日あれば、うまくいく。長田英知著
「時間がなくて、なかなか自分のやりたいことができない」 「一念発起して何かを始めても、いつも三日坊主で終わってしまう」 「色んなことを先延ばしにしたまま、時間だけが過ぎていく」 そこで本書では、そんな著者が独自に開発した、 まったく新しい目標達成メソッド「100日デザイン」について、 その知識と技術を、余すところなくご紹介します。

まんがで納得ナポレオン・ヒル 思考は現実化する
OLとして雑務をこなす日々に飽き足らず、科学者だった父が残した薬品を商品化すべく、起業を決意した内山麻由(27)。彼女はセミナーで知り合った謎の女性からサポートを得ながら、彼女と二人三脚でナポレオン・ヒルの成功哲学を実践し、さまざまな問題を乗り越えていく。 ヒル博士の<ゴールデンルール>に従い、仕事に、恋に全力疾走する彼女の、成功への物語。

今日は人生最悪で最高の日 1秒で世界を変えるたったひとつの方法 ひすいこたろう著
偉人の伝記を読むと、最悪な日は、不幸な日ではなく、新しい自分が始まる日であることがわかります。最悪な出来事は、自分の人生が、想像を超えて面白くなる兆しなのです。偉人伝を読むことで、このときの不幸があったおかげで、未来にこういう幸せがくるのかと、人生を俯瞰する視線が立ち上がるのです。

ご飯は私を裏切らない heisoku著
辛い現実から目を背けて食べるご飯は、いつも美味しく幸せを届けてくれる。 29歳、中卒、恋人いない歴イコール年齢。バイト以外の職歴もなく、短期バイトを転々とする日々。ぐるぐると思索に耽るけど、ご飯を食べると幸せになれる。奇才の新鋭・heisokuが贈るリアル労働グルメ物語!

【最新版Gemini 3に対応!】できるGemini (できるシリーズ)
Geminiを「最強の知的生産パートナー」として使いこなすための、実践的なノウハウを凝縮した一冊です。 基本的な操作方法から、具体的なビジネスシーンでの活用、日々の業務を自動化するGoogle Workspaceとの連携、さらには自分だけのオリジナルAIを作成する方法まで余すところなく解説します。

Rustプログラミング完全ガイド 他言語との比較で違いが分かる!
Rustの各手法や考え方を幅広く解説! 500以上のサンプルを掲載。実行結果も確認。 全24章の包括的なチュートリアル。

ポチらせる文章術
販売サイト・ネット広告・メルマガ・ブログ・ホームページ・SNS… 全WEB媒体で効果バツグン! カリスマコピーライターが教える「見てもらう」「買ってもらう」「共感してもらう」すべてに効くネット文章術

小型で便利な Type-C アダプター USB C オス - USB3.1 オスアダプター
Type-C端子のマイコンボードをこのアダプタを介して直接Raspberry Piに挿すことができます。ケーブルなしで便利なツールです。

Divoom Ditoo Pro ワイヤレススピーカー
15W高音質重低音/青軸キーボード/Bluetooth5.3/ピクセルアート 専用アプリ/USB接続/microSDカード

電源供給USBケーブル スリム 【5本セット】
USB電源ケーブル 5V DC電源供給ケーブル スリム 【5本セット】 電源供給 バッテリー 修理 自作 DIY 電子工作 (100cm)

Copyright © 2011-2027 Sarako Tsukiyono All rights reserved®.