Arduino-IDE活用   

 1 はじめに 
 2 準備するもの 
 3 テストラン 
 4 winavr流に書いてみる 
 5 RTCモジュール8564NBの読み書き(I2C) 
 6 SDカードR/Wモジュールの試用 
 7 SDカード(スタンダードサイズ)R/Wモジュール 
 8 マイクロSDカードに温度を記録する 
 9 Arduino-IDEメモ  MEMO
 10 RTC DS3231モジュールの実験 





 1 はじめに 
 Arduinoボード(あるいは互換ボード)を購入して、周辺回路のシールドを加えて求める機能を実現するのであれば正規の方法に従えば良いのですが、私の場合はそれをまったく考えていません。
流れとして自由な回路を考えてavr-gccでコンパイルして作るのが私の楽しみなのです。 エディタでたどたどしいプログラムを書きgccでコンパイルしてエラーがあればその解決方法を考え出すのが楽しみの一つでもあります。

日常的な環境として、Cで書いてhexファイルを作る環境、hexファイルをデバイスにロードする環境が整っています。
そこで、Arduino-IDEで書いたプログラムが動くハードウエアを準備して、Arduino-IDEの多くのライブラリを利用したAVRプログラムを考えて本来のCによるプログラム作りに役立てようとするのがここでのArduino-IDEの活用法です。Arduinoを楽しむ目的でないことをご承知下さい。

したがってそのプロセスは、第一にArduino-IDEが適用できるハードウエアを準備して、Arduino-IDEでプログラム(スケッチ)を書いて、生成されたhexファイルをavrプログラマで書き込む、となります。

具体的には、ATmega328を16MHzで使用するハードウエアを準備して、それに該当するArduinoボードを仮に割り当て、Arduino-IDEを用いてコンパイルします。ArduinoシステムはUARTを通じてプログラムを書こうとしますがその機能は準備せず別のアップロードソフトであるsenshuさんのavrdude-GUIとライタはpic18spxかHIDaspxを使う方法です。

C単独では到達しにくい部分にArduinoライブラリの補助で近づこうと考えるのがArduino-IDEを併用する理由です。実際にI2Cルーチンの学習には非常に効果的でした。




 2 準備するもの 
準備するものは次のとおりです。

1 ハードウエア ATmega328ボード: 16MHz水晶クロック リセットsw IPSコネクタ UARTはTDとRDのコネクタ 5V電源端子 抵抗つきLED(19番ピン)
(2018.07.01) 周辺素子の関係で3.3Vで使用するときは8MHzの水晶に変更しています。

2 Arduino-IDE:このページからwindowsを選択してarduino-1.0-windows.zipをダウンロードしました。解凍して適当な場所に置きます。以前の版とは違ってファイル群をその場所に置くだけで使えるようです。付属のファイル群を特定のフォルダに作ることはなくなったようです。マイドキュメントにホルダができるようですが当初は空になっています。ショートカットをデスクトップに作っておきます。
(2018.07.01)現在のArduino-IDEバージョンは 1.8.5 です。C:\_0MicroComp\に置いています。

3 書き込みソフト:avrdude-GUIを使います。多くのプログラマが使えること、Arduino-IDEが生成するhexファイルの場所を簡単に指定できること、Arduino-IDEが作ったtempフォルダの無用のファイル群を一括削除する機能があること、から最適なソフトだと思います。

4 プログラマ(ハードウエア):日頃から pic18spx を多用しています。 HIDaspx や他のものでも変わりません。




 3 テストラン 

Arduino-IDEの起動:ショートカットから起動します。

使用ボードの選択:起動画面から「tools」→「board」を選択して「Arduino Duemilanove w/ ATmega328」にしました。

プログラム(スケッチ)のロード:「File」→「Exampls」→「1.Basics」と進み、「blink」を選択します。

コンパイル:変更することなく操作アイコンの左から2つ目の○に矢印(upload)をクリックします。少しの時間を経て「Binary sketch size: 1026 bytes (of a 30720 byte maximum)」と表示されますのでコンパイルが終わったことがわかります。この後Arduino-IDEはUART経由でプログラムを書き込もうとしますがハードウエアが繋がっていないのでエラーになります。が放置します。

プログラムの書き込み:avrdude-GUIを起動して(ライタの条件を設定して)、プログラムの「write」をクリックすると自動的にhexファイルを読み出してAVRに書き込みます。ダメなときはもう一度クリックします。ここでLEDが2秒周期で点滅すると関係のハードとソフトは問題なく完成していることになります。

おまけ:プログラムの delay(1000); を delay(200); に変更して書き込んでみます。点滅がはやくなるはずです。




 4 winavr流に書いてみる 

setup(){}とloop(){}の条件は守ってwinavr流にプログラムを書いてみました。
seup()はリセット後に一度だけ実行されるルーチンで、loop()はその後続けられるルーチンです。
#include <avr/io.h>
#include <util/delay.h>

uint8_t d=32;

void setup(){
  DDRB=0b00100000;
} 

