機能拡張ボードをArduinoIDEで開発(SD/RTC編)

[概要] [CircuitPythonで開発] [OLED編]   〔Seeeduino XIAOの使い方に戻る〕


機能拡張ボード用のプログラムをCircuitPythonで開発&実験をしたかったが、メモリ不足で断念した。
やっぱりぃArduino IDEよねぇ、ってんでぇArduino IDEで開発&実験を行う。
尚、「wiki.Seeeduino XIAO Expansion board」を参考にします。

《ライブラリーの準備》

と言う事でぇ、このページで扱うライブラリ―をダウンロードして置きます。

・ 前ページで使った[U8g2]ライブラリーのOLEDも使います。

・ [PCF8563]ライブラリーでRTC用です。
  このライブラリーは直接ダウンロードしてArduino IDEを起動させ、
  IDEのメニューバーから[スケッチ]->[ライブラリをインクルード]->[.ZIP型式のライブラリを
  インストール]でダウンロードファイルを選択しインストールします。
  尚、機能拡張ボードで利用するなら時刻の書き込みと読み出しのみ使用できます

・ PCF8563ライブラリーは、アラーム機能とカウントダウン機能は用意していないので"skPCF8563"を
  ダウンロードしインストールてください。
  PCF8563ライブラリーと一緒に使用する事が前提条件となります。

《RTC》

以前こちらのページでXIAOボードに搭載されている水晶振動子を使って時刻を表示する事は行いました。
ここでは機能拡張ボードに搭載のRTC(PCF8563)を動作させます。
I2C接続でI2Cアドレスは0x51の様です。
尚、Chipの"INT/CLKOUT"ピンはプルアップされていて出力がない様なのでクロック出力やアラームと
タイマー機能割り込み出力は出来ないが、ソフトウェアでアラームフラグとタイマーフラグを読み取れば
出来ます。

日付と時刻をOLEDに表示してみる

PCF8563ライブラリは曜日を読み取れるがセットが出来ないのでSetWeekdays()関数を付けときました。
起動後、「2021/01/23(土曜日)15:24:00」の時刻からスタートします。

@ Seeeduino XIAOを機能拡張ボードに取り付けてから、PCとType-Cケーブルを接続します。

A Arduino IDE開き、下記スケッチをコピーしてコンパイル & XIAOへの書き込みを行います。
--------------------------------------------------------------------------------
#include <U8g2lib.h>
#include <PCF8563.h>

// RTC(PCF8563)を使用する為のコンストラクタ
PCF8563 rtc ;
// SSD1306をグラフィックディスプレイとして使用する為のコンストラクタ
// メモリバッファ(RAM)は1ページ分の128b分確保
// ローテーション(画面回転)なし、SCL=5番ピン SDA=4番ピン
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0,5,4) ;

#define PCF8563_address  0x51 // PCF8563のI2Cアドレス
#define PCF8563_Weekdays 0x06 // 曜日をセットするレジスタアドレス値
// PCF8563に曜日を設定する(0:日曜〜6:土曜)
void SetWeekdays(uint8_t wdays) {
  const uint8_t data = (((wdays/10) << 4)|(wdays%10));
  Wire.beginTransmission(PCF8563_address);
  Wire.write(PCF8563_Weekdays);
  Wire.write(data);
  Wire.endTransmission();
}
char WeekDayData[7][4] = {"日","月","火","水","木","金","土"} ;

