Vol.902 13.Sep.2024

WinMerge PICミニBB(12)_SPI接続EEPROM

D ファイルやテキストなどの比較ツール  〜WinMerge

by fjk

 データに違いがあるかチェックするツールとして差分(Diff)ツールがありますが、その代表的なものとしてwindowsで使える無料のWinMerge(日本語対応)を使ってみた。
 WinMergeはフォルダーとファイルの両方の比較ができる。比較する対象は2または3個で、「一致、不一致、どちらかにしかない」を判断し、その結果を表示してくれる。
 ダウンロードは WinMerge公式ページ から入手。WinMergeの詳細、使い方は以下を参照。
  ・WinMergeのヘルプ
  ・WinMergeの基本的な使い方(qiita)
 
【WinMergeの主な機能】 (WinMerge-2.16.42-jp-2-x64の場合)

1,テキストの比較
テキストだけでなくバイナリーでも比較できる(比較条件を指定可)
2.ファイルの比較
Excel、Word、HTMLファイル、画像などの比較
3.フォルダーの比較
ファイルの内容、保存時間、ファイルサイズなどの条件を指定可
4.マージ
ファイル間で異なるところを結合
5.レポート
差分をレポートとして出力

「ファイル」menu

「編集」menu

「表示」menu

「マージ」menu

「ツール」menu
 

「ウィンドゥ」menu

テキストファイルを比較した例

左のファイルをバイナリーで比較


P PICミニBBシリーズ(12)  〜SPI接続EEPROM(25LC1024)

by fjk

 abc899で、PICで外付けI2C接続EEPROMの使用を紹介したが、同様のSPI(Serial Peripheral Interface)接続メモリについて読み書きテストを行った。使用したメモリは、25LC1024(128kByte、詳細は「25LC1024の使い方(pdf)」参照)で、i2c接続の24LC1025と同じ容量で、電源電圧も5Vで使用できる。主な仕様は 、
・ 最大クロック速度 20 MHz
・ バイトおよびページレベルの書き込み操作:
- 256 バイト/ページ、32kバイト/セクター
- 最大書き込みサイクル時間 6 ms
- ページまたはセクターなどの事前消去は不要
・ 低電力 CMOS テクノロジー

25LC1024ピン配置

【ハードウェア】
 abc896のspi接続グラフィックLCDセットをベースに、CPU(16F18326)とLCDの間にspi接続メモリを配置。
 メモリボード上でのspi配線に伴い、メモリ−LCD間のコネクタは6Pに変更
 なお、使用したMSSPはSPI1に固定。


全体回路図(CS0:RA0、gCS:RA2、gRS:RC3)

メモリボード配線図
(余分な配線もありますが・・)

【ソフトウェア】
 プログラムは、spi接続グラフィックLCD表示用のabc896に、I2Cメモリアクセス用abc899をSPI用に変更したものを追加した。なお、グラフィックフォントはFont_5x7.hとして別ファイルとした。

【シリアル通信コマンド】(Mコマンド無し)
W[aaaa][,[d0,・・[,]]]   (Write)
アドレスaaaa(省略時:現在アドレス)からバイトデータを書込む。コンマで終わると連続書込モード。
R[aaaa][,n]   (Read)
アドレスaaaa(省略時:現在アドレス)からn(省略時:1)バイトデータを読出す。
Q   (Quit Sequence_Mode)
連続書込モード(プロンプトが#)の解除(プロンプトが$)。W、S以外のコマンドでもモード解除。
U[aaaa]   (dUmp)
アドレスaaaa(省略時:現在アドレス)が含まれる64バイト単位でデータをDBuf[]に読出し、表示。
P   (Page)
64バイトのデータバッファーの表示
PW[aaaa]   (Page Write)
[aaaa]番地を含む64バイトにバッファーの内容を書込む
PR[aaaa]   (Page Read)
[aaaa]番地を含む64バイトをバッファーに読込み、表示
S[aa][.[d0,・・[,]]]   (Set data)
64バイトのデータバッファーに書込む以外はWコマンドと同じ。
連続書込モードも有効。aa(省略時:現在アドレス)は下位6ビットのみ有効。
 
※W、SおよびRコマンド実行後、現在アドレスは次のアドレスになる。

テラターム画面例


メモリ読み書きテスト中

※ 全てのパターンのテストが完全に終わっていないので、バグがあるかも知れません(謝)。

【abc902-18326.cファイル】 abc902-18326.c(zip)

/*******************************(abc902-18326)******
 *   SPI_EEP-ROM 読み書きテスト with AQM1248A  
 ***************************************************/

#include "mcc_generated_files/mcc.h"
#include "myProject.h"
#include "AQM1248A_m2.h"
#include "spi_EEPROM.h"

// #define  BFSIZE 20           	// myProject.hで宣言
char     sBf[BFSIZE];           	// 文字列作業エリア
char     RBuf[BFSIZE];          	// シリアル受信バッファ
uint8_t  SFlg;                  	// シリアル受信フラグ

//---- ターミナルにプロンプト文字表示
void prtPrompt(uint8_t m){
    if(m)  printf("# ");        	// 連続書込モード中
    else   printf("$ ");        	// 通常書込モード
}

/*-----------------------------------
 *        Main application
 ------------------------------------*/
void main(void){
    uint8_t cmd;

    SYSTEM_Initialize();
    SSP1CON1bits.SSPEN = 1;     	//SPI1を有効に
    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();

    gLCD_int();
    gLCD_clr(0);                	// 黒ベタは0xFF
    puts("Ready\r");             	// 動作確認用
    gLCD_strX(0,0,"Ready");
            #ifndef VBA
                prtPrompt(spiRom_SqMode());
            #endif
    SPI_Rom_RdStat();
    
    while (1){
        if(SFlg){
            gLCD_strX(0,0,RBuf);  	// LCDにコマンド表示
            cmd = RBuf[0];       	// コマンドは第1文字
            #ifndef VBA
                printf("\n\r");
            #endif
            switch(cmd) {        	// 各コマンドの実行
                case 'W': spiRom_cmd_W(RBuf);	break;
                case 'R': spiRom_cmd_R(RBuf); 	break;
                case 'Q': spiRom_cmd_Q(RBuf); 	break;
                case 'U': spiRom_cmd_U(RBuf); 	break;
                case 'S': spiRom_cmd_W(RBuf); 	break;
                case 'P': spiRom_cmd_P(RBuf); 	break;
                            		// 有効なコマンドがなく
                default:  if(spiRom_SqMode()){
                            spiRom_cmd_WSQ(RBuf);
                          }      	// 連続書込モード中なら
                      	break;  	// 書込シーケンスを実行
            }
            SFlg = 0;
            #ifndef VBA
                prtPrompt(spiRom_SqMode());
            #endif
        }
        IO_RA4_Toggle();
        __delay_ms(500);
    }
}
/****  End of File  ****/

 
【myProject.hファイル】 myProject.h(zip)

/**************************(myProject.h)***
 *   プロジェクト個別条件指定ファイル     *
 *   (main.cと同じフォルダーに作成)     *
 ******************************************/
#include "mcc_generated_files/mcc.h"
#include "i2cLCDST7032i.h"

#define  ON    1
#define  OFF   0

/***** 使用電源電圧 ***********************/
//--- VCC宣言無しは5V(VCC=50と同じ)
#define VCC   50		// 電源電圧は5V
//#define VCC   33		// 電源電圧は3.3V

/***** EUSARTモジュール *******************/
#define BFSIZE  80
//--- ECHO宣言はどちらかを必ず行うこと
#define ECHO  ON		// エコー有り
//#define ECHO  OFF		// エコー無し

//define VBA       		// Excel_VBAを使用

/***** MSSPモジュール *********************/
#define MASTER 1
#define SRAVE  2
//#define I2CMODE MASTER   	// マスターとして使用
//#define I2CMODE SRAVE    	// スレーブとして使用
//--- MSSPが一つしか無い場合は I2C宣言無し
#define I2C  1            	// I2C1を使用
//#define I2C  2            	// I2C2を使用

#if I2CMODE == MASTER
    #if I2C == 1
        #include "mcc_generated_files/examples/i2c1_master_example.h"
    #elif I2C == 2
        #include "mcc_generated_files/examples/i2c2_master_example.h"
    #else
        #include "mcc_generated_files/examples/i2c_master_example.h"
    #endif
  //  #define I2CLCD_Adr 0x3c // LCDのI2Cアドレス(i2cLCD_ST7032i.hで設定)
#endif

 
【spi_EEPROM.hファイル】 spi_EEPROM.c/h(zip)

/******************************(spi_EEPROM.h)***************
 *	SPI接続EEPROM(25LC1024)読み書きヘッダーファイル
 ***********************************************************/

#include "AQM1248A_m2.h"
#include "ctype.h"                      // isxdigit()
#include "stdio.h"                      // puts(),..
#include "stdlib.h"                     // atoi()...
#include "string.h"                     // strtok()...
#include "myProject.h"

#define SPIROM_WR_STATS 0x01	// WRSR
#define SPIROM_WRITEcmd 0x02	// WRITE
#define SPIROM_READcmd  0x03	// READ
#define SPIROM_RD_STATS 0x05	// RDSR
#define SPIROM_WR_ENABL 0x06	// WREN
#define SPIROM_PG_ERASE 0x42	// PE	Page Erase
#define SPIROM_CP_ERASE 0xC7	// CE	Chip Erase
#define SPIROM_SC_ERASE 0xD8	// SE	Sector Erase

//----- Wコマンド   W[aaaa][,[d0,・・[,]]]
uint16_t spiRom_cmd_W(char *str);

//---- Rコマンド  R[aaaa][,n]
uint16_t spiRom_cmd_R(char *str);

//---- WSQ (連続書込モード)
uint16_t spiRom_cmd_WSQ(char *str);

//---- MBコマンド  MBn
int spiRom_cmd_M(char *str);

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

//---- U(dump)コマンド    U[aaaa]
//         (64バイトのデータをDBuf[]に読み出す)
int spiRom_cmd_U(char *str);

//---- P(Page)コマンド    PW[aaaa]
//          (DBuf[]の64バイトをメモリに書き込む)
int spiRom_cmd_P(char *str);

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

//--- SPI-ROMのステータスの取得
uint8_t SPI_Rom_RdStat();
【spi_EEPROM.cファイル】

/******************************(spi_EEPROM.c)***************
 *	SPI接続EEPROM(25LC1024)読み書きライブラリ
 ***********************************************************/

#include 
#include 
#include "mcc_generated_files/mcc.h"
#include "spi_EEPROM.h"

#define MBFSIZE	78                      // メモリ操作用バッファーサイズ

//=== グローバル変数 ============
char     Dlm[] = ",";                   // 文字列区切りデリミタ
char     SBuf[20];                      / 文字列処理用バッファー

//--- SPIメモリー用
static uint32_t CrtAdr = 0;             // カレントアドレス
static uint8_t  DBuf[MBFSIZE];          // ページメモリ用バッファー
static uint8_t  SqMode = 0;             // シーケンスモードフラグ
static char     lstCmd;                 // 最終コマンド

/*************************************
 *  SPI EEPROM function
 **************************************/

//---- ステータスの読み出し
uint8_t SPI_Rom_RdStat(){
    uint8_t stat;
    CS0_SetLow();
    SPI1_ExchangeByte(SPIROM_RD_STATS);
    stat = SPI1_ExchangeByte(0xAA);     // 0xAAはダミー
    CS0_SetHigh();
    return stat;
}

//---- コマンドと24ビットアドレスを送信 
void sendAdr(uint8_t cmd, uint32_t adr){
    CS0_SetLow();
    SPI1_ExchangeByte(cmd);             // send Command
    SPI1_ExchangeByte((uint8_t)(adr>>16);
    SPI1_ExchangeByte((uint8_t)(adr>>8));
    SPI1_ExchangeByte((uint8_t)adr);
} 

//-----  アドレスadrから、1バイトを読み出す
uint8_t SPI_Rom_Read(uint32_t adr){
    uint8_t dat;
//    CS0_SetLow();
    sendAdr(SPIROM_READcmd,adr);	// send Read_CMD & Address
    dat = SPI1_ExchangeByte(0xAA);
    CS0_SetHigh();
    return dat;
}

//-----  アドレスadrに、1バイトdtを書き込む
uint8_t SPI_Rom_Write(uint32_t adr, uint8_t dat){
    CS0_SetLow();
    SPI1_ExchangeByte(SPIROM_WR_ENABL);	// Set Write Enable Bit
    CS0_SetHigh();
//    CS0_SetLow();
    sendAdr(SPIROM_WRITEcmd,adr);	// send Write_CMD & Address
    SPI1_ExchangeByte(dat);
    CS0_SetHigh();
    __delay_ms(6);
    return dat;
}

//-----  アドレスadrからnバイトをary[]に読み出す
void SPI_Rom_NRead(uint32_t adr,uint8_t *ary,uint16_t cnt){
//    CS0_SetLow();
    sendAdr(SPIROM_READcmd,adr);	// send Read_CMD & Address
//    SPI1_ExchangeBlock(ary,cnt);
    SPI1_ReadBlock(ary,cnt);        // block read
    CS0_SetHigh();
    while(SPI_Rom_RdStat() & 1);    
}

//-----  アドレスadrからary[]のnバイトを書き込む
void SPI_Rom_NWrite(uint32_t adr,uint8_t *ary,uint16_t cnt){
    CS0_SetLow();
    SPI1_ExchangeByte(SPIROM_WR_ENABL);	// Set Write Enable Bit
    CS0_SetHigh();
    __delay_us(20);
//    CS0_SetLow();
    sendAdr(SPIROM_WRITEcmd,adr);	// send Write_CMD & Address
//    SPI1_ExchangeBlock(ary,cnt);	// block write
    SPI1_WriteBlock(ary,cnt);	//  block write
    CS0_SetHigh();
    while(SPI_Rom_RdStat() & 1);
}

//----- チップ全体の消去
void SPI_Rom_ChipErase(){
    CS0_SetLow();
    SPI1_ExchangeByte(SPIROM_WR_ENABL);
    CS0_SetHigh();
    __delay_us(20);
    CS0_SetLow();
    SPI1_ExchangeByte(SPIROM_CP_ERASE);
    CS0_SetHigh();
    __delay_ms(15);
}

/****** 汎用サブルーチン ********************/
//---- ページバッファープリント
void prt_PBf(){
    uint8_t i;
    for(i = 0; i < 64; i++){            // 16x4文字表示
        #ifdef VBA
            if(i % 16 == 0) printf("\r");
        #else
            if(i % 16 == 0) printf("\n\r");
        #endif
        printf(" %02X",DBuf[i]);
    }
    puts("\r");
}

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

/************************************
 * 	コマンドアクション
 ************************************/
//----- Wコマンド   W[aaaa][,[d0,・・[,]]]
uint16_t spiRom_cmd_W(char *str){
    char     *pt;                       // 文字列操作用ポインタ
    uint8_t  last;                      // 文字列の最後の位置
    uint32_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]!=','){                    // 第2文字がコンマか?
        pt = strtok(str+1,Dlm);         // コマンドの次文字から
        if(pt != NULL){                 // アドレスデータがある
            res = my_xtoi(pt);
            CrtAdr = res;
        }
        pt = strtok(NULL,Dlm);          // 次文字列取込
    }else{
        pt = strtok(str+1,Dlm);         // 文字列取込
    } 
    do{
        if(pt != NULL){                 // データがあるか?
            res = my_xtoi(pt);
            dt = (uint8_t)res;
            if(lstCmd=='W'){
                SPI_Rom_Write(CrtAdr,dt);   // データを書き込む
            }else if(lstCmd == 'S'){
                DBuf[CrtAdr & 0x3F]= dt;  
            }
            sprintf(SBuf,"%05lX < %02X",CrtAdr,dt);
            #ifdef VBA
                printf("%s\r",SBuf);
            #else
                printf("%s\n\r",SBuf);
            #endif            
            gLCD_clrX(0,16,0,128);
            gLCD_strX(0,16,SBuf);
            CrtAdr++; cnt++;          
            pt = strtok(NULL,Dlm);      // 次文字へ
        }
    }while(pt != NULL);                 // データがあるだけ繰り返す
    return cnt;                         // 書き込んだ文字数を返す
}