void loop(){
  PORTB=d;
  _delay_ms(250);
  PORTB=0b00000000;
  _delay_ms(750);
}
専用プログラム(スケッチ)と同じ動作になります。Arduino-IDEはWINAVRの文法がそのまま使えそうです。
このことからwinavr(avr-gcc)のプログラムを考えるときの一つの方法としてArduino-IDEが使えると考えます。




 5 RTCモジュール8564NBの読み書き 

秋月がら購入したこのRTCモジュールはI2C通信によるインターフェイスです。以前にも報告していますが復習のために改めて試行しました。
Arduino-1.0になって一部取り扱いが変更されています。
ブレッドボード上に下記の回路を組み立てました。




プログラム:
RTCの基本的な設定(書き込み)と読み出しテストが目的です。このRTCはI2Cですから当然I2Cの取り扱いの復習も目的になります。
設定は時分秒だけとして日付データなどは設定しません。24時間時計としてTeraTermから時刻設定と1秒毎の時刻表示をさせます。継続して使用できるようにスーパーキャパシタでバックアップをして、その時のために時刻設定をするか、継続時刻を表示させるかをTeraTermから指定できるようにします。リセットするとTeraTermに時刻設定の有無をたずね、'y'ならhhmmss6桁の入力を求め、'n'なら継続時間を表示します。
プログラムは次のようになりました(avr-gccを日頃扱っていますのでその流儀になっています)。
/* rtc_8564nb2 時刻設定文字列を使う */
#include <Wire.h>

uint8_t data[7];  //RTC バッファ
char str[8]; //時刻用文字列
char ans;
uint8_t i,d=99,hh,mm,ss;

#define RTCaddress 0xa2 >> 1      //TWI address of RTC-unit
#define inPin 8                   //set digital8 as input

void setup() {
  pinMode(inPin, INPUT);
  Serial.begin(19200);
  Wire.begin();             // I2Cマスタとして使用

  Serial.println("set time? y/n");
  while(Serial.available()== 0){};
  ans=Serial.read();
  Serial.println(" ");

  if(ans=='y'){
    Serial.print("hhmmss ?  ");
    while(Serial.available()<7){};
    i=0;
    while (Serial.available()!= 0 ){     //読み取りできる間
      str[i] = Serial.read();
      i++;
      if(i==7){break;}
    }
    hh=(str[0]-'0')*16+str[1]-'0';
    mm=(str[2]-'0')*16+str[3]-'0';
    ss=(str[4]-'0')*16+str[5]-'0';
    Wire.beginTransmission(RTCaddress);  // マスタから書込開始(7ビットスレーブアドレス)
    Wire.write(uint8_t(0x00));         // 書込開始の内部アドレス指定
    /* initial time */
    Wire.write(uint8_t(0x00));         //controlバイト1 = 0x00 第1バイト書込
    Wire.write(uint8_t(0x00));           //controlバイト2 = 0x00 第2バイト書込
    Wire.write(ss);                      //秒 
    Wire.write(mm);                      //分 
    Wire.write(hh);                      //時   最終バイト書込
    Wire.endTransmission();       //書込終了
    delay(10);
  }// endif ans=y

  Serial.println(" ");
  Serial.println("finished initializing RTC unit 初期化完了");  //init message
}

void loop(void){

  Wire.beginTransmission(RTCaddress);  // マスタから書込開始 読み取り開始内部アドレス指定のため
  Wire.write(uint8_t(0x02));           // 内部アドレス0x02から読み取り
  Wire.endTransmission();              // 書込終了
  Wire.requestFrom(RTCaddress,3);      // マスタから読み取り開始 読み取りバイト数指定
  /* 読み取りループ */
    i=0;                               //
    while (Wire.available()!= 0 ){     // 読み取りできる間 連続読み取り
     data[i] = Wire.read();            //
     i++;                              // 終了の指定はしない
    }

  if(d!=data[0]){                      //秒の値が変化したら表示  
    Serial.print(data[2] & 0x3f,HEX); //時標示 上位2ビット不要(マスクすること)
    Serial.print(" ");
    Serial.print(data[1] & 0x7f,HEX); //分標示 上位1ビット不要(マスクすること)
    Serial.print(" ");
    Serial.print(data[0] & 0x7f,HEX); //秒標示 上位1ビット不要(マスクすること)
    Serial.println("     ");
    d=data[0];
  }
}

Arduino-1.0から変更された点は
 Wire.send()  → Wire.write() に、
 Wire.recieve() → Wire.read() になったことと、
このときの引数に 0x00 が使えたものが バイト型指定をせねばならないこと。
すなわち、byte(0x00) とするか uint8_t(0x00) とキャストする必要が生じたことです。

 一般にI2Cのread/writeシーケンスは次の手順で行われますが、
    書き込みシーケンス          
 
  スタート・コンディション発行       
  コントロールバイト送信(writeアドレス)  
  内部アドレス(H)送信            
  内部アドレス(L)送信            
  第1データ(バイト)送信          
  第2データ(バイト)送信         
  最終データ(バイト)送信         
  ストップ・コンディション発行       
 
   読み取りシーケンス
 
  スタート・コンディション発行   
   コントロールバイト送信(readアドレス)
  内部アドレス(H)送信
  内部アドレス(L)送信
  リピート・コンディション発行(AVRではs・cと同じ)  
  第1データ(バイト)受信(ACK応答)
  第2データ(バイト)受信(ACK応答)
  最終データ(バイト)受信(NACK応答)
   ストップ・コンディション発行
