Industrial Communication/CANopen: Distributed Control Stack

[STM32F429 CANopen] bxCAN Peripheral 기반 CANopenNode 스택 포팅 및 HAL_CAN_ConfigFilter 구현 가이드

임베디드 친구 2025. 8. 7. 20:15
반응형

1. STM32F429 bxCAN 컨트롤러 기반 CANopenNode 포팅 배경

산업용 하드웨어 제어 시스템에서 CiA 301 규격을 준수하는 CANopen 프로토콜은 센서, 액추에이터, 마이크로컨트롤러 간 표준화된 통신을 보장하는 핵심 사양입니다. STM32F429ZI MCU에 내장된 bxCAN(Basic Extended CAN) 주변 장치는 CAN 2.0B Active 규격을 지원하며, CANopenNode와 같은 오픈소스 C 스택과 결합하여 높은 신뢰성의 임베디드 노드를 구성할 수 있습니다.

기존 기본 구현 방식은 HAL_CAN_AddTxMessage 및 HAL_CAN_GetRxMessage 호출 구조만 단순 언급하여, 실시간 멀티태스킹 환경에서 발생하는 Frame Drop 및 SDO Timeout 문제를 제대로 해결하지 못합니다. 또한, bxCAN Acceptance Filter(CAN_FilterTypeDef)의 비트 시프트 연산 오류, Rx Interrupt 기반 FIFO 처리 미비, CO_process() 타이머 동기화 문제 등은 통신 신뢰도를 저하시키는 주요 원인입니다.

본 포스팅에서는 STM32CubeIDE 및 HAL 라이브러리 환경에서 CANopenNode 스택을 올바르게 포팅하기 위해 필요한 하드웨어 레지스터 설정, 인터럽트 콜백 구현, Object Dictionary(OD) 연동 방안을 상세히 분석합니다.

2. STM32F429 CANopenNode 구현 핵심 요약

  • bxCAN Hardware Filter 설정: 11-bit Standard ID(COB-ID) 할당을 위해 FilterIdHigh 및 FilterMaskIdHigh 레지스터에 반드시 5-bit Left Shift 연산(ID << 5)을 적용합니다.
  • Rx Interrupt 기반 수신 버퍼링: HAL_CAN_RxFifo0MsgPendingCallback 콜백 함수 내에서 수신 데이터를 CO_CANinterrupt()로 전달하여 FIFO Overrun 및 Frame Drop을 방지합니다.
  • 1ms Hardware Timer 기반 CO_process() 동기화: TIMx 인터럽트를 통해 1ms 주기 타이머 틱을 발생시키고 CO_process()를 호출하여 SDO, PDO, Heartbeat 타이밍 요구사항을 충족합니다.

3. STM32F429 bxCAN 하드웨어 및 CANopenNode 스택 상세 분석

3.1 CANopen 스택 비교 분석: CANopenNode vs CANFestival

임베디드 타겟 마이크로컨트롤러 환경에 적합한 CANopen 스택 선택을 위해 구조적 특징을 비교합니다.

비교 항목 CANopenNode CANFestival
코드 구조 ANSI C (C99), 모듈화된 객체 지향 스타일 C 기반, POSIX 및 OS 종속적 구조
메모리 점유 (Footprint) 매우 작음 (SRAM < 4KB, Flash < 32KB) 중간 (SRAM ~ 8KB, Flash ~ 64KB)
Object Dictionary (OD) OD Editor (EDSEditor)를 통한 자동 코드 생성 ObjectDictionaryEditor (Python GUI) 활용
RTOS 지원 Bare-metal, FreeRTOS, CMSIS-RTOS 완벽 지원 POSIX Thread, Linux, Win32 중심
STM32 HAL 호환성 HAL_CAN API와의 드라이버 바인딩 용이 드라이버 추상화 레이어 수정 범위가 크음

3.2 bxCAN Hardware Filter 레지스터 구조 및 FilterIdHigh 설정

STM32F429 bxCAN 필터 뱅크는 32비트 레지스터 레벨에서 Standard ID(STID[10:0])를 상위 11비트에 배치합니다. 단순 값을 대입할 경우 하드웨어가 ID 비트를 오인하여 메시지를 Reject합니다.

32-bit Filter Register Bit Mapping:
[31 : 21] - STID[10:0] (Standard Identifier)
[20 : 3]  - EXID[17:0] (Extended Identifier)
[2]       - IDE (Identifier Extension Bit)
[1]       - RTR (Remote Transmission Request Bit)
[0]       - 0 (Reserved)

따라서 FilterIdHigh 설정 시 5-bit Left Shift 연산이 필수적입니다.

3.3 CO_driver_STM32F4.c 드라이버 레이어 완벽 구현

STM32F429 HAL 라이브러리와 CANopenNode 간 인터페이스 코드 구현체입니다.

#include "stm32f4xx_hal.h"
#include "CO_driver.h"

extern CAN_HandleTypeDef hcan1;

/* Initialize CAN hardware peripheral and filters */
CO_ReturnError_t CO_CANmodule_init(CO_CANmodule_t *CANmodule, CAN_TypeDef *CANport) {
    CAN_FilterTypeDef sFilterConfig;

    /* Configure Filter Bank 0 to accept standard CANopen messages */
    sFilterConfig.FilterBank = 0;
    sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
    sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;

    /* 11-bit Standard ID must be shifted left by 5 bits */
    sFilterConfig.FilterIdHigh = (0x000 << 5);
    sFilterConfig.FilterIdLow = 0x0000;
    sFilterConfig.FilterMaskIdHigh = (0x000 << 5);
    sFilterConfig.FilterMaskIdLow = 0x0000;
    sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
    sFilterConfig.FilterActivation = ENABLE;
    sFilterConfig.SlaveStartFilterBank = 14;

    if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) {
        return CO_ERROR_ILLEGAL_ARGUMENT;
    }

    if (HAL_CAN_Start(&hcan1) != HAL_OK) {
        return CO_ERROR_ILLEGAL_ARGUMENT;
    }

    if (HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) {
        return CO_ERROR_ILLEGAL_ARGUMENT;
    }

    return CO_ERROR_NO;
}