//---- Rコマンド  R[aaaa][,n]
uint16_t spiRom_cmd_R(char *str){
    char *pt;                           // 文字列操作用ポインタ
    uint16_t n = 1;                     // 指定された読込バイト数
    uint32_t res = 0;                   // Hex文字数値変換結果
    uint8_t  dt;                        // 読み込んだバイトデータ
    uint16_t cnt = 0;                   // 実際に読み出されたバイト数
    uint16_t  i;                        // ループ変数
    
    lstCmd = str[0];
    if(strlen(str) > 1){
        if(str[1]==','){
            pt = strtok(str+2,Dlm);
            if(pt != NULL){             // データ無し?
                res = my_xtoi(pt);
                n = (uint16_t)res;
            }
        }else{
            pt = strtok(str+1,Dlm);
            if(pt != NULL){             // addressがある?
                res = my_xtoi(pt);
                CrtAdr = res;
            }
            pt = strtok(NULL,Dlm);      // 次文字へ
            if(pt != NULL){             // データ無し?
                res = my_xtoi(pt);
                n = (uint16_t)res;
            }
        }
    }
    for(i = 0; i < n; i++ ){
        dt = SPI_Rom_Read(CrtAdr);      // データを読み出す
        sprintf(SBuf,"%05lX > %02X",CrtAdr,dt);
        #ifdef VBA
            printf("%s\r",SBuf);
        #else
            printf("%s\n\r",SBuf);
        #endif
        gLCD_clrX(0,16,0,128);
        gLCD_strX(0,16,SBuf);
        CrtAdr++; cnt++;
    }
    SqMode = 0;
    return cnt;                         // 読み込んだデータ数を返す
}