Arduino-IDEでは形を変えた関数になっているようです。これらに相当する部分をプログラム中に赤字のコメントで入れました。
マスタからスレーブを読み書きするときは赤字の部分に注意してプログラムすると良いでしょう。

なお、シリアル通信ではTeraTermで受ける場合に UTF-8 UTF-8m に設定が必要です。SHIFT-JISでは文字化けしました。

上記のプログラムで 5,122バイトのhexファイルになりました。avr-gccの2倍程度の量になるかと思います。mega328を使って簡単なものを作るには問題がないようです。
I2Cの通信方法がavr-gccではわからないときにこのRTCの読み書きができたことはその後のI2Cの学習に大きな役割を果たしました。Arduino-IDEの文法も必要ですから負担にはなりますが一つの方法で行き詰まったときの解決策としてArduino-IDEを使うのも利用法の一つではないかと考えます。

(付) ブレッドボードの配線方法を変えました。細い線では接触不良になる危険を感じますので、0.65mmのUEWを買いました。線の長さはデバイダで測り紙の上に写して、それより14mm長く切ります。両端をハンダで被膜を剥がしてラジオペンチに付けた7mmの印に合わせて直角に曲げます。手間はかかりますが『適当に』切っていたときより気持ち良く配線できました。写真の太いUEW部分です。(コネクタに接続している部分は前のままです)
 もどる 



 6 SDカードR/Wモジュールの試用  2016.06.25

 アマゾンで HiLetgo Micro SD メモリ カード シールド モジュール 6 ピン SPI が出ていましたので試しに買ってみました。送料込みで¥250でした。レビューにarduinoで動作するとありましたので実験してみました。aruduinoのボードではなくATmega328をブートローダーなく使っていますので適当にminiかunoでコンパイルしてAVR用のライタで書き込んでいます。

とりあえず試運転  2016.06.25
 ATmega328、8MHzX'talのブレッドボードにモジュールを差し込みました。そのままでは挿入長さが足りないのでアダプターを介しています。接続は次のとおりです。
 SDモジュール    mega328
 1 GND       GND
 2 Vcc       Vcc
 3 MISO      18 MISO
 4 MOSI      17 MOSI
 5 SCK       19 SCK
 6 CS       16 SS (aruduinoのpin10)
この他に、PCとteratarm通信用に 2 RXD と 3 TXD 及びGNDをつないでいます。
(もちろん Xtal、RESET、ISP回路を用意してあります。電源は3.3Vです。)



プログラムはaruduino IDEから、
File→スケッチの例→SD→CardInfo を選びました。aruduinoボードは pro mini 8MHz 3.3V としています。
プログラムは次のとおりですが、SS端子だけ4を10に変更しています。
/*
  SD card test

 This example shows how use the utility libraries on which the'
 SD library is based in order to get info about your SD card.
 Very useful for testing a card when you're not sure whether its working or not.

 The circuit:
  * SD card attached to SPI bus as follows:
 ** MOSI - pin 11 on Arduino Uno/Duemilanove/Diecimila
 ** MISO - pin 12 on Arduino Uno/Duemilanove/Diecimila
 ** CLK - pin 13 on Arduino Uno/Duemilanove/Diecimila
 ** CS - depends on your SD card shield or module.
 		Pin 4 used here for consistency with other Arduino examples


 created  28 Mar 2011
 by Limor Fried
 modified 9 Apr 2012
 by Tom Igoe
 */
// include the SD library:
#include <SPI.h>
#include <SD.h>

// set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
SdFile root;

// change this to match your SD shield or module;
// Arduino Ethernet shield: pin 4
// Adafruit SD shields and modules: pin 10
// Sparkfun SD shield: pin 8
const int chipSelect = 4;    ← ここは 10 に変更した

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }


  Serial.print("\nInitializing SD card...");

  // we'll use the initialization code from the utility libraries
  // since we're just testing if the card is working!
  if (!card.init(SPI_HALF_SPEED, chipSelect)) {
    Serial.println("initialization failed. Things to check:");
    Serial.println("* is a card inserted?");
    Serial.println("* is your wiring correct?");
    Serial.println("* did you change the chipSelect pin to match your shield or module?");
    return;
  } else {
    Serial.println("Wiring is correct and a card is present.");
  }

  // print the type of card
  Serial.print("\nCard type: ");
  switch (card.type()) {
    case SD_CARD_TYPE_SD1:
      Serial.println("SD1");
      break;
    case SD_CARD_TYPE_SD2:
      Serial.println("SD2");
      break;
    case SD_CARD_TYPE_SDHC:
      Serial.println("SDHC");
      break;
    default:
      Serial.println("Unknown");
  }

  // Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
  if (!volume.init(card)) {
    Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
    return;
  }


  // print the type and size of the first FAT-type volume
  uint32_t volumesize;
  Serial.print("\nVolume type is FAT");
  Serial.println(volume.fatType(), DEC);
  Serial.println();

  volumesize = volume.blocksPerCluster();    // clusters are collections of blocks
  volumesize *= volume.clusterCount();       // we'll have a lot of clusters
  volumesize *= 512;                            // SD card blocks are always 512 bytes
  Serial.print("Volume size (bytes): ");
  Serial.println(volumesize);
  Serial.print("Volume size (Kbytes): ");
  volumesize /= 1024;
  Serial.println(volumesize);
  Serial.print("Volume size (Mbytes): ");
  volumesize /= 1024;
  Serial.println(volumesize);


  Serial.println("\nFiles found on the card (name, date and size in bytes): ");
  root.openRoot(volume);

  // list all files in the card with date and size
  root.ls(LS_R | LS_DATE | LS_SIZE);
}