void setup() {
  u8g2.begin() ;    // OLEDの初期化
  u8g2.enableUTF8Print() ; // Arduino print()関数のUTF8サポートを有効にする

  rtc.init() ;      // RTCの初期化
  // 2021年1月23日 土曜日 15時24分00秒で初期設定
  rtc.stopClock() ; // RTCのカウントを止める
  rtc.setYear(21) ; // 年の設定(00-99)
  rtc.setMonth(1) ; // 月の設定(01-12)
  rtc.setDay(23) ;  // 日の設定(01-31)
  SetWeekdays(6) ;  // 土曜日の設定(0-6)
  rtc.setHour(15) ; // 時の設定(00-23)
  rtc.setMinut(24) ;// 分の設定(00-59)
  rtc.setSecond(0) ;// 秒の設定(00-59)
  rtc.startClock() ;// RTCのカウントを開始する
}
void loop() {
  char buf[12] ;

  Time nowTime = rtc.getTime() ; // 現在の時刻を得る
  u8g2.setFont(u8g2_font_b16_t_japanese2) ; // 日本語フォント高さ16bit

  u8g2.firstPage() ;
  do {
    // 日付の表示
    u8g2.setCursor(7, 16) ;
    sprintf(buf,"%d/%02d/%02d",nowTime.year+2000,nowTime.month,nowTime.day) ;
    u8g2.print(buf) ;
    // 曜日の表示
    u8g2.setCursor(87, 16) ;
    sprintf(buf,"(%s)",WeekDayData[nowTime.weekday]) ;
    u8g2.print(buf) ;
    // 時刻の表示
    u8g2.setCursor(30, 32) ;
    sprintf(buf,"%02d:%02d:%02d",nowTime.hour,nowTime.minute,nowTime.second) ;
    u8g2.print(buf) ;
  } while ( u8g2.nextPage() ) ;

  delay(500) ;
}
--------------------------------------------------------------------------------

アラーム機能とタイマー機能を行ってみる

上のスケッチにアラーム機能とタイマー機能を追加したスケッチとなります。
起動後、「2021/01/23(土曜日)15:24:00」の時刻からスタートします。

アラームの設定は、毎時間25分に発生するので、まずは1分後に"アラーム発生"が500ms期間表示され、
次の発生は1時間後の25分後になります、これを繰り返します。(アラームを無効にしていないのでぇ)

タイマーの設定は、3分置きにカウントダウンを繰り返します。(タイマーを無効にしていないのでぇ)
表示は"カウントダウン"と500ms期間表示されます。
--------------------------------------------------------------------------------
#include <U8g2lib.h>
#include <PCF8563.h>
#include <skPCF8563.h>

char WeekDayData[7][4] = {"日","月","火","水","木","金","土"} ;

// RTC(PCF8563)を使用する為のコンストラクタ
PCF8563 rtc ;
skPCF8563 skRTC ;
// SSD1306をグラフィックディスプレイとして使用する為のコンストラクタ
// メモリバッファ(RAM)は1ページ分の128b分確保
// ローテーション(画面回転)なし、SCL=5番ピン SDA=4番ピン
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0,5,4) ;