/* Transmit CAN frame using HAL_CAN_AddTxMessage */
void CO_CANsend(CO_CANmodule_t *CANmodule, CO_CANtx_t *buffer) {
    CAN_TxHeaderTypeDef TxHeader;
    uint32_t TxMailbox;
    uint8_t data[8];

    TxHeader.StdId = buffer->ident;
    TxHeader.ExtId = 0x00;
    TxHeader.RTR = CAN_RTR_DATA;
    TxHeader.IDE = CAN_ID_STD;
    TxHeader.DLC = buffer->DLC;
    TxHeader.TransmitGlobalTime = DISABLE;

    for (uint8_t i = 0; i < buffer->DLC; i++) {
        data[i] = buffer->data[i];
    }

    if (HAL_CAN_AddTxMessage(&hcan1, &TxHeader, data, &TxMailbox) != HAL_OK) {
        /* Handle Tx transmission error */
    }
}

/* Interrupt handler callback for CAN Rx FIFO0 */
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) {
    CAN_RxHeaderTypeDef RxHeader;
    uint8_t data[8];

    if (hcan->Instance == CAN1) {
        if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, data) == HAL_OK) {
            /* Pass message to CANopen stack processing function */
            CO_CANinterrupt(hcan1.Instance);
        }
    }
}

3.4 Object Dictionary(OD) 연동 및 TIMx 타이머 인터럽트 구성

CANopenNode 스택이 정상 동작하기 위해서는 1ms 주기 타이머 인터럽트에서 CO_process() 메인 루틴 및 타임틱 함수가 호출되어야 합니다.

/* TIM3 1ms Period Interrupt Callback */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    if (htim->Instance == TIM3) {
        /* Increment CANopen timer tick by 1 millisecond */
        CO_timerTick1ms();
    }
}

4. STM32F429 CANopen 디버깅 및 네트워크 검증 팁

  1. SWV ITM Data Trace를 활용한 State Monitoring:
  2. STM32CubeIDE의 SWV(Serial Wire Viewer) ITM 트레이스 기능을 통해 CO_NMT_reset_cmd_t 상태 변화 및 SDO Abort Code를 핀 차단 없이 실시간 로그로 모니터링합니다.
  3. CAN Bus Analyzer 분석:
  4. PC 기반 CAN 분석기(예: PEAK PCAN-USB, Kvaser)를 사용하여 COB-ID 0x700 + Node_ID(Heartbeat Frame)가 설정된 주기(예: 1000ms)마다 정확히 전송되는지 측정합니다.
  5. Termination Resistor 측정:
  6. CAN_H와 CAN_L 라인 간 임피던스를 측정하여 버스 양단에 120Ω 종단 저항 2개가 병렬 연결되어 합성 저항이 60Ω을 유지하는지 계측기로 확인합니다.

5. STM32F429 CANopen 포팅 시 흔히 하는 실수 및 해결책

5.1 Acceptance Filter Bit Shift 누락으로 인한 Rx Interrupt 미발생

  • 현상: Master 노드에서 Node ID 0x05로 SDO Read Request(COB-ID: 0x605)를 전송하지만, STM32F429 수신 측에서 HAL_CAN_RxFifo0MsgPendingCallback 콜백이 호출되지 않습니다.
  • 원인: FilterIdHigh 레지스터 설정 시 (0x605 << 5) 형태로 5비트 시프트를 적용하지 않고 0x605 값을 직접 대입하여, 하드웨어 필터가 비교 ID를 오인하고 프레임을 Reject합니다.
  • 해결책: sFilterConfig.FilterIdHigh = (0x605 << 5); 구문으로 수정하여 11-bit Standard ID 비트 위치를 정렬합니다.

5.2 CAN2 Peripheral Clock 미활성화로 인한 HardFault 발생

  • 현상: CAN1이 아닌 CAN2 주변 장치를 사용할 때 HAL_CAN_Init(&hcan2) 함수 호출 시 MCU에서 HardFault_Handler 예외가 발생합니다.
  • 원인: STM32F429의 bxCAN 아키텍처 상 CAN2는 CAN1의 Master SRAM 및 Filter Bank를 공유합니다. 따라서 __HAL_RCC_CAN1_CLK_ENABLE()이 먼저 호출되지 않으면 CAN2 레지스터에 접근할 때 Bus Error가 발생합니다.
  • 해결책: CAN2 초기화 전 반드시 __HAL_RCC_CAN1_CLK_ENABLE(); 및 __HAL_RCC_CAN2_CLK_ENABLE();을 둘 다 활성화합니다.

6. STM32F429 CANopenNode 포팅 결론

STM32F429 MCU에서 CANopenNode 스택을 올바르게 구현하기 위해서는 bxCAN 하드웨어 레지스터의 비트 구조 이해, FilterIdHigh 5-bit Shift 설정, Interrupt 기반 수신 버퍼링, 1ms Hardware Timer 연동이 필수적입니다. 본 가이드라인에서 제공한 드라이버 코드 및 레지스터 설정 방식을 적용하면, SDO Timeout 및 Frame Drop 없는 안정적인 산업용 CANopen 노드를 구현할 수 있습니다.


STM32도 함께 공부하세요.

반응형