MCU
STM32F103(ST)
IDE
STM32CubeIDE
목표
I2C LCD(SSD1306)를 구동함

 

0. SSD1306

- SSD1306은 단일 칩 CMOS OLED/PLED 드라이버로, 유기/고분자 발광을 위한 컨트롤러

- OLED는 저전력 소비, 높은 대비, 선명한 이미지 품질로 알려져 있어 작은 전자 프로젝트, 웨어러블 기기 및 임베디드 시스템에 이상적

- 다이오드 도트 매트릭스 그래픽 디스플레이 시스템. 128개의 세그먼트와 64개의 공통으로 구성

- SSD 1306에는 256단계 밝기 제어, 디스플레이 RAM 및 오실레이터가 내장

- 일반적으로 다양한 크기로 제공되며, 0.96인치에서부터 더 큰 크기도 있음

- SSD1306 디스플레이를 프로젝트에서 사용하려면 I2C(Inter-Integrated Circuit) 사용하여 통신

 

- SSD1306 Datashet

 

1. STM32CubeIDE - CubeMX

- I2C2 Mode and Configuration : Disable → I2C

- Project - Generate Code


 

2. github에서 library 다운로드

https://github.com/afiskon/stm32-ssd1306

 

GitHub - afiskon/stm32-ssd1306: STM32 library for working with OLEDs based on SSD1306, SH1106, SH1107 and SSD1309, supports I2C

STM32 library for working with OLEDs based on SSD1306, SH1106, SH1107 and SSD1309, supports I2C and SPI - afiskon/stm32-ssd1306

github.com

 

- *.c는 프로젝트 폴더의 Core\Src 폴더 안에 넣음

- *.h는 프로젝트 폴더의 Core\Inc 폴더 안에 넣음

- Build Project 하면 자동으로 Compile 함

- ssd1306.c, ssd1306.h : ssd1306을 사용하기 위한 함수 모음

- ssd1306_fonts.c, ssd1306_fonts.h : 글자를 뿌리기 위한 폰트(크기별)

 

3. STM32CubeIDE - IDE

- main.c의 main() 수정

  /* USER CODE BEGIN 2 */
  ssd1306_Init();
  HAL_Delay(1000);

  ssd1306_SetCursor(0,0);
  ssd1306_WriteString("LINE1", Font_7x10, White);

  ssd1306_SetCursor(0,10);
  ssd1306_WriteString("LINE2", Font_7x10, White);

  ssd1306_SetCursor(0,20);
  ssd1306_WriteString("LINE3", Font_7x10, White);

  ssd1306_SetCursor(0,30);
  ssd1306_WriteString("LINE4", Font_7x10, White);

  ssd1306_SetCursor(0,40);
  ssd1306_WriteString("LINE5", Font_7x10, White);

  ssd1306_SetCursor(0,50);
  ssd1306_WriteString("LINE6", Font_7x10, White);

  ssd1306_UpdateScreen();
  /* USER CODE END 2 */
 

- 실행 결과

 

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

STM32F103C8 - RAM 48KB + bonus?  (0) 2025.08.08
STM32F103C8 - LL driver + ADC + DMA  (0) 2025.08.08
STM32F103C8 - 1cycle Time  (0) 2025.08.08
STM32F103C8 - I2C HAL driver 오류  (0) 2025.08.08
STM32F103C8 - ADC(DMA interrupt)  (1) 2025.08.08

 

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

+ Recent posts