void setup() {
  u8g2.begin() ;    // OLEDの初期化
  u8g2.enableUTF8Print() ; // Arduino print()関数のUTF8サポートを有効にする
  u8g2.setFont(u8g2_font_b16_t_japanese2) ; // 日本語フォント高さ16bit

  rtc.init() ;      // RTCの初期化
  // 2021年1月23日 土曜日 15時24分00秒で初期設定
  rtc.stopClock() ; // RTCのカウントを止める
  rtc.setYear(21) ; // 年の設定(00-99)
  rtc.setMonth(1) ; // 月の設定(01-12)
  rtc.setDay(23) ;  // 日の設定(01-31)
  skRTC.SetWeekdays(6) ;  // 土曜日の設定(0-6)
  rtc.setHour(15) ; // 時の設定(00-23)
  rtc.setMinut(24) ;// 分の設定(00-59)
  rtc.setSecond(0) ;// 秒の設定(00-59)
  rtc.startClock() ;// RTCのカウントを開始する
  // アラームの時間を設定し有効にする(毎25分に発生)
  skRTC.SetAlarmTime(25, 80, 80, 80) ;
  // タイマーのカウントダウンを開始する(3分間毎にカウントダウンが発生)
  skRTC.TimerCountdownStart(3,PCF8563_TimerF_1_60Hz) ;
}
void loop() {
  uint8_t almflg, timerflg ;
  char buf[12] ;
 
  // アラーム発生をチェック
  almflg = skRTC.AlarmMatch() ;
  // タイマーのカウントダウンをチェック
  timerflg = skRTC.TimerCountdownCheck() ;
  // 現在の時刻を得る
  Time nowTime = rtc.getTime() ;

  u8g2.firstPage() ;
  do {
    // タイマーのカウントダウンが発生したかチェックする
    if (timerflg == 1) {
      skRTC.TimerClearFlag() ; // アラームフラグをクリア  
      u8g2.setCursor(10, 63) ;
      u8g2.print("カウントダウン") ;      
    }
    // アラームが発生したかチェックする
    if (almflg == 1) {
      skRTC.AlarmClearFlag() ; // アラームフラグをクリア  
      u8g2.setCursor(20, 63) ;
      u8g2.print("アラーム発生") ;
    }
    // 日付の表示
    u8g2.setCursor(7, 16) ;
    sprintf(buf,"%d/%02d/%02d",nowTime.year+2000,nowTime.month,nowTime.day) ;
    u8g2.print(buf) ;
    // 曜日の表示
    u8g2.setCursor(87, 16) ;
    sprintf(buf,"(%s)",WeekDayData[nowTime.weekday]) ;
    u8g2.print(buf) ;
    // 時刻の表示
    u8g2.setCursor(30, 32) ;
    sprintf(buf,"%02d:%02d:%02d",nowTime.hour,nowTime.minute,nowTime.second) ;
    u8g2.print(buf) ;
  } while ( u8g2.nextPage() ) ;

  delay(500) ;
}
--------------------------------------------------------------------------------
skPCF8563ライブラリの使い方
 [PCF8563]ライブラリーと一緒に使う様に設計されています。
 #include <PCF8563.h>
 #include <skPCF8563.h>

 PCF8563 rtc ;
 skPCF8563 skRTC ;

 void setup() {
   rtc.init() ;
  
 }
 こんな感じになります。
 void skRTC.SetWeekdays(uint8_t wdays)
  曜日の設定を行う処理です。
  初期時刻の設定を行う時に使用します、"PCF8563"ライブラリに無かったので作成しました。
  wdays : 曜日の値を指定します(0:日曜〜6:土曜)

 void skRTC.SetAlarmWeekday(uint8_t wday)
  アラーム発生時の"曜日"を設定をする処理です。
  曜日の設定は利用しない時は"80"と指定します。
  wday : アラームを発生させる曜日の値を指定します(0:日曜〜6:土曜 or 80)

 void skRTC.SetAlarmDay(uint8_t day)
  アラーム発生時の"日"を設定をする処理です。
  日の設定は利用しない時は"80"と指定します。
  day : アラームを発生させる日の値を指定します(01-31 or 80)

 void skRTC.SetAlarmHour(uint8_t hour)
  アラーム発生時の"時"を設定をする処理です。
  時の設定は利用しない時は"80"と指定します。
  hour : アラームを発生させる時の値を指定します(00-23 or 80)

 void skRTC.SetAlarmMinute(uint8_t minute)
  アラーム発生時の"分"を設定をする処理です。
  分の設定は利用しない時は"80"と指定します。
  minute : アラームを発生させる分の値を指定します(00-59 or 80)

 void skRTC.SetAlarmTime(uint8_t minute,uint8_t hour,uint8_t day,uint8_t wday)
  アラーム発生の時刻を設定し、アラームを有効にする処理です。
  各時刻の設定は利用しない時は"80"と指定します。
  minute : アラームを発生させる"分"の値を指定します(00-59 or 80)
  hour  : アラームを発生させる"時"の値を指定します(00-23 or 80)
  day  : アラームを発生させる"日"の値を指定します(01-31 or 80)
  wday : アラームを発生させる"曜日"の値を指定します(0:日曜〜6:土曜 or 80)
  例)// 毎30分(1時間毎に1回発生)に起動させるなら
    skRTC.SetAlarmTime(30, 80, 80, 80) ;
    // 毎12時00分(1日毎に1回発生)に起動させるなら
    skRTC.SetAlarmTime(00, 12, 80, 80) ;
    // 毎月一日の12時00分(一ヶ月毎に1回発生)に起動させるなら
    skRTC.SetAlarmTime(00, 12, 1, 80) ;
    // 毎週日曜日の12時00分(毎週に1回発生)に起動させるなら
    skRTC.SetAlarmTime(00, 12, 80, 0) ;
 uint8_t skRTC.AlarmMatch()
  アラームの発生状況をポーリングする処理です。
  この関数はloop()内に入れて常にループさせます。
  RETURN : 0=アラームの発生は無し 1=アラームの発生は有り

 void skRTC.AlarmEnable()
  アラーム発生を有効にする処理です。
  "SetAlarmTime()"を使わない場合はこの関数をコールします。

 void skRTC.AlarmDisable()
  アラーム発生を無効にする処理です。

 void skRTC.AlarmClearFlag()
  アラームフラグをクリアする処理です。
  アラーム発生後はフラグはセットされたままです、この関数でリセットする必要が有ります。

 void skRTC.TimerCountdownStart(uint8_t timer,uint8_t frequency)
  タイマーのカウントダウンを開始する処理です。
  timer   : カウントダウンの値を指定します(0-255)
  frequency : タイマーソースクロック周波数を指定します(カウントダウンの速度が変わります)
        PCF8563_TimerF_1_60Hz = 1分間に1回カウントダウン
        PCF8563_TimerF_1Hz  = 1秒間に1回カウントダウン
        PCF8563_TimerF_64Hz  = 15.6ms毎?
        PCF8563_TimerF_4096Hz = 244us毎?

 uint8_t skRTC.TimerCountdownCheck()
  タイマーのカウントダウンをポーリングする処理です。
  この関数はloop()内に入れて常にループさせます。
  RETURN : 0=カウントダウンしていない 1=カウントダウンした

 void skRTC.TimerDisable()
  タイマーのカウントダウンを無効にする処理です。

 void skRTC.TimerClearFlag()
  タイマーフラグをクリアする処理です。
  カウントダウン後はフラグはセットされたままです、この関数でリセットする必要が有ります。

 void skRTC.write(uint8_t address, uint8_t data)
  PCF8563のレジスタに1バイトのデータを書き込む処理です。
  address : 書き込みを行うレジスタのアドレスを指定します
  data  : レジスタに書き込みたいデータのバイト

 void skRTC.read(uint8_t address)
  PCF8563のレジスタから1バイトのデータを読み取る処理です。
  address : 読み込みを行うレジスタのアドレスを指定します
  RETURN : レジスタから読み取られたデータのバイト

