1. STM32F429 bxCAN Peripheral 기반 CANopen 프로토콜 구현 배경
CANopen 프로토콜(CiA 301)은 산업 자동화 및 임베디드 제어 시스템에서 다양한 장치 간 표준 통신을 구현하기 위해 널리 사용되는 상위 계층 프로토콜입니다. STM32F429 마이크로컨트롤러에 내장된 bxCAN(Basic Extended CAN) 컨트롤러는 물리 계층 및 데이터 링크 계층의 통신을 담당하며, 개발자는 하드웨어 필터 설정 및 타이밍 조절을 명확히 수행해야 합니다.
기존 작성된 가이드는 단순 절차 위주로 설명되어 있어, 실제 개발 환경에서 bxCAN 필터 비트 시프트(Bit Shift) 오류, SDO/PDO 통신 매핑 구조, 그리고 NMT(Network Management) 상태 전이 메커니즘을 적용하는 과정에서 예외가 발생할 수 있습니다. 본 글에서는 STM32CubeIDE와 HAL 라이브러리를 활용하여 CANopen 스택(CANopenNode/CANFestival)을 올바르게 통합하고, SDO 및 PDO 통신을 검증하는 프로세스를 분석합니다.
2. STM32F429 CANopen 노드 구축 핵심 요약
- 하드웨어 및 필터 설정: STM32F429의 bxCAN 필터 레지스터(FilterIdHigh, FilterMaskIdHigh)에 Standard ID(11-bit) 대입 시 반드시 5비트 좌측 시프트(<< 5) 연산을 적용해야 수신 인터럽트(HAL_CAN_RxFifo0MsgPendingCallback)가 정상 동작합니다.
- SDO 및 PDO 통신 구조: SDO는 Object Dictionary(OD) 파라미터의 읽기/쓰기를 위한 1:1 확정 응답 통신이며, PDO는 NMT가 Operational 상태일 때 사전 정의된 COB-ID를 통해 전송되는 실시간 비확정 데이터 통신입니다.
- NMT State Machine 연동: Slave 노드는 Boot-up 메시지(COB-ID: 0x700 + Node_ID) 송신 후 Pre-Operational 상태에 진입하며, Master의 NMT Start Command(0x01)를 수신해야 PDO 통신이 활성화됩니다.
3. STM32F429 bxCAN 레지스터 설정 및 CANopen Protocol 상세 분석
3.1 bxCAN Bit Timing 및 Filter Register 설정
STM32F429의 CAN 컨트롤러는 APB1 버스 클럭(45MHz)을 기반으로 동작합니다. 500kbps Baud Rate를 설정하기 위해서는 Prescaler와 Time Segment(BS1, BS2) 값을 명확히 지정해야 합니다.
Standard ID(11-bit) 필터링 시 레지스터 MSB 정렬 특성으로 인해 비트 시프트 연산이 필수적입니다.
| 항목 (Parameter) | 설정 값 (Value) | 설명 (Description) |
|---|---|---|
| Prescaler | 6 | APB1 Clock 45MHz 기준 Tq 설정 |
| Time Segment 1 (BS1) | 12 Time Quanta | Sample Point 위치 조정 (약 80%) |
| Time Segment 2 (BS2) | 2 Time Quanta | Bit Synchronization 구간 설정 |
| Filter Scale | CAN_FILTERSCALE_32BIT | 32-bit Identifier Mask Mode |
| FilterIdHigh | (0x000 << 5) | Standard ID Bit Align [15:5] |
| FilterMaskIdHigh | (0x000 << 5) | All Receive Mode (Mask 0x000) |
/* bxCAN Filter Configuration for STM32F429 */
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = 0x0000;
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000;
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.SlaveStartFilterBank = 14;
if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) {
/* Filter Configuration Error Handler */
Error_Handler();
}3.2 Object Dictionary(OD) 구조 및 SDO Service 구현
Object Dictionary는 CANopen 노드의 모든 구성 변수가 저장되는 16비트 Index 및 8비트 Sub-index 기반 데이터베이스입니다.
| Index 영역 (Hex) | 데이터 유형 (Object Type) | 주요 기능 (Description) |
|---|---|---|
| 0x0001 - 0x02FF | Data Type Definitions | 표준 데이터 타입 정의 |
| 0x1000 - 0x1FFF | Communication Profile | Device Type, Error Register, Heartbeat 등 |
| 0x2000 - 0x5FFF | Manufacturer Specific Profile | 사용자 정의 변수 및 파라미터 매핑 |
| 0x6000 - 0x9FFF | Standardized Device Profile | CiA 401(I/O), CiA 402(Motion) 프로파일 |
SDO Server 구현 시 SDO Read/Write 요청 프레임의 Command Specifier를 구별하여 OD 변수를 조작해야 합니다.
/* SDO Server Read/Write Handler Example */
#define OD_INDEX_USER_PARAM 0x2000
uint32_t user_parameter_val = 0x12345678;
uint32_t Handle_SDO_Read(uint16_t index, uint8_t subindex) {
/* Verify Index and Subindex */
if (index == OD_INDEX_USER_PARAM && subindex == 0x00) {
return user_parameter_val;
}
return 0;
}
uint8_t Handle_SDO_Write(uint16_t index, uint8_t subindex, uint32_t write_data) {
if (index == OD_INDEX_USER_PARAM && subindex == 0x00) {
user_parameter_val = write_data;
return 1; /* Success */
}
return 0; /* Abort Code Generation */
}3.3 PDO Mapping 및 HAL_CAN_AddTxMessage 실시간 전송
PDO는 수신 데이터 검증 절차가 없는 비확정 프로토콜이므로 즉각적인 데이터 처리가 가능합니다. TPDO1의 기본 COB-ID는 0x180 + Node_ID 구조를 가집니다.
/* Transmit TPDO1 via STM32 HAL Library */
void CANopen_Send_TPDO1(CAN_HandleTypeDef *hcan, uint8_t node_id, uint16_t sensor_data) {
CAN_TxHeaderTypeDef txHeader;
uint8_t txData[8] = {0};
uint32_t txMailbox;
/* Set COB-ID for TPDO1 (0x180 + Node_ID) */
txHeader.StdId = 0x180 + node_id;
txHeader.ExtId = 0x00;
txHeader.RTR = CAN_RTR_DATA;
txHeader.IDE = CAN_ID_STD;
txHeader.DLC = 2;
txHeader.TransmitGlobalTime = DISABLE;
/* CANopen Data Format: Little-Endian (LSB First) */
txData[0] = (uint8_t)(sensor_data & 0xFF);
txData[1] = (uint8_t)((sensor_data >> 8) & 0xFF);
if (HAL_CAN_AddTxMessage(hcan, &txHeader, txData, &txMailbox) != HAL_OK) {
/* Transmit Failure Handling */
Error_Handler();
}
}4. STM32F429 CANopen 개발 및 디버깅 팁
- PEAK PCAN-View 분석 도구 활용: PCAN-View를 통해 CAN Bus의 Bus Load, Error Frame 발생 여부를 확인하고, Bitrate 설정이 STM32F429 bxCAN 설정과 일치하는지 실시간 모니터링합니다.
- NMT Command 수신 확인: Master에서 송신하는 NMT 명령(COB-ID: 0x000, Payload: 0x01 + Node_ID)을 수신하여 노드의 상태가 Operational로 전환되었는지 스택 내 NMT_State 변수를 디버거로 확인합니다.
- 종단 저항(Termination Resistor) 측정: CAN_H와 CAN_L 라인 사이의 멀티미터 저항 측정 값이 약 60Ω(120Ω 병렬 연결)을 유지하는지 물리 계층을 점검합니다.
5. STM32F429 CANopen 구현 시 흔히 하는 실수 및 해결책
5.1 bxCAN Standard ID Shift 누락으로 인한 수신 인터럽트 미발생
- 현상: Master에서 메시지를 송신해도 HAL_CAN_RxFifo0MsgPendingCallback 콜백 함수가 호출되지 않음.
- 원인: bxCAN 32-bit Filter Register 구조에서 Standard ID(11-bit)는 상위 비트 영역([15:5])에 정렬되어야 함. FilterIdHigh에 ID 값을 그대로 입력하면 필터가 잘못된 ID를 비교함.
- 해결책: 필터 설정 시 반드시 << 5 비트 시프트 연산을 적용합니다.
/* Correct Filter ID Alignment */
sFilterConfig.FilterIdHigh = (0x180 << 5);
sFilterConfig.FilterMaskIdHigh = (0x7FF << 5);5.2 Pre-Operational 상태에서 PDO 데이터 처리 시도
- 현상: Slave 노드가 Boot-up 후 TPDO를 전송하지 않거나 RPDO 데이터를 수신하지 못함.
- 원인: CiA 301 규격 상 Pre-Operational 상태에서는 SDO 및 NMT 통신만 허용되며, PDO 통신은 비활성화됨.
- 해결책: Master에서 NMT Start Node Command(0x01)를 전송하여 Slave 노드를 Operational 상태로 전이시킨 후 PDO 데이터 송수신을 진행합니다.
5.3 CANopen 데이터 엔디안(Endianness) 불일치 오류
- 현상: Master에서 16-bit 변수 0x1234 전송 시 Slave에서 0x3412로 읽히는 현상 발생.
- 원인: CANopen 프로토콜 표준은 바이트 오더로 Little-Endian(LSB First)을 사용함. Big-Endian 방식을 사용하는 시스템과 연동 시 데이터 반전 발생.
- 해결책: 송수신 버퍼 구성 시 LSB부터 배열하도록 비트 연산을 적용합니다.
6. STM32F429 CANopen 노드 구축 마무리
STM32F429 MCU 환경에서 CANopen 프로토콜을 성공적으로 운용하기 위해서는 bxCAN 하드웨어 레지스터 필터의 정렬 구조를 명확히 이해하고, CiA 301 사양에 정의된 NMT State Machine 및 SDO/PDO 동작 특성을 준수해야 합니다. 본 가이드에서 제공한 bxCAN 설정, SDO/PDO 구현 방식, 그리고 디버깅 요소를 적용하면 안정적인 산업용 Embedded CANopen 노드를 구축할 수 있습니다.