void loop(void) {

}
スケッチ → コンパイルしたバイナリを出力 を選んでファイルに納め、いつものとおりavrdude-GUIとHIDaspxで書き込みました。ただし、モジュールのSPIとAVRの書き込みが干渉してライタが動きません。書き込み時はSDモジュールを抜いておきます。
フォルダに自動車のドライブレコーダーで使っていた4GBのマイクロSDを挿入し、teratarmを立ててresetしたところ次のような表示が出ました。


とりあえず「使えるらしい」といったところです。

2016.07.08追記
手元に4GBHCが2枚と2GBが1枚ありますが、4GBHCの内の1枚は SD2 と認識するのですがフォーマットしろ、と表示されて内容は読み取れません。USB接続のリーダー(¥100均品)ではフォーマット、書き込み、読み取りすべてOKなのにこの装置では使えませんでした。原因は不明です。

 もどる 



  7 SDカード(スタンダードサイズ)R/Wモジュール 

 アマゾンでスタンダードサイズのSDカード用モジュールがありましたので買いました。(EasyWordMall SDカードスロットソケットリーダーモジュールArduino用 ¥225)
3.3V電源レギュレータとプルアップ抵抗が付いているだけです。これで5V使用できるのかと少し疑問ですが、私は3.3Vで使うので問題はありません。


上記のマイクロSDソケットとパラレルに接続して抜き差しでどちらも実験できるようにしました。
実は、手持ちに64MBminiSDがあって、これが使えるかどうかを見たかったのです。結果として、3枚の内2枚は書き込みができました。残りの1枚はSD1と認識するのですがフォーマットせよとのメッセージが出て書き込みができません。windowsではまったく認識されません。原因不明です。 64MBのSD(GBではありません)はもう求めることもできないと思いますが、単純に計算して58バイトのデータが100万個記録できるこtになります。毎秒58バイトのデータを保存しても1年はゆうに記録できます。
もう一つ不思議な事がありました。マイクロSDモジュールで使用できなかった1枚の4GBカードが、サイズアダプタを使えばスタンダードモジュールで書き込みができました。これはwindowsでは使えるのですがマイクロSDモジュールでは使えなかったものです。

プログラムは上記のSDinfoにカウントアップする数値と一定の文字列asdfを書き込むものとしました。上記と同じaruduino mini 8MHzとしています。
/*
 The circuit:
  * SD card attached to SPI bus as follows:
 ** MOSI - pin 11 on Arduino Uno/Duemilanove/Diecimila
 ** MISO - pin 12 on Arduino Uno/Duemilanove/Diecimila
 ** CLK - pin 13 on Arduino Uno/Duemilanove/Diecimila
 ** CS - depends on your SD card shield or module.
 		Pin 4 used here for consistency with other Arduino examples
 */
// include the SD library:
#include <SPI.h>
#include <SD.h>

// set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
SdFile root;

const int chipSelect = 10; //変更
int value = 123;     //ファイル書き込みのため追加

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }


  Serial.print("\nInitializing SD card...");

  // we'll use the initialization code from the utility libraries
  // since we're just testing if the card is working!
  if (!card.init(SPI_HALF_SPEED, chipSelect)) {
    Serial.println("initialization failed. Things to check:");
    Serial.println("* is a card inserted?");
    Serial.println("* is your wiring correct?");
    Serial.println("* did you change the chipSelect pin to match your shield or module?");
    return;
  } else {
    Serial.println("Wiring is correct and a card is present.");
  }

  // print the type of card
  Serial.print("\nCard type: ");
  switch (card.type()) {
    case SD_CARD_TYPE_SD1:
      Serial.println("SD1");
      break;
    case SD_CARD_TYPE_SD2:
      Serial.println("SD2");
      break;
    case SD_CARD_TYPE_SDHC:
      Serial.println("SDHC");
      break;
    default:
      Serial.println("Unknown");
  }

  // Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
  if (!volume.init(card)) {
    Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
    return;
  }


  // print the type and size of the first FAT-type volume
  uint32_t volumesize;
  Serial.print("\nVolume type is FAT");
  Serial.println(volume.fatType(), DEC);
  Serial.println();

  volumesize = volume.blocksPerCluster();    // clusters are collections of blocks
  volumesize *= volume.clusterCount();       // we'll have a lot of clusters
  volumesize *= 512;                            // SD card blocks are always 512 bytes
  Serial.print("Volume size (bytes): ");
  Serial.println(volumesize);
  Serial.print("Volume size (Kbytes): ");
  volumesize /= 1024;
  Serial.println(volumesize);
  Serial.print("Volume size (Mbytes): ");
  volumesize /= 1024;
  Serial.println(volumesize);


  Serial.println("\nFiles found on the card (name, date and size in bytes): ");
  root.openRoot(volume);

  // list all files in the card with date and size
  root.ls(LS_R | LS_DATE | LS_SIZE);

}