//---- WSQ (連続書込モード)
uint16_t spiRom_cmd_WSQ(char *str){
    char     *pt;                       // 文字列操作用ポインタ
    uint8_t  dt;                        // 書き込むバイトデータ
    uint32_t res;                       // Hex文字数値変換結果
    uint16_t cnt = 0;                   // 実際に読み出されたバイト数

    pt = strtok(str,Dlm);
    while(pt != NULL){                  // データがあるだけ繰り返す
        res = my_xtoi(pt);
        dt = (uint8_t)res;
        if(lstCmd == 'W'){
            SPI_Rom_Write(CrtAdr,dt);       // データを書き込む
        }else if(lstCmd=='S'){
            DBuf[CrtAdr & 0x3F]= dt;  
        }
        sprintf(SBuf,"%05lX < %02X",CrtAdr,dt);
        #ifdef VBA
            printf("%s\r",SBuf);
        #else
            printf("%s\n\r",SBuf);
        #endif
        gLCD_clrX(0,16,0,128);
        gLCD_strX(0,16,SBuf);
        CrtAdr++; cnt++;
        pt = strtok(NULL,Dlm);          // 次文字列へ
    }
    return cnt;                         // 書き込んだデータ数を返す
}

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

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

    lstCmd = str[0];
    pt = strtok(str+1,Dlm);
    if(pt != NULL){                     // アドレス有り?
        res = my_xtoi(pt);
        CrtAdr = res;
    }
    ad = CrtAdr & 0xFFFFC0;               // 64バイト単位に 
    SPI_Rom_NRead(ad, DBuf, 64);          // 64バイトをDBuf[]に読出し
    sprintf(SBuf,"%05lX - %05lX", ad, ad + 63);
    printf("%s\r",SBuf);
    gLCD_clrX(0,16,0,128);
    gLCD_strX(0,16,SBuf);
    prt_PBf();
    SqMode = 0;
    return 0;
}

