Vol.898 12.Jul.2024

VBA_テキストファイルを読み込む PICミニBB(8)_RTCモジュール

E Excel_VBAのチップス  〜VBAでテキストファイルを読み込む

by fjk

 ExcelでCSVやtextファイルを読み込むには、開いたexcelシートにファイルをドロップするだけで読み込める。
 一方、VBAでテキストファイルを読み込む方法にはイロイロあるが、以下の2つの方法が代表的。

@ Workbooks.OpenText (必ず新規Workbookが作られ、そこにデータが格納される)
A QueryTables.Add (既存シートの任意の位置から、データを上書き・挿入が可能)

【OpenText関数】(詳細はここなどを参照)

object.OpenText ( filename [, origin, startrow, datatype, textqualifier, consecutivedelimiter, tab, semicolon, comma, space, other, otherchar, fieldinfo, ・・・] )
<主な引数>
object ・・・  対象となる Workbooks コレクションを指定。
filename ・・・ 開きたいテキストファイルの名前を文字列式で指定。(パスを省略した場合は、カレントフォルダ内のファイルが対象)
origin ・・・  (省略可)1:Macintosh、2:Microsoft Windows、3:MS-DOS。省略すると自動で選択
startrow ・・・ (省略可) 読み込みを開始する行を指定。既定値は、1 。
datatype・・・ (省略可)ファイルデータ形式。(xlDelimited:区切り文字、xlFixedWidth:固定長)
textqualifier ・・・(省略可)データの引用符を指定。(ダブル・シングルクォテーション/なし)
consecutivedelimiter ・・・(省略可)連続した区切り文字を1文字として扱うときは True。
tab ・・・   (省略可)区切り文字にタブを使うときは True を指定。既定値は False。
semicolon ・・・(省略可)区切り文字にセミコロン(;)を使うときは True。既定値は False 。
comma ・・・  (省略可)区切り文字にカンマ(,)を使うときは Trueを指定。既定値は False
space ・・・  (省略可)区切り文字にスペースを使うときは True を指定。既定値は False。
other ・・・  (省略可)区切り文字にothercharで指定した文字を使う時True。既定値 False 。
otherchar ・・・引数 other が True のとき、区切り文字を指定。先頭の文字のみ有効。
fieldinfo ・・・各列のデータ形式または固定長形式の区切り位置を配列(Array)で指定。

★OpenText関数によるテキストファイルの読み込みテスト
    Sub Sample_OpenText()
    	Dim strFilePath As String
    	strFilePath = "sampletest.txt"              ' ファイル名
       Workbooks.OpenText Filename:=strFilePath, _
                    DataType:=xlDelimited, _        ' 文字区切り
                    Comma:=True                     ' コンマ有効
       Dim ws As Worksheet
       Set ws = Workbooks.Item(Workbooks.Count).Sheets(1) 
       ws.Move After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count)
       Dim ShtNam As String
       ShtNam = ActiveSheet.Name                    ' 新シート名を取得 
    	MsgBox ShtNam
    End Sub

sampletest.txt

OpenText使用例

 標準モジュールを追加後、上記コードを記述。マクロを実行すると読み込んだテキストデータは、追加された新しいシートに格納される。
 

【QueryTables.Add関数】(詳細はここなどを参照)

object.QueryTables.Add(Connection, Destination, [Options])
<主な引数>
Connection・・・   読み込むファイル形式・ファイルパスを設定
Destination・・・   値の貼り付け開始位置を指定
Options・・・
  TextFilePlatform     文字コードの指定(932:Shift_JIS、65001:UTF-8)
  TextFileParseType     区切り文字の方式(xlDelimited:文字、xlFixedWidth:固定幅)
  TextFileCommaDelimiter  カンマ区切りに設定。True/Falseで指定。
  TextFileTabDelimiter   タブ区切りに設定。True/Falseで指定。
  TextFileSemicolonDelimiter  セミコロン区切りに設定。True/Falseで指定。
  TextFileSpaceDelimiter  スペース区切りに設定。True/Falseで指定。
  TextFileOtherDelimiter  区切り文字を指定できるに設定。True/Falseで指定。
  TextFileStartRow     読み込み開始行の指定に設定。True/Falseで指定。
  RefreshStyle       セルを上書きするか追加するかを設定する
       =xlOverwriteCells :   セルは追加せず上書きする
       =xlInsertDeleteCells : レコードに合わせ行を追加削除する
       =xlInsertEntireRows : セル又は行を削除せず、行を挿入する
  FieldNames        フィールド名を列名に設定。True/Falseで指定。
  Refresh          実行するとデータを表示する[メソッド]
  Delete          み込むファイルとの接続を解除[メソッド]

