ATTiny84　クロック試験

#ATTinyのクロック精度を調べる

　前回のATTiny85を使ったRaspberryPi電源コントローラはピン数も少なく内蔵のクロックを使って動作させてしました。しかし内蔵クロックの精度が大変悪くて２４時間で３０分ズレというとんでもない値になっておりました。
　時間精度が求められるシステムには到底使い物にならないので、通常はここでDS1307等のリアルタイムクロックを使う処かと思います。ただ、RTCはRaspberryPi用に一個つけているし、i2cの通信をしなければならないのでATTinyもマスタにしてマルチマスタ化するとか、そうするとRasberryPiとの通信はどうするとか、、、etc. 面倒が多すぎます。
　そこでATTinyを外部クロックで動作させるとどれほど精度が出るのかを実際に確かめて見る事にしました。システムクロックが精確ならソフトで時刻をみても実用上問題無い精度のコントロールが出来るのではないかと考えました。
##回路は
　とりあえず最低限の機能としてスイッチサイエンス2x8キャラクタのLCDを時刻表示に使う事にします。https://www.switch-science.com/catalog/1407/
回路図はこんな感じ

　ATTiny85ではピン数が少なすぎるのでATTiny84を使ってみることにします。
![ATTNY84clock.PNG](https://qiita-image-store.s3.amazonaws.com/0/158760/f4b3cadd-4c87-4681-a052-64af0afac2f0.png)

外部クリスタルは秋月 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がありますのでそれらを組み合わせて時間を測って見ます。

```c
#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を並べて相対で時間を測ってみます。

![写真 2017-05-06 14 17 28.jpg](https://qiita-image-store.s3.amazonaws.com/0/158760/cf3de675-75a8-d6d2-08d2-b34420f75ed5.jpeg)

![写真 2017-05-07 18 07 27.jpg](https://qiita-image-store.s3.amazonaws.com/0/158760/5620ec0a-f2c5-efd4-e81e-7f70443a58c7.jpeg)

約28時間ランニングして経過時間から誤差を求めた処、おおよそ1sec の遅れでした。10ppmの誤差という事になります。
この精度ならRaspberryPiの定時起動には実用になるのではないかと思われます。