Skip to content

Instantly share code, notes, and snippets.

@weldtype
Created May 7, 2016 22:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save weldtype/1f3dcbeb007cec08d43da628a9ce099a to your computer and use it in GitHub Desktop.
Save weldtype/1f3dcbeb007cec08d43da628a9ce099a to your computer and use it in GitHub Desktop.
// RTC-8564をつないでみる。
// 2016/04/29:edy
// 2016/04/30:detachInterrupt を使う。
// 2016/05/01:変数名の変更、RTCとのやり取りはBCDで行うが、内部ではDECIMAL
// 2016/05/07:初期電源投入時の問題を回避
// 2016/05/08:デジット6行16桁7セグメントLCDとつなぐ
//
//参考にしたサイト
//
//Arduinoで実験 (RTCモジュール)
//http://baticadila.dip.jp/arduino_104.html
//
//RTC8564リアルタイムクロック
//http://iizukakuromaguro.sakura.ne.jp/365_rtc8564/365_rtc8564.html
//
//曜日を調べるプログラム
//http://edu.clipper.co.jp/pg-2-47.html
//
//Arduino 割り込みについてのメモ
//http://genine.web.fc2.com/contents/arduino_memo.html
//
//シリアルモニタから入力した数字列を数値に変換する
//forum.arduino.cc/index.php?topic=100429.msg753262#msg753262
#include <Wire.h>
// デジットLCD用配列
// 上から4行目(CS3)のアドレス変換
const uint8_t addrCS3[] = {8, 9, 6, 7, 4, 5, 2, 3, 0, 1, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 30, 31, 28, 29, 26, 27, 24, 25, 22, 23
};
// 上から5行目(CS2)のアドレス変換
const uint8_t addrCS2[] = {18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1,
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17
};
// 7segmentパターン
// 0,1,2,3,4,5,6,7,8,9,,A,b,C,d,E,F,
// 空白,-,H,L,n,o,P,u,μ,°,左縦,両縦
const int seg_pattern[] = {235, 96, 199, 229, 108, 173, 175, 232, 239, 237, 238, 47, 7, 103, 143, 142,
0, 4, 110, 11, 38, 39, 206, 35, 78, 204, 10, 106
};
// row to CS
const uint8_t rowToCS[] = {0, 5, 6, 4, 3, 2, 1};
#define I2C_ADDR (0xA2 >> 1) // 1ビット目未使用のため1ビット右シフトする。
// 年月日時分秒用グローバル変数、DECIMAL not BCD
int gYear, gMonth, gDay, gSec, gMin, gHour, gWeek;
//割り込み処理
void clk_IRQ(void)
{
interrupts();// wireライブラリが割り込みを使うので、割り込みを有効にする。
Wire.beginTransmission(I2C_ADDR);
Wire.write(0x02);
Wire.endTransmission();
delay(1);//不要のはずだからコメントアウトする。
Wire.requestFrom(I2C_ADDR, 7);
//RTC-8564の読み出しデータの不要ビットマスク及びBCD->DECIMAL変換する。
gSec = bcd2dec(Wire.read() & 0x7f) ; // 秒
gMin = bcd2dec(Wire.read() & 0x7f) ; // 分
gHour = bcd2dec(Wire.read() & 0x3f) ; // 時
gDay = bcd2dec(Wire.read() & 0x3f) ; // 日
gWeek = bcd2dec(Wire.read() & 0x07) ; // 曜日
gMonth = bcd2dec(Wire.read() & 0x1f) ; // 月
gYear = bcd2dec(Wire.read()); // 年
Serial.print(gYear);
Serial.print("/");
Serial.print(gMonth);
Serial.print("/");
Serial.print(gDay);
Serial.print("(");
Serial.print(gWeek);
Serial.print(")");
Serial.print(gHour);
Serial.print(":");
Serial.print(gMin);
Serial.print(":");
Serial.println(gSec);
//デジットLCDに表示
// 年表示
DispNum(1, 0, gYear / 10, 0); //"恒春"セグメントは表示しない
DispNum(1, 1, gYear % 10, 1 );// "年"セグメント表示
//月表示
DispNum(1, 2, (gMonth / 10 == 0) ? 16 : gMonth / 10, 0); //月表示をゼロサプレス
DispNum(1, 3, gMonth % 10, 1);// "月"セグメント表示
//日表示
DispNum(1, 4, (gDay / 10 == 0) ? 16 : gDay / 10, 0); //日表示をゼロサプレス
DispNum(1, 5, gDay % 10, 1 );// "日"セグメント表示
//時表示
DispNum(1, 6, (gHour / 10 == 0) ? 16 : gHour / 10, 0); //時表示をゼロサプレス
DispNum(1, 7, gHour % 10 , 1);// コロンセグメント表示
//分表示
DispNum(1, 8, gMin / 10, 0); //分表示はゼロサプレスしない
DispNum(1, 9, gMin % 10, 1 );// コロンセグメント表示
//秒表示
DispNum(1, 10, gSec / 10, 0); //秒表示はゼロサプレスしない
DispNum(1, 11, gSec % 10, 0 ); //"星期"セグメントは表示しない
}
void setup() {
// デジットLCD用設定値
// コマンドのビットデータ: modeID(100)+各コマンドのビットデータ
#define SYS_EN 0b100000000010
#define LCD_ON 0b100000000110
#define BIAS2_2 0b100001000000
#define BIAS2_3 0b100001001000
#define BIAS2_4 0b100001010000
#define BIAS3_2 0b100001000010
#define BIAS3_3 0b100001001010
#define BIAS3_4 0b100001010010
#define CS1 3
#define CS2 4
#define CS3 5
#define CS4 6
#define CS5 7
#define CS6 8
#define WR 9
#define DATA 10
pinMode(CS1 , OUTPUT);
pinMode(CS2 , OUTPUT);
pinMode(CS3 , OUTPUT);
pinMode(CS4 , OUTPUT);
pinMode(CS5 , OUTPUT);
pinMode(CS6 , OUTPUT);
pinMode(WR , OUTPUT);
pinMode(DATA , OUTPUT);
digitalWrite(CS1, HIGH);
digitalWrite(CS2, HIGH);
digitalWrite(CS3, HIGH);
digitalWrite(CS4, HIGH);
digitalWrite(CS5, HIGH);
digitalWrite(CS6, HIGH);
digitalWrite(WR, HIGH);
digitalWrite(DATA, HIGH);
Serial.begin(9600);
Serial.println("segment check");
// 各HT1621を初期化
for (int row = 1 ; row <= 6 ; row++) {
WriteCommand(row, SYS_EN);
WriteCommand(row, LCD_ON);
WriteCommand(row, BIAS3_4);
//WriteCommand(row, BIAS2_4);
}
// チェックのため全表示
for (int row = 1 ; row <= 6 ; row++) {
for (int addr = 0 ; addr < 32 ; addr++) {
WriteData(row, addr, 0b1111);
}
}
delay(1000);
// 全消去
for (int row = 1 ; row <= 6 ; row++) {
for (int addr = 0 ; addr < 32 ; addr++) {
WriteData(row, addr, 0b0000);
}
}
delay(100);
Serial.println("RTC Test Start");
Wire.begin(); // I2C の初期化
//
//0Vからの初期電源投入時には、パワーオンリセットによってVLビットは1にセットされるので
//初期化する必要がある。
Wire.beginTransmission(I2C_ADDR);
Wire.write(byte(0x02)); // RTCモジュールへレジスタの先頭番号を指定(レジスタアドレス:0x02)
Wire.endTransmission();
Wire.requestFrom(I2C_ADDR, 1); // レジスタの先頭より1バイト転送依頼
byte b = Wire.read(); // 1バイト受信
if (b & 0x80) { // データの bit7(VLbit)を判定し true なら
setRTC(16, 1, 1, 12, 0, 0); // 暫定的に日時を2016年1月1日12時0分0秒にセット
Serial.println("temporary date/time set");
Serial.println("Set correct date/time");
}
attachInterrupt(0, clk_IRQ, CHANGE);
}
void loop() {
String sInput;
while (Serial.available()) {
delay(3); //delay to allow buffer to fill
if (Serial.available() > 0) {
char c = Serial.read(); //gets one byte from serial buffer
sInput += c; //makes the string readString
}
}
//入力が12桁以上なら設定データ(年月日時分秒・各2桁)とみなす。
//データの正当性はチェックしていない。
if (sInput.length() >= 12) {
Serial.print("length:"); Serial.println(sInput.length());//確認のため表示
Serial.println(sInput);
//年月日時分秒・各2桁を切り分けて数値化し変数へ代入
int year = (sInput.substring(0, 2)).toInt();
int month = (sInput.substring(2, 4)).toInt();
int day = (sInput.substring(4, 6)).toInt();
int hour = (sInput.substring(6, 8)).toInt();
int min = (sInput.substring(8, 10)).toInt();
int sec = (sInput.substring(10, 12)).toInt();
detachInterrupt(0);//RTCに書き込む前にRTCからの割り込みを無効にしておく。
setRTC(year, month, day, hour, min, sec);
attachInterrupt(0, clk_IRQ, CHANGE);
}
// 秒が30以上なら分を切り上げ、未満なら切捨て。切り捨て。
if (sInput.substring(0, 1) == "*")
{
Serial.println("sec clear");
if (gSec >= 30) {
// 処理が複雑になるので59分では切り上げはしない
if (gMin < 59) {
gMin++;
}
}
gSec = 0;
detachInterrupt(0);//RTCに書き込む前にRTCからの割り込みを無効にしておく。
setRTC(gYear, gMonth, gDay, gHour, gMin, gSec);
attachInterrupt(0, clk_IRQ, CHANGE);
}
sInput = "";
}
void setRTC(int year, int month, int day, int hour, int min, int sec)
{
Wire.beginTransmission(I2C_ADDR);
Wire.write((0x00)); // データを転送する先頭のレジスタ番号を指定
Wire.write((0x20)); // 00 Control 1 STOP(bit5)-1 をセットし動作を停止させる。
Wire.write((0x00)); // 01 Control 2
Wire.write(byte(dec2bcd(sec))); // 02 Seconds 初期値を転送(秒)0 ~ 59
Wire.write(byte(dec2bcd(min))); // 03 Minutes    〃   (分)0 ~ 59
Wire.write(byte(dec2bcd(hour))); // 04 Hours     〃   (時)0 ~ 23
Wire.write(byte(dec2bcd(day))); // 05 Days      〃   (日)1 ~ 31
Wire.write(byte(dec2bcd(subZeller(year + 2000, month, day)))); // 06 Weekdays    〃   (曜日)0 ~ 6
Wire.write(byte(0x80 | dec2bcd(month))); // 07 Months     〃   (月)1 ~ 12
Wire.write(byte(dec2bcd(year))); // 08 Years     〃   (年)0 ~ 99
Wire.write(byte(0x00)); // 09 Minutes Alarm アラームの初期値を転送(分)0 ~ 59
Wire.write(byte(0x00)); // 0A Hours Alarm       〃      (時)0 ~ 23
Wire.write(byte(0x00)); // 0B Days Alarm        〃      (日)1 ~ 31
Wire.write(byte(0x00)); // 0C Weekdays Alarm     〃      (曜日)0 ~ 6
Wire.write(byte(0xff)); // 0D CLKOUT     CLKOUT用レジスタ CLKOUT Enable freq=1Hzに設定
Wire.write(byte(0x00)); // 0E Timer control タイマー用レジスタ
Wire.write(byte(0x00)); // 0F Timer         〃
Wire.write(byte(0x00)); // 00 Control 1 STOP(bit5)-0 をリセットし動作を開始する。
// アドレス 0F の次は先頭アドレスの 00 に戻る。
Wire.endTransmission();
}
// row 1...6
// digit_pos 0...15
// num 表示データ,配列seg_pattern参照のこと。
// 0,1,2,3,4,5,6,7,8,9,,A,b,C,d,E,F,
// 空白,-,H,L,n,o,P,u,μ,°,左縦,両縦
// dp:dpセグメントオンオフ、LSB(bit0)=1でオン
void DispNum(int row, int digit_pos, int num, int dp)
{
WriteData(row, digit_pos * 2, seg_pattern[num] & 0x0f );
WriteData(row, digit_pos * 2 + 1, (seg_pattern[num] >> 4) | (dp & 1));
}
// row 1...6
void WriteData(int row, int addr , int data)
{
int bitdata = 0x1400 | (addr << 4) | reverse4bit(data) ;// 0x1400: modeID 101 を10ビット左シフト
CS_LOW(row);
for (int i = 1 ; i <= 13 ; i++) {
if ((bitdata & 0x1000) == 0) {
digitalWrite(DATA, LOW);
}
else {
digitalWrite(DATA, HIGH);
}
bitdata <<= 1;
digitalWrite(WR, LOW);
digitalWrite(WR, HIGH);
}
CS_HIGH(row);
}
void WriteCommand(int row, int bitdata)
{
CS_LOW(row);
for (int i = 1 ; i <= 12 ; i++) {
if ((bitdata & 0x0800) == 0) {
digitalWrite(DATA, LOW);
}
else {
digitalWrite(DATA, HIGH);
}
bitdata <<= 1;
digitalWrite(WR, LOW);
digitalWrite(WR, HIGH);
}
CS_HIGH(row);
}
void CS_HIGH(int row)
{
switch (rowToCS[row]) {
case 1: digitalWrite(CS1, HIGH);
break;
case 2: digitalWrite(CS2, HIGH);
break;
case 3: digitalWrite(CS3, HIGH);
break;
case 4: digitalWrite(CS4, HIGH);
break;
case 5: digitalWrite(CS5, HIGH);
break;
case 6: digitalWrite(CS6, HIGH);
break;
default: break;
}
}
void CS_LOW(int row)
{
switch (rowToCS[row]) {
case 1: digitalWrite(CS1, LOW);
break;
case 2: digitalWrite(CS2, LOW);
break;
case 3: digitalWrite(CS3, LOW);
break;
case 4: digitalWrite(CS4, LOW);
break;
case 5: digitalWrite(CS5, LOW);
break;
case 6: digitalWrite(CS6, LOW);
break;
default: break;
}
}
// ビット逆順(4ビット)
int reverse4bit(int data)
{
int rev = 0;
rev = data & 1 ;
for (int i = 1 ; i <= 3 ; i++) {
data >>= 1;
rev <<= 1;
rev |= data & 1;
}
return rev;
}
int subZeller( int y, int m, int d )
{
if ( m < 3 ) {
y--; m += 12;
}
return ( y + y / 4 - y / 100 + y / 400 + ( 13 * m + 8 ) / 5 + d ) % 7;
}
// DECIMAL -> BCD
byte dec2bcd( byte data )
{
return ((( data / 10) << 4) + (data % 10));
}
// BCD -> DECIMAL
byte bcd2dec( byte data )
{
return ((( data >> 4) * 10) + (data % 16));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment