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モジュール