void loop(void) {
  SD.begin();                   //この行以下はファイル書き込みのため追加した
  File dataFile = SD.open("aaa.txt", FILE_WRITE);
   // もしファイルが開けたら値を書き込む
  if (dataFile) {
    value += 1;
    dataFile.print(value);
    dataFile.println(", asdf");
    dataFile.close();
    // シリアルポートにも出力
    Serial.println(value);
  }
  // ファイルが開けなかったらエラーを出力
  else {
    Serial.println(F("error opening aaa.txt"));
  } 

  // 一秒待つ
  delay(1000);

}
 もどる 



 8 マイクロSDカードに温度を記録する

 aruduino-IDEはシリアル通信やI2C、LCD、SDカードなどの取り扱いが簡単でプログラムが楽にかけます。そこで測定データをLCDに表示しながらSDカードに記録するのにどれほどのROM量を必要とするのか、それはATmega328Pで可能なのかどうかを調べたくてブレッドボードに組んでみました。測定データとしては扱いが簡単な温度センサーを使っています。



MCP ATmega328P 8MHz
温度センサー DS7505 (デジタル値出力)I2C接続
RTC DS3231 (高精度品)I2C接続
マイクロSD 自作フォルダ+2GB SPI接続(miso mosi cs clk Vcc GND)

                   mega328Pの接続:
1 RST
2 RD
3 TD
4
5
6 LCD DB4 緑
7 Vcc
8 GND
9 Xtal
10 Xtal
11 LCD DB5 青
12 LCD DB6 灰
13 LCD DB7 白
14 LCD RS  茶
15 LCD E  黄
16 SD  CS
17 SD  MOSI
18 SD  MISO
19 SD  SCK
20 AVcc
21
22 GND
23
24
25
26
27 I2C SDA
28 I2C SCK

I2Cの SCK と SDA はありあわせの10kでプルアップしています。

表示とSDカード記録はループ任せで、特にSDには1秒に5サンプルほど記録されています。実用には細かな設定が必要だとは思いますが、現在で32kROMの54%に収まっています。この分では温度の記録は大丈夫です。しかし、気圧も扱うとどうなるでしょうか。なんとか収まりそうにも思うのですが。
例によってATmega328P 8MHzの基板をaruduinoに見立ててArduino-IDEでコンパイルして、AVR用のライタで書き込んでいます。AVRのMCとクロックが自由にならないのですが、aruduinoのライブラリが使えるのがメリットです。
/* *********************************************************************************
 *  name SD160810.ino
 *  ATmega328p 8MHz 3.3V
 *  温度データを時刻とともにSDカードに記録するテストプログラム。
 *  RTC:DS3231  温度センサー:MAXIM DS7505  SDカード:フォルダ自作 2GB 
 *  年月日時分秒と温度(小数1位まで)をLCDに表示し、SDカードにtxtとして保存する。
 *  保存テキストファイル名は BBB.txt である。
 *  
 *  記録は自由ループのため毎秒数回採られている(変更の必要あり)。
 *  最大30,720バイトのフラッシュメモリのうち、スケッチが16,228バイト(52%)を使って
 *  います.
 *  最大2,048バイトのRAMのうち、グローバル変数が1,139バイト(55%)を使っています。
 * 
 ********************************************************************************** */
#include <LiquidCrystal.h>
  LiquidCrystal lcd(8,9,4,5,6,7);
#include <Wire.h>
#include <SPI.h>
#include <SD.h>

// val
  const int chipSelect = 10; // 328p CS端子
  uint8_t i,t1,t2,mm,ss,u;  // u:generally
  uint8_t t[10],tp[2];  // t[]:RTCdata  tp[]:TEMPdata
  char d[18],s[15];  // string for print & input
  
void setup() {
  Serial.begin(9600);
  lcd.begin(16,2);
  Wire.begin(0x90>>1); // 温度センサー  DS7505
  Wire.begin(0xd0>>1); // RTC ds3231
  SD.begin();
  pinMode(2, INPUT_PULLUP); 
}
  
