본문 바로가기
MCU/AVR

AVR I2C Bit-Bang을 STM32 HAL API 스타일로 구현 (Implementing I2C Bit-Bang in STM32 HAL Style on AVR)

by linuxgo 2025. 8. 2.
반응형

이 포스트에서는 ATMega128A 마이크로컨트롤러에서 GPIO를 사용해 I2C bit-bang 통신을 STM32 HAL API 스타일로 구현하는 방법을 설명합니다 (This post explains how to implement I2C bit-bang communication on the ATMega128A microcontroller using GPIO in the style of STM32 HAL API). 하드웨어 I2C 모듈 없이도 STM32 HAL과 유사한 인터페이스를 제공하며, 안정적인 통신을 구현할 수 있습니다 (It provides an interface similar to STM32 HAL without a hardware I2C module, enabling reliable communication).

특징 (Features)

  • STM32 HAL 스타일의 함수명과 구조체 사용 (예: HAL_I2C_Init, I2C_HandleTypeDef) (Uses STM32 HAL-style function names and structures, e.g., HAL_I2C_Init, I2C_HandleTypeDef)
  • SCL: PB0, SDA: PB1 (필요 시 변경 가능) (SCL: PB0, SDA: PB1, configurable as needed)
  • 약 100kHz 속도 (지연 시간 조정 가능) (Approximately 100kHz speed, adjustable delay)
  • 오픈 드레인 출력을 위한 외부 풀업 저항 필요 (4.7kΩ 권장) (External pull-up resistors required for open-drain output, 4.7kΩ recommended)

코드 (Code)

1. 헤더 파일 - i2c_bitbang.h (Header File - i2c_bitbang.h)

I2C 핀 정의와 함수 프로토타입을 포함합니다 (Contains I2C pin definitions and function prototypes). STM32 HAL 스타일의 상태 코드와 핸들 구조체를 정의합니다 (Defines STM32 HAL-style status codes and handle structure).

#ifndef I2C_BITBANG_H
#define I2C_BITBANG_H

#include <avr/io.h>
#include <util/delay.h>

// I2C 핀 정의 (PB0 = SCL, PB1 = SDA) (I2C Pin Definitions, PB0 = SCL, PB1 = SDA)
#define I2C_PORT PORTB
#define I2C_DDR DDRB
#define I2C_PIN PINB
#define I2C_SCL_PIN (1 << PB0)
#define I2C_SDA_PIN (1 << PB1)

// I2C 상태 코드 (I2C Status Codes)
typedef enum {
    HAL_I2C_OK       = 0x00U,
    HAL_I2C_ERROR    = 0x01U,
    HAL_I2C_BUSY     = 0x02U,
    HAL_I2C_TIMEOUT  = 0x03U
} HAL_I2C_StatusTypeDef;

// I2C 핸들 구조체 (I2C Handle Structure)
typedef struct {
    uint8_t scl_pin;           // SCL 핀 (SCL Pin)
    uint8_t sda_pin;           // SDA 핀 (SDA Pin)
    volatile uint8_t* port;    // 포트 레지스터 (Port Register)
    volatile uint8_t* ddr;     // 방향 레지스터 (Direction Register)
    volatile uint8_t* pin;     // 입력 레지스터 (Input Register)
} I2C_HandleTypeDef;

// 함수 프로토타입 (Function Prototypes)
HAL_I2C_StatusTypeDef HAL_I2C_Init(I2C_HandleTypeDef *hi2c);
HAL_I2C_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint8_t dev_addr, uint8_t *pData, uint16_t size, uint32_t timeout);
HAL_I2C_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint8_t dev_addr, uint8_t *pData, uint16_t size, uint32_t timeout);

#endif /* I2C_BITBANG_H */

2. 소스 파일 - i2c_bitbang.c (Source File - i2c_bitbang.c)

I2C bit-bang 구현을 포함하며, 시작/정지 조건, 바이트 전송/수신, 마스터 모드 통신을 지원합니다 (Contains the I2C bit-bang implementation, supporting start/stop conditions, byte transmission/reception, and master mode communication).

#include "i2c_bitbang.h"

// I2C 핀 제어 매크로 (I2C Pin Control Macros)
#define SCL_HIGH() (*hi2c->port |= hi2c->scl_pin)
#define SCL_LOW() (*hi2c->port &= ~hi2c->scl_pin)
#define SDA_HIGH() (*hi2c->port |= hi2c->sda_pin)
#define SDA_LOW() (*hi2c->port &= ~hi2c->sda_pin)
#define SDA_READ() (*hi2c->pin & hi2c->sda_pin)

