• 公開日:2019年10月30日
  • | 更新日:2022年12月01日

化学出身者がマイコンボードでしゃべる挨拶時計をつくってみた(4)~RTCで時間の設定~

前回までのあらすじ

 

BeagleBone Black(BBB)に7セグLEDを組み込み、ダイナミック点灯で時計機能を持たせました。芳しくない結果になりましたが、回路やプログラミングについて学ぶことができました。

今回は、正確な時間を表示するために必要なRTCを組み込みたいと思います。

 

RTCとは

 

RTC(Real-Time Clock)はコンピュータなどが内蔵する時計や、時計の機能が実装されている集積回路(IC)のことを指します。特徴として、システムの電源が切られても、バッテリバックアップにより時間をカウントし続けることができます。これにより再びシステムの電源を投入した時、正確な時間を表示することができます。

BBBにはバッテリバックアップされたRTCがないため、DS1307搭載RTCモジュールを外付けすることにしました。このモジュールを外付けする際には注意することがあるので後述します。

 

I2Cとは

 

RTCは同期式シリアル通信であるI2C(Inter-Integrated Circuit)でデータ通信を行います。I2Cはマスタとスレーブを繋ぐシリアルデータ(SDA)とシリアルクロック(SCL)の2本の信号線からなります。このSDAとSCLにはプルアップ抵抗をつける必要があります。

またI2Cは、1つのマスタに対して複数のスレーブを接続することが可能で、SDAを送受信することによりRead / Writeを行うことができます。接続された複数のスレーブの中から通信したいスレーブを選択するためには、マスタからスレーブに対してスレーブアドレスを送信する必要があります。スレーブアドレスはスレーブのData Sheetを見ると分かります。

 

 

RTCの設定とRead

 

いきなり故障・・・

RTCモジュールをはんだ付けしてBBBに接続。さあ動かすぞ!

やばい、システム電源を入れたらBBBが故障しました。やってしまいました・・・

なんで壊れたのか・・・原因は回路図の理解不足でした。こちらのサイト(https://learn.adafruit.com/adding-a-real-time-clock-to-beaglebone-black/overview

を参考にし、「付属品の抵抗(R1, R2)を使わずに0Ωで繋ぐ」と勘違いしはんだ付けを行った結果、5 VでプルアップしたSDAをBBBのI2Cピンに入力してしまいました。BBBの拡張ピンには3.3 V以上の電圧を入力してはいけません。そこに5 Vの電圧を入れたので壊してしまいました…。

解決策としてSDA・SCLを3.3 Vでプルアップしました。これでBBBを壊すことなく使えるようになりました。

その時の回路図がこちらです。

 

 

新しいBBBが到着!RTCを設定しよう!

新しくBBBを購入しました。

輸入品なので到着まで1週間程かかり、かなり時間をロスしてしまいました。発表予定日まで時間がないので急いで作業を進めます。

3.3VでプルアップしたRTCをBBBに接続して時間の設定を行います。プルアップ抵抗により壊れないと分かっていても、若干の不安があり電源を入れるのが怖かったです。

 

購入時のRTCは時刻が正確でないため、コマンドプロンプト(私の場合はTera Term)を使って正しい時間に設定します。

まずTera TermにUSBシリアルで接続し、root権限でログインします。ログインIDとパスワードは「root」です。

 

 

ログインができたらBBBにI2Cデバイスが接続されていることを確認して、DS1307をBBBのシステム管理下におきます。以下2つのサイトを参考にして行いました。この設定はBBBのシステム電源を落としたら初期化され、起動する度に設定しなければいけないのでめんどくさいです。そこでシステム起動時に自動でDS1307をシステム管理下においてくれるシェルスクリプトを作成します。1つ目のサイトにやり方が載っていました。

1.http://morecatlab.akiba.coocan.jp/lab/index.php/2014/01/beaglebone-black-2/

2.https://learn.adafruit.com/adding-a-real-time-clock-to-beaglebone-black/set-rtc-time

 

ここまでできたらRTCに現在の時間を書き込みます。BBBをインターネットに接続していれば時刻同期プロトコル(ntp)を利用して標準時間を読み取ることが可能ですが、インターネット接続をしていないのでTera Termを使って手動で行います。

手順としては、

1.Linuxカーネルのシステム・クロックの時間を変更する。

2.システム・クロックの時間をハードウェア・クロック(RTC)に書き込む。

これでオッケーです!

 

 

 

実際に2019年10月25日18:51:35をRTCに書き込みます。

 

 

時間の設定が完了しました。これで、BBBのシステム電源をOffにしても正確な時間でカウントし続けてくれます!

 

7セグLEDとRTCを統合させる

 

RTCから時間のデータをReadします。オシロで波形を確認すると68RA以降に時間のデータがちゃんと出力されていることが分かります。

 

今回使いたいデータはminとhourの部分なので、このアドレスをReadできるようにプログラムします。DS1307のData Sheetを見るとアドレス01hと02hがminとhourに対応しているので01hから16bit分Readすれば完璧です。

 

 

引用:https://datasheets.maximintegrated.com/en/ds/DS1307.pdf

あとは先ほどReadした16bitを7セグLEDの各桁に合わせて分解してあげれば完成するはずです!

minの[4:7]bitが10の位で[0:3]bitが1の位を表します。同じようにhourの[4:7]bitが10の位で[0:3]bitが1の位を表しているので4bitずつに分解してあげます。

これらを前回作った7セグのプログラムに組み合わせて動かします。

 


main.js

var b = require(‘bonescript’);

//RTC初期設定
var port = ‘/dev/i2c-1’;
var RTC = 0x68;
var min = 0; //BCDコードmin10進表記(1の位)
var min_10 = 0; //BCDコードmin10進表記(10の位)
var hour = 0; //BCDコードhour10進表記(1の位)
var hour_10 = 0; //BCDコードhour10進表記(10の位)
var RTCcounts = [hour_10, hour, min_10, min];
var time = [];

b.i2cOpen([port, RTC]);

//7seg初期設定
var MOS1 = ‘P9_21’;
var MOS2 = ‘P9_22’;
var MOS3 = ‘P9_23’;
var MOS4 = ‘P9_24’;
var segA = ‘P8_11’;
var segB = ‘P8_12’;
var segC = ‘P8_13’;
var segD = ‘P8_14’;
var segE = ‘P8_15’;
var segF = ‘P8_16’;
var segG = ‘P8_17’;

var i = 0;
var cathode = [MOS1, MOS2, MOS3, MOS4];
var anode = [segA, segB, segC, segD, segE, segF, segG];

//7segダイナミック点灯
for(var x in cathode) { //cathodeをすべてLOWにする(LED消灯)
b.pinMode(cathode[x], b.OUTPUT);
b.digitalWrite(cathode[x], b.LOW);
}

for(var y in anode){ //anodeの表示をBlankにする
b.pinMode(anode[y], b.OUTPUT);
b.digitalWrite(anode[y], b.LOW);
}

//数字の設定
var number0 = function(){
b.digitalWrite(segA, b.HIGH);
b.digitalWrite(segB, b.HIGH);
b.digitalWrite(segC, b.HIGH);
b.digitalWrite(segD, b.HIGH);
b.digitalWrite(segE, b.HIGH);
b.digitalWrite(segF, b.HIGH);
b.digitalWrite(segG, b.LOW);
};

var number1 = function(){
b.digitalWrite(segA, b.LOW);
b.digitalWrite(segB, b.HIGH);
b.digitalWrite(segC, b.HIGH);
b.digitalWrite(segD, b.LOW);
b.digitalWrite(segE, b.LOW);
b.digitalWrite(segF, b.LOW);
b.digitalWrite(segG, b.LOW);
};

var number2 = function(){
b.digitalWrite(segA, b.HIGH);
b.digitalWrite(segB, b.HIGH);
b.digitalWrite(segC, b.LOW);
b.digitalWrite(segD, b.HIGH);
b.digitalWrite(segE, b.HIGH);
b.digitalWrite(segF, b.LOW);
b.digitalWrite(segG, b.HIGH);
};

var number3 = function(){
b.digitalWrite(segA, b.HIGH);
b.digitalWrite(segB, b.HIGH);
b.digitalWrite(segC, b.HIGH);
b.digitalWrite(segD, b.HIGH);
b.digitalWrite(segE, b.LOW);
b.digitalWrite(segF, b.LOW);
b.digitalWrite(segG, b.HIGH);
};

var number4 = function(){
b.digitalWrite(segA, b.LOW);
b.digitalWrite(segB, b.HIGH);
b.digitalWrite(segC, b.HIGH);
b.digitalWrite(segD, b.LOW);
b.digitalWrite(segE, b.LOW);
b.digitalWrite(segF, b.HIGH);
b.digitalWrite(segG, b.HIGH);
};

var number5 = function(){
b.digitalWrite(segA, b.HIGH);
b.digitalWrite(segB, b.LOW);
b.digitalWrite(segC, b.HIGH);
b.digitalWrite(segD, b.HIGH);
b.digitalWrite(segE, b.LOW);
b.digitalWrite(segF, b.HIGH);
b.digitalWrite(segG, b.HIGH);
};

var number6 = function(){
b.digitalWrite(segA, b.HIGH);
b.digitalWrite(segB, b.LOW);
b.digitalWrite(segC, b.HIGH);
b.digitalWrite(segD, b.HIGH);
b.digitalWrite(segE, b.HIGH);
b.digitalWrite(segF, b.HIGH);
b.digitalWrite(segG, b.HIGH);
};

var number7 = function(){
b.digitalWrite(segA, b.HIGH);
b.digitalWrite(segB, b.HIGH);
b.digitalWrite(segC, b.HIGH);
b.digitalWrite(segD, b.LOW);
b.digitalWrite(segE, b.LOW);
b.digitalWrite(segF, b.HIGH);
b.digitalWrite(segG, b.LOW);
};

var number8 = function(){
b.digitalWrite(segA, b.HIGH);
b.digitalWrite(segB, b.HIGH);
b.digitalWrite(segC, b.HIGH);
b.digitalWrite(segD, b.HIGH);
b.digitalWrite(segE, b.HIGH);
b.digitalWrite(segF, b.HIGH);
b.digitalWrite(segG, b.HIGH);
};

var number9 = function(){
b.digitalWrite(segA, b.HIGH);
b.digitalWrite(segB, b.HIGH);
b.digitalWrite(segC, b.HIGH);
b.digitalWrite(segD, b.HIGH);
b.digitalWrite(segE, b.LOW);
b.digitalWrite(segF, b.HIGH);
b.digitalWrite(segG, b.HIGH);
};

//関数設定
//delay関数
function delay(delay_ms){
var nowtime = Date.now();
var timeout = nowtime + delay_ms;
while(nowtime < timeout){
nowtime = Date.now();
}
}

//main関数
main();

function main(){
while(true){
RTCread();
display_number();
delay(2);
ledOff();
}
}

//ダイナミック点灯関数
function display_number(l){ //表示する数字のanodeをHIGHにする
l = n(i);
b.digitalWrite(cathode[l], b.HIGH); //表示する桁のcathodeをHIGHにする
if (counts[l] == 0){
number0();
}else if(counts[l] == 1){
number1();
}else if(counts[l] == 2){
number2();
}else if(counts[l] == 3){
number3();
}else if(counts[l] == 4){
number4();
}else if(counts[l] == 5){
number5();
}else if(counts[l] == 6){
number6();
}else if(counts[l] == 7){
number7();
}else if(counts[l] == 8){
number8();
}else {
number9();
}
}

function ledOff(){
for(var x in cathode) { //cathodeをすべてLOWにする(LED消灯)
b.digitalWrite(cathode[x], b.LOW);
}
for(var y in anode){ //anodeの表示をBlankにする
b.digitalWrite(anode[y], b.LOW);
}
i++; if(i > 3) i = 0; //表示する桁を変更する
};

function n(i){
if(i >= cathode.length) return 0;
else return i;
}

//RTC時間読み取り
function RTCread(){
time = b.i2cReadBytes([port, RTC], 0x01, 2);
var min = time[0] & 0x0F; //BCDコードmin10進表記(1の位)
var min_10 = time[0] >> 4; //BCDコードmin10進表記(10の位)
var hour = time[1] & 0x0F; //BCDコードhour10進表記(1の位)
var hour_10 = time[1] >> 4; //BCDコードhour10進表記(10の位)
RTCcounts = [hour_10, hour, min_10, min];
}

 

結果は・・・ちゃんと現時刻を表示してくれました!!!

ちらつきはあるにしても、本当にうれしいです!

基板実装

プログラミングの統合が終わったので部品を基板にはんだ付けしていきます。間違えないようにはんだ付け用の回路図を作り、作業に取り掛かりました。

完成品をコンパクトにしたかったので、小さいユニバーサル基板を使って作りました。

完成品がこちらです。

 

 

予想はしていたけど7セグLEDのはんだ付けが大変でした。導通しないように絶縁体を使ったり、細かい範囲で抵抗を繋いだり頑張りました。はんだ付けのクオリティーは見逃してください。

これらとBBBをジャンパーワイヤーで繋ぎ、動作確認を行います。2か所ほど配線ミスが見つかりましたが修正して無事完成しました。

 

おわりに

 

Tera Termを使ったRTCの設定やI2Cのプログラミングなど分からないことだらけでしたが、調べて取り組んで失敗を繰り返して完成させることが出来ました。また、RTCと7セグLEDとの統合も無事に終わり達成感に満ち溢れています。残りの機能も作らなければいけないのでこの後も頑張ります。