C言語(SDCC)を使用して、STC8G1K08AでのI2Cアドレスサーチのプログラムを作成したいと思います。

回路図は、シリアル通信に使用している下記回路です。
SDAは8pin(P3.3)、SCLは7pin(P3.2)になります。図示はしていませんが、SDA及びSCLは2.2Kでプルアップしています。

まずは、I2C関連のレジスターを登録します。

#define CLKDIV (*(unsigned char volatile __xdata *)0xfe01)

#define I2CCFG (*(unsigned char volatile __xdata *)0xfe80)
#define I2CMSCR (*(unsigned char volatile __xdata *)0xfe81)
#define I2CMSST (*(unsigned char volatile __xdata *)0xfe82)
#define I2CSLCR (*(unsigned char volatile __xdata *)0xfe83)
#define I2CSLST (*(unsigned char volatile __xdata *)0xfe84)
#define I2CSLADR (*(unsigned char volatile __xdata *)0xfe85)
#define I2CTXD (*(unsigned char volatile __xdata *)0xfe86)
#define I2CRXD (*(unsigned char volatile __xdata *)0xfe87)

//BYTE 指定可能なレジスター

__sfr __at  0x8e AUXR  ;
__sfr __at  0xba P_SW2  ;

__sfr __at  0xb2 P3M0  ;	
__sfr __at  0xb1 P3M1  ;	
__sfr __at  0xca P5M0  ;	
__sfr __at  0xc9 P5M1  ;

__sbit __at 0xb3 SDA;
__sbit __at 0xb2 SCL;

今回は、I2C通信はマスターモードでポーリングしています。(割り込みは使用していません)。

P_SW2レジスターのbit4,5でI2Cを設定できます。STC8G1K08Aでは00でSCL(P3.2)、SDA(3.3)を設定できます。
また、以後XFR(拡張RAM領域特殊機能レジスタ)をアクセスすることなりますのでbit7は"1"に設定します(0x80)
I2CCFGの7bit目で1:enable、6bit目で1:Masterにしています(0xe0)
I2CMSSTをアイドル状態に設定(0x00)

	P_SW2 = 0x80;
	I2CCFG = 0xe0;     //マスターモード
	I2CMSST = 0x00;    