RTCの初期時間設定や時間合わせ用スケッチについて

機能拡張ボードに搭載のOLEDと押しボタンを使って時間設定を行うスケッチの紹介です。
スケッチ(XIAOrtcset.ino)はダウンロードした"skPCF8563"ライブラリに付属しています。

@ 起動させるとRTCの現在時間をOLEDに表示して"年"の位置で数値が点滅しています。

A ボタンを押すとその場所の数値がアップします、ボタンを押す毎に0−9の範囲で変更されます。
  (ボタンは500ms以内の短い押し方で行います)

B 数値を選択したら今度は500mms以上のボタン長押しを行います。
  するとぉ、次の数値へ移動します。数値選択が必要ないならそのまま長押しを行います。

C A-Bの操作を最後の曜日設定まで終わらせます。

D 曜日設定から次に進めると、[OK][NO]が表示されますのでボタン長押しで[OK]or[NO]を決めます。

E 決定したら短い押し方で次に進みます。
  [NO]なら再度@から再入力を行います。
  [OK]ならRTCに書き込まれます、その後はRTCの現在時刻を表示します、
  表示中に押しボタンを押せば再度@から再入力が行えます。

押しボタンが1個なので操作が少々はんざつですがぁ...まあ、我慢してちょうだい。

《SD》

機能拡張ボードに搭載のmicroSDカードからXBMデータを読み込みOLED(SSD1306)に描画します。
下記のサンプル例では、読み込む前にサンプルXBMデータをSDに書き込んでそれを読み出しています。
XBMデータの作成方法は前ページのここを参考にしましょう。
尚、SPI接続ですのでCSピンは2番(D2)ピンが接続されている様です。