//---- P(Page)コマンド    PW[aaaa]
//          (DBuf[]の64バイトをメモリに書き込む)
int spiRom_cmd_P(char *str){
    char     *pt;                       // 文字列操作用ポインタ
    uint32_t res;                       // Hex文字数値変換結果
    uint16_t ad;                        // 指定アドレス

    lstCmd = str[0];
    if(strlen(str)==1){
        printf("PageBuffer");
        prt_PBf();
        return 0;
    }
    if(str[1]=='R'){
        spiRom_cmd_U(str+1);
        return 0;
    }
    if(str[1] != 'W') return -1;        // 第2文字がW?
    pt = strtok(str+2,Dlm);
    if(pt != NULL){                     // アドレス有り?
        res = my_xtoi(pt);
        CrtAdr = res;
    }
    ad = CrtAdr & 0xFFFFC0;             // 64バイト単位に
    SPI_Rom_NWrite(ad,DBuf,64);         // DBuf[]の64バイト書込
    sprintf(SBuf,"Page Write %05lX - %05lX",ad,ad+63);
    #ifdef VBA
      	printf("%s\r",SBuf);
    #else
       	printf("%s\n\r",SBuf);
    #endif
    gLCD_clrX(0,16,0,128);
    gLCD_strX(0,16,SBuf);
    SqMode = 0;    
    return 0;
}

//----  シーケンスモードの確認
uint8_t spiRom_SqMode(){
	return SqMode;
}
/******* End of File ********/


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


WinMerge PICミニBB(12)_SPI接続EEPROM