Vol.908 13.dec.2024

INDIRECT関数(Excel) 漢字ROMにI2C接続EEPROM(M24M02)

E Excelで別シートの参照セルを数式で指定 〜indirect関数

by fjk

 INDIRECT 関数を使うと、数式自体を変更しないで、数式内で使用しているセル参照を変更することができる。
 (注:参照するブックが開いていないと「エラー#REF」となる) 詳細は、microsoft参照。

【書式】
INDIRECT(参照文字列[,参照形式])
    参照文字列: 文字列は""で囲む、数値やセルはそのまま記述、文字列は"&"で繋ぐ
    参照形式:  TRUE:A1形式(省略時)、FALSE:R1C1形式
 
INDIRECT関数は、引数に指定した文字列のセル参照を返す関数です。
  • 参照文字列で適切なセル参照を指定していない場合はエラー値 #REF! が返されます。
  • 参照文字列で他のブックを参照している場合(外部参照)、対象のブックを開いておく必要があり、開いていない場合はエラー値 #REF! が返されます。
  • 参照文字列で行数の上限1,048,576または列数の上限16,384(XFD)の範囲外を参照している場合は、エラー値 #REF! が返されます。
  • INDIRECT関数を活用すると、セルの内容をそのまま移すだけでなく、セル参照を動的に変更することができます。
  • INDIRECT関数を使っていると、エラーが起きてしまうことがあります。その場合は、参照文字列の指定方法を確認する必要があります。
【別シートを参照する例】
=INDIRECT("'シート名!'"!&セルの場所,参照文字列)
 
セル上でシート名を参照するときは「'シート名!'」を使う。indirect関数内にシート名の参照を組み込むためには「" "」で文字列として認識させる。シート名は「’ ’」で囲われていることに注意。  (使用例
・セルの参照方法には@A1形式AR1C1形式、の2つがある。数式を使ってセルを参照したい場合にはAR1C1形式を使う。
 
indirect関数内での具体的な書き方は、下図のようになるが、青字のセルの参照方法を「FALSE」にすることでR1C1形式になる。
(TRUEでA1形式になる)
  
※参考:ADDRESS 関数を使うと、行番号と列番号を指定して、ワークシート内のセルのアドレスを取得できる。。address参照例


P PICミニBBシリーズ(18)  〜漢字ROMにI2C接続EEPROM(M24M02)

by fjk

 M24M02(@600程度、STマイクロエレクトロニクス)はI2C接続で2Mビット(256Kバイト)のメモリ容量があるEEPROMで、16x16ドットのJIS第2水準までの漢字フォントを収納できる。abc899では1Mビット(128kバイト)の24FC1025を用いたが、JIS第1水準漢字までしか収納できない(24FC1025を第1水準用と第2水準用にと、2個使えば格納可能だが・・、本プログラムを少し変更することで対応可能)。

【M24M02の使い方】 (詳細はSTマイクロエレクトロニクス参照)
M24M02はメモリが24FC1025の2倍ですが、8ピンのピン配置もほぼ同じで、ピン3をLかHにすることで、I2Cアドレスが50Hと54Hの2つのメモリを使うことができる。24FC1025ではピン3をHにする必要があるので、abc907の回路のまま使用するため、54Hとした。
I2Cの使い方も24FC1025とほぼ同じである(ステータスは異なる)。書込サイクルは最大5mSとなっている

デバイスの選択コード
【(東雲)フォントの書込マップ】 (東雲フォント付きフォント書込Excelファイル:BDFViewWriterA3ZZ.xlsm(zip)
JIS第1、第2水準漢字すべてのフォントデータをEEPROMに保存したので、ユーザーフォントは無くした。そして、EEPROM利用時にWE端子をHにすることでEEPROMへの書込を禁止し、フォントデータを保護できるようにした。
ユーザーが使えるエリアをなるべく多く残すように、フォントデータは詰めて保存している。必要であれば、EEPROMの空きエリアにユーザーが作成したフォントを書き込んでおくこともできる。また、フォントデータの保存に2個のEEPROMを使う場合にも対応できるよう、第1水準と第2水準文字をエリアを分けて保存した。
 
フォントデータ収納アドレスマップ  (FontRom2.xlsx(zip)
  
【テストコマンド】
シリアル通信コマンドは、abc902と同じだが、K、Lコマンドを追加した。
   ( []はオプション、{}はオプション省略時 )
 K[H][aaaa] 字形を最大16/8文字表示
H: Hを指定すると半角表示
aaaa:格納アドレス指定{次アドレス}
 L[code] 字形と16進データを表示
code:文字コードを指定{次文字}

M24M02を搭載しフォント書込中

Kコマンド実行例

Lコマンド実行例

 
【abc908-18325.cファイル】 abc908-18325.c + myProject.h(zip)、mpProject.hはabc907と同じ

/*****************************(abc908-18325.c)***
 *      OLEDライブラリーテスト
 *************************************************/
#include "mcc_generated_files/mcc.h"
#include "myProject.h"

#define VBA

char  RBuf[BFSIZE];
uint8_t SFlg;

/***********************************************
        Main application
 ***********************************************/
void main(void){
    uint8_t cmd;
    
    SYSTEM_Initialize();
    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();

    i2cOLED_init();
    puts("ready!\n"); 
    i2cOLED_Clr(0,7,0);
    i2cOLED_str("ready!");
    
    while (1)
    {
        if(SFlg){
            cmd = RBuf[0];
            #ifndef VBA
                puts("\r");
            #endif
            switch(cmd){
//                case 'C': i2cOLED_Clr(0,7,0); break;
                case 'W': i2cMem_cmd_W(RBuf);  break;
                case 'R': i2cMem_cmd_R(RBuf);  break;
                case 'Q': i2cMem_cmd_Q(RBuf);  break;
                case 'U': i2cMem_cmd_U(RBuf);  break;
                case 'S': i2cMem_cmd_W(RBuf);  break;
                case 'P': i2cMem_cmd_P(RBuf);  break;
                case 'K': i2cMem_cmd_K(RBuf);  break;
                case 'L': i2cMem_cmd_L(RBuf); break;
                                    // 有効なコマンドがなく
                default:  if(i2cMem_SqMode()){
                            i2cMem_cmd_WSQ(RBuf);
                          }         // 連続書込モード中なら
                          break;    // 書込シーケンスを実行
            } 
            SFlg = 0;
        }
        IO_RA4_Toggle();
        __delay_ms(500);
    }
}/
**===  End of File  ===*/

 
【i2c_SSD1306.hファイル】 i2c_SSD1306.c/h + Font_5x7.h (zip)

/*********************** (i2c_SSD1306.h) *******
 *      OLED library Headee                    *
 *      use for 0.96OLED  ,....                *
 ************************************************/

//--- OLEDで改行
uint8_t i2cOLED_LF();

//--- 描画エリアの設定 (for Hor/Ver Mode)--------------
/*    p0: ページ開始位置、  p1: ページ終了位置(-7) [バイト単位]
 *    x0: 列(X)開始位置、  x1: 列(X)終了位置(-127)  */
void i2cOLED_RangePX(uint8_t p0,uint8_t p1, uint8_t x0, uint8_t x1);

//--- 描画開始(カーソル)位置 (for Page Mode)---------
/*    pg: ページ位置[バイト単位]、  cX: 列(x)位置  */
void i2cOLED_posPX(uint8_t pg, uint8_t cX);

//--- ページ列消去(カーソルは行先頭に移動) ---------
/*    pg: 消去したいページ[バイト単位]、  dt: 消去データ */
void i2cOLED_LnClr(uint8_t pg, uint8_t dt);

//--- OLED画面消去 (行範囲指定付き)-----------------
/*    P0: 消去開始ページ、 p1: 消去終了ページ[バイト単位]
 *    dt: 消去データ         */ 
void i2cOLED_Clr(uint8_t p0, uint8_t p1, uint8_t dt);

//--- OLEDに5x7文字を1文字表示する ------------------------
/*      (事前にi2cOLED_posPXで位置指定しておくこと)
 *      ch: 表示文字(ASCII)  */
void i2cOLED_chr (char ch);

//--- OLEDに5x7文字列(プロポーショナル)を表示 ----
/*      (事前にi2cOLED_posPXで位置指定しておくこと)
 *      *str: 表示文字列(ASCII)  */
void i2cOLED_str(char *str);

//--- OLEDの初期化 
void i2cOLED_init(void);

//--- OLEDにパターンを表示 -----------
/*      pg: 表示開始ページ位置(0-7)、cx:表示開始列位置(0−127)
 *      pn: ページ数(1-8)、  xn:列数(1-64) pn*xnは64以内
 *      *ptn: 表示パターンデータのポインタ
 *  RTN: (uint8_t)次表示データの表示列(x)位置       */
uint8_t i2cOLED_Ptn(uint8_t pg,uint8_t cx,uint8_t pn, uint8_t xn, char *ptn);

//---- 半角(8x16)文字表示(次xpos位置を返す)-----
/*      xpos: 表示X位置、 pg:表示Y位置(ページ)
 *      chCode:半角文字コード           */
uint8_t i2cOLED_HchrX(uint8_t xpos, uint8_t pg, uint8_t chCode);

//-------- バイト単位で半角文字列表示(次xpos位置を返す)-----
/*      xpos: 表示X位置、 pg:表示Y位置(ページ)
 *      *str:表示文字列           */
uint8_t i2cOLED_HstrX(uint8_t xpos, uint8_t pg, char *str);

//---- バイト単位で全角(漢字)文字表示
/*      xpos: 表示X位置、 pg:表示Y位置(ページ)
 *      kCd: 全角文字コード           */
uint8_t i2cOLED_ZchrX(uint8_t xpos, uint8_t pg, uint16_t kCd,uint8_t ed);

//---- バイト単位位置で全角(漢字)文字列表示
/*      xpos: 表示X位置、 pg:表示Y位置(ページ)
 *      kCd: 全角文字コード、 ed:エンディアン */
uint8_t i2cOLED_ZstrX(uint8_t xpos, uint8_t pg, char *str,uint8_t ed);

//---- 半角フォントアドレス計算
/*     hCode:半角文字コード
 *  RTN:フォント格納アドレス  */
uint32_t HfntAdr(uint8_t hCode);

//---- 全角フォントアドレス計算
/*     kCode:全角文字コード
 *  RTN:フォント格納アドレス  */
uint32_t KfntAdr(uint16_t kCode);
【i2c_SSD1306.cファイル】

/************************(i2c_SSD1306.c) ****
 *      OLED library                        *
 *      use for 0.96OLED  ,....             *
 ********************************************/
#include "myProject.h"
#include "Font_5x7.h"

#define I2COLED_ADR     0x3C        // 0x3Dもある
#define OLED_BF_SIZE    80

//---  フォントデータアドレス)
#define F_H_ASC     0x00200         // 半角ASCII
#define F_H_KANA    0x00800         // 半角カナ
#define F_SYM1      0x01000         // 記号1(2121-217E)
#define F_SYM2      0x01BC0         // 記号2(2221-222E)
#define F_SYM3      0x01D80         // 記号3(223A-2241)
#define F_SYM4      0x01E80         // 記号4(224A-2250)
#define F_SYM5      0x01F60         // 記号5(225C-226A)
#define F_SYM6      0x02140         // 記号6(2272-2279)
#define F_SYM7      0x02240         // 記号7(227E)
#define F_NUM       0x02260         // 数字(2330-2339)
#define F_ASC       0x023A0         // ASCii[大](2341-235A)
#define F_ASC2      0x026E0         // ASCii[小](2361-237A)
#define F_HIRA      0x02A20         // ひらかな(2421-2473)
#define F_KATA      0x03480         // カタカナ(2521-2576)
#define F_GRK1      0x1B2A0         // ギリシャ[大](2621-2638)
#define F_GRK2      0x1B5A0         // ギリシャ[小](2641-2668)
#define F_CRL1      0x1B8A0         // キリル[大](2721-2741)
#define F_CRL2      0x1BCC0         // キリル[小](2751-2771)
#define F_RULE      0x1C0E0         // 罫線 (2821-2840)
#define F_KAN1      0x04000         // 漢字1水準(3021-4F53)
#define F_KAN2      0x20000         // 漢字2水準(5021-7426)

#define NO_FONT     0x11000         // フォントが無い場合

//--- 静的変数
static uint8_t CrtLn = 0;           // 現在行(ページ)位置
static uint8_t gBf[OLED_BF_SIZE];   // グラフィック表示用

//---- OLEDで改行
uint8_t i2cOLED_LF(){
    CrtLn += 1;
    if(CrtLn > 7) CrtLn = 0;
    return CrtLn;
}

//----- Set Draw Area (for Hor/Ver Mode)-------
void i2cOLED_RangePX(uint8_t p0,uint8_t p1, uint8_t x0, uint8_t x1){
    gBf[0] = 0x00;                          // 連続コントロール指示	
    gBf[1] = 0x20; gBf[2] = 0;              // 水平モード指定
    gBf[3] = 0x21; gBf[4] = x0; gBf[5] = x1; // 水平範囲(x0-x1)) 
    gBf[6] = 0x22; gBf[7] = p0; gBf[8] = p1; // ページ範囲(p0-p1)
    I2C1_WriteNBytes(I2COLED_ADR, gBf,9);
}

//----- Cursor X,Y (for Page Mode)--------------
void i2cOLED_posPX(uint8_t pg, uint8_t cX){
    i2cOLED_RangePX(0,7,0,127);             // Rangeを全画面に
    gBf[0] = 0x00;                          // 連続コントロール指示
    gBf[1] = 0x20; gBf[2] = 0x02;           // Pageモード指定
    gBf[3] = 0xB0 + (pg & 0x07);            // page指定 (0xB0-B7)
    gBf[4] = cX & 0x0F;                     // 行位置(ページ)指定
    gBf[5] = 0x10 + ((cX >> 4) & 0x07);
    I2C1_WriteNBytes(I2COLED_ADR, gBf,6);
    CrtLn = pg;
}

//-------- clear LINE --------------------------
void i2cOLED_LnClr(uint8_t pg, uint8_t dt){
    uint8_t i;
    i2cOLED_posPX(pg,0);                    // ページモードで位置指定
    gBf[0]=0x40;                            // データコントロール指示
    for(i=1;i<=65;i++) gBf[i]=dt;
    I2C1_WriteNBytes(I2COLED_ADR, gBf,65);
    I2C1_WriteNBytes(I2COLED_ADR, gBf,65);
    CrtLn = pg;
}

//-------- clear OLED --------------------------
void i2cOLED_Clr(uint8_t p0, uint8_t p1,uint8_t dt){
	uint8_t i;
    for(i = p0; i <= p1; i++){
        i2cOLED_LnClr(i,dt);
    }
    i2cOLED_posPX(p0,0);
    CrtLn = p0;
}

//-------- OLEDに一文字表示する ------------------------
//     事前にi2cOLED_posPXで位置指定しておくこと
void i2cOLED_chr(char dat){// 配列アドレスを計算
    gBf[0] = 0x40;                          // データコントロール指示
    for(i=0; i<5; i++) {
        gBf[i+1] = Font[dat][i];
        if(gBf[i+1] == 0xFF) break;         // 幅狭文字なら抜ける
    }
    gBf[i+1]=0;                             // 1ドット空ける
    I2C1_WriteNBytes(I2COLED_ADR, gBf,i+2);
}

//-------- display strings -------------------------
void i2cOLED_str(char *str){
    while(*str)  i2cOLED_chr(*str++);      //pointer increment
}

//------ OLEDにパターンを表示
uint8_t i2cOLED_Ptn(uint8_t pg,uint8_t cx,uint8_t pn, uint8_t xn, char *ptn){
    uint8_t i; 
    i2cOLED_RangePX(pg,pg+pn-1,cx,cx+xn-1); // エリア指定
    gBf[0] = 0x40;                          // データコントロール指示
    for(i = 1; i <= pn*xn; i++){
        gBf[i] = *ptn++;
    }
    I2C1_WriteNBytes(I2COLED_ADR, gBf, pn*xn+1);
    return cx+xn;
}

//-------- OLED initialize  ------------------
void i2cOLED_init(void){
    __delay_ms(20);                     // 20ms wait
    uint8_t Cmds[]={
                0x00,                   // 連続コントロール指示
                0xA8,0x3F,              // 総ライン数 64行
                0xD3,0x00,              // 表示オフセット無し
                0x40,                   // 表示開始ライン=0(下位6ビット)
//                0xA0,                 // 左右反転 0xA0:→/0xA1:←
//                0xC0,                 // 上下反転 0xC0:↓/0xC8:↑
//                0xDA,0x02,            // 表示モード=Alternative
//                0x81,0x7F,            // コントラスト= 127(/0-255)
//                0xA4,                 // 描画の許可?
//                0xA6,                 // 白黒反転 A6:無し/A7:反転
//                0xD5,0x80,            // クロック分周比=128
//                0x20,0x00,            // アドレスモード=horizontal
                0x8D,0x14,              // チャージポンプ設定(必須)
                0xAF                    // 表示開始
                };
    I2C1_WriteNBytes(I2COLED_ADR, Cmds,9);  //送信バイト数
}

//---- 半角文字格納アドレス取得
uint32_t HfntAdr(uint8_t chCode){
    if((chCode >= 0x20) & (chCode < 0x7F)){
        return (chCode - 0x20) * 16 + F_H_ASC;
    }else if((chCode > 0xA0) & (chCode <= 0xDF)){
        return  (chCode - 0xA0) * 16 + F_H_KANA;        
    }
    return 0;
}

//---- 半角(8x16)文字表示(次xpos位置を返す)-----
uint8_t i2cOLED_HchrX(uint8_t xpos, uint8_t pg, uint8_t chCode){
    uint32_t adr;
    char  ptn[16];
    adr = HfntAdr(chCode);
    if(adr > 0){
        I2C_Mem_NRead(adr,(uint8_t *)ptn,16);
        xpos = i2cOLED_Ptn(pg,xpos,2,8,ptn);
    }
    return xpos;
}

//-------- バイト単位で半角文字列表示(次xpos位置を返す)-----
uint8_t i2cOLED_HstrX(uint8_t xpos, uint8_t pg, char *str){
    while(*str)  xpos = i2cOLED_HchrX(xpos, pg, *str++);
    return xpos;                                //次表示位置を返す
}

//---- シフトJISー>JIS変換
uint16_t SJis2Jis(uint16_t sCd){
    uint8_t hi,lo;
    hi = sCd >> 8;
    lo = (uint8_t)sCd;
    if(hi <= 0x9F){
        if(lo < 0x9F)   hi = hi * 2 - 0xE1;
        else            hi = hi * 2 - 0xE0;
    }else{
        if(lo < 0x9F)   hi = (hi - 0xB0) * 2 - 1;
        else            hi = (hi - 0xB0) * 2;
    }
    if(lo < 0x7F)       lo = lo - 0x1F;
    else{
        if(lo < 0x9F)   lo = lo - 0x20;
        else            lo = lo - 0x7E;
    }
    return (hi * 256 + lo);
}

//--- 全角(漢字)フォントアドレス取得)
uint32_t KfntAdr(uint16_t kCode){
    uint8_t msb, lsb;
    uint32_t lmsb, llsb;
    msb = kCode >> 8;
    lsb = (uint8_t)kCode & 0x000FF;
    lmsb = (uint32_t)msb;
    llsb = (uint32_t)lsb;
    if(msb>=0x21 & msb<0x30){
      if(msb==0x21){                        // 記号1
        if(lsb>=0x21 & lsb<=0x7E) return F_SYM1+(lsb-0x21)*32;
        else                        return NO_FONT;
      }else if(msb==0x22){                  // 記号2
        if(lsb>=0x21 & lsb<=0x2E) return F_SYM2+(lsb-0x21)*32;
        else                      return NO_FONT;
      }else if(msb==0x23){                  // ASCII
        if(lsb>=0x30 & lsb<=0x39) return F_NUM + (lsb-0x30)*32;
        if(lsb>=0x41 & lsb<=0x5A) return F_ASC + (lsb-0x41)*32;
        if(lsb>=0x61 & lsb<=0x7A) return F_ASC2 + (lsb-0x61)*32;
        else                      return NO_FONT;;
      }else if(msb==0x24){                  // ひらかな
        if(lsb>=0x21 & lsb<=0x73) return F_HIRA + (lsb-0x21)*32;
        else                      return NO_FONT;
      }else if(msb==0x25){                  // カタカナ
        if(lsb>=0x21 & lsb<=0x76) return F_KATA + (lsb-0x21)*32;
        else                      return NO_FONT;
      }else if(msb==0x26){                  // ギリシャ
        if(lsb>=0x21 & lsb<=0x38) return F_GRK1 + (lsb-0x21)*32;
        if(lsb>=0x41 & lsb<=0x58) return F_GRK2 + (lsb-0x41)*32;
        else                      return NO_FONT;;
      }else if(msb==0x27){                  // キリル
        if(lsb>=0x21 & lsb<=0x41) return F_CRL1 + (lsb-0x21)*32;
        if(lsb>=0x51 & lsb<=0x71) return F_CRL2 + (lsb-0x51)*32;
        else                      return NO_FONT;;
      }else if(msb==0x28){                  // 罫線
        if(lsb>=0x21 & lsb<=0x40) return F_RULE + (lsb-0x21)*32;
        else                      return NO_FONT;
      }
    }else if(msb<0x50){                     // 漢字(第1水準)
        if(lsb>=0x21 & lsb<=0x7E) 
                        return F_KAN1 + (lmsb-0x30)*3008+(llsb-0x21)*32;
        else            return NO_FONT;
    }else if(msb<0x75){                     // 漢字(第2水準)
        if(lsb>=0x21 & lsb<=0x7E) 
                        return F_KAN2 + (lmsb-0x50)*3008+(llsb-0x21)*32;
        else            return NO_FONT;
    }
    return NO_FONT; 
}

//---- バイト一単位で全角(漢字)文字表示
uint8_t i2cOLED_ZchrX(uint8_t xpos, uint8_t pg, uint16_t kCd,uint8_t ed){
    uint32_t adr;
    uint16_t kk;
    uint8_t h,l;
    char ptn[32];
    if(ed & 0x01){
        h = (uint8_t)(kCd>>8);
        l = (uint8_t)kCd;
        kCd = l*256+h;
    }
    if(ed < 2)  kk = SJis2Jis(kCd);
    else        kk = kCd;	
//    sPrt_AdrDt(sBf,(uint32_t)kk, (uint8_t)kCd, '>'); puts(sBf); 
    adr = KfntAdr(kk);
    I2C_Mem_NRead(adr,(uint8_t *)ptn,32); 
 //    sPrt_AdrDt(sBf,adr, ptn[0], '>'); puts(sBf); 
    if(adr)	return i2cOLED_Ptn(pg,xpos,2,16,ptn);
    return xpos;
}

//---- バイト単位位置で全角(漢字)文字列表示
uint8_t i2cOLED_ZstrX(uint8_t xpos, uint8_t pg, char *str,uint8_t ed){
    uint16_t cd, *kjP;
    kjP = (uint16_t *)str;
    while(*kjP & 0xFF){
        xpos = i2cOLED_ZchrX(xpos,pg,*kjP,ed);
        kjP++;
    }
    return xpos;
}

 
【i2c_EEPROM.hファイル】 i2c-EEPROM.c/h(zip)

/*******************************(i2c-EEPROM.h)**************
 *	I2C接続メモリ利用ライブラリヘッダー(EEPROM/24FC1025)
 ***********************************************************/

//-----  アドレスadrから、1バイトを読み出す
/*    adr: 読み出したいデータのアドレス
 *  RTN:  読み出したバイトデータ    */
uint8_t I2C_Mem_Read(uint32_t adr);

//-----  アドレスadrに、1バイトdtを書き込む
/*    adr: 書き込みたいアドレス、  dt:書き込むデータ */
void I2C_Mem_Write(uint32_t adr, uint8_t dt);

//-----  アドレスadrからnバイトをary[]に読み出す
/*    adr: 読み出したいデータのアドレス、
 *    *ary:読込データを格納する配列、 n: 読み出すバイト数 */
void I2C_Mem_NRead(uint32_t adr, uint8_t *ary, uint8_t n);

//-----  アドレスadrからary[]のnバイトを書き込む
/*    adr: 書き込みたいデータのアドレス、
 *    *ary:書込たい配列データ、 n: 書き込むバイト数 */
void I2C_Mem_NWrite(uint32_t adr, uint8_t  *ary, uint8_t n);

/****** 汎用サブルーチン ********************/

//---- 16進文字列を10進数に変換  str -> dec
/*    *str:変換したい文字列                 *
 *  RTN: 変換後の32ビット数値               */
int32_t my_xtoi(char *str);

//---- 16進数文字変換  uint16 -> str(Hex)
/*    *str:変換後に格納する文字列、wd:16ビットデータ、dg:桁数 */
void my_utoa(uint8_t *str, uint16_t wd, uint8_t dg);

//---  標準出力へ文字列出力(putch関数のみ利用)
/*    *str: 出力する文字列 */
void my_puts(char *str);

//---  標準出力へ文字列出力(改行付き)
/*    *str: 出力する文字列 */
void my_putsLf(char *str);

//---- ページバッファープリント (16進数でダンプ表示[改行付き])
/*    *cBf:出力したいデータのアドレス、  n:出力バイト数 */
void prt_PBf(uint8_t *cBf, uint16_t n);

//---- 20ビットアドレスと8ビットデータを16進数で文字列に格納
/*    *strP:格納する文字列、 adr:アドレス
 *    dt:データ、  ch:接続する文字    */
void sPrt_AdrDt(char *strP,uint32_t adr, uint8_t dt, char ch);

//---- 20ビットアドレス1と20ビットアドレス2を16進数で文字列に格納
/*    *strP:格納する文字列、 adr1:アドレス
 *    adr2:データ、  ch:接続する文字    */
void sPrt_AdrAdr(char *strP,uint32_t adr1, uint32_t adr2, char ch);

/*****  コマンドアクション *********************
 *     *strは入力されたコマンドラインデータ    *
 *---------------------------------------------*/

//---- Wコマンド   W[aaaa][,][d0,・・][,]
/*    aaaa:書き込みたいアドレス、 d0:書き込むバイトデータ */
uint16_t i2cMem_cmd_W(char *str);

//---- Rコマンド   R[aaaa][,n]
/*    aaaa:読み込みたいアドレス、 n:読み込むバイト数 */
uint16_t i2cMem_cmd_R(char *str);

//---- WSQ コマンド(連続書込モード)
uint16_t i2cMem_cmd_WSQ(char *str);

//---- Qコマンド(連続書込モードの解除)   Q
void i2cMem_cmd_Q(char *str);

//---- U(dump)コマンド   U[aaaa] (64バイトのデータをDBuf[]に読み出す)
/*    aaaa:読み込みたいアドレス      */
int i2cMem_cmd_U(char *str);

//---- P(Page)コマンド  P[R/W][aaaa](DBuf[]の64バイトをメモリに書き込む)
/*    (パラメータが無い場合、ページバッファー内を表示)
 *    aaaa:読込み(R)or書込み(W)たいアドレス      */
int i2cMem_cmd_P(char *str);

//----  シーケンスモードの確認
uint8_t i2cMem_SqMode();

//---- K(漢字)表示コマンド  K[H][aaaa]
/*    H:Hがあると半角16文字を表示
 *    aaaa:表示開始アドレス(16バイト単位、省略すると次アドレス) */ 
int i2cMem_cmd_K(char *str);

//---- L(文字)表示コマンド  L[code] (JIS文字コード)
/*    code:文字コード(半/全角は自動判断、省略すると次コード) */ 
void i2cMem_cmd_L(char *str);
【i2c_EEPROM.cファイル】

/*******************************(i2c-EEPROM.c)**************
 *	I2C接続メモリ利用ライブラリ  (24FC1025/M24M02)
 ***********************************************************/

#define ROM_24FC1025    0       // 値を0にするとdefinが無効となる
#define ROM_M24M02      2       // 値を0にするとdefinが無効となる
#define ROM_ADR	0x50 
#define MBFSIZE	64              // メモリ操作用バッファーサイズ

#include "mcc_generated_files/mcc.h"
#include "mcc_generated_files/examples/i2c1_master_example.h"
#include "i2c_SSD1306.h"
#include "ctype.h"              // isxdigit()
#include "stdio.h"              // puts(),..
#include "stdlib.h"             // atoi()...
#include "string.h"             // strtok()...
#include "myProject.h"

//=== グローバル変数 ========================
static char     Dlm[] = ",";        // 文字列区切りデリミタ

//--- I2Cメモリー用
static uint8_t  Device = 0;         // デバイス・ブロック番号
static uint32_t CrtAdr = 0;         // カレントアドレス
static uint8_t  DBuf[MBFSIZE+2];    // ページメモリ用バッファー
static uint8_t  SqMode;             // 連続モード(=1)か?
static char     SBuf[31];           // 表示用(1行最大30文字)
static uint8_t  LstCmd;             // 現在(最終)のコマンド
static uint16_t LstChr = 0;         // 最終表示文字
//static uint8_t  fntPtn[32];       // フォントパターン用

/*********************************************
 *  I2C EEPROM function
 **********************************************/

//----  デバイスのアドレス&ブロック計算
uint8_t calcDev(uint32_t adr){
    uint8_t dv;
  #if  ROM_24FC1025
    dv = ROM_ADR + (uint8_t)(adr >> 17);
    if(adr & 0x10000) dv += 0x04;
  #elif ROM_M24M02
    dv = ROM_ADR + (uint8_t)(adr >> 16);
	dv += 0x04;                     // E = high
  #endif
    return dv;
}

//-----  アドレスadrから、1バイトを読み出す
uint8_t I2C_Mem_Read(uint32_t adr){
    uint8_t bf[2];
    if(adr<0x80000){                    // 0-7FFFFH: EEPROM
        Device = calcDev(adr);
        bf[0] = (uint8_t)((adr & 0x1FFFF) >> 8);
        bf[1] = (uint8_t)adr;               // アドレスセット
        I2C1_WriteNBytes(Device, bf, 2);    // アドレス送信
        I2C1_ReadNBytes(Device, bf, 1);     // データ読込み
        return bf[0];
    }
    return 0;
}

//-----  アドレスadrに、1バイトdtを書き込む
void I2C_Mem_Write(uint32_t adr, uint8_t dt){
    uint8_t bf[3];
    if(adr<0x80000){                     // 0-7FFFFH: EEPROM
        Device = calcDev(adr);
        bf[0] = (uint8_t)((adr & 0x1FFFF) >> 8);
        bf[1] = (uint8_t)adr;               // アドレスセット
        bf[2] = dt;                         // データセット
        I2C1_WriteNBytes(Device, bf, 3);    //書き込む
        __delay_ms(5);
    }
}

//-----  アドレスadrからnバイトをary[]に読み出す
void I2C_Mem_NRead(uint32_t adr, uint8_t *ary, uint8_t n){
    uint8_t bf[2];
    if(adr<0x80000){                      // 0-7FFFFH: EEPROM
        Device = calcDev(adr);
        bf[0] = (uint8_t)((adr & 0x1FFFF) >> 8);
        bf[1] = (uint8_t)adr;               // アドレスセット
        if(n > MBFSIZE) n = MBFSIZE;
        I2C1_WriteNBytes(Device, bf, 2);    // アドレス送信
        I2C1_ReadNBytes(Device, ary, n);    // nバイト読込み
    }
}

//-----  アドレスadrからary[]のnバイトを書き込む
void I2C_Mem_NWriteuint32_t adr, uint8_t  *ary, uint8_t n){
    uint8_t bf[MBFSIZE + 2];
    if(adr<0x80000){                       // 0-7FFFFH: EEPROM
        Device = calcDev(adr);
        bf[0] = (uint8_t)((adr & 0x1FFFF) >> 8);
        bf[1] = (uint8_t)adr;               // ページ境界に注意!
        if(n > MBFSIZE) n = MBFSIZE;
        memcpy(bf+2,ary,n);                 // ary[]をbf[]にコピー
        I2C1_WriteNBytes(Device, bf, n+2);  // nバイト書込み
        __delay_ms(5);
    }
}

/****** 汎用サブルーチン ********************/
//---- 16進文字列を10進数に変換
int32_t my_xtoi(char *str){
    uint32_t dec;
    dec = strtoul(str,NULL,16);
//    if(errno == ERANGE) return -1;          // 変換エラー
    return (int32_t)dec;
}

//   16進数文字変換  uint16 -> str(Hex)
uint8_t my_utoa2(uint16_t n){
    n &= 0x0F;
    if(n < 10)  return '0'+ (uint8_t)n;
    else        return 'A'+ ((uint8_t)n - 10);
}

void my_utoa(uint8_t *str, uint16_t wd, uint8_t dg){
    if(dg > 3)  *str++ = my_utoa2(wd >> 12);
    if(dg > 2)  *str++ = my_utoa2(wd >> 8);
    if(dg > 1)  *str++ = my_utoa2(wd >> 4);
    *str++ = my_utoa2(wd);
    *str = '\0';
}

//---  文字列出力関数(標準出力へ)
void my_puts(char *str){            // 文字列のみ出力
    while(*str) putch(*str++);
}

void my_putsLf(char *str){          // 次行に移動(LF)付き
    my_puts(str);
    putch('\r');
}

//---- ページバッファープリント
void prt_PBf(uint8_t *cBf, uint16_t n){
    uint16_t i;
    for(i = 0; i < n; i++){
        if(i % 16 == 0) putch('\r');
        my_utoa((uint8_t *)SBuf,cBf[i],2);
        putch(SBuf[0]); putch(SBuf[1]); putch(' ');
        if(i < 48){
            if(i % 8 == 0) i2cOLED_posPX(i2cOLED_LF(),0);
            strcat(SBuf," ");
            i2cOLED_str(SBuf);
        }
    }
    puts("\r");
}

//---- 20ビットアドレスと8ビットデータを16進数で文字列に格納
void sPrt_AdrDt(char *strP,uint32_t adr, uint8_t dt, char ch){
    uint16_t a16;
    uint8_t  h;
    h = (uint8_t)((adr>>16) & 0x0F);
    *strP++ = my_utoa2(h);
    a16 = (uint16_t)(adr & 0x0FFFF);
    my_utoa((uint8_t *)strP,a16,4);
    strP += 4;
    *strP++ = ' '; *strP++ = ch; *strP++ =' ';
    my_utoa((uint8_t *)strP,(uint16_t)dt,2);
    strP += 2;
    *strP = '\0';
}

//---- 20ビットアドレス1と20ビットアドレス2を16進数で文字列に格納
void sPrt_AdrAdr(char *strP,uint32_t adr1, uint32_t adr2, char ch){
    uint16_t a16;
    uint8_t  h;
    h = (uint8_t)((adr1>>16) & 0x0F);
    *strP++ = my_utoa2(h);
    a16 = (uint16_t)(adr1 & 0x0FFFF);
    my_utoa((uint8_t *)strP,a16,4);
    strP += 4;
    *strP++ = ' '; *strP++ = ch; *strP++ =' ';
    h = (uint8_t)((adr2>>16) & 0x0F);
    *strP++ = my_utoa2(h);
    a16 = (uint16_t)(adr2 & 0x0FFFF);
    my_utoa((uint8_t *)strP,a16,4);
    strP += 4; 
    *strP = '\0';
}

/*********************************************
 * 		コマンドアクション
 *********************************************/
//----- Wコマンド   W[aaaa][,][d0,・・][,]
uint16_t i2cMem_cmd_W(char *str){
    char     *pt;                   // 文字列操作用ポインタ
    uint8_t  last;                  // 文字列の最後の位置
    int32_t  res = 0;               // Hex文字数値変換結果
    uint8_t  dt = 0;                // 書き込むバイトデータ
    uint16_t cnt = 0;               // 実際に書き込んだバイト数

    LstCmd = str[0];
    if(strlen(str)==1){             // 'W'のみなら
        SqMode = 1;                 // 連続書込モードにセット
      #ifndef VBA
        puts("SqMode ON\n\r");
      #endif
        return 0;                   // データは書き込まない
    }
    last = (uint8_t)strlen(str)-1;  // 文字列の行末位置を取得
    if(str[last] == ','){           // 行末がコンマなら
        SqMode = 1;                 // 連続書込モードにセット
      #ifndef VBA
        puts("SqMode ON\n\r");
      #endif
        str[last]='\0';             // 行末のコンマを消す
    }
    if(str[1]!=','){
        pt = strtok(str+1,Dlm);     // コマンドの次文字から
        if(pt != NULL){             // アドレスデータがある
            res = my_xtoi(pt);
//            if(res < 0) return cnt; // 文字列変換エラー
            CrtAdr = (uint32_t)res;
        }
        pt = strtok(NULL,Dlm);      // 次文字列へ
    }else{
        pt= strtok(str+1,Dlm);      // 文字列取得
    }
    i2cOLED_Clr(0,7,0);
    do{
        if(pt != NULL){
            res = my_xtoi(pt); 
//            if(res < 0) return cnt; 
            dt = (uint8_t)res;
            if(LstCmd=='W'){         // コマンドが'W'
                I2C_Mem_Write(CrtAdr,dt);
            }else if(LstCmd == 'S'){ // コマンドが'S'
                DBuf[CrtAdr & 0x3F]= dt; // 64バイトまで
            }
            sPrt_AdrDt(SBuf,CrtAdr,dt,'<');
            my_putsLf(SBuf);
            i2cOLED_str(SBuf);
            i2cOLED_posPX(i2cOLED_LF(),0);
            CrtAdr++; cnt++;
            pt = strtok(NULL,Dlm);  // 次文字列へ
        }
    }while(pt != NULL);
    return cnt;
}

//---- Rコマンド  R[aaaa][,n]
uint16_t i2cMem_cmd_R(char *str){
    char *pt;                   // 文字列操作用ポインタ
    uint16_t n = 1;             // 指定された読込バイト数
    int32_t  res = 0;           // Hex文字数値変換結果
    uint8_t  dt;                // 読み込んだバイトデータ
    uint16_t cnt = 0;           // 実際に読み出されたバイト数
    uint8_t  i;                 // ループ変数

    LstCmd = str[0];
    if(strlen(str) > 1){
        if(str[1]==','){
            pt = strtok(str+2,Dlm);
            if(pt != NULL){             // 文字無し?
                res = my_xtoi(pt);
//                if(res < 0) return cnt;
                n = (uint16_t)res;
            }          
        }else{
            pt = strtok(str+1,Dlm);
            if(pt != NULL){             // addressがある?
                res = my_xtoi(pt);
//                if(res < 0) return cnt;
                CrtAdr = (uint32_t)res;
            }
            pt = strtok(NULL,Dlm);      // 次文字へ
            if(pt != NULL){             // 文字無し?
                res = my_xtoi(pt);
//                if(res < 0) return cnt;
                n = (uint16_t)res;
            }
        }
    }
    i2cOLED_Clr(0,7,0);
    for(i = 0; i < n;i++ ){
        dt = I2C_Mem_Read(CrtAdr);  // バイトデータを読み出す
        sPrt_AdrDt(SBuf,CrtAdr,dt,'<');
        my_putsLf(SBuf);
        i2cOLED_str(SBuf);
        i2cOLED_posPX(i2cOLED_LF(),0);
        CrtAdr++; cnt++;
    }
    SqMode = 0;
    return cnt;
}

//---- WSQ コマンド(連続書込モード)
uint16_t i2cMem_cmd_WSQ(char *str){
    char     *pt;               // 文字列操作用ポインタ
    uint8_t  dt;                // 書き込むバイトデータ
    int32_t  res;               // Hex文字数値変換結果
    uint16_t cnt = 0;           // 実際に読み出されたバイト数
    pt = strtok(str,Dlm);	
    while(pt != NULL){
        res = my_xtoi(pt);
//        if(res < 0) return cnt;
        dt = (uint8_t)res;
        I2C_Mem_Write(CrtAdr,dt);  // データを書き込む
        sPrt_AdrDt(SBuf,CrtAdr,dt,'<');
        my_putsLf(SBuf);
        i2cOLED_str(SBuf);
        CrtAdr++; cnt++;
        pt = strtok(NULL,Dlm);  // 次文字へ
    }
    return cnt;
}

//---- Qコマンド(連続書込モードの解除)   Q
void i2cMem_cmd_Q(char *str){
    SqMode = 0;
  #ifndef VBA
    puts("SqMode OFF\r");
  #endif
}

//---- U(dump)コマンド(64バイトのデータをDBuf[]に読み出す)
int i2cMem_cmd_U(char *str){
    char     *pt;               // 文字列操作用ポインタ
    int32_t  res;               // Hex文字数値変換結果
    uint32_t ad;                // 指定アドレス
    uint8_t  i;                 // ループカウンタ用

    LstCmd = str[0];
    pt = strtok(str+1,Dlm);
    if(pt != NULL){             // アドレス有り?
        res = my_xtoi(pt);
//        if(res < 0) return -1;
        CrtAdr = (uint32_t)res;
    }
    ad = CrtAdr & 0xFFFE0;      // 32バイト単位に 
    I2C_Mem_NRead(ad,DBuf,64);  // 64バイトをDBuf[]に読出し
    sPrt_AdrAdr(SBuf,ad,ad + MBFSIZE - 1,'-');
    my_puts(SBuf);
    i2cOLED_Clr(0,7,0);
    i2cOLED_str(SBuf);
    i2cOLED_posPX(i2cOLED_LF(),0);
    prt_PBf(DBuf,MBFSIZE);
    SqMode = 0;
    return 0;
}

void prt_MemDat(uint32_t adr){
    if(LstCmd == 'H'){                  // 第2文字がHか?
        I2C_Mem_NRead(adr, DBuf, 16);    // 16バイトをDBuf[]に
        i2cOLED_Ptn(0,0,2,8,(char *)DBuf);
        i2cOLED_posPX(2,0);
        prt_PBf(DBuf,16);
    }else{
        I2C_Mem_NRead(adr, DBuf, 32);  // 32バイトをDBuf[]に
        i2cOLED_Ptn(0,0,2,16,(char *)DBuf);
        i2cOLED_posPX(2,0);
        prt_PBf(DBuf,32);
    }    
}

//--  フォントパターンデータの表示
int i2cMem_cmd_u(char *str){
    char    *pt;                        // 文字列操作用ポインタ
    uint32_t res;                       // Hex文字数値変換結果
    uint32_t ad;                        // 指定アドレス

    pt = strtok(str+1,Dlm);
    if(pt != NULL){                     // アドレス有り?
        res = (uint32_t)my_xtoi(pt);
        CrtAdr = res;
    }
    ad = CrtAdr & 0xFFFFF0;             // 16バイト単位に 
    prt_MemDat(ad);
    SqMode = 0;
    return 0;
}

//---- P(Page)コマンド    PW[aaaa]
int i2cMem_cmd_P(char *str){
    char     *pt;                       // 文字列操作用ポインタ
    uint32_t res;                       // Hex文字数値変換結果
    uint32_t ad;                        // 指定アドレス
    static char msgP[] = "PageBuffer";

    LstCmd = str[0];
    if(strlen(str)==1){                 // コマンドが'P'のみ
        puts(msgP);                     // ページ内データ表示
        i2cOLED_Clr(0,7,0);
        i2cOLED_str(msgP);
        i2cOLED_posPX(i2cOLED_LF(),0);
        prt_PBf(DBuf,MBFSIZE);
        return 0;
    }
    if(str[1]=='R'){                    // 第2文字='R'
        i2cMem_cmd_U(str+1);            // Uコマンドと同じ
        return 0;
    }
    if(str[1] != 'W') return -1;        // 第2文字が'W'?
    pt = strtok(str+2,Dlm);
    if(pt != NULL){                     // アドレス有り?
        res = (uint32_t)my_xtoi(pt);
        CrtAdr = (uint32_t)res;
    }
    ad = CrtAdr & 0xFFFC0;              // 64バイト単位に
    I2C_Mem_NWrite(ad,DBuf,64);         // DBuf[]の64バイト書込
    sPrt_AdrAdr(SBuf,ad,ad + MBFSIZE - 1,'-');
    my_putsLf(SBuf);
    i2cOLED_Clr(0,7,0);
    i2cOLED_str(SBuf);
    SqMode = 0;    
    return 0;
}

//----  シーケンスモードの確認
uint8_t i2cMem_SqMode(void){
	return SqMode;
}

//---- K(漢字)表示コマンド  K[H][aaaa] 
int i2cMem_cmd_K(char *str){
    char *sp;
    uint8_t  cn;                        // コラムドット数
    uint8_t  ofs;                       // 文字列解読オフセット
    uint8_t  i,n;                       // ループ変数
    int32_t  res;                       // 取得アドレス

    LstCmd = str[0];
    if((strlen(str)>1)&(str[1]=='H')){
        ofs = 2; cn = 8;
    }else{
        ofs = 1; cn = 16;
    }
    sp = strtok(str+ofs,Dlm);
    if(sp != NULL){
        res = my_xtoi(sp) & 0xFFFF0;   // 16バイト単位
        if(res>0) CrtAdr = (uint32_t)res;
    }
    i2cOLED_Clr(0,7,0);
    sPrt_AdrAdr((char *)SBuf, CrtAdr, CrtAdr+16*cn,'-');
    my_putsLf((char *)SBuf);
    i2cOLED_str(SBuf);
    n = 8 * ofs;
    for(i = 0; i < n; i++){
        I2C_Mem_NRead(CrtAdr,(uint8_t *)DBuf,2*cn);
        i2cOLED_Ptn(2,i*cn,2,cn,(char *)DBuf);
        CrtAdr += 2*cn;
    }
    SqMode = 0;
    return i;
}

//---- L(文字)表示コマンド  L[code] (文字コード)
void i2cMem_cmd_L(char *str){
    char     *pt;                       // 文字列操作用ポインタ
    uint32_t res;                       // Hex文字数値変換結果
    uint16_t ftCd = 0;                  // フォントコード
    
    LstCmd = str[0];
    if(strlen(str)==1){                 // アドレス無し	
        LstChr += 1;                    // 次文字
    }else{
        pt = strtok(str+1,Dlm);
        if(pt != NULL){                 // アドレス有り?
            res = (uint32_t)my_xtoi(pt);
            if(res>0)   LstChr = (uint16_t)res;
        }
    }
    i2cOLED_Clr(0,7,0);
    my_utoa((uint8_t *)SBuf,LstChr,4); my_putsLf(SBuf);
    i2cOLED_str(SBuf);
    if(LstChr < 256){
        i2cOLED_HchrX(32,0,(uint8_t)LstChr);
        I2C_Mem_NRead(HfntAdr((uint8_t)LstChr),DBuf,16);
        i2cOLED_posPX(2,0);
        prt_PBf(DBuf,16);
    }else{
        i2cOLED_ZchrX(32,0,LstChr,2);
        I2C_Mem_NRead(KfntAdr(LstChr),DBuf,32);
        i2cOLED_posPX(2,0);
        prt_PBf(DBuf,32);
    }
    SqMode = 0;
}


※プログラムのリストをハイライト無しのスタイルで見る場合はここをクリック


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


INDIRECT関数(Excel) 漢字ROMにI2C接続EEPROM(M24M02)