@ MicroSDをセットします。

A Seeeduino XIAOを機能拡張ボードに取り付けてから、PCとType-Cケーブルを接続します。

B Arduino IDE開き、シリアルモニターも開いて置いた方が良いかもね。
  下記スケッチをコピーしてコンパイル & XIAOへの書き込みを行います。
--------------------------------------------------------------------------------
#include <U8g2lib.h>
#include <SD.h>

#define bitmap_width  97 // 画像ビットマップの幅幅ビット数(13バイト)
#define bitmap_height 51 // 画像ビットマップの高さビット数(51バイト)

static const unsigned char bitmap_data_bits[] U8X8_PROGMEM = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x3c, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00,
   0x00, 0x00, 0x3c, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe,
   0x03, 0x00, 0x00, 0x3c, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0xff, 0x03, 0x00, 0x00, 0x3c, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x80, 0xcf, 0x07, 0x00, 0x00, 0x3c, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x80, 0x83, 0x07, 0x00, 0x00, 0x3c, 0x80, 0x07, 0xf8, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x81, 0x07, 0x00, 0x00, 0x3c, 0x80, 0x07, 0xfc, 0x03,
   0x1c, 0x00, 0x3e, 0x1c, 0xc0, 0x03, 0x00, 0x00, 0x3c, 0x80, 0x07, 0xff,
   0x07, 0x7f, 0x80, 0xff, 0x3f, 0xe0, 0x01, 0x00, 0x00, 0x3c, 0x80, 0x07,
   0xff, 0x8f, 0xff, 0xc1, 0xff, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x3c, 0x80,
   0x87, 0xff, 0xdf, 0xff, 0xc1, 0xc3, 0x07, 0x7c, 0x00, 0x00, 0x00, 0x3c,
   0x80, 0x87, 0x0f, 0xfe, 0xff, 0xe3, 0x81, 0x03, 0x1e, 0x00, 0x00, 0x00,
   0x3c, 0x80, 0xc7, 0x07, 0xfc, 0xe3, 0xe3, 0x81, 0x07, 0x0f, 0x00, 0x00,
   0x00, 0x3c, 0x80, 0xc7, 0x07, 0xf8, 0xc1, 0xe7, 0x81, 0x87, 0xff, 0x07,
   0x00, 0x00, 0x3c, 0x80, 0xc7, 0x03, 0xf0, 0x80, 0xe7, 0xc3, 0x87, 0xff,
   0x07, 0x00, 0x00, 0x3c, 0x80, 0xc7, 0x03, 0x70, 0x80, 0xc7, 0xe7, 0x83,
   0xff, 0x07, 0x00, 0x00, 0x3c, 0x80, 0xc7, 0x03, 0x78, 0x80, 0xc7, 0xff,
   0x03, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x80, 0xc7, 0x03, 0xf8, 0xc0, 0x87,
   0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x80, 0xc7, 0x07, 0xfc, 0xc1,
   0xc7, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0xc0, 0x87, 0x0f, 0xfe,
   0xff, 0xe3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xc0, 0x83, 0xff,
   0xdf, 0xff, 0xe3, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf1, 0x03,
   0xff, 0x8f, 0xff, 0xe1, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff,
   0x01, 0xfe, 0x0f, 0xff, 0xc0, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xe0,
   0xff, 0x00, 0xfc, 0x03, 0x7c, 0xc0, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00,
   0x80, 0x3f, 0x00, 0xf8, 0x01, 0x00, 0xe0, 0x01, 0x1e, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x1e, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x1e, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xf0, 0xc7, 0x0f,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xe0, 0xff,
   0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xc0,
   0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff,
   0x01, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff,
   0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x24, 0x20, 0x00,
   0x00, 0x08, 0x46, 0x02, 0x00, 0x80, 0xc0, 0x40, 0x00, 0x0c, 0x6e, 0x6a,
   0xc0, 0xa4, 0x48, 0x04, 0xaa, 0xac, 0x8c, 0xaa, 0xac, 0x00, 0x6a, 0xa4,
   0xaa, 0x20, 0xea, 0xa4, 0x64, 0x66, 0xaa, 0x46, 0x4a, 0x8a, 0x00, 0x4c,
   0xa4, 0xaa, 0x20, 0xaa, 0xa2, 0x44, 0x2a, 0xaa, 0x28, 0xaa, 0x4c, 0x00,
   0xe8, 0xa8, 0x6c, 0xc4, 0xa4, 0x42, 0xee, 0x2a, 0xcc, 0x26, 0x6c, 0xe8,
   0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00 };