void loop() {
  lcd.clear();
   
 // 温度センサー12ビット読み取りに設定
  Wire.beginTransmission(0x90>>1); 
  Wire.write(0x01); // inner=01 set mode 9bit or 12bit
  Wire.write(0x60); delay(10);  // set 12bit mode
  Wire.endTransmission() ; delay(20);

 //温度センサー  データ読み取りモードに戻す
  Wire.beginTransmission(0x90>>1);
  Wire.write(0x00); delay(10); // inner=00 set mode read data
  Wire.endTransmission() ; delay(20);
    lcd.clear();

#if 0 // set RTC 電池バックアップのため必要なときに有効にして設定する。
  Serial.println("Input ! YYMMDDdhhmmss 24hsys.");
  delay(20000);// 入力終了を待たずに読み込むため20秒のwaitを入れてある
  i=0;
  while(Serial.available()) { // 受信したデータが存在する
    s[i] = Serial.read(); // 受信データを s[] に読み込む
    i++;
  }

  for(i=0;i<13;i++){ Serial.println(s[i]);}// 入力確認用
  
 // inner address該当のd[0]=0 のためd[1]=ssとなり1だけずれる
  t[7]=(s[0]-'0')*16+(s[1]-'0'); // yy
  t[6]=(s[2]-'0')*16+(s[3]-'0'); // mm
  t[5]=(s[4]-'0')*16+(s[5]-'0'); // dd
  t[4]=(s[6]-'0');               // day of week sun=1
  t[3]=(s[7]-'0')*16+(s[8]-'0'); // hh
  t[2]=(s[9]-'0')*16+(s[10]-'0'); // mm
  t[1]=(s[11]-'0')*16+(s[12]-'0'); //ss
  t[3]=d[3]&0B10111111;          // set 24h
  t[0]=0;

  Wire.beginTransmission(0xd0>>1);
  for(i=0;i<8;i++){Wire.write(t[i]);}
  Wire.endTransmission() ;  
#endif  // end of set RTC


while(1){ // 以下実質のループ

  // 以下時刻処理
  Wire.beginTransmission(0xd0>>1);  // RTC write mode
  Wire.write(0x00); delay(10);      // inner address
  Wire.endTransmission(false) ;     // restart
  Wire.requestFrom(0xd0>>1,7);    // デバイス(アドレス=0x48)に対し7バイトをread要求
  i=1;
  while(Wire.available())  {   // 要求より短いデータが来る可能性あり
    t[i] = Wire.read();      // 1バイトを受信
    i++;
  }
  d[1]=t[7]/16+'0';    d[2]=t[7]%16+'0';    d[3]='/';   // year
  d[4]=t[6]/16+'0';    d[5]=t[6]%16+'0';    d[6]='/';   // month
  d[7]=t[5]/16+'0';    d[8]=t[5]%16+'0';                // day
  
  d[9]=t[3]/16+'0';    d[10]=t[3]%16+'0';   d[11]=':';  // hour
  d[12]=t[2]/16+'0';   d[13]=t[2]%16+'0';   d[14]=':';  // minute
  d[15]=t[1]/16+'0';   d[16]=t[1]%16+'0';               // second
  delay(20);

// 分未満四捨五入 pin2(4番)= L の時
  if( digitalRead(2)==LOW){
    //delay(2000);
    if(t[1]>=0x30){t[2]++;}
    t[1]=0; t[0]=0;
    Wire.beginTransmission(0xd0>>1);
    for(i=0;i<3;i++){Wire.write(t[i]);}
    Wire.endTransmission() ;
    delay(100);
  }
// end of 分未満四捨五入 pin2(4番)= L の時
      
  // 時刻表示
  lcd.setCursor(1,0);
  for(i=1;i<9;i++){lcd.print(d[i]);}  // YY/MM/DD
  lcd.setCursor(1,1);
  for(i=9;i<17;i++){lcd.print(d[i]);} // hh:mm:ss
  // 時刻表示 終わり
// 以上時刻処理

// 以下温度処理
  Wire.requestFrom(0x90>>1,2);    // デバイス(アドレス=0x90>>1)に対し2バイトを要求
  i=1;
  while(Wire.available())  {   // 要求より短いデータが来る可能性あり
    tp[i] = Wire.read();      // 1バイトを受信
    i++;
  }
   // 温度表示
   s[1]=tp[1]/10+0x30;    s[2]=t[1]%10+0x30;    s[3]='.';
   s[4]=(((t[2]/16)*10)/16)+0x30;   s[5]=' ';
    lcd.setCursor(11,1);
    for(i=1;i<6;i++){lcd.print(s[i]);}  // 配列は設定できない
// 以上 温度処理・表示

// 以下 SDへデータ書き込み
  File dataFile = SD.open("bbb.txt", FILE_WRITE); // プログラム中のファイル名、SDのファイル名
  // もしファイルが開けたら値を書き込む
  if (dataFile) {
    for(i=1;i<9;i++){dataFile.print(t[i]);} // YYMMDD
    dataFile.print(" ");
    for(i=9;i<17;i++){dataFile.print(t[i]);}  // hhmmss
    dataFile.print(" ");
    for(i=1;i<5;i++){dataFile.print(s[i]);}   // temperture
    dataFile.println(" ");
    dataFile.close();
  }
  delay(100);

}//while 実質ループ終わり

} // program end
時計について、分未満を四捨五入する機能を追加しました。aruduinoのpin2(mega328Pの4番pin)を一瞬GNDに落とすことで30秒以上なら次の分の0秒に進め、30秒未満ならその分の0秒に遅らせます。RTCは精度が高く、またリチウム電池でバックアップしているのでしばらく使っても誤差は数秒程度です。この機能を使って合わせれば時刻設定機能はまず必要がありません。ただ、手抜きしていますので繰り上がりの時間(分の1位が9の時)は使えません。ご動作します。完璧にするには 分→時→日→月→年 まで繰り上がりに配慮が必要です。

 もどる 




 9 Arduino-IDEメモ 

    1 mega8(RC8MHz)に適用する
    2 Arduino-IDEリファレンス
    3 ボードの追加 ESP8266,AtTiny2313



