STM32 마이크로컨트롤러로 AHT10 온습도 센서 데이터 읽기

STM32이 하드웨어 I2C로 AHT10 온습도 센서 데이터를 읽어 0.96인치 OLED 화면에 표시하기

제가 사용한 MCU는 STM32F103C8T6이며, 코드는 ST 표준 라이브러리로 작성했습니다.

STM32이 하드웨어 I2C로 SHTC3 온습도 센서 읽기: https://blog.zeruns.com/archives/692.html

전자/MCU 기술 모임 단톡: 2169025065

구현 결과 사진


I2C 프로토콜 소개

I2C 통신 프로토콜(Inter-Integrated Circuit)은 필립스(Philips)가 개발했으며, 핀이 적고 하드웨어 구현이 간단하며 확장성이 뛰어나 USART, CAN 등의 외부 트랜시버 없이(레벨 변환 칩 없이) 시스템 내 여러 IC 간 통신에 폭넓게 사용됩니다.

I2C는 SDA(Serial Data Line)라는 하나의 데이터 버스만 있어 1비트씩 전송하는 시리얼 통신이며, 하프 듀플렉스 방식입니다.

하프 듀플렉스: 양방향 통신이 가능하나 동시 양방향은 불가하고 번갈아 전송해야 하며, 한 순간에는 한 방향만 전송되어야 하므로 데이터선 1개면 충분합니다.
I2C 통신은 물리 계층과 프로토콜 계층으로 나눕니다. 물리 계층은 기계·전기적 특성(하드웨어)을 규정해 물리 매체에서 원시 데이터 전송을 보장하고, 프로토콜 계층은 통신 로직을 규정해 송수신 측의 데이터 패킹/언패킹 기준(소프트웨어)을 통일합니다.

I2C 물리 계층

I2C 통신 장치 간 일반 연결 방식

  1. 다중 장치 버스: 여러 장치가 공유하는 신호선. 하나의 I2C 버스에 여러 마스터·슬레이브 장치 연결 가능.
  2. 두 개 선만 사용: 양방향 SDA(Serial Data Line)와 SCL(Serial Clock Line). 데이터선은 데이터 표현, 클럭선은 동기화.
  3. 풀업 저항: 버스를 전원에 연결. I2C 장치가 idle일 때 하이 임피던스 출력 → 모든 장치가 idle이면 풀업 저항이 버스를 하이로 당김.
    I2C 통신 시 MCU GPIO는 오픈 드레인 출력으로 설정해야 하며, 그렇지 않으면 쇼트 위험이 있습니다.

STM32 I2C에 대한 더 자세한 내용: https://url.zeruns.com/JC0Ah

AHT10 온습도 센서

소개

AHT10은 저렴하고 정밀하며 소형인 국산 온습도 센서입니다. 새로 설계된 ASIC, 개선된 MEMS 용량성 습도 소자 및 표준 온도 소자를 탑재해 이전 세대보다 신뢰성이 크게 향상되었으며 혹독한 환경에서도 안정적입니다.

AHT10 데이터시트: https://url.zeruns.com/EDEwF



주요 스펙:

  • 온도 범위: –40 ℃ ~ 85 ℃
  • 온도 오차: ±0.3 ℃
  • 습도 범위: 0 % ~ 100 %
  • 습도 오차: ±2 %
  • 동작 전압: 1.8 V ~ 3.6 V
  • 인터페이스: I2C
  • 클럭: 100 kHz, 400 kHz

장치 주소 및 읽기/쓰기 명령

I2C 시작 후 1바이트로 전송하며, 하위 1비트가 R/W 비트, 상위 7비트가 AHT10 주소.

  • 쓰기: 0x70 (0b0111 0000)
  • 읽기: 0x71 (0b0111 0001)
    STM32 하드웨어 I2C 사용 시 0x70만 입력하면 하위 비트는 라이브러리가 처리합니다.

온습도 데이터 읽기

측정 주기는 다음 3단계로 구성됩니다:

  1. 측정 명령 전송
  2. 측정 완료 대기
  3. 측정 데이터 수신