// SSD1306をグラフィックディスプレイとして使用する為のコンストラクタ
// メモリバッファ(RAM)は128X64バイト(画面フルサイズ分)
// ローテーション(画面回転)なし、SCL=5番ピン SDA=4番ピン
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0,5,4) ;

////////////////////////////////////////////////////////////////////////////////
// この関数では、XBMデータをSDカードに書き込みます。
void writeFile(const char *name, uint8_t w, uint8_t h, const uint8_t *bitmap) {
  uint8_t x;
  uint8_t y;

  File myFile = SD.open(name, FILE_WRITE);
  if (myFile) {
    myFile.write(w);      // 最初のバイトはビットマップの幅です
    myFile.write(h);      // 2番目のバイトはビットマップの高さです
    w = (w+7)/8;
    // 次のループでXBMデータを書き込みます
    for( y = 0; y < h; y++ ) {
      for( x = 0; x < w; x++ ) {
        myFile.write(u8x8_pgm_read(bitmap));
        bitmap++;
      }    
    }
    myFile.close();
    Serial.println("write done.");
  } 
  else {
    Serial.println("write error.");
  }
}
////////////////////////////////////////////////////////////////////////////////
// この関数では、指定された位置x、yにビットマップを描画します。
// ビットマップは、SDカードのファイル「ファイル名」から読み込みながら描画されます。
void drawFile(u8g2_int_t x, u8g2_int_t y, const char *filename) {
  uint8_t w;
  uint8_t h;
  uint8_t b;
  uint8_t mask;
  uint8_t len;
  u8g2_int_t xpos;

  File myFile = SD.open(filename);
  if (myFile) {
    // ビットマップの寸法を読み取る
    w = myFile.read();        // ビットマップの幅
    h = myFile.read();        // ビットマップの高さ
    // ビットマップのすべての行を処理する
    while( h > 0 ) {
      xpos = x;               // x位置の初期描画座標を設定
      len  = w;               // lenには水平方向のバイト数が含まれます
      mask = 1;
      b = myFile.read();      // 最初の8ピクセルを"b"にロードします
      while(len > 0) {        // 1本の線(1行)の全てのピクセルを描画します
        if ( b & mask ) {     // 1ピクセルを確認してください
          u8g2.setDrawColor(1);
          u8g2.drawPixel(xpos,y);
        } else {
          u8g2.setDrawColor(0);
          u8g2.drawPixel(xpos,y);
        }
        xpos++;               // ピクセルの次のx位置を計算します
        mask <<= 1;           // マスクを更新する
        if ( mask == 0 ) {    // 1バイト終了したかチェックします
          mask = 1;           // マスクを元に戻し ...
          b = myFile.read();  // ... ファイルから次の8ピクセル値をロードします
        }
        len--;                // 水平方向の幅を小さくします(残りのピクセル)
      }
      y++;                    // 次の行に移動
      h--;                    // 残りの行数を減らす
    }
    myFile.close();           // 全て完了したので、ファイルを閉じます
  }
  u8g2.setDrawColor(1);       // 色を復元する
}
////////////////////////////////////////////////////////////////////////////////
// メインの処理
void setup() {
  // シリアル通信を開く
  Serial.begin(9600);
  while (!Serial) {
    ; // シリアルポートが接続するのを待ちます(ネイティブUSBポートにのみ必要)
  }

  if (!SD.begin(2)) {         // CSは2番ピン
    Serial.println("initialization failed!");
    while (1);
  }
  Serial.println("initialization done.");

  // SDカードにサンプルデータを書き込みます
  writeFile("u8g2.bin", bitmap_width, bitmap_height, bitmap_data_bits);

  u8g2.begin();

  u8g2.clearDisplay();
  u8g2.setFont(u8g2_font_helvR10_tr);
  u8g2.firstPage();
  do {
    u8g2.setCursor(0, 12);
    u8g2.print(F("Read from SD:"));
    // SDカードからXBMデータを読み込みディスプレイに描画します
    drawFile(0, 20, "u8g2.bin");
  } while ( u8g2.nextPage() );
}
void loop() {
}
--------------------------------------------------------------------------------