1 mega8(RC8MHz)に適用する  2017.05.22
手持ちのmeg8をI2CドライブのLCDスレーブにするためにArduino-IDEに合う設定を探しました。デバイスへの書込みは慣れたavrdude-GUIで行いますのでhexファイルができれば良いことになります。
以前のbords.txtは拡張(追加)が簡単だったのですが今のものはできないようです。したがってmega8のボードを探さねばなりません。幸いなことに Arduino NG or older ボードがありました。内蔵RC発振8MHzで使うのですが、このボードは水晶の16MHzになっています。ヒューズはIDEでは書き込まないので(avrdudeで書き込む)変更の必要はないのですが目についたので変えました。16MHzのままでもいいのですが時間が関係するルーチンに影響しますので8MHzに変えたほうがよいでしょう。
次の変更を行いました。
## Arduino NG or older w/ ATmega8 で追加及び修正した場所

atmegang.build.f_cpu=8000000L
atmegang.menu.cpu.atmega8=ATmega8(3.3V, 8 MHz)
atmegang.menu.cpu.atmega8.bootloader.low_fuses=0xe4
atmegang.menu.cpu.atmega8.bootloader.high_fuses=0xd9
Arduino-IDEで 「スケッチ」→「コンパイルしたバイナリを出力」を選択してできた.hexファイルを書き込んでいます。

2 Arduino-IDEリファレンス  2017.05.22
文法については Arduino 日本語リファレンス を参照しています。
なお、現在(2017.5)はArduino-IDE のVer1.8.1を使っています。


3 ボードの追加 ESP8266,AtTiny2313  2017.05.29
ボードを追加するには次の手順で行います。
1 Arduino-IDEの「ファイル」→「環境設定」→「追加のボードマネージャーのURL」に次のURLを貼り付けます。
   ESP8266 :http://arduino.esp8266.com/stable/package_esp8266com_index.json
   Tiny2313:http://drazzy.com/package_drazzy.com_index.json
  このとき、上記の両方共必要なときは2つとも書かねばなりません。そのまま追加すると前のものが消されてしまします。
  右端のページマーク(?)をクリックすると複数行の記入ができて便利です。
2 「ツール」→「ボード」から最上部の「ボードマネージャー」を開きます。
3 ESP8266 で検索して、「esp8266 by ESP8266 Community」の最新版をインストールします。
  attiny で検索して、上方にある「ATTinyCore by Spence Konde」の最新版をインストールします。

 Top 



 10 RTC DS3231モジュールの実験  2018.06.30

 MAXIMのRTC DS3231 は2ppmという精度を持った素晴らしいICだと思います。しかし、最近のMAXIMのページでは主力は5ppmの製品に移っているように思えます。また、1000個単位の購入価格が単価3ドルを超えています。しかし、中国製のモジュールがなぜか安く買えます。もっとも、以前は100円ほどだったようですが今は231円しています。やがてはなくなるかも、と思って追加購入しました。

現在は時計を作るつもりはないのですが、専用のライブラリを使わずにどの程度で液晶時計(時刻表示まで)ができるのか調べてみたくて組んでみました。ブレッドボードを簡単にするために、LCDはR8C/M12Aを使って3.3VでシリアルのRXDだけで動くものを使っています。LCDへの命令は'!'でクリア、101始まりで1行目1カラムから、201始まりで2行目1カラムからとしています。

このRTCモジュールは、CR2032のホルダがついていますが、充電池用なのでVccとつながっています。一次電池を使いますので回路の200Ωを外して電圧がかからないように変更しています。



MCUは mega328p で、Arduino-IDEは Arduino pro mini 8MHz を使っています。Arduino-IDEでコンパイルしますがAVRへの書き込みは専用の書き込み(AVRdude-GUI+pic18spx)を使っています。
今回のArduino-IDEのバージョンは 1.8.5 を使っています。



14番ピンにタクトスイッチを入れて、@押したままリセットすればyymmddhhmmssで時刻設定ができ、A動作状態で押すと分未満四捨五入で時刻調整ができるよにしています。

