ATTinyのクロック精度を調べる
前回のATTiny85を使ったRaspberryPi電源コントローラはピン数も少なく内蔵のクロックを使って動作させてしました。しかし内蔵クロックの精度が大変悪くて24時間で30分ズレというとんでもない値になっておりました。
時間精度が求められるシステムには到底使い物にならないので、通常はここでDS1307等のリアルタイムクロックを使う処かと思います。ただ、RTCはRaspberryPi用に一個つけているし、i2cの通信をしなければならないのでATTinyもマスタにしてマルチマスタ化するとか、そうするとRasberryPiとの通信はどうするとか、、、etc. 面倒が多すぎます。
そこでATTinyを外部クロックで動作させるとどれほど精度が出るのかを実際に確かめて見る事にしました。システムクロックが精確ならソフトで時刻をみても実用上問題無い精度のコントロールが出来るのではないかと考えました。
回路は
とりあえず最低限の機能としてスイッチサイエンス2x8キャラクタのLCDを時刻表示に使う事にします。https://www.switch-science.com/catalog/1407/
回路図はこんな感じ
ATTiny85ではピン数が少なすぎるのでATTiny84を使ってみることにします。
外部クリスタルは秋月 http://akizukidenshi.com/catalog/g/gP-01763/
負荷容量30pF なので47pFx2 で駆動させます。これでおおよそ28.5pFのはず。
スケッチ
開発はArduino IDE 1.82 にATTiny84用のボードライブラリをインストールし使用しました。i2c LCD 通信のために ATTiny84用には TinyWireM ライブラリを使用します。i2cマスタ用のライブラリになります。
https://github.com/adafruit/TinyWireM
その他時間関連のライブラリでTimeLibがありますのでそれらを組み合わせて時間を測って見ます。
# define __AVR_ATtiny84__ 1
# include <TimeLib.h>
# include <TinyWireM.h>
/* AT84 */
/*
* Time Clock
* mini LCD
* with delay 1/16
* non delay 1/18
*
*/
// ATMEL ATTINY84 / ARDUINO FemotoCow, Universal Plug
// Counter Clock Wise
//
// +--U--+
// VCC 1| |14 GND
// (D 0) PB0 2| |13 PA0 (D 10) AREF
// (D 1) PB1 3| |12 PA1 (D 9)
// RSET (D 11) PB3 4| |11 PA2 (D 8)
// PWM INT0 (D 2) PB2 5| |10 PA3 (D 7)
// PWM (D 3) PA7 6| |9 PA4 (D 6) SCL
// PWM SDA (D 4) PA6 7| |8 PA5 (D 5) PWM
// +-----+
// ATMEL ATTINY84 / ARDUINO
//
// +--U--+
// VCC 1| |14 GND
// (D 10) PB0 2| |13 PA0 (D 0) AREF
// (D 9) PB1 3| |12 PA1 (D 1)
// RSET PB3 4| |11 PA2 (D 2)
// PWM INT0 (D 8) PB2 5| |10 PA3 (D 3)
// PWM (D 7) PA7 6| |9 PA4 (D 4)
// PWM (D 6) PA6 7| |8 PA5 (D 5) PWM
// +-----+
# define LED_PIN 2 // ATT84
# define sdaPin 4 // ATT84
# define sclPin 6 // ATT84
# define I2Cadr 0x3e // 固定
byte contrast = 33; // コントラスト(0~63)
void setup() {
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN,1);
// Serial.begin(9600);
setTime(00,00,00,1,1,2000);
delay(500);
TinyWireM.begin();
lcd_cmd(0b00111000); // function set
lcd_cmd(0b00111001); // function set
lcd_cmd(0b00000100); // EntryModeSet
lcd_cmd(0b00010100); // interval osc
lcd_cmd(0b01110000 | (contrast & 0xF)); // contrast Low
lcd_cmd(0b01011100 | ((contrast >> 4) & 0x3)); // contast High/icon/power
lcd_cmd(0b01101100); // follower control
delay(200);
lcd_cmd(0b00111000); // function set
lcd_cmd(0b00001100); // Display On
lcd_cmd(0b00000001); // Clear Display
delay(2);
}
unsigned long globalsec=0;
void outputime()
{
Serial.print(month());
Serial.print("/");
Serial.print(day());
Serial.print(" ");
Serial.print(hour());
Serial.print(":");
Serial.print(minute());
Serial.print(":");
Serial.print(second());
Serial.print("\n");
}
int yr,mon,dy,hr,mi,sc;
void setdate()
{
long _timesec_;
char buf[16];
int yr,mon,dy,hr,mi,sc;
// Serial.readBytes(buf,12);
yr=(buf[0]-0x30)*10+buf[1]-0x30;
yr=yr+2000;
Serial.print(yr);
Serial.print("\n");
mon=(buf[2]-0x30)*10+(buf[3]-0x30);
// Serial.print(mon);
dy=(buf[4]-0x30)*10+(buf[5]-0x30);
hr=(buf[6]-0x30)*10+(buf[7]-0x30);
mi=(buf[8]-0x30)*10+(buf[9]-0x30);
sc=(buf[10]-0x30)*10+(buf[11]-0x30);
setTime(hr,mi,sc,dy,mon,yr);
outputime();
}
int count=0;
int last;
void loop() {
char str1[32];
char str2[16];
int c;
if(last!=second()){
last = second();
digitalWrite(LED_PIN,digitalRead(LED_PIN)^1);
}
sprintf(str1,"%02d/%02d",month(),day());
sprintf(str2,"%02d:%02d:%02d",hour(),minute(),second());
lcd_setCursor(0, 0);
lcd_printStr(str1);
lcd_setCursor(0, 1);
lcd_printStr(str2);
count=0;
}
void lcd_cmd(byte x) {
TinyWireM.beginTransmission(I2Cadr);
TinyWireM.write(0b00000000); // CO = 0,RS = 0
TinyWireM.write(x);
TinyWireM.endTransmission();
}
void lcd_contdata(byte x) {
TinyWireM.write(0b11000000); // CO = 1, RS = 1
TinyWireM.write(x);
}
void lcd_lastdata(byte x) {
TinyWireM.write(0b01000000); // CO = 0, RS = 1
TinyWireM.write(x);
}
// 文字の表示
void lcd_printStr(const char *s) {
TinyWireM.beginTransmission(I2Cadr);
while (*s) {
if (*(s + 1)) {
lcd_contdata(*s);
} else {
lcd_lastdata(*s);
}
s++;
}
TinyWireM.endTransmission();
}
// 表示位置の指定
void lcd_setCursor(byte x, byte y) {
lcd_cmd(0x80 | (y * 0x40 + x));
}
実行結果
最低限の構成のATTiny84単体で外部から時間設定する術がないのでiPhoneを並べて相対で時間を測ってみます。
約28時間ランニングして経過時間から誤差を求めた処、おおよそ1sec の遅れでした。10ppmの誤差という事になります。
この精度ならRaspberryPiの定時起動には実用になるのではないかと思われます。