// I2C 지연 (약 100kHz) (I2C Delay, approx. 100kHz)
#define I2C_DELAY() _delay_us(5)

// I2C 초기화 (I2C Initialization)
HAL_I2C_StatusTypeDef HAL_I2C_Init(I2C_HandleTypeDef *hi2c) {
    if (hi2c == NULL) return HAL_I2C_ERROR;

    // 핀 설정: SCL, SDA를 출력, 초기 HIGH (오픈 드레인) (Configure pins: SCL, SDA as output, initial HIGH, open-drain)
    *hi2c->ddr |= hi2c->scl_pin | hi2c->sda_pin;
    *hi2c->port |= hi2c->scl_pin | hi2c->sda_pin;

    return HAL_I2C_OK;
}

// I2C 시작 조건 (I2C Start Condition)
static void I2C_Start(I2C_HandleTypeDef *hi2c) {
    SDA_HIGH();
    SCL_HIGH();
    I2C_DELAY();
    SDA_LOW();
    I2C_DELAY();
    SCL_LOW();
}

// I2C 정지 조건 (I2C Stop Condition)
static void I2C_Stop(I2C_HandleTypeDef *hi2c) {
    SDA_LOW();
    I2C_DELAY();
    SCL_HIGH();
    I2C_DELAY();
    SDA_HIGH();
    I2C_DELAY();
}

// I2C 바이트 전송 (I2C Byte Transmission)
static HAL_I2C_StatusTypeDef I2C_WriteByte(I2C_HandleTypeDef *hi2c, uint8_t data) {
    for (uint8_t i = 0; i < 8; i++) {
        if (data & 0x80) {
            SDA_HIGH();
        } else {
            SDA_LOW();
        }
        I2C_DELAY();
        SCL_HIGH();
        I2C_DELAY();
        SCL_LOW();
        data <<= 1;
    }

    // ACK 확인 (Check ACK)
    *hi2c->ddr &= ~hi2c->sda_pin; // SDA 입력 모드 (SDA to input mode)
    I2C_DELAY();
    SCL_HIGH();
    I2C_DELAY();
    uint8_t ack = SDA_READ() ? HAL_I2C_ERROR : HAL_I2C_OK;
    SCL_LOW();
    *hi2c->ddr |= hi2c->sda_pin; // SDA 출력 모드 (SDA to output mode)

    return ack;
}

// I2C 바이트 수신 (I2C Byte Reception)
static uint8_t I2C_ReadByte(I2C_HandleTypeDef *hi2c, uint8_t ack) {
    uint8_t data = 0;

    *hi2c->ddr &= ~hi2c->sda_pin; // SDA 입력 모드 (SDA to input mode)
    for (uint8_t i = 0; i < 8; i++) {
        data <<= 1;
        I2C_DELAY();
        SCL_HIGH();
        I2C_DELAY();
        if (SDA_READ()) data |= 0x01;
        SCL_LOW();
    }
    *hi2c->ddr |= hi2c->sda_pin; // SDA 출력 모드 (SDA to output mode)

    // ACK/NACK 전송 (Send ACK/NACK)
    if (ack) {
        SDA_LOW();
    } else {
        SDA_HIGH();
    }
    I2C_DELAY();
    SCL_HIGH();
    I2C_DELAY();
    SCL_LOW();

    return data;
}

// I2C 마스터 전송 (I2C Master Transmit)
HAL_I2C_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint8_t dev_addr, uint8_t *pData, uint16_t size, uint32_t timeout) {
    I2C_Start(hi2c);

    // 장치 주소 전송 (쓰기) (Send device address, write)
    if (I2C_WriteByte(hi2c, (dev_addr << 1) | 0) != HAL_I2C_OK) {
        I2C_Stop(hi2c);
        return HAL_I2C_ERROR;
    }

    // 데이터 전송 (Send data)
    for (uint16_t i = 0; i < size; i++) {
        if (I2C_WriteByte(hi2c, pData[i]) != HAL_I2C_OK) {
            I2C_Stop(hi2c);
            return HAL_I2C_ERROR;
        }
    }

    I2C_Stop(hi2c);
    return HAL_I2C_OK;
}