요약:

  1. 쓰기(0x70) → 트리거 명령 0xAC + 0x33 + 0x00 전송
  2. 데이터시트 기준 75 ms 이상 대기
  3. 읽기(0x71)로 6바이트 연속 수신. 첫 바이트는 상태 워드:
    • Bit3(캘리브레이션 enable)이 1인지 확인, 아니면 초기화 명령 전송
    • Bit7(바쁨 표시)가 0이면 측정 완료, 다음 단계 진행
  4. 수신 데이터 변환 처리

데이터 계산

데이터시트에 따르면:

  • 습도 = (RAW × 100) / 2²⁰ [%]
  • 온도 = (RAW × 200 / 2²⁰) – 50 [℃]

예:
습도 RAW 0x0C6501 = 812289 → 812289 × 100 / 1048576 = 77.46 %
온도 RAW 0x056A00 = 354816 → (354816 × 200 / 1048576) – 50 = 17.67 ℃

필요 부품

STM32 최소 시스템 보드: https://s.click.taobao.com/bqMwZRu
AHT10 모듈: https://s.click.taobao.com/gIF09Ru
OLED 모듈: https://s.click.taobao.com/aNlvZRu
점퍼 와이어: https://s.click.taobao.com/xAkAJRu
브레드보드: https://s.click.taobao.com/ShJAJRu
ST-LINK V2: https://s.click.taobao.com/C8ftZRu

프로그램

main.c, AHT10.c, OLED.c 3개 주요 코드만 아래에 공개하며, 나머지는 압축 파일로 다운로드 받으세요.

전체 프로젝트: https://url.zeruns.com/AHT10

AHT10 및 OLED 모듈의 SCL은 PB6, SDA는 PB7에 연결합니다.

VSCode로 Keil 대체해 STM32·51 개발하기: https://blog.zeruns.com/archives/690.html

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "IWDG.h"
#include "AHT10.h"

uint16_t numlen(uint16_t num);

int main(void)
{
	IWDG_Configuration();	// 초기화: 독립형 와치독
	OLED_Init();			// 초기화: OLED
	AHT10_Init();			// 초기화: AHT10
	
	OLED_ShowString(1, 1, "T:");
	OLED_ShowString(2, 1, "H:");

	uint32_t a=0;
	uint16_t err_count=0;
	
	while (1)
	{
		a++;
		OLED_ShowNum(3, 1, a, 9);	// 실행 확인용 카운터
		if(a==999999999)a=0;

		float Temp,Hum;				// 온·습도 저장 변수
		
		/*
		https://blog.zeruns.com
		*/

		if(ReadAHT10(&Hum,&Temp))	// 온·습도 읽기
		{
			if(Temp>=0)
			{
				char String[10];
				sprintf(String, "+%.2fC", Temp);	// 문자열 포맷
				OLED_ShowString(1, 3, String);		// 온도 표시

				sprintf(String, " %.2f%%", Hum);
				OLED_ShowString(2, 3, String);		// 습도 표시
			}else
			{
				char String[10];
				sprintf(String, "-%.2fC", Temp);
				OLED_ShowString(1, 3, String);		// 음수 온도 표시
				
				sprintf(String, " %.2f%%", Hum);
				OLED_ShowString(2, 3, String);		// 습도 표시
			}
		}
		else
		{
			err_count++;
			OLED_ShowNum(4,1, err_count, 5);	// 에러 카운터 표시
		}

		Delay_ms(100);		// 100 ms 대기

		IWDG_FeedDog();		// 와치독 갱신(1초 내 미갱신 시 리셋)
	}
}
```**AHT10.c**

```c
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"

/* AHT10 주소 */
#define AHT10_ADDRESS 0x38<<1 // 슬레이브 주소는 7비트, 마지막 비트는 전송 방향 비트이므로 한 비트 왼쪽으로 시프트

/* 사용할 I2C 선택 */
#define I2Cx I2C1

/*
https://blog.zeruns.com
*/


/* 시작 신호 전송 */
void AHT10_I2C_START(){
    while( I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY)); // 버스가 유휴 상태가 될 때까지 대기
    I2C_GenerateSTART(I2Cx, ENABLE); // 시작 신호 전송
    while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)==ERROR); // EV5 이벤트 감지
}

/* 정지 신호 전송 */
void AHT10_I2C_STOP(){
    I2C_GenerateSTOP(I2Cx, ENABLE); // 정지 신호 전송
}

