HALドライバでI2C EEPROM制御

STM32CubeMXを利用したHALドライバによるI2C EEPROM制御コードの記録です。

ターゲットマイコン：[STM32L152RE](https://www.st.com/ja/microcontrollers-microprocessors/stm32l152re.html "STM32L152RE")

一部結果の入出力関数、遅延関数については割愛しております。
細かい説明抜きにソースコード貼り付けいたします。

【注意事項】
本記事中のソフトウェアはサンプルコードであり、ソフトウェアを使用した結果いかなる損害等が発生しても当方は一切責任を負いません。

●ソースコード

```ruby:i2c_eep.c
/****************************************************************************
 *	I2C EEPROM (24LC512 or 24LC1025) 制御プログラム
 *--------------------------------------------------------------------------*
 *	File	i2c_eep.c
 *	Date	2020.01
 *	Rev		00.01
 *	Edit	goroja
 *
 ****************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include	<stdlib.h>
#include	<string.h>
#include	<ctype.h>

#include	"main.h"
#include	"stm32l1xx.h"
#include	"stm32l1xx_hal.h"
#include	"i2c_eep.h"
#include	"_lib.h"

/* Private typedef -----------------------------------------------------------*/

/* Private define ------------------------------------------------------------*/
//#define 	DBG_I2C_EEP				// I2C EEPROM制御プログラムデバッグ機能有効 (外部定義可)

#define 	EEP_SLAVE_ADDRESS		0xA0
#define 	EEP_BLOCK0				0x00
#define 	EEP_BLOCK1				0x08

#define 	I2C_SLAVE_ADDRESS7		0x00

/* Private macro -------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/

/* Private function prototypes -----------------------------------------------*/

/* Private functions ---------------------------------------------------------*/

/* Gowbal variables ----------------------------------------------------------*/

extern I2C_HandleTypeDef hi2c1;

#define 	hI2C		&hi2c1			// I2Cハンドル定義

HAL_StatusTypeDef		i2c_result;		// I2Cドライバ最終実行結果

/********************************************************
 *	初期化
 *	in:
 *		なし
 *	out:
 *		EEP_OK			正常終了
 ********************************************************/
int eep_ini(void)
{
	if (stf_I2C1_Init) return EEP_OK; // 初期化済み

	//	自動生成コードにて初期化されるので本関数ではなにも行わない。

	stf_I2C1_Init = 1;	// I2C1初期化フラグセット

	return EEP_OK;
}

/********************************************************
 *	データ読み込み (1byte)
 *	in:
 *		adr			メモリアドレス (0x00000～0x1FFFF)
 *		rdp			データ格納ポインタ
 *		len			読み込み数
 *	out:
 *		EEP_OK			正常終了
 *		EEP_ERR_RD		リードエラー
 ********************************************************/
int eep_read(uint32_t adr, uint8_t* rdp, uint32_t len)
{
	uint16_t CtrlByte;

	if (!stf_I2C1_Init) return EEP_NO_INIT; // 未初期化

	if (eep_rdy() != EEP_OK) return EEP_BUSY; /* バスビジー */

	CtrlByte = (adr & 0x10000)?
		EEP_SLAVE_ADDRESS | EEP_BLOCK1:		// Block-1 select
		EEP_SLAVE_ADDRESS | EEP_BLOCK0;		// Block-0 select

	// Ready待ち
	for (int i=0; i<500; i++) {
		i2c_result = HAL_I2C_IsDeviceReady(hI2C, CtrlByte, 1, 100); // レディー待ち
		if (i2c_result == HAL_OK) break;
		dly_tsk(1/MSEC);
	}
	if (i2c_result != HAL_OK) {
		return EEP_BUSY; // ビジー
	}
	// Read
	i2c_result = HAL_I2C_Mem_Read(hI2C, CtrlByte, (uint16_t)(adr&0xFFFF), sizeof(uint16_t), rdp, (uint16_t)len, 100);
	if (i2c_result != HAL_OK) {
		return EEP_ERR_RD;
	}
	return EEP_OK;
}

/********************************************************
 *	データ書き込み (1byte)
 *	in:
 *		adr			メモリアドレス (0x00000～0x1FFFF)
 *		wdp			書き込みデータポインタ
 *		len			書き込み数
 *	out:
 *		
 ********************************************************/
int eep_write(uint32_t adr, uint8_t* wdp, uint32_t len)
{
	uint16_t CtrlByte;

	if (!stf_I2C1_Init) return EEP_NO_INIT; // 未初期化

	if (eep_rdy() != EEP_OK) return EEP_BUSY; /* バスビジー */

	CtrlByte = (adr & 0x10000)?
		EEP_SLAVE_ADDRESS | EEP_BLOCK1:		// Block-1 select
		EEP_SLAVE_ADDRESS | EEP_BLOCK0;		// Block-0 select

	// Ready待ち
	for (int i=0; i<500; i++) {
		i2c_result = HAL_I2C_IsDeviceReady(hI2C, CtrlByte, 1, 100); // レディー待ち
		if (i2c_result == HAL_OK) break;
		dly_tsk(1/MSEC);
	}
	if (i2c_result != HAL_OK) {
		return EEP_BUSY; // ビジー
	}

	// 書き込み
	i2c_result = HAL_I2C_Mem_Write(hI2C, CtrlByte, (uint16_t)(adr&0xFFFF), sizeof(uint16_t), wdp, (uint16_t)len, 100);
	if (i2c_result != HAL_OK) {
		return EEP_ERR_RD;
	}
	return EEP_OK;
}

/********************************************************
 *	レディーチェック
 *	in:
 *		なし
 *	out:
 *		EEP_OK			正常終了
 *		EEP_BUSY		ビジー
 ********************************************************/
int eep_rdy(void)
{
	__IO uint32_t Timeout = 1000;
	/* While the bus is busy */
    while (__HAL_I2C_GET_FLAG(hI2C, I2C_FLAG_BUSY) != RESET) {
		if((Timeout--) == 0) {
			i2c_result = HAL_BUSY;
			return EEP_BUSY;
		}
		dly_tsk(1/MSEC);
	}
	return EEP_OK;
}

#ifdef DBG_I2C_EEP
/********************************************************
 *	データダンプ
 *	in:
 *		adr			メモリアドレス
 *		len			ダンプ数
 *		mod			表示モード (0:Dump 1:LF)
 *	out:
 *		EEP_OK			正常終了
 *		EEP_ERR_RD		リードエラー
 *		EEP_ADR_OVR		アドレス範囲超過
 ********************************************************/
int eep_dump(uint32_t ba, uint32_t len, int mod)
{
	static uint32_t adr	= 0L;
	static uint32_t wod				= 256L;

	int i, j, rsl;
	uint32_t end;
	uint8_t buf[16];

	if (ba<=0x000FFFFF) adr = ba;	/* set address */
	if (len) wod = len;				/* set length */
	if (mod==0) {
		/* ダンプ表示 */
		_printf("address : ");
		for (i=0,j=adr%16; i<16; i++,j++) _printf("+%01X ", j%16);
		_crlf();
		for (i=0,end=adr+wod; adr < end; i=(i+1)%16) {
			if (i==0) _printf("%08lX: ", adr);
			rsl = eep_read(adr, &buf[i], 1);
			if (rsl != EEP_OK) {
				// エラー
				_printf("\r\n dump error. %d\r\n", rsl);
				return rsl;
			}
			_printf("%02x ", (int)buf[i]);
			if (i==15) {
				_printf("   ");
				for (j=0; j<16; j++) {	/* 文字ｲﾒｰｼﾞ表示 */
//					_printf("%c", (isprint(buf[j]))? (int)buf[j]: '.');
					_printf("%c", (0x20<=buf[j] && buf[j]<=0x7E)? (int)buf[j]: '.' ); //←ARM系コンパイラ & Tera Termで表示こけ対策版
				}
				_crlf();
			}
			adr++;
			if (adr>0x000FFFFF) break;
			if (_stat()) { if (_getc() == 0x1B) break; } // 中断判定
		}
		i%=16;
		if (i!=0) {
			/* 残表示 */
			for (j=16-i; j!=0; j--) _printf("   ");
			_printf("   ");
			for (j=0; j<i; j++) {		/* 文字ｲﾒｰｼﾞ表示 */
//				_printf("%c", (isprint(buf[j]))? (int)buf[j]: '.');
				_printf("%c", (0x20<=buf[j] && buf[j]<=0x7E)? (int)buf[j]: '.' ); //←ARM系コンパイラ & Tera Termで表示こけ対策版
			}
			_crlf();
		}
	}
	else {
		/* 改行表示 */
		for (end=adr+wod; adr < end; ) {
			rsl = eep_read(adr, buf, 1);
			if (rsl == EEP_OK) {
				_printf( "%08lX,%02x\r\n", adr, (unsigned int)buf[0]);
			} else {
				// リードエラー
				_printf("\r\n dump error. %d\r\n", rsl);
				return rsl;
			}
			adr++;
			if (adr>0x000FFFFF) break;
			if (_stat()) { if (_getc() == 0x1B) break; } // 中断判定
		}
	}
	if (adr>0x000FFFFF) {
		adr = 0L;
		return EEP_ADR_OVR;
	}
	return EEP_OK;
}

/*****************************************************************************
 * SERIAL FLASH MEMORY DEBUG FUNCTION
 *****************************************************************************/
int i2c_eep_main(int argc, char *argv[])
{
	char *cmd;

	cmd = argv[1];

	strtolower(cmd); /* 小文字へ */

	if (!strcmp(cmd, "init")) {
		// フラッシュメモリ制御の初期化
		int rsl;
		stf_I2C1_Init = 0; // 初期化済みフラグクリア
		rsl = eep_ini();
		_printf(" done. (rsl:%d)\r\n", rsl);
	}
	else if (!strcmp(cmd, "read") || !strcmp(cmd, "r")) {
		// シリアルフラッシュREADテスト
		int rsl;
		UB dt;
		UW adr = 0L;
		if (argv[2]) adr = strtoul(argv[2], NULL, 16);
		rsl = eep_read(adr, &dt, 1);
		_printf(" READ [%08X] -> %02X (r:%d)\r\n", adr, dt, rsl);
	}
	else if (!strcmp(cmd, "write") || !strcmp(cmd, "w")) {
		// シリアルフラッシュWRITEテスト
		int rsl;
		UB dt = 0;
		UW adr = 0L;
		if (argv[2]) {
			adr = strtoul(argv[2], NULL, 16);
			if (argv[3]) {
				dt = (UB)axtoi(argv[3]);
			}
		}
		rsl = eep_write(adr, &dt, 1);
		_printf(" WRITE [%08X] <- %02X (r:%d)\r\n", adr, dt, rsl);
	}
	else if (!strcmp(cmd, "dump") || !strcmp(cmd, "d")) {
		// シリアルフラッシュ メモリダンプ
		UW adr = 0L;
		uint32_t wod = 0L;
		int mod = 0;
		int rsl, c;
		if (argv[2]) adr = strtoul(argv[2], NULL, 16);
		if (argv[3]) wod = strtoul(argv[3], NULL, 16);
		if (argv[4]) mod = atoi(argv[4]);
		do {
			rsl = eep_dump(adr, wod, mod);
			if (rsl != EEP_OK) break; // 終了
			if (wod != 0L) break; // 終了
			adr = (UW)(-1L);
			while (!_stat()) dly_tsk(10L/MSEC); // キー入力まで待つ
			c = _getc();
			if (c == CHR_ESC) {
				_printf("\r\n stop\r\n");
				break; // 終了
			}
		} while (1);
		_puts(" done.");
	}
	else if (!strcmp(cmd, "rsl") || !strcmp(cmd, "result")) {
		// 最終実行結果表示
		_printf(" HAL Last result = %02XU\r\n", i2c_result);
	}
	else {
		_puts(" Illegal command.");
	}
	return 0;
}
#endif //DBG_I2C_EEP

/*** End of file ***/
```

●ヘッダファイル

```ruby:i2c_eep.h
/**
  ******************************************************************************
  * @file    i2c_eep.h
  ******************************************************************************
  */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __I2C_EEP_H
#define __I2C_EEP_H

#ifdef __cplusplus
 extern "C" {
#endif

#include	"main.h"
#include	"stm32l1xx.h"

#if defined(EEP_24LC512)
 #define 	EEP_MAX_SIZE	(64*1024)	// 64KB
#elif defined(EEP_24LC1025)
 #define 	EEP_MAX_SIZE	(128*1024)	// 128KB
#else
 #warning 	※EEPROM型番を定義する。(容量は初期値 4096kB)
 #define 	EEP_MAX_SIZE	(4096)		// 4KB
#endif

/*** エラーコード定義 ***/
#define 	EEP_OK			(0)			// 正常終了
#define 	EEP_BUSY		(-1)		// デバイスビジー
#define 	EEP_ERR_RD		(-2)		// リードエラー
#define 	EEP_ERR_WR		(-3)		// ライトエラー
#define 	EEP_ADR_OVR		(-4)		// アドレス範囲超過
#define 	EEP_ERR_BER		(-5)		// ブロック消去エラー
#define 	EEP_ERR_CER		(-6)		// チップ消去エラー
#define 	EEP_ERR_PRM		(-7)		// パラメータエラー
#define 	EEP_ERR_INTER	(-9)		// 内部エラー
#define 	EEP_TMO1		(-11)		// 処理タイムアウト
#define 	EEP_TMO2		(-12)		// 処理タイムアウト
#define 	EEP_TMO3		(-13)		// 処理タイムアウト
#define 	EEP_TMO4		(-14)		// 処理タイムアウト
#define 	EEP_TMO5		(-15)		// 処理タイムアウト
#define 	EEP_TMO6		(-16)		// 処理タイムアウト
#define 	EEP_TMO7		(-17)		// 処理タイムアウト
#define 	EEP_TMO8		(-18)		// 処理タイムアウト
#define 	EEP_TMO9		(-19)		// 処理タイムアウト
#define 	EEP_NO_INIT		(-20)		// 未初期化エラー
#define 	EEP_INV_CALL	(-21)		// 無効関数呼び出し

/*** 関数プロトタイプ定義 ***/
int eep_ini(void);
int eep_wen(void);
int eep_read(uint32_t ba, uint8_t* rdp, uint32_t len);
int eep_write(uint32_t ba, uint8_t* wdp, uint32_t len);
int eep_rdy(void);
int eep_poll(uint8_t CtrlByte);
int eep_ber(uint32_t ba, int bs, int dt);
int eep_cer(void);
int eep_dump(uint32_t ba, uint32_t len, int mod);

#ifdef __cplusplus
}
#endif

#endif /* __I2C_EEP_H */


/****END OF FILE****/
```

以上