// I2C 마스터 수신 (I2C Master Receive)
HAL_I2C_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint8_t dev_addr, uint8_t *pData, uint16_t size, uint32_t timeout) {
    I2C_Start(hi2c);

    // 장치 주소 전송 (읽기) (Send device address, read)
    if (I2C_WriteByte(hi2c, (dev_addr << 1) | 1) != HAL_I2C_OK) {
        I2C_Stop(hi2c);
        return HAL_I2C_ERROR;
    }

    // 데이터 수신 (Receive data)
    for (uint16_t i = 0; i < size; i++) {
        pData[i] = I2C_ReadByte(hi2c, (i < (size - 1)) ? 1 : 0);
    }

    I2C_Stop(hi2c);
    return HAL_I2C_OK;
}

3. 메인 파일 - main.c (Main File - main.c)

I2C 초기화와 데이터 전송/수신 예제를 포함합니다 (Includes I2C initialization and data transmission/reception examples).

#include "i2c_bitbang.h"
#include <avr/io.h>
#include <util/delay.h>

int main(void) {
    // I2C 핸들 설정 (I2C Handle Setup)
    I2C_HandleTypeDef hi2c;
    hi2c.scl_pin = I2C_SCL_PIN;
    hi2c.sda_pin = I2C_SDA_PIN;
    hi2c.port = &PORTB;
    hi2c.ddr = &DDRB;
    hi2c.pin = &PINB;

    // I2C 초기화 (Initialize I2C)
    HAL_I2C_Init(&hi2c);

    // 예제: I2C 장치로 데이터 전송 (Example: Transmit data to I2C device)
    uint8_t data[] = {0x01, 0x02, 0x03};
    uint8_t dev_addr = 0x50; // 장치 주소 (7비트) (Device address, 7-bit)
    HAL_I2C_Master_Transmit(&hi2c, dev_addr, data, 3, 1000);

    // 예제: I2C 장치에서 데이터 수신 (Example: Receive data from I2C device)
    uint8_t rx_data[3];
    HAL_I2C_Master_Receive(&hi2c, dev_addr, rx_data, 3, 1000);

    while (1) {
        // 무한 루프 (Infinite loop)
    }

    return 0;
}

사용 방법 (How to Use)

  1. 하드웨어 설정 (Hardware Setup)
    •    SCL (PB0)과 SDA (PB1)에 4.7kΩ 풀업 저항을 연결하세요 (Connect 4.7kΩ pull-up resistors to SCL (PB0) and SDA (PB1)).
    •    필요 시 헤더 파일에서 핀 정의를 수정하세요 (Modify pin definitions in the header file if needed).
  2. 컴파일 (Compilation)
    •    AVR-GCC를 사용해 컴파일하세요 (Compile with AVR-GCC).
    •    <avr/io.h><util/delay.h> 라이브러리가 필요합니다 (Requires <avr/io.h> and <util/delay.h> libraries).
  3. 속도 조정 (Speed Adjustment)
    •    _delay_us(5)는 약 100kHz 속도를 구현합니다 ( _delay_us(5) implements approximately 100kHz speed).
    •    ATMega128A의 클럭 주파수에 따라 지연 시간을 조정하세요 (Adjust delay based on ATMega128A clock frequency).
  4. 장치 주소 (Device Address)
    •    dev_addr는 7비트 I2C 주소를 사용합니다 ( dev_addr uses 7-bit I2C address).
    •    읽기/쓰기 비트는 함수 내부에서 자동으로 처리됩니다 (Read/write bit is handled automatically by functions).

주의사항 (Notes)

  •  오픈 드레인 출력을 위해 SCL과 SDA 라인에 외부 풀업 저항이 필수입니다 (External pull-up resistors are mandatory for SCL and SDA lines for open-drain output).
  •  타임아웃 기능은 기본적으로 구현되어 있으며, 필요 시 확장할 수 있습니다 (Timeout functionality is implemented and can be extended if needed).
  •  특정 I2C 장치에 맞춘 추가 설정이 필요할 수 있습니다 (Additional configuration may be required for specific I2C devices).

이 코드는 ATMega128A에서 안정적인 I2C bit-bang 통신을 제공하며, STM32 HAL API 스타일을 따라 직관적인 인터페이스를 제공합니다 (This code provides reliable I2C bit-bang communication on ATMega128A with an intuitive interface following the STM32 HAL API style).

반응형