/**
  * @brief  3바이트 데이터 전송
  * @param  cmd 명령 바이트
  * @param  DATA0 첫 번째 매개변수
  * @param  DATA1 두 번째 매개변수
  * @retval 없음
  */
void AHT10_WriteByte(uint8_t cmd, uint8_t DATA0, uint8_t DATA1)
{
    AHT10_I2C_START();  // 시작 신호 전송
    
    I2C_Send7bitAddress(I2Cx, AHT10_ADDRESS, I2C_Direction_Transmitter);    // 장치 쓰기 주소 전송
    while(I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);  // EV6 이벤트 감지

    I2C_SendData(I2Cx, cmd); // 명령 전송
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // EV8 이벤트 감지

    I2C_SendData(I2Cx, DATA0); // 명령 매개변수 상위 8비트 데이터 전송
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // EV8 이벤트 감지
   
    I2C_SendData(I2Cx, DATA1); // 명령 매개변수 하위 8비트 데이터 전송
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // EV8 이벤트 감지
    
    I2C_GenerateSTOP(I2Cx, ENABLE); // 정지 신호 전송    
}


/**
  * @brief  명령을 전송하여 AHT10 상태 읽기
  * @retval 읽은 상태 바이트
  */
/*uint8_t AHT10_ReadStatus(void){
    AHT10_I2C_START(); // 시작 신호 전송  
    I2C_Send7bitAddress(I2Cx,AHT10_ADDRESS,I2C_Direction_Receiver); // 장치 읽기 주소 전송
    while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR); // EV6 이벤트 감지
    while (!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_RECEIVED)); // EV7 이벤트 감지
    I2C_AcknowledgeConfig(I2Cx, DISABLE); // 응답 신호 비활성화
    uint8_t status = I2C_ReceiveData(I2Cx); // 데이터 읽기 및 반환
    AHT10_I2C_STOP();   // 정지 신호 전송
    I2C_AcknowledgeConfig(I2Cx,ENABLE); // 응답 신호 다시 활성화
    return status;
}*/

/**
  * @brief  데이터 읽기
  * @retval 읽은 바이트 데이터
  */
uint8_t AHT10_ReadData(void)
{
    while (!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_RECEIVED)); // EV7 이벤트 감지
    return I2C_ReceiveData(I2Cx); // 데이터 읽기 및 반환
}

/* AHT10 소프트웨어 리셋 */
void AHT10_SoftReset(void)                    
{
    AHT10_I2C_START();  // 시작 신호 전송
    I2C_Send7bitAddress(I2Cx, AHT10_ADDRESS, I2C_Direction_Transmitter);    // 장치 쓰기 주소 전송
    while(I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);  // EV6 이벤트 감지
    I2C_SendData(I2Cx, 0xBA); // 소프트 리셋 명령 전송
    while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // EV8 이벤트 감지
    I2C_GenerateSTOP(I2Cx, ENABLE); // 정지 신호 전송
    Delay_ms(20);
}


/* 핀 초기화 */
void AHT10_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); // I2C1 클럭 활성화
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); // GPIOB 클럭 활성화
 
    /* STM32F103 칩의 하드웨어 I2C1: PB6 -- SCL; PB7 -- SDA */
    GPIO_InitTypeDef  GPIO_InitStructure;               // GPIO 구성을 위한 구조체 정의
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;     // 출력 모드를 오픈 드레인으로 설정, 풀업 저항 필요
    GPIO_Init(GPIOB, &GPIO_InitStructure);              // GPIO 초기화
    
    I2C_DeInit(I2Cx); // I2C 외부 장치 레지스터를 기본값으로 재설정
    I2C_InitTypeDef  I2C_InitStructure;                 // I2C 구성을 위한 구조체 정의
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;          // 작동 모드
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;  // 클럭 듀티 사이클, Tlow/Thigh = 2
    I2C_InitStructure.I2C_OwnAddress1 = 0x88; // 마스터의 I2C 주소, 사용하지 않으면 아무 값이나 써도 됨, 영향 없음
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; // 응답 비트 활성화
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; // 주소 길이 7비트 설정
    I2C_InitStructure.I2C_ClockSpeed = 400000; // I2C 전송 속도, 400K, 사용하는 칩 매뉴얼에서 지원하는 속도 확인 필요
    I2C_Init(I2Cx, &I2C_InitStructure);         // I2C 초기화

    I2C_Cmd(I2Cx, ENABLE);  // I2C 활성화
    Delay_ms(20); // 전원 대기 시간
    AHT10_WriteByte(0XE1,0X08,0x00); // 초기화 명령 전송
    Delay_ms(20);

}

