본문 바로가기
MCU/AVR

[ATMega128A]SPI Bit-Bang 을 STM32 HAL 스타일로 구현 (Implementing SPI Bit-Bang in STM32 HAL Style on ATMega128A)

by linuxgo 2025. 8. 2.
반응형

이 포스트에서는 ATMega128A 마이크로컨트롤러에서 GPIO를 사용해 SPI bit-bang 통신을 STM32 HAL API 스타일로 구현하는 방법을 설명합니다 (This post explains how to implement SPI bit-bang communication on the ATMega128A microcontroller using GPIO in the style of STM32 HAL API). 하드웨어 SPI 모듈 없이 GPIO로 SPI 프로토콜을 구현하며, STM32 HAL과 유사한 직관적인 인터페이스를 제공합니다 (It implements the SPI protocol using GPIO without a hardware SPI module, providing an intuitive interface similar to STM32 HAL).

특징 (Features)

  • STM32 HAL 스타일의 함수명과 구조체 사용 (예: HAL_SPI_Init, SPI_HandleTypeDef) (Uses STM32 HAL-style function names and structures, e.g., HAL_SPI_Init, SPI_HandleTypeDef)
  • SCK: PB7, MOSI: PB5, MISO: PB6, SS: PB4 (필요 시 변경 가능) (SCK: PB7, MOSI: PB5, MISO: PB6, SS: PB4, configurable as needed)
  • 약 100kHz 속도 (지연 시간 조정 가능) (Approximately 100kHz speed, adjustable delay)
  • 마스터 모드만 지원 (Only master mode is supported)

코드 (Code)

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

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

#ifndef SPI_BITBANG_H
#define SPI_BITBANG_H

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

// SPI 핀 정의 (PB7 = SCK, PB5 = MOSI, PB6 = MISO, PB4 = SS) (SPI Pin Definitions, PB7 = SCK, PB5 = MOSI, PB6 = MISO, PB4 = SS)
#define SPI_PORT PORTB
#define SPI_DDR DDRB
#define SPI_PIN PINB
#define SPI_SCK_PIN (1 << PB7)
#define SPI_MOSI_PIN (1 << PB5)
#define SPI_MISO_PIN (1 << PB6)
#define SPI_SS_PIN (1 << PB4)

// SPI 상태 코드 (SPI Status Codes)
typedef enum {
    HAL_SPI_OK       = 0x00U,
    HAL_SPI_ERROR    = 0x01U,
    HAL_SPI_BUSY     = 0x02U,
    HAL_SPI_TIMEOUT  = 0x03U
} HAL_SPI_StatusTypeDef;

// SPI 핸들 구조체 (SPI Handle Structure)
typedef struct {
    uint8_t sck_pin;           // SCK 핀 (SCK Pin)
    uint8_t mosi_pin;          // MOSI 핀 (MOSI Pin)
    uint8_t miso_pin;          // MISO 핀 (MISO Pin)
    uint8_t ss_pin;            // SS 핀 (SS Pin)
    volatile uint8_t* port;    // 포트 레지스터 (Port Register)
    volatile uint8_t* ddr;     // 방향 레지스터 (Direction Register)
    volatile uint8_t* pin;     // 입력 레지스터 (Input Register)
} SPI_HandleTypeDef;

// 함수 프로토타입 (Function Prototypes)
HAL_SPI_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi);
HAL_SPI_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t size, uint32_t timeout);
HAL_SPI_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t size, uint32_t timeout);
HAL_SPI_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t size, uint32_t timeout);

#endif /* SPI_BITBANG_H */

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

SPI bit-bang 구현을 포함하며, 클럭 생성, 데이터 전송/수신, 마스터 모드 통신을 지원합니다 (Contains the SPI bit-bang implementation, supporting clock generation, data transmission/reception, and master mode communication). CPOL=0, CPHA=0 (모드 0)을 기본으로 사용합니다 (Uses CPOL=0, CPHA=0 (Mode 0) by default).

#include "spi_bitbang.h"

// SPI 핀 제어 매크로 (SPI Pin Control Macros)
#define SCK_HIGH() (*hspi->port |= hspi->sck_pin)
#define SCK_LOW() (*hspi->port &= ~hspi->sck_pin)
#define MOSI_HIGH() (*hspi->port |= hspi->mosi_pin)
#define MOSI_LOW() (*hspi->port &= ~hspi->mosi_pin)
#define MISO_READ() (*hspi->pin & hspi->miso_pin)
#define SS_HIGH() (*hspi->port |= hspi->ss_pin)
#define SS_LOW() (*hspi->port &= ~hspi->ss_pin)

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

// SPI 초기화 (SPI Initialization)
HAL_SPI_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi) {
    if (hspi == NULL) return HAL_SPI_ERROR;

    // 핀 설정: SCK, MOSI, SS 출력, MISO 입력, 초기 상태 설정 (Configure pins: SCK, MOSI, SS as output, MISO as input, set initial state)
    *hspi->ddr |= hspi->sck_pin | hspi->mosi_pin | hspi->ss_pin;
    *hspi->ddr &= ~hspi->miso_pin;
    *hspi->port |= hspi->ss_pin; // SS 초기 HIGH (SS initial HIGH)
    *hspi->port &= ~hspi->sck_pin; // SCK 초기 LOW (SCK initial LOW, CPOL=0)
    *hspi->port &= ~hspi->mosi_pin; // MOSI 초기 LOW (MOSI initial LOW)

    return HAL_SPI_OK;
}

