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