Industrial Communication/CAN Bus: Protocol & Physical Layer

ESP32 MCP2515 CAN 통신 구현: SPI 설정 및 ESP-IDF 송수신 예제

임베디드 친구 2025. 2. 26. 13:45
반응형

1. 개요

ESP32에는 내부 CAN 컨트롤러(TWAI)가 있지만, 일부 프로젝트에서는 외부 CAN 컨트롤러를 사용해야 할 수도 있습니다. 대표적인 외부 CAN 컨트롤러로는 MCP2515가 있으며, SPI 인터페이스를 통해 ESP32와 연결하여 사용할 수 있습니다.

이 글에서는 ESP32 IDF(ESP-IDF) 환경에서 MCP2515를 사용하여 CAN 통신을 설정하는 방법을 자세히 설명합니다.

Gemini AI 생성 이미지입니다.

2. MCP2515 SPI 인터페이스 설정

MCP2515는 SPI(Serial Peripheral Interface) 프로토콜을 통해 마이크로컨트롤러와 통신합니다. SPI는 빠른 데이터 전송 속도를 제공하며, MCP2515를 제어하는 데 필수적인 요소입니다.

2.1 MCP2515 핀 구성

MCP2515는 SPI 인터페이스를 사용하여 ESP32와 연결됩니다. 주요 핀은 다음과 같습니다:

MCP2515 핀 설명 ESP32 핀 예제
VCC 전원 공급 (5V 또는 3.3V) 3.3V
GND 그라운드 GND
CS SPI 칩 선택 (Chip Select) GPIO5
SCK SPI 클록 (Serial Clock) GPIO18
SI SPI 데이터 입력 (MOSI) GPIO23
SO SPI 데이터 출력 (MISO) GPIO19
INT 인터럽트 핀 GPIO4

2.2 ESP32 IDF에서 SPI 설정

ESP-IDF에서 SPI 통신을 설정하려면 spi_device_interface_config_t를 사용하여 SPI 버스를 구성해야 합니다.

#include "driver/spi_master.h"
#include "driver/gpio.h"

#define PIN_NUM_MISO 19
#define PIN_NUM_MOSI 23
#define PIN_NUM_CLK  18
#define PIN_NUM_CS   5

spi_device_handle_t mcp2515_handle;

void spi_init() {
    spi_bus_config_t buscfg = {
        .miso_io_num = PIN_NUM_MISO,
        .mosi_io_num = PIN_NUM_MOSI,
        .sclk_io_num = PIN_NUM_CLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
    };
    spi_device_interface_config_t devcfg = {
        .command_bits = 0,
        .address_bits = 0,
        .dummy_bits = 0,
        .clock_speed_hz = 1 * 1000 * 1000, // 1MHz
        .mode = 0,
        .spics_io_num = PIN_NUM_CS,
        .queue_size = 1,
    };
    spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO);
    spi_bus_add_device(SPI2_HOST, &devcfg, &mcp2515_handle);
}

3. 라이브러리 활용하여 데이터 송수신

ESP-IDF 환경에서는 직접 MCP2515 레지스터를 제어하거나, 오픈소스 라이브러리를 활용하여 MCP2515를 쉽게 사용할 수 있습니다.

3.1 MCP2515 설정

MCP2515를 사용하려면 먼저 초기화해야 합니다. 아래는 MCP2515를 초기화하는 코드 예제입니다.

#include "mcp2515.h"
#include "driver/spi_master.h"

void mcp2515_init() {
    struct mcp2515_config config;
    config.spi_handle = mcp2515_handle;
    config.cs_pin = PIN_NUM_CS;
    mcp2515_initialize(&config);
    mcp2515_set_bitrate(MCP_500KBPS, MCP_8MHZ);
    mcp2515_set_mode(MCP_NORMAL);
}

int mcp2515_send_message(struct can_frame *frame) {
    uint8_t tx_buffer[13];
    tx_buffer[0] = (uint8_t)(frame->can_id >> 3);
    tx_buffer[1] = (uint8_t)((frame->can_id & 0x07) << 5);
    tx_buffer[2] = 0; // Standard frame, no extended ID
    tx_buffer[3] = 0; // No RTR
    tx_buffer[4] = frame->can_dlc & 0x0F;
    memcpy(&tx_buffer[5], frame->data, frame->can_dlc);

    spi_transaction_t t;
    memset(&t, 0, sizeof(t));
    t.length = (5 + frame->can_dlc) * 8;
    t.tx_buffer = tx_buffer;
    return spi_device_transmit(mcp2515_handle, &t);
}

int mcp2515_receive_message(struct can_frame *frame) {
    uint8_t rx_buffer[13];
    spi_transaction_t t;
    memset(&t, 0, sizeof(t));
    t.length = 13 * 8;
    t.rx_buffer = rx_buffer;

    if (spi_device_transmit(mcp2515_handle, &t) != ESP_OK) {
        return -1;
    }

    frame->can_id = (rx_buffer[0] << 3) | (rx_buffer[1] >> 5);
    frame->can_dlc = rx_buffer[4] & 0x0F;
    memcpy(frame->data, &rx_buffer[5], frame->can_dlc);

    return 0;
}

3.2 데이터 전송 예제

CAN 버스를 통해 데이터를 송신하려면 CAN 메시지를 작성한 후 MCP2515에 전송 요청을 해야 합니다.

void send_can_message() {
    struct can_frame frame;
    frame.can_id = 0x100;
    frame.can_dlc = 2;
    frame.data[0] = 0xAB;
    frame.data[1] = 0xCD;
    mcp2515_send_message(&frame);
}

3.3 데이터 수신 예제

CAN 메시지를 수신하려면 MCP2515의 인터럽트를 사용하거나 폴링 방식으로 데이터를 읽어올 수 있습니다.

void receive_can_message() {
    struct can_frame frame;
    if (mcp2515_receive_message(&frame) == MCP2515_OK) {
        printf("CAN ID: 0x%X\n", frame.can_id);
        printf("Data: ");
        for (int i = 0; i < frame.can_dlc; i++) {
            printf("%02X ", frame.data[i]);
        }
        printf("\n");
    }
}

4. MCP2515 ESP32 IDF 예제 코드

아래는 ESP32 IDF 환경에서 MCP2515를 초기화하고, CAN 메시지를 송수신하는 전체 예제 코드입니다.

#include "driver/spi_master.h"
#include "mcp2515.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#define PIN_NUM_MISO 19
#define PIN_NUM_MOSI 23
#define PIN_NUM_CLK  18
#define PIN_NUM_CS   5

spi_device_handle_t mcp2515_handle;

void app_main() {
    // SPI 초기화
    spi_init();

    // MCP2515 초기화
    mcp2515_init();

    // CAN 메시지 송신
    send_can_message();

    // 주기적으로 CAN 메시지 수신
    while (1) {
        receive_can_message();
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

5. 결론

이 글에서는 ESP32 IDF 환경에서 MCP2515를 SPI를 통해 설정하고, CAN 데이터를 송수신하는 방법을 설명하였습니다.

  • SPI 인터페이스 설정: ESP32와 MCP2515 간 SPI 통신을 설정하는 방법을 설명하였습니다.
  • 라이브러리 활용: MCP2515를 설정하고 CAN 메시지를 송수신하는 방법을 설명하였습니다.
  • 예제 코드 제공: MCP2515를 사용한 전체 코드를 제공하여 실제 프로젝트에서 활용할 수 있도록 하였습니다.

이를 통해 ESP32 IDF 환경에서 외부 CAN 컨트롤러를 활용하여 다양한 프로젝트를 진행할 수 있을 것입니다.

반응형