I2Cのシーケンス(Arduino-IDEの)は次のとおりです。
  #include <Wire.h> ヘッダファイルのインクルード   Wire.begin(); setup()で開始 ()内はマスタでは「なし」 書き込み時 Wire.beginTransmission(0x68); //RTC address Wire.write(0);            //inner address for (n = 0; n < 7; n++) { Wire.write(d[n]); } //書き込みデータ(バイト型) Wire.endTransmission();       //終了 読み込み時   Wire.beginTransmission(0x68); //RTC address Wire.write(0); //inner address Wire.endTransmission(false); //restart Wire.requestFrom(0x68, 7);     // request 7 bytes from slave device 0x68 n = 0; while (Wire.available()) { d[n] = Wire.read(); n++; }  // slave may send less than requested

スケッチをコンパイルしたところ、ROMは4362バイト(14%)、RAMは398バイト(19%)となりました。

プログラムは次のとおりです。(一部 F()マクロ を使ってRAMを節約しています。)
/* *******************************************************************
    RTC DS3231モジュールの読み取り、シリアル表示ルーチン
   ds3231_reader.ino 2018.06.29
   ATmega328 (use Arduino pro mini 8MHz)
   LCD表示は シリアル駆動LCD 使用
   14番pin sw=LOW & reset → 時刻設定 yymmddhhmmss
   表示中に  14番pin sw=LOW → 分未満四捨五入
  ******************************************************************** */
#include <Wire.h>

void setup() {
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
  pinMode(8, INPUT_PULLUP); // no.14
//  pinMode(7, INPUT_PULLUP); // no.13
}



//--------------------------------------------------------------------
void loop() {
  uint8_t d[10];
  char s1[16], s2[16];// LCD表示文字列 1行目と2行目
  uint8_t n, f1, f2;

  Serial.println('!');    delay(100);
  if (digitalRead(8) == LOW) { // if sw on 時刻設定
    Serial.println(F("yymmddhhmmss at 24h"));    delay(50);
    n = 0;
    while (Serial.available() < 13) {}
    while (Serial.available() > 0) {
      s1[n] = Serial.read(); // 受信データを読み込む
      n++;
    }
    //    Serial.println();    delay(100);
    //    Serial.println(s1);    delay(100);
    d[0] = (s1[10] - '0') * 16 + (s1[11] - '0'); //sec
    d[1] = (s1[8] - '0') * 16 + (s1[9] - '0'); //min
    d[2] = (s1[6] - '0') * 16 + (s1[7] - '0'); //hour
    d[3] = 1; //w_day:none
    d[4] = (s1[4] - '0') * 16 + (s1[5] - '0'); //day
    d[5] = (s1[2] - '0') * 16 + (s1[3] - '0'); //month
    d[6] = (s1[0] - '0') * 16 + (s1[1] - '0'); //year
    Wire.beginTransmission(0x68);          //RTC address
    Wire.write(0);//inner address
    for (n = 0; n < 7; n++) {
      Wire.write(d[n]);
    }
    Wire.endTransmission();
  }
  while (1) {

    //読み取り表示ルーチン
    Wire.beginTransmission(0x68);          //RTC address
    Wire.write(0);                         //inner address
    Wire.endTransmission(false);           //restart
    Wire.requestFrom(0x68, 7);    // request 7 bytes from slave device 0x68
    n = 0;
    while (Wire.available()) { // slave may send less than requested
      d[n] = Wire.read(); // receive a byte as character
      n++;
    }

    f2 = f1; f1 = d[0];
    if (f2 != f1) { // 秒の数値が変わったときだけ表示する

      // d[0]:sec d[1]:min d[2]:hour d[3]:w_day d[4]:day d[5]:mon d[6]:year
      s1[0] = (d[6] / 16) + '0'; s1[1] = (d[6] % 16) + '0'; s1[2] = ' ';
      s1[3] = (d[5] / 16) + '0'; s1[4] = (d[5] % 16) + '0'; s1[5] = ' ';
      s1[6] = (d[4] / 16) + '0'; s1[7] = (d[4] % 16) + '0'; s1[8] = ' ';
      s1[9] = 0;

      s2[0] = (d[2] / 16) + '0'; s2[1] = (d[2] % 16) + '0'; s2[2] = ':';
      s2[3] = (d[1] / 16) + '0'; s2[4] = (d[1] % 16) + '0'; s2[5] = ':';
      s2[6] = (d[0] / 16) + '0'; s2[7] = (d[0] % 16) + '0'; s2[8] = ' ';
      s2[9] = '\0';

      Serial.print(F("10120")); delay(50);
      Serial.println(s1); delay(50);          //year month day
      Serial.print(F("201        ")); delay(50);
      Serial.println(s2); delay(50);          // hour minute second
    }
    // 分未満四捨五入
    if (digitalRead(8) == LOW) {
      uint8_t min2;
      min2 =  (d[1] / 16) * 10 + d[1] % 16;
      if (d[0]>=48) min2++;// >=0x30
      min2 = (min2 / 10) * 16 + min2 % 10;
      Wire.beginTransmission(0x68);
      Wire.write(0);
      Wire.write(0);
      Wire.write(min2);
      Wire.endTransmission();
    }// end of 分未満四捨五入
  }// end of while
}// end of loop

 Top 





















工事中

 0 タイトル  2017.05.22


 Top 















  

inserted by FC2 system