ファイルのタイムスタンプ(更新時刻等)を設定したい!

SDカードにファイルを書き込む際の更新時刻等は"2000/01/01 01:00"と固定されています、
せっかくRTCを使うなら現在時刻を書き込みたいよね。
機能拡張ボードは、バックアップバッテリ出来るので1回だけ時刻を設定しておけばいいよね。
下記のサンプルはSDカードにファイルを書き込んでいるだけですがタイムスタンプの方法
スケッチしています。
--------------------------------------------------------------------------------
#include <SD.h>
#include <PCF8563.h>

// RTC(PCF8563)を使用する為のコンストラクタ
PCF8563 rtc ;

////////////////////////////////////////////////////////////////////////////////
// RTCの現在の日付と時刻を返すコールバック関数
void dateTime(uint16_t *date, uint16_t *time) {
     Time nowTime = rtc.getTime() ; // PCF8563から現在の時刻を得る
     *date = FAT_DATE(nowTime.year+2000, nowTime.month, nowTime.day);
     *time = FAT_TIME(nowTime.hour, nowTime.minute, nowTime.second);
}

////////////////////////////////////////////////////////////////////////////////
// メインの処理
void setup() {
     File fds  ;

     // シリアル通信の初期化
     Serial.begin(9600) ;
     while (!Serial) {
         ; // シリアルポートが接続するのを待ちます(ネイティブUSBポートにのみ必要)
     }
     Serial.println("SD test start") ;

     rtc.init() ;      // RTCの初期化
     // RTCに現在時刻を設定する
     // バックアップバッテリ搭載して1回実行させれば良い
     // 2021年1月23日 土曜日 15時24分00秒で初期設定
     rtc.stopClock() ; // RTCのカウントを止める
     rtc.setYear(21) ; // 年の設定(00-99)
     rtc.setMonth(1) ; // 月の設定(01-12)
     rtc.setDay(23) ;  // 日の設定(01-31)
     // skRTC.SetWeekdays(6) ;  // 土曜日の設定(0-6)
     rtc.setHour(15) ; // 時の設定(00-23)
     rtc.setMinut(24) ;// 分の設定(00-59)
     rtc.setSecond(0) ;// 秒の設定(00-59)
     rtc.startClock() ;// RTCのカウントを開始する

     // SDカードの初期化処理(フォーマットではないよ)
     if (!SD.begin(2)) {         // CSは2番ピン
         // カードの初期化に失敗したか、またはSDが入っていない
         Serial.println("Card failed, or not present") ;
         while(1) ;
     }
     // SD用コールバック関数の定義(現在の日付と時刻を返す関数)
     SdFile::dateTimeCallback( &dateTime ) ;

     Serial.println("Complete initialization !") ;// 初期化完了

     // ファイルの書込みオープン
     // test3.txtファイルが無い場合は作成されます、あればファイルの最後に追加されます。
     fds = SD.open("test3.txt",FILE_WRITE) ;
     if (fds) {
          // 文字列を書込む\r\nは改行コード文字
          fds.write("1234567890\r\n") ;
          Serial.write("1234567890\r\n") ;
          fds.write("abcdefg\r\n") ;
          Serial.write("abcdefg\r\n") ;
          // ファイルのクローズ
          fds.close() ;
     } else {
          // ファイルのオープンエラー
          Serial.println("error opening") ;
     }
}
void loop() {
}
--------------------------------------------------------------------------------




【きむ茶工房ガレージハウス】
Copyright (C) 2006-2021 Shigehiro Kimura All Rights Reserved.