I2Cのアドレスは、二重のFOR文で00~7Fまで作成し、そのアドレスをチェックしていきます。
実際のアドレスは addr=i*0x10+j となります。
I2Cアドレスのチェックはi2c_addr(addr)で戻り値として、”0”か”1”を返します。
switch文で”1”であればNACKであり、無応答なので”*”を表示します。
”0”であればACKであり応答したとみなし、そのアドレスを表示します。

    	printf_tiny("Addr 0 1 2 3 4 5 6 7 8 9 a b c d e f\r\n");
    	for (i = 0; i <= 0x7; i++) {
        	printf_tiny("  %x ", i*0x10);
        	for (j = 0; j <= 0xf; j++) {
            	addr = i * 0x10 + j;
	        	switch(i2c_addr(addr)){
					case 0x1:					//addr+R >> NACK
		    			printf_tiny("* ");
		          		break;
					case 0x0:					//addr+R >> ACK
		          		printf_tiny("%x",addr);
		          		break;
					default:
		           		printf_tiny("er");
			}
		}
		printf_tiny("\r\n");

関数i2c_addrでは、引数としてint addrを受け取ります。
I2CMSCRレジスタに0x01を設定することにより、I2C動作がスタートします。
I2CMSSTの6bit目(MSIF)は、I2CMSCRにコマンド(ここでは0x01)が入力されると、割り込み要求フラグが”1”になります。while文でこのフラグをチェックします(”while(!(I2CMSST & 0x40))”)、ここではポーリングしているので、割り込みは発生しません。MSIFビットは、ソフトでクリアする必要があります。(”I2CMSST &= ~0x40”)
次にI2CTXDにデータを送信します。ここではI2Cアドレス+Readモードを設定します。
I2CMSCRにデータ送信コマンド0x02を設定し、I2CTXDに設定したデータを送信します。

送信したデータの返答をreply()でACKまたは、NACKの確認をします。
I2CMSCRにACKコマンドの受信(0x03)を設定します。
I2CMSSTの2bit目(MSACK1)をチェックします。
ACKが返答されば”0”に、NACKが返答されば”1”になります。

最後にI2CMSCRにストップデータ0x06を設定します。

int i2c_addr(int addr)						    //I2Cアドレスサーチ
{	
		int i;

		I2CMSCR = 0x01;						//I2C スタート
		while(!(I2CMSST & 0x40));
		I2CMSST &= ~0x40;	
	
		I2CTXD = ((addr << 1) | 0x01);				//I2C_ADDR + READ 送信
		I2CMSCR = 0x02;
		while(!(I2CMSST & 0x40));
		I2CMSST &= ~0x40;

		i=reply();						//ACK、NACK返答確認

		I2CMSCR = 0x06;						//I2C ストップ
		while(!(I2CMSST & 0x40));
		I2CMSST &= ~0x40;		
		return i;
}
int reply(void)					//ACK、NACK返答確認
{
	int i;
	I2CMSCR = 0x03;
	while(!(I2CMSST & 0x40));
	i=I2CMSST & 0x02;
	I2CMSST &= ~0x40;
	return (i >> 1);			//ACK =0,NACK=1
}

全体的なプログラムは下記のとおりです。

// STC8G1K08A I2C アドレスサーチ C言語版
//
// STC8G1K08A   MPU
// Flash ROM 8Kbyte
// RAM       1Kbyte
// 
// SDCC 4.0.0
//
// file name STC8G1K08A_I2C_search.c
// $ sdcc -mmcs51 --model-small --xram-size 0x400 --xram-loc 0x0000 --code-size 0x2000 STC8G1K08A_I2C_search.c
// $ packihx STC8G1K08A_I2C_search.ihx > STC8G1K08A_I2C_search.hex
// $ stcgal -P stc8g -t 22118.4 -p /dev/ttyUSB0 STC8G1K08A_I2C_search.hex
//

#include <8052.h>
#include <stdio.h>

//XFR(拡張RAM領域特殊機能レジスタ)

#define CLKDIV (*(unsigned char volatile __xdata *)0xfe01)

#define I2CCFG (*(unsigned char volatile __xdata *)0xfe80)
#define I2CMSCR (*(unsigned char volatile __xdata *)0xfe81)
#define I2CMSST (*(unsigned char volatile __xdata *)0xfe82)
#define I2CSLCR (*(unsigned char volatile __xdata *)0xfe83)
#define I2CSLST (*(unsigned char volatile __xdata *)0xfe84)
#define I2CSLADR (*(unsigned char volatile __xdata *)0xfe85)
#define I2CTXD (*(unsigned char volatile __xdata *)0xfe86)
#define I2CRXD (*(unsigned char volatile __xdata *)0xfe87)

//BYTE 指定可能なレジスター

__sfr __at  0x8e AUXR  ;
__sfr __at  0xba P_SW2  ;

__sfr __at  0xb2 P3M0  ;    
__sfr __at  0xb1 P3M1  ;    
__sfr __at  0xca P5M0  ;    
__sfr __at  0xc9 P5M1  ;

__sbit __at 0xb3 SDA;
__sbit __at 0xb2 SCL;


    
// uart 初期化
void uart_init(void) {
    
    P_SW2 = 0x80;
    CLKDIV = 0x02;  //システムクロック分周値を 1/2にセット
    P_SW2 = 0x00;
    
    AUXR = 0xc0;    //SYSCLK/1 timer0 or timer1 clock souce 
    SCON  = 0x50;   //UARTはモード1に設定、受信イネーブル
                    //SCON=01010000B
    TMOD |= 0x20;   //タイマー1を8ビット自動リロードモードに設定
                    //TMOD=00100000B
    TH1   = 0xf7;   //タイマーカウンターセット
    TL1   = 0xf7;
    TR1   = 1;      // タイマー1スタート

}
// I2C 関連関数

int reply(void)                 //ACK、NACK返答確認
{
    int i;
    I2CMSCR = 0x03;
    while(!(I2CMSST & 0x40));
    i=I2CMSST & 0x02;
    I2CMSST &= ~0x40;
    return (i >> 1);            //ACK =0,NACK=1
}


// 1文字シリアル出力
int putchar(int data) {
    TI = 0;         //SCONのTI(1bit目)をケリア
    SBUF = data;    //dataからSBUFバッファーに格納
    while (!TI);    //SCONのTI(1bit目)が"1"なるまでループ
    return data;
}

// 1文字シリアル入力
int getchar(void) {
    while (!RI);     //SCONのRI(0bit目)が"1"なるまでループ
    RI = 0;          //SCONのRI(0bit目)をクリア
    return SBUF;     // 受信データ返却
}
int i2c_addr(int addr)                      //I2Cアドレスサーチ
{   
        int i;

        I2CMSCR = 0x01;                     //I2C スタート
        while(!(I2CMSST & 0x40));
        I2CMSST &= ~0x40;   
    
        I2CTXD = ((addr << 1) | 0x01);      //I2C_ADDR + READ 送信
        I2CMSCR = 0x02;
        while(!(I2CMSST & 0x40));
        I2CMSST &= ~0x40;

        i=reply();                          //ACK、NACK返答確認

        I2CMSCR = 0x06;                     //I2C ストップ
        while(!(I2CMSST & 0x40));
        I2CMSST &= ~0x40;       
        return i;
}
void main(void)     //main関数
{
    P3M0 = 0x00;    //P3ポートを準双方向モードに
    P3M1 = 0x00;
    P5M0 = 0x00;    //P5ポートを準双方向モードに
    P5M1 = 0x00;    

    uart_init();
    
    printf_tiny("STC8G1K08A I2C address search Test !\r\n");

    int i, j, addr;

    P_SW2 = 0x80;
    I2CCFG = 0xe0;     //マスターモード
    I2CMSST = 0x00;    

    do{ printf_tiny("I2C Address Search (STC8G1K08A)\r\n");
        printf_tiny("Addr 0 1 2 3 4 5 6 7 8 9 a b c d e f\r\n");
        for (i = 0; i <= 0x7; i++) {
            printf_tiny("  %x ", i*0x10);
            for (j = 0; j <= 0xf; j++) {
                addr = i * 0x10 + j;
                switch(i2c_addr(addr)){
                    case 0x1:                   //addr+R >> NACK
                        printf_tiny("* ");
                        break;
                    case 0x0:                   //addr+R >> ACK
                        printf_tiny("%x",addr);
                        break;
                    default:
                        printf_tiny("er");
            }
        }
        printf_tiny("\r\n");
    };
    printf_tiny("s key = research!\r\n");
    }while(getchar() == 's');
}   

コンパイルします。

$ sdcc -mmcs51 --model-small --xram-size 0x400 --xram-loc 0x0000 --code-size 0x2000 STC8G1K08A_I2C_search.c

ihxからインテルHEXコードhexに変換します。

$ packihx STC8G1K08A_I2C_search.ihx > STC8G1K08A_I2C_search.hex
packihx: read 46 lines, wrote 81: OK.

STC8G1K08Aに書き込みします。

$ stcgal -P stc8g -t 22118.4 -p /dev/ttyUSB0 STC8G1K08A_I2C_search.hex
Waiting for MCU, please cycle power: done
Target model:
  Name: STC8G1K08A-8PIN
  Magic: F794
  Code flash: 8.0 KB
  EEPROM flash: 4.0 KB
Target frequency: 22.094 MHz
Target BSL version: 7.3.13U
Target wakeup frequency: 36.800 KHz
Target ref. voltage: 1185 mV
Target mfg. date: 2024-07-01
Target options:
  reset_pin_enabled=False
  clock_gain=high
  watchdog_por_enabled=False
  watchdog_stop_idle=True
  watchdog_prescale=64
  low_voltage_reset=False
  low_voltage_threshold=2
  eeprom_erase_enabled=True
  bsl_pindetect_enabled=False
  por_reset_delay=long
  rstout_por_state=high
  uart1_remap=False
  uart2_passthrough=True
  uart2_pin_mode=push-pull
  epwm_open_drain=True
  program_eeprom_split=8192
Loading flash: 1172 bytes (Intel HEX)
Target frequency: Target 22.118 MHz
Adjusted frequency: 22.102 MHz(-0.076%)
Switching to 115200 baud: done
Erasing flash: done
Writing flash: 1600 Bytes [00:00, 6286.93 Bytes/s]
Finishing write: done
Setting options: done
Target UID: F794C4161F25D0
Disconnected!

ターミナルで確認します。
まずはI2Cデバイスを接続していないとき、続いてEEPROMである24FC1025を接続したとき(A0~A2はオープンのとき)次にA0だけをプルアップしたときのI2Cアドレスをサーチしてみました。

STC8G1K08A I2C address search Test !
I2C Address Search (STC8G1K08A)
Addr 0 1 2 3 4 5 6 7 8 9 a b c d e f
  0 * * * * * * * * * * * * * * * * 
  10 * * * * * * * * * * * * * * * * 
  20 * * * * * * * * * * * * * * * * 
  30 * * * * * * * * * * * * * * * * 
  40 * * * * * * * * * * * * * * * * 
  50 * * * * * * * * * * * * * * * * 
  60 * * * * * * * * * * * * * * * * 
  70 * * * * * * * * * * * * * * * * 
s key = research!
I2C Address Search (STC8G1K08A)
Addr 0 1 2 3 4 5 6 7 8 9 a b c d e f
  0 * * * * * * * * * * * * * * * * 
  10 * * * * * * * * * * * * * * * * 
  20 * * * * * * * * * * * * * * * * 
  30 * * * * * * * * * * * * * * * * 
  40 * * * * * * * * * * * * * * * * 
  50 50* * * * * * * * * * * * * * * 
  60 * * * * * * * * * * * * * * * * 
  70 * * * * * * * * * * * * * * * * 
s key = research!
I2C Address Search (STC8G1K08A)
Addr 0 1 2 3 4 5 6 7 8 9 a b c d e f
  0 * * * * * * * * * * * * * * * * 
  10 * * * * * * * * * * * * * * * * 
  20 * * * * * * * * * * * * * * * * 
  30 * * * * * * * * * * * * * * * * 
  40 * * * * * * * * * * * * * * * * 
  50 * 51* * * * * * * * * * * * * * 
  60 * * * * * * * * * * * * * * * * 
  70 * * * * * * * * * * * * * * * * 
s key = research!

次はI2Cデバイスを色々と接続してみたいですね。

おすすめの記事