★ QueryTables.Add関数によるテキストファイルの読込テスト
    Sub Sample_Query()
    	Dim ws As Worksheet
    	Set ws = ActiveSheet                    ' データを取り込むシート
    	Dim strFilePath As String
    	strFilePath = "sampletest.txt"          ' ファイルの(パスと)ファイル名
    	Dim qt As QueryTable
    	Set qt = ws.QueryTables.Add(Connection:="TEXT;" & strFilePath, _
                             Destination:=ws.Range("B2")) 	' CSV を開く
    	With qt
            .TextFilePlatform = 932             ' 文字コードを指定
            .TextFileParseType = xlDelimited    ' 区切り文字の形式
            .TextFileCommaDelimiter = True      ' カンマ区切り
            .RefreshStyle = xlOverwriteCells    ' セルに書き込む方式
            .Refresh                            ' データを表示
            .Delete                             ' CSVファイルとの接続を解除
    	End With
    	MsgBox "完了"
    End Sub

sampletest.txt

queryTableAdd使用例

 OpenTextと同様に、上記のコードを記述後、マクロを実行すると、指定したシート(sheet1)の指定位置(B2)からテキストデータが挿入される。OpenTextと同様の結果が得られるが、新しいシートは追加されない。

---------------------------------------------------
※「名前付き引数」とは
 引数の位置ではなく名前で引数データを指定する方法。
   VBAでは、  「引数名」 := 「引数データ」


P PICミニBBシリーズ(8)  〜I2C接続RTC(DS1307)モジュール