/**
  * @brief  AHT10 데이터 읽기
  * @param  *Hum 습도
  * @param  *Temp 온도
  * @retval 1 - 읽기 성공; 0 - 읽기 실패
  */
uint8_t ReadAHT10(float *Hum,float *Temp)
{
    uint8_t Data[5]; // 읽은 데이터를 저장할 변수 선언

    AHT10_WriteByte(0XAC,0X33,0x00); // 측정 트리거 명령 전송

    Delay_ms(70); // 측정 완료 대기 시간 70ms

    AHT10_I2C_START(); // 시작 신호 전송  
    I2C_Send7bitAddress(I2Cx,AHT10_ADDRESS,I2C_Direction_Receiver); // 장치 읽기 주소 전송
    while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR); // EV6 이벤트 감지
    
    uint8_t i;
    for(i=0;i<6;i++) // 6번 반복하여 6바이트 데이터 읽기
    {
        if (i == 5) // 마지막 바이트 읽을 때 응답 신호 비활성화
        {
            I2C_AcknowledgeConfig(I2Cx, DISABLE); // 응답 신호 비활성화
        }
        Data[i] = AHT10_ReadData(); // 데이터 읽기
        if (i == 5)
            I2C_GenerateSTOP(I2Cx, ENABLE); // 정지 신호 전송
    }
    I2C_AcknowledgeConfig(I2Cx,ENABLE); // 응답 신호 다시 활성화

    if( (Data[0]&0x08) == 0 ) // 0x08(00001000) 상태 바이트의 3번째 비트(캘리브레이션 활성 비트)가 0인지 확인
    {
        AHT10_WriteByte(0XE1,0X08,0x00); // 초기화 명령 전송
        Delay_ms(20);
        return 0;
    }
    else if( (Data[0]&0x80) == 0 ) // 0x80(10000000) 상태 바이트의 7번째 비트(바쁨/유휴 표시)가 0인지 확인
    {
        
        uint32_t SRH = (Data[1]<<12) | (Data[2]<<4) | (Data[3]>>4); // 습도 데이터 처리
        uint32_t ST = ((Data[3]&0x0f)<<16) | (Data[4]<<8) | Data[5]; // 온도 데이터 처리

        *Hum = (SRH * 100.0) / 1024.0 / 1024;    // 매뉴얼에 제공된 공식에 따라 습도 데이터 변환
        *Temp = (ST * 200.0) / 1024.0 / 1024 - 50; // 매뉴얼에 제공된 공식에 따라 온도 데이터 변환

        return 1;
    }

    I2C_GenerateSTOP(I2Cx, ENABLE); // 정지 신호 전송
    return 0;    

}

/*
https://blog.zeruns.com
*/
```**OLED.c**

```c
#include "stm32f10x.h"
#include "OLED_Font.h"

/*OLED 화면 주소*/
#define OLED_ADDRESS 0x78

/*어느 I2C를 사용할지*/
#define I2Cx I2C1

/*
https://blog.zeruns.com
*/

/*핀 초기화*/
void OLED_I2C_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);	//I2C1 클럭 활성화
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//GPIOB 클럭 활성화
 
	/*STM32F103 칩의 하드웨어 I2C: PB6 -- SCL; PB7 -- SDA */
	GPIO_InitTypeDef  GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;		//출력 모드를 오픈 드레인으로 설정, 풀업 저항 필요
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	I2C_DeInit(I2Cx);	//I2C 외부 레지스터를 기본값으로 재설정
	I2C_InitTypeDef  I2C_InitStructure;
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;			//작업 모드
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;	//클럭 듀티 사이클, Tlow/Thigh = 2
	I2C_InitStructure.I2C_OwnAddress1 = 0x88;	//마스터 I2C 주소, 사용하지 않으면 아무 값이나 써도 무방
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;	//응답 비트 활성화
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//주소 길이 7비트로 설정
	I2C_InitStructure.I2C_ClockSpeed = 400000;	//I2C 전송 속도, 400K, 사용하는 칩 매뉴얼에서 지원 속도 확인.	
	I2C_Init(I2Cx, &I2C_InitStructure);

	I2C_Cmd(I2Cx, ENABLE);
}