// SPI 바이트 전송 (SPI Byte Transmission)
static HAL_SPI_StatusTypeDef SPI_WriteByte(SPI_HandleTypeDef *hspi, uint8_t data) {
    for (uint8_t i = 0; i < 8; i++) {
        // 데이터 비트 설정 (Set data bit)
        if (data & 0x80) {
            MOSI_HIGH();
        } else {
            MOSI_LOW();
        }
        SPI_DELAY();
        SCK_HIGH(); // 클럭 펄스 (Clock pulse)
        SPI_DELAY();
        SCK_LOW();
        data <<= 1;
    }
    return HAL_SPI_OK;
}

// SPI 바이트 수신 (SPI Byte Reception)
static uint8_t SPI_ReadByte(SPI_HandleTypeDef *hspi) {
    uint8_t data = 0;
    for (uint8_t i = 0; i < 8; i++) {
        data <<= 1;
        SPI_DELAY();
        SCK_HIGH(); // 클럭 펄스 (Clock pulse)
        SPI_DELAY();
        if (MISO_READ()) data |= 0x01;
        SCK_LOW();
    }
    return data;
}

// SPI 전송 (SPI Transmit)
HAL_SPI_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t size, uint32_t timeout) {
    SS_LOW(); // 슬레이브 선택 (Select slave)
    for (uint16_t i = 0; i < size; i++) {
        if (SPI_WriteByte(hspi, pData[i]) != HAL_SPI_OK) {
            SS_HIGH();
            return HAL_SPI_ERROR;
        }
    }
    SS_HIGH(); // 슬레이브 선택 해제 (Deselect slave)
    return HAL_SPI_OK;
}

// SPI 수신 (SPI Receive)
HAL_SPI_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t size, uint32_t timeout) {
    SS_LOW(); // 슬레이브 선택 (Select slave)
    for (uint16_t i = 0; i < size; i++) {
        pData[i] = SPI_ReadByte(hspi);
    }
    SS_HIGH(); // 슬레이브 선택 해제 (Deselect slave)
    return HAL_SPI_OK;
}

// SPI 전송 및 수신 (SPI Transmit and Receive)
HAL_SPI_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t size, uint32_t timeout) {
    SS_LOW(); // 슬레이브 선택 (Select slave)
    for (uint16_t i = 0; i < size; i++) {
        if (SPI_WriteByte(hspi, pTxData[i]) != HAL_SPI_OK) {
            SS_HIGH();
            return HAL_SPI_ERROR;
        }
        pRxData[i] = SPI_ReadByte(hspi);
    }
    SS_HIGH(); // 슬레이브 선택 해제 (Deselect slave)
    return HAL_SPI_OK;
}

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

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

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

int main(void) {
    // SPI 핸들 설정 (SPI Handle Setup)
    SPI_HandleTypeDef hspi;
    hspi.sck_pin = SPI_SCK_PIN;
    hspi.mosi_pin = SPI_MOSI_PIN;
    hspi.miso_pin = SPI_MISO_PIN;
    hspi.ss_pin = SPI_SS_PIN;
    hspi.port = &PORTB;
    hspi.ddr = &DDRB;
    hspi.pin = &PINB;

    // SPI 초기화 (Initialize SPI)
    HAL_SPI_Init(&hspi);

    // 예제: SPI 장치로 데이터 전송 (Example: Transmit data to SPI device)
    uint8_t tx_data[] = {0x01, 0x02, 0x03};
    HAL_SPI_Transmit(&hspi, tx_data, 3, 1000);

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

    // 예제: SPI 전송 및 수신 (Example: Transmit and receive data)
    uint8_t tx_rx_data[3] = {0x04, 0x05, 0x06};
    uint8_t rx_buffer[3];
    HAL_SPI_TransmitReceive(&hspi, tx_rx_data, rx_buffer, 3, 1000);

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

    return 0;
}

사용 방법 (How to Use)

  1. 하드웨어 설정 (Hardware Setup)
    •    SCK (PB7), MOSI (PB5), SS (PB4)를 출력으로, MISO (PB6)를 입력으로 설정하세요 (Configure SCK (PB7), MOSI (PB5), SS (PB4) as output and MISO (PB6) as input).
    •    필요 시 헤더 파일에서 핀 정의를 수정하세요 (Modify pin definitions in the header file if needed).
    •    SPI 슬레이브 장치와 연결하세요 (Connect to the SPI slave device).
  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. SPI 모드 (SPI Mode)
    •    기본적으로 CPOL=0, CPHA=0 (모드 0)을 사용합니다 (Uses CPOL=0, CPHA=0 (Mode 0) by default).
    •    다른 모드(CPOL, CPHA 조합)가 필요하면 SPI_WriteByteSPI_ReadByte 함수를 수정하세요 (Modify SPI_WriteByte and SPI_ReadByte functions for other modes (CPOL, CPHA combinations)).

주의사항 (Notes)

  • SPI는 풀업 저항이 필요하지 않지만, SS 핀을 명시적으로 제어해야 합니다 (SPI does not require pull-up resistors, but the SS pin must be explicitly controlled).
  • 타임아웃 기능은 기본적으로 구현되어 있으며, 필요 시 확장할 수 있습니다 (Timeout functionality is implemented and can be extended if needed).
  • 특정 SPI 장치에 맞춘 추가 설정(예: 데이터 순서, 클럭 극성)이 필요할 수 있습니다 (Additional configuration, such as data order or clock polarity, may be required for specific SPI devices).

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

반응형