ATOLLIC에서 I2C HAL driver를 사용하여 I2C 방식 EEPROM 을 읽고 쓰는 글을 올렸었다.

테스트 과정에서 EEPROM 읽기 쓰기가 동작 했다 안했다 하여 한참 고생한 적이 있다.

 

정확한 원인은 찾지 못했으나,

- 오실로스코프로 측정 하여 MX_I2C1_Init() 과정에서 이상 파형이 출력되는 것을 확인

- 기준 clock이 enable 되는 시점이 문제라고 판단하여 그부분을 수정하였고 정상 동작됨을 확인 하였다.

(아래 파형 사진은 Time division이 모두 같음)

 

1. 현상

: I2C 초기화 중 SDA, SCL에 pulse 파형이 출력(MX_I2C1_Init()만 호출해도 결과는 같음)

 

2. 조치

: MX_I2C1_Init()의 I2C clock enable 시점을 변경하여 문제 해결

(회로 변경 X)

 

재미있는건, ST MCU shortage로 GigaDevice 복제품을 테스트 중이라 2번 조치 없이 I2C를 테스트 해보았는데, 파형은 아래와 같으며 정상 동작 한다.

 

정확한 원인을 아시거나, 같은 문제로 고민하시는 분은 댓글 달아주시면 많은 도움이 될것입니다.

 

'ST > STM32F103C8' 카테고리의 다른 글

STM32F103C8 - LL driver + ADC + DMA  (0) 2025.08.08
STM32F103C8 - 1cycle Time  (0) 2025.08.08
STM32F103C8 - ADC(DMA interrupt)  (1) 2025.08.08
STM32F103C8 - I2C(EEPROM)  (1) 2025.08.08
STM32F103C8 - RTC(Systick handler)  (0) 2025.08.08

 

MCU
STM32F103(ST)
IDE
Atollic(TrueSTUDIO), STM32CubeMX
목표
HAL driver를 사용하여 I2C EEPROM 쓰고 읽기

 

1. STM32F103C8 - I2C EEPROM 회로도

- SDA, SCL pull-up 저항 값은 아래와 같이 설정되므로 적당히 아무거나(10k) 붙이면 통신이 안될수도 있음(NXP, UM10204 참고)

 

2. STM32CubeMX : I2C 설정

- 'Pinout & Configuration' Tab으로 이동

1) I2C1 선택

2) I2C : I2C 선택

3) I2C Clock Speed는 기본 100kHz로 설정된다. 사용하려는 device가 이 속도를 지원하는지 확인 할것

- PB7(I2C1_SDA), PB6(I2C1_SCL) 자동 할당(녹색 표시)

- GENERATE CODE 클릭


3. ATOLLIC : main.c 수정

- main() : 변수 선언