void I2C_WriteByte(uint8_t addr,uint8_t data)
{
	
	while( I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));
	
	//시작 신호 전송
	I2C_GenerateSTART(I2Cx, ENABLE);
	//EV5 이벤트 감지
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);
	//장치 쓰기 주소 전송
	I2C_Send7bitAddress(I2Cx, OLED_ADDRESS, I2C_Direction_Transmitter);
	//EV6 이벤트 감지
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);
	
	//장치 내부 주소 전송
	I2C_SendData(I2Cx, addr);
	//EV8_2 이벤트 감지
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
	
	I2C_SendData(I2Cx, data);//데이터 전송
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

	//정지 신호 전송
	I2C_GenerateSTOP(I2Cx, ENABLE);
}
 
/**
  * @brief  OLED에 명령어 쓰기
  * @param  Command 쓸 명령어
  * @retval 없음
  */
void OLED_WriteCommand(unsigned char Command)//명령어 쓰기
{
	I2C_WriteByte(0x00, Command);
}
 
/**
  * @brief  OLED에 데이터 쓰기
  * @param  Data 쓸 데이터
  * @retval 없음
*/
void OLED_WriteData(unsigned char Data)//데이터 쓰기
{
	I2C_WriteByte(0x40, Data);
}

/**
  * @brief  OLED 커서 위치 설정
  * @param  Y 왼쪽 위를 원점으로 아래 방향 좌표, 범위: 0~7
  * @param  X 왼쪽 위를 원점으로 오른쪽 방향 좌표, 범위: 0~127
  * @retval 없음
  */
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
	OLED_WriteCommand(0xB0 | Y);					//Y 위치 설정
	OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4));	//X 위치 상위 4비트
	OLED_WriteCommand(0x00 | (X & 0x0F));			//X 위치 하위 4비트
}

/**
  * @brief  OLED 화면 지우기
  * @param  없음
  * @retval 없음
  */
void OLED_Clear(void)
{  
	uint8_t i, j;
	for (j = 0; j < 8; j++)
	{
		OLED_SetCursor(j, 0);
		for(i = 0; i < 128; i++)
		{
			OLED_WriteData(0x00);
		}
	}
}

/**
  * @brief  OLED 부분 지우기
  * @param  Line 행 위치, 범위: 1~4
  * @param  start 열 시작 위치, 범위: 1~16
  * @param  end 열 끝 위치, 범위: 1~16
  * @retval 없음
  */
void OLED_Clear_Part(uint8_t Line, uint8_t start, uint8_t end)
{  
	uint8_t i,Column;
	for(Column = start; Column <= end; Column++)
	{
		OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);		//커서 위치를 상반부로 설정
		for (i = 0; i < 8; i++)
		{
			OLED_WriteData(0x00);			//상반부 내용 표시
		}
		OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);	//커서 위치를 하반부로 설정
		for (i = 0; i < 8; i++)
		{
			OLED_WriteData(0x00);		//하반부 내용 표시
		}
	}
}

/**
  * @brief  OLED에 한 문자 표시
  * @param  Line 행 위치, 범위: 1~4
  * @param  Column 열 위치, 범위: 1~16
  * @param  Char 표시할 한 문자, 범위: ASCII 가시 문자
  * @retval 없음
  */
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{     	
	uint8_t i;
	OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);		//커서 위치를 상반부로 설정
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i]);			//상반부 내용 표시
	}
	OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);	//커서 위치를 하반부로 설정
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]);		//하반부 내용 표시
	}
}

/**
  * @brief  OLED에 문자열 표시
  * @param  Line 시작 행 위치, 범위: 1~4
  * @param  Column 시작 열 위치, 범위: 1~16
  * @param  String 표시할 문자열, 범위: ASCII 가시 문자
  * @retval 없음
  */
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i++)
	{
		OLED_ShowChar(Line, Column + i, String[i]);
	}
}

/**
  * @brief  OLED 거듭제곱 함수
  * @retval X의 Y승 반환
  */
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y--)
	{
		Result *= X;
	}
	return Result;
}