by fjk

 abc897で、簡易温度データーロガーが実現できたが、できれば何時に測定したのかも記録したい。そこで、時計機能(RTC)を追加することにした。利用したRTCはDS1307で、@I2C接続(5V動作)、Aバックアップ電源専用ピンあり、B外付けの水晶発振子が必要、という特徴がある。
 ここでは、DS1307に水晶振動子とバックアップ電源(ボタン電池)が一体となった秋月電子の「I2C接続RTCモジュール」(#115488、560円)を利用した。このモジュールは、ボタン電池(CR1220、3V)を付けておくと、電源が切れても時計機能が動作を継続している。ブレッドボードで使うには、付属のピンヘッダーを取り付けて、ボードに差し込むだけである。
 DS1307のI2Cアドレスは0x68で、8つのアドレスがRTC用で、0x08から0x3Fまでは汎用の不揮発性メモリとなっていて、電源を切っても消えない。注意すべきは、RTCのデータはBCD形式となっており、7セグLED等で直ぐに使えるが、パソコンで使うにはチョット不便(BDC-Bin変換関数を準備)。時計は12時間と24時間を選択できるが、今回は24時間に固定した(詳細は取説を参照)。また、(今回は使用していないが)方形波を出力できるピンもある。なお、曜日と数値(1-7)との対応はユーザで行うこと(例えば1="Sun")。


DS1307のアドレス(レジスタ)マップ

全回路図

RTC動作テスト中
(余分な配線もあるが・・)

DS1307のピン配置
(RTCモジュールは電池・
振動子が配線済み)

RTCボード配線

 LCDに日付と時刻を表示。時刻の設定は"Thh:mm:ss"、日付の設定は"Dyy/MM/dd/w"(年yyは下2桁)。
 電池を付け、一端電源を切って、再投入してみると、RTCデータが正常に動作していることを確認。
 ※無効なデータでも入力できるので注意!(例えば13月33日26時70分など)
 
 ・ハードはabc897のPIC-BAS14とLCDユニット(i2cLCD_ST7032i.c/h(zip)が必要)にRTCボードを追加した。
 ・USB接続パソコンと通信用にEUSARTを用いた。EUSARTの設定はabc897を参照。
 ・頻繁に表示データを更新すると表示がちらつき見づらいので、1秒単位で表示するようにした。

【RTCテストプログラム】 abc898-18325.c(zip)

/********************************(abc898-18325.c)***
 *  RTC (DS1307) テスト
 ****************************************************/
#include "mcc_generated_files/mcc.h"
#include "mcc_generated_files/examples/i2c1_master_example.h"
#include "string.h"
#include "i2cLCD_ST7032i.h"

#define DS1307  0x68    // RTCのI2Cアドレス

//=== グローバル変数 ============
uint8_t RBuf[BFSIZE];   // シリアル受信用
uint8_t SFlg;           // シリアル受信フラグ
char    SBuf[20];       // 文字列処理用バッファー
                //----RTC用変数(2進数で格納)
uint8_t DayTm[] = { 0,	// seconds
                    30,	// minutes
                    10,	// Hours(24h)
                    6,	// day[week]
                    12,	// date
                    7,	// month
                    24,	// year (下2桁)
                    0	// SQW/OUT
                 };

/****** 汎用関数 ***************************/
//---- BCD数を2進数に変換
uint8_t bcd2bin(uint8_t bcd){
    return  ((bcd >> 4)& 0x0F) * 10 + (bcd & 0x0F);
}

//---- 2進数をBCD数に変換
uint8_t bin2bcd(uint8_t bin){
    return  (uint8_t)((uint16_t)(bin/10) << 4)+(bin % 10);
}

//---- 文字列から数値を取り出し、1バイト配列に逆順で格納
//      buf:    取り出した数値を格納する1バイト配列
//      src:    数値を取り出す文字列
//      delim:  区切り文字を文字列にしたもの
//  (戻り値): 取得出来た数値の数
uint8_t get_NumRev(uint8_t *buf, char *src, char *delim){
    char    *ptr;
    uint8_t bt, n = 0;
    ptr = strtok(src, delim);
    while(ptr != NULL){
        bt = (uint8_t)atoi(ptr);	//無効データでも0を返す
        *buf-- = bt;
        n++;
        ptr = strtok(NULL, delim);
    }
    return n;
}

/****** RTC用関数 ******************************/
//--- RTCのadr番地にdatデータを書き込む
void RTC_write(uint8_t adr, uint8_t dat){
    uint8_t bf[3];
    bf[0] = adr;                     // Address = adr
    bf[1] = dat;                     // Set Data
    I2C1_WriteNBytes(DS1307, bf, 2); // Write Data
}

//--- RTCのadr番地のデータを読み込む
uint8_t RTC_read(uint8_t adr){
    uint8_t bf[3];
    bf[0] = adr;                     // Address = adr
    I2C1_WriteNBytes(DS1307, bf, 1); // Set Address
    I2C1_ReadNBytes(DS1307, bf, 1);  // Read Data
    return bf[0];
}

//--- RTCの全てのrtcデータを読み込む
void RTC_readAll(uint8_t *buf){
    uint8_t rtc[8], i;
    rtc[0] = 0;                       // Address = 0
    I2C1_WriteNBytes(DS1307, rtc, 1); // Set Address
    I2C1_ReadNBytes(DS1307, rtc, 8);  // Read Data
    for(i = 0; i < 8; i++){
        buf[i] = bcd2bin(rtc[i]);
    }
}

//--- RTCの全てにrtcデータを書き込む
void RTC_writeAll(uint8_t *buf){
    uint8_t rtc[9], i;
    rtc[0]=0;                         // Address = 0
    for(i=0; i < 8; i++){
        rtc[i+1] = bin2bcd(buf[i]);
    }
    I2C1_WriteNBytes(DS1307, rtc, 9);
}

//--- RTCの指定アドレスからnバイトのデータを書き込む
//      buf:    書き込むデータが格納された配列
//      adr:    書き込むRTCアドレス
//      n:      書き込むバイト数
void RTC_writePrt(uint8_t *buf, uint8_t adr, uint8_t n){
    uint8_t rtc[9], i;
    rtc[0] = adr;                      // Address = adr
    for(i = 0;i < n; i++){
        rtc[i+1] = bin2bcd(buf[adr+i]);
    }
    I2C1_WriteNBytes(DS1307, rtc, n+1);
}

/***** コマンド処理 ********************/
//---- R(read)コマンド
void cmd_r(){
    uint8_t i;
    RTC_readAll(DayTm);
    for(i = 1; i < 9; i++){
        printf("%d: %02d\n\r", i, DayTm[i]);
    }
}

//---- D(date)コマンド		"Dyy/MM/dd/w"
void cmd_d(){
    uint8_t n;
    n = get_NumRev(DayTm+6, RBuf+1, "/");
    if((n > 0)&&(n < 5)){
        RTC_writePrt(DayTm, 3, 4);
    }
}

//---- T(time)コマンド		"Thh:mm:ss"
void cmd_t(){
    uint8_t n;
    n = get_NumRev(DayTm+2, RBuf+1, ":");
    if((n > 0)&&(n < 4)){
        RTC_writePrt(DayTm, 0, 3);
    }
}

/****** Main application ****************************/
void main(void){
    uint8_t sec, cmd;   
    SYSTEM_Initialize();
    LCD_init();              // LCD初期化
    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();
    puts("ready|n\r");
//  RTC_writeAll(Dt);  			// for test
    sec = 0;
    while (1){
        if(SFlg){
            cmd = RBuf[0];
            switch(cmd){
                case 'R': cmd_r();  break;
                case 'T': cmd_t();  break;
                case 'D': cmd_d();  break;
                default:            break;
            }
            SFlg = 0;
        }
        RTC_readAll(Dt);
        if(sec != DayTm[0]){		//  表示は秒単位
            sprintf(SBuf, "20%02d/%02d/%02d(%d)",
                           DayTm[6],DayTm[5],DayTm[4],DayTm[3]);
            LCD_clr();  LCD_str(SBuf); 
          //  printf("%s ", SBuf);
            sprintf(SBuf, "%02d:%02d:%02d",
                           DayTm[2], DayTm[1], DayTm[0]);
            LCD_cursor(0,1); LCD_str(SBuf);
          // printf("%s\n\r", SBuf);
            sec = DayTm[0];
        }
    }
}


※ 本レポートの参考・利用は、あくまでも自己責任でお願いします。


VBA_テキストファイルを読み込む PICミニBB(8)_RTCモジュール