int main(void)
{
  /* USER CODE BEGIN 1 */
	HAL_StatusTypeDef status = HAL_ERROR;

	int i = 0;

	uint16_t FM24V02_addr = 0xA0;

	uint8_t tx_buffer[65] = {};
	uint8_t rx_buffer[65] = {};

  /* USER CODE END 1 */
 

- Address는 A2, A1, A0를 GND('0')에 연결하여 '0b1010000'이다.

- HAL_I2C_Mem_Write(), Read() 사용하려면 7bit address를 <<1 shift 하라고 되어있으므로 최종 주소는 '0xA0' (HAL_I2C_Mem_Write() 설명은 ATOLLIC에서 함수 이름에 마우스 커서를 올려놓거나, 키보드 F3을 누르거나 또는 HAL lib manual에서 확인 가능)

 

- main() : 실행 코드 작성

  /* USER CODE BEGIN 2 */
	tx_buffer[0] = 0x12;

	// Byte Write
	status = HAL_I2C_Mem_Write(&hi2c1, FM24V02_addr, 0x0001, 2, tx_buffer, 1, 1000);
	if(status != HAL_OK)
	{
		printf("I2C Byte Write error\n");
	}

	// Byte Read
	status = HAL_I2C_Mem_Read(&hi2c1, FM24V02_addr, 0x0001, 2, rx_buffer, 1, 1000);
	if(status == HAL_OK)
	{
		printf("Byte Read : %d\n", rx_buffer[0]);
	}
	else
	{
		printf("I2C Byte Read error\n");
	}

	// Page Write
	for (i = 0; i < 64; i++)
	{
		tx_buffer[i] = i;
	}
	status = HAL_I2C_Mem_Write(&hi2c1, FM24V02_addr, 0x0100, 2, tx_buffer, 64, 1000);
	if(status != HAL_OK)
	{
		printf("I2C Page Write error\n");
	}

	// Page Read
	status = HAL_I2C_Mem_Read(&hi2c1, FM24V02_addr, 0x0100, 2, rx_buffer, 64, 1000);
	if(status == HAL_OK)
	{
		for (i = 0; i < 64; i++)
		{
			printf("Page Read(%d) : %d\n", i, rx_buffer[i]);
		}
	}
	else
	{
		printf("I2C Page Read error\n");
	}

  /* USER CODE END 2 */
 

- 실행 결과

- 실행 결과 이하 생략

 

- 참고 : 일부러 Address를 틀리게 썼을때 실행 결과


4. 8bit 데이터는 위와 같이 하면 문제 없이 동작하나, 16bit 데이터(256이상)를 쓰고 읽어오려면 약간 다른 방법으로 해야 한다.

- write() : 16bit 데이터를 8bit로 쪼개서 쓰기

- read() : 8bit씩 읽어서 16bit로 합침

uint16_t FM24V02_addr = 0xA0;
uint8_t reg_addr = 0x0001;

uint8_t data_8bit = 0;
uint16_t data_16bit[65] = {};

void eeprom_i2c_write(uint16_t size)
{
	HAL_StatusTypeDef status = HAL_ERROR;
	reg_addr = 0x0001;

	for(int i = 0; i< size; i++)
    {
		data_8bit = (uint8_t)(data_16bit[i] >> 8);
		status = HAL_I2C_Mem_Write(&hi2c1, FM24V02_addr, reg_addr+(i*2), 2, &data_8bit, 1, 1000);
		HAL_Delay(10);

		data_8bit = (uint8_t)(data_16bit[i] & 0xff);
		status = HAL_I2C_Mem_Write(&hi2c1, FM24V02_addr, reg_addr+(i*2)+1, 2, &data_8bit, 1, 1000);

		if(status != HAL_OK)
		{
		    printf("EEPROM Byte Write error : %d\n", status);
		}
    }
}

void eeprom_i2c_read(uint16_t size)
{
	HAL_StatusTypeDef status = HAL_ERROR;
	reg_addr = 0x0001;

    for(int i = 0; i< size; i++)
    {
    	status = HAL_I2C_Mem_Read(&hi2c1, FM24V02_addr, reg_addr+(i*2), 2, &data_8bit , 1, 1000);
    	data_16bit[i] = data_8bit << 8;
        HAL_Delay(10);

        status = HAL_I2C_Mem_Read(&hi2c1, FM24V02_addr, reg_addr+(i*2)+1, 2, &data_8bit , 1, 1000);
        data_16bit[i] += data_8bit;

		if(status == HAL_OK)
		{
			printf("EEPROM Page Read(%d) : %d\n", i, data_16bit[i]);
		}
		else
		{
			printf("EEPROM Byte Read error\n");
		}
    }
}
 

'ST > STM32F103C8' 카테고리의 다른 글

STM32F103C8 - I2C HAL driver 오류  (0) 2025.08.08
STM32F103C8 - ADC(DMA interrupt)  (1) 2025.08.08
STM32F103C8 - RTC(Systick handler)  (0) 2025.08.08
STM32F103C8 - PWM  (0) 2025.08.08
STM32F103C8 - TIMER  (1) 2025.08.08

 

Target
Board
Raspberry Pi CM4 + IO Board
Host
PC - Window
목표
CM4의 EEPROM 부트로더를 최신화하고 BOOT_ORDER를 수정하여 부팅 순서 설정

 

1. Raspberry Pi Compute Module 4(이하 CM4)의 EEPROM 부트로더 개요

- CM4에는 EEPROM이 내장되어 있으며, 이 EEPROM에는 부트로더(Bootloader)가 저장됨

- CM4는 EEPROM에 저장된 부트로더를 실행하여 운영체제를 로드

 

2. CM4의 부트 과정

CM4의 부팅 과정은 다음과 같음

(1). EEPROM에서 부트로더 실행

- CM4는 부팅 시 SPI EEPROM에서 부트로더를 로드하고 실행

- 이 부트로더는 Raspberry Pi 재단에서 제공하며, 펌웨어 업데이트를 통해 변경할 수 있음

 

(2). 부팅 소스 확인

 

(3). 부트로더는 설정된 부팅 순서(BOOT_ORDER)에 따라 가능한 부팅 장치를 검색

기본적인 부팅 순서는 다음과 같습니다:

- eMMC (또는 SD 카드)

- USB 장치 (USB Storage, USB Boot 등)

- Network Boot (PXE, TFTP)

- Fallback (재시도 및 복구 모드 진입)

 

(4). 운영체제 커널 로드 및 실행

- 선택된 부팅 장치에서 운영체제 커널을 로드하여 실행


1. CM4 EEPROM Bootloader 업데이트를 위한 준비물

 
HOST
CM4 또는 rpiboot 도구가 설치된 Linux PC
DEVICE
CM4
케이블
USB to USB-A 케이블 (CM4의 경우 USB-C to USB-A 케이블)

 

HOST $ sudo apt install git libusb-1.0-0-dev pkg-config build-essential
HOST $ git clone --recurse-submodules --shallow-submodules --depth=1 https://github.com/raspberrypi/usbboot
 

 

- bootloader 수정

HOST $ cd ~/usbboot/recovery
HOST $ nano boot.conf
 

 

- 아래와 같이 수정

[all]
BOOT_UART=0
WAKE_ON_GPIO=1
POWER_OFF_ON_HALT=0
 
# Try SD first (1), followed by, USB PCIe, NVMe PCIe, USB SoC XHCI then network
BOOT_ORDER=0xf25641
 
# Set to 0 to prevent bootloader updates from USB/Network boot
# For remote units EEPROM hardware write protection should be used.
ENABLE_SELF_UPDATE=1
 
HOST $ ./update-pieeprom.sh
 

 

STEP2 : DEVICE

- CM4를 USB 모드로 설정 : EMMC-DISABLE / nRPIBOOT(GPIO 40)를 GND에 연결. ROM을 usbboot 모드로 전환해야 함. 그렇지 않으면 SPI EEPROM 부트로더 이미지가 대신 로드됨

- CM4를 HOST의 USB 포트에 연결

- 전원을 공급

 

STEP3 : HOST

- recovery/pieeprom.bin부트로더 EEPROM에 쓰려면 다음 명령을 실행

- 다음 명령을 실행하여 업데이트된 pieeprom.bin이미지를 EEPROM에 씁니다.

HOST $ cd ~/usbboot

HOST $ sudo ./rpiboot -d recovery
 

완료되면 EEPROM_nWP는 low로 떨어짐

 

 

HOST 화면

 

DEVICE 화면 : rpiboot 실행 전 rpi-eeprom-config로 boot.conf 확인

 

DEVICE 화면 : rpiboot 실행 후 rpi-eeprom-config로 boot.conf 확인

 

BOOT_ORDER의 다양한 부팅 모드 설정 속성

Value
Mode
Description
0x0
SD CARD DETECT
Try SD then wait for card-detect to indicate that the card has changed - deprecated now that 0xf (RESTART) is available.
0x1
SD CARD
/eMMC
SD card (or eMMC on Compute Module 4).
0x2
NETWORK
Network boot
0x3
RPIBOOT
RPIBOOT
0x4
USB-MSD
USB mass storage boot
0x5
BCM-USB-MSD
USB 2.0 boot from USB Type C socket (CM4: USB type A socket on CM4IO board).
0x6
NVME
CM4 only: boot from an NVMe SSD connected to the PCIe interface.
0x7
HTTP
HTTP boot over ethernet.
0xe
STOP
Stop and display error pattern. A power cycle is required to exit this state.
0xf
RESTART
Restart from the first boot-mode in the BOOT_ORDER field i.e. loop

 

사용 예

0xf25641 Network → BCM-USB-MSD → NVME → USB → SD/eMMC
0xf14 USB → SD/eMMC → Network
0x1 SD/eMMC 우선
0xf21 Network → USB → SD/eMMC

 

+ Recent posts