/**
  * @brief  OLED에 숫자 표시(십진수, 양수)
  * @param  Line 시작 행 위치, 범위: 1~4
  * @param  Column 시작 열 위치, 범위: 1~16
  * @param  Number 표시할 숫자, 범위: 0~4294967295
  * @param  Length 표시할 숫자 길이, 범위: 1~10
  * @retval 없음
  */
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

/**
  * @brief  OLED에 숫자 표시(십진수, 부호 있는 수)
  * @param  Line 시작 행 위치, 범위: 1~4
  * @param  Column 시작 열 위치, 범위: 1~16
  * @param  Number 표시할 숫자, 범위: -2147483648~2147483647
  * @param  Length 표시할 숫자 길이, 범위: 1~10
  * @retval 없음
  */
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
	uint8_t i;
	uint32_t Number1;
	if (Number >= 0)
	{
		OLED_ShowChar(Line, Column, '+');
		Number1 = Number;
	}
	else
	{
		OLED_ShowChar(Line, Column, '-');
		Number1 = -Number;
	}
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

/**
  * @brief  OLED에 숫자 표시(십육진수, 양수)
  * @param  Line 시작 행 위치, 범위: 1~4
  * @param  Column 시작 열 위치, 범위: 1~16
  * @param  Number 표시할 숫자, 범위: 0~0xFFFFFFFF
  * @param  Length 표시할 숫자 길이, 범위: 1~8
  * @retval 없음
  */
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i, SingleNumber;
	for (i = 0; i < Length; i++)							
	{
		SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
		if (SingleNumber < 10)
		{
			OLED_ShowChar(Line, Column + i, SingleNumber + '0');
		}
		else
		{
			OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
		}
	}
}

/**
  * @brief  OLED에 숫자 표시(이진수, 양수)
  * @param  Line 시작 행 위치, 범위: 1~4
  * @param  Column 시작 열 위치, 범위: 1~16
  * @param  Number 표시할 숫자, 범위: 0~1111 1111 1111 1111
  * @param  Length 표시할 숫자 길이, 범위: 1~16
  * @retval 없음
  */
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
	}
}

/**
  * @brief  OLED 초기화
  * @param  없음
  * @retval 없음
  */
void OLED_Init(void)
{
	uint32_t i, j;
	
	for (i = 0; i < 1000; i++)			//전원 대기
	{
		for (j = 0; j < 1000; j++);
	}
	
	OLED_I2C_Init();			//포트 초기화
	
	OLED_WriteCommand(0xAE);	//디스플레이 끄기
	
	OLED_WriteCommand(0xD5);	//디스플레이 클럭 분배비/오실레이터 주파수 설정
	OLED_WriteCommand(0x80);
	
	OLED_WriteCommand(0xA8);	//멀티플렉스 비율 설정
	OLED_WriteCommand(0x3F);
	
	OLED_WriteCommand(0xD3);	//디스플레이 오프셋 설정
	OLED_WriteCommand(0x00);
	
	OLED_WriteCommand(0x40);	//디스플레이 시작 행 설정
	
	OLED_WriteCommand(0xA1);	//좌우 방향 설정, 0xA1 정상 0xA0 좌우 반전
	
	OLED_WriteCommand(0xC8);	//상하 방향 설정, 0xC8 정상 0xC0 상하 반전

	OLED_WriteCommand(0xDA);	//COM 핀 하드웨어 구성 설정
	OLED_WriteCommand(0x12);
	
	OLED_WriteCommand(0x81);	//대비도 제어 설정
	OLED_WriteCommand(0xCF);

	OLED_WriteCommand(0xD9);	//프리차지 주기 설정
	OLED_WriteCommand(0xF1);

	OLED_WriteCommand(0xDB);	//VCOMH 선택 해제 수준 설정
	OLED_WriteCommand(0x30);

	OLED_WriteCommand(0xA4);	//전체 디스플레이 켜기/끄기 설정

	OLED_WriteCommand(0xA6);	//정상/반전 디스플레이 설정

	OLED_WriteCommand(0x8D);	//충전 펌프 설정
	OLED_WriteCommand(0x14);

	OLED_WriteCommand(0xAF);	//디스플레이 켜기
		
	OLED_Clear();				//OLED 화면 지우기
}

추천 읽기

1개의 좋아요