1. STM32 마이크로컨트롤러를 활용한 CAN 버스 인터페이스 하드웨어 및 임베디드 드라이버 통합 구현 (작성 배경 및 취지)
임베디드 시스템 및 자동차 내부 통신 네트워크(In-Vehicle Network) 설계 시 CAN(Controller Area Network, ISO 11898) 프로토콜은 뛰어난 실시간성과 노이즈 내성, Multi-Master 구조의 특징을 기반으로 필수 기술 표준으로 활용되고 있습니다.
그러나 실제 현장에서 MCU 내부 CAN 컨트롤러(STM32 bxCAN)를 활용해 네트워크를 구성할 때, 비트 타이밍(Bit Timing) 계산 오류, CAN 필터(Filter Bank) 미설정으로 인한 수신 인터럽트 드롭, 물리 계층 트랜시버(SN65HVD230, TJA1050)의 전압 레벨 불일치 등 다양한 하드웨어 및 소프트웨어 예외가 자주 발생합니다.
본 포스팅에서는 STM32F1/F4 bxCAN 모듈의 하드웨어 연결 방식과 C/C++ HAL 펌웨어 드라이버를 구현하는 올바른 방법을 다룹니다. 또한, Linux 환경에서의 SocketCAN(can-utils) 디버깅 기법과 하드웨어 문제 예방법을 상세히 보강하여 작성되었습니다.
2. STM32 HAL CAN 통신 핵심 요약 (핵심 요약)
- STM32 bxCAN 수신 실패 해결: STM32 bxCAN 하드웨어는 Power-On Reset 시 모든 필터 은행(Filter Bank)이 비활성화되므로, HAL_CAN_ConfigFilter()를 구현하여 명시적으로 FilterActivation = ENABLE로 지정해야 인터럽트 콜백(HAL_CAN_RxFifo0MsgPendingCallback)이 호출됩니다.
- STM32 bxCAN 비트 타이밍 설정: APB1 Clock을 기준으로 Sample Point가 75%~80% 범위에 형성되도록 Prescaler와 Time Segment(BS1, BS2)를 정확히 산출하여 비트 동기화 오류를 방지합니다.
- 물리 계층 및 디버깅: CAN_H와 CAN_L 선간 양단에 120Ω 종단 저항(Termination Resistor)을 병렬 설치(합성 저항 약 60Ω)해야 하며, Linux 버스 모니터링 시 candump 및 cansend 도구를 활용합니다.
3. STM32 CAN 버스 컨트롤러 분석 및 펌웨어 드라이버 상세 구현 (본문 분석 및 구현)
3.1 STM32 bxCAN 하드웨어 인터페이스 특징 및 트랜시버 연결
CAN 프로토콜은 OSI 7계층 중 데이터 링크 계층(Data Link Layer)과 물리 계층(Physical Layer)에 해당합니다. STM32에 내장된 bxCAN(Basic Extended CAN) 컨트롤러의 특징은 다음과 같습니다.
| 구 분 | STM32 bxCAN 상세 사양 |
|---|---|
| 호환 표준 | CAN 2.0A / 2.0B Active |
| 호스트 인터페이스 | APB1 System Bus (내장) |
| 수신 FIFO 버퍼 | 2-Mailbox (3-Level Deep) |
| 송신 버퍼 | 3 Transmit Mailboxes |
| 필터 은행 (Filter) | 14 또는 28 Configurable Banks |
| 권장 물리 트랜시버 | SN65HVD230 (3.3V Logic), TJA1050 (5V Logic) |
3.2 STM32 bxCAN 모듈 펌웨어 구현 및 Bit Timing 계산
STM32F1/F4 시리즈의 bxCAN을 구동하기 위해서는 APB1 클록 기반의 Bit Timing 계산이 올바르게 이루어져야 합니다.
$$\text{Baud Rate} = \frac{\text{APB1 Clock}}{\text{Prescaler} \times (1 + \text{BS1} + \text{BS2})}$$
Sample Point 공식:
$$\text{Sample Point (\%)} = \frac{1 + \text{BS1}}{1 + \text{BS1} + \text{BS2}} \times 100$$
ISO 11898 표준에 맞춰 Sample Point를 약 75%~80% 지점으로 맞춰야 신호 지연에 따른 에러를 방지할 수 있습니다.
STM32 HAL CAN 초기화 및 Filter Config 소스 코드
#include "stm32f1xx_hal.h"
CAN_HandleTypeDef hcan1;
void CAN1_Init(void) {
/* Step 1: Configure CAN Hardware Instance and Bit Timing Params */
hcan1.Instance = CAN1;
hcan1.Init.Prescaler = 18; // APB1 Clock = 36MHz -> Tq = 18 / 36MHz = 0.5us
hcan1.Init.Mode = CAN_MODE_NORMAL; // Normal Operating Mode
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ; // SJW = 1TQ
hcan1.Init.TimeSeg1 = CAN_BS1_13TQ; // BS1 = 13TQ
hcan1.Init.TimeSeg2 = CAN_BS2_2TQ; // BS2 = 2TQ (Nominal Bit Time = 1+13+2 = 16TQ -> 500kbps)
hcan1.Init.TimeTriggeredMode = DISABLE;
hcan1.Init.AutoBusOff = ENABLE; // Automatically recover from Bus-Off state
hcan1.Init.AutoWakeUp = DISABLE;
hcan1.Init.AutoRetransmission = ENABLE;
hcan1.Init.ReceiveFifoLocked = DISABLE;
hcan1.Init.TransmitFifoPriority = DISABLE;
if (HAL_CAN_Init(&hcan1) != HAL_OK) {
/* Initialization Error Handling */
Error_Handler();
}
/* Step 2: Configure Hardware Acceptance Filter Bank (Mandatory) */
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; // Pass all Standard/Extended IDs
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
sFilterConfig.FilterActivation = CAN_FILTER_ENABLE;
sFilterConfig.SlaveStartFilterBank = 14;
if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) {
Error_Handler();
}
/* Step 3: Start CAN Peripheral and Enable Receive Interrupt */
if (HAL_CAN_Start(&hcan1) != HAL_OK) {
Error_Handler();
}
if (HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) {
Error_Handler();
}
}
/* Step 4: Implement Non-blocking Receive Callback Function */
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) {
CAN_RxHeaderTypeDef rxHeader;
uint8_t rxData[8];
if (hcan->Instance == CAN1) {
if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rxHeader, rxData) == HAL_OK) {
/* Process received CAN Frame (ID, DLC, Data) */
}
}
}
4. USB-to-CAN 및 Linux SocketCAN을 이용한 네트워크 디버깅 (개발을 위한 팁)
개발 단계에서 CAN 버스의 데이터 트래픽 분석 및 에러 프레임 감지를 위해 PC 기반 인터페이스(PCAN-USB, Kvaser, CANable)와 Linux can-utils 패키지를 적극적으로 활용해야 합니다.
SocketCAN 인터페이스 설정 및 명령어 집합
Linux OS(Ubuntu, Raspberry Pi OS) 환경에서는 USB-to-CAN 장치를 SocketCAN 커널 모듈에 바인딩하여 표준 네트워크 인터페이스로 등록할 수 있습니다.
# 1. Bring up CAN interface at 500kbps bitrate
sudo ip link set can0 type can bitrate 500000
sudo ip link set can0 up
# 2. Display CAN interface detailed parameters and status
ip -details -statistics link show can0
# 3. Monitor CAN Bus Traffic in Real-Time
candump -tz can0
# 4. Transmit CAN Message (Standard ID: 0x123, Data Payload: DE AD BE EF)
cansend can0 123#DEADBEEF
# 5. Bring down CAN interface
sudo ip link set can0 down
5. STM32 CAN 통신 구현 시 발생하는 하드웨어/소프트웨어 주요 예외 및 해결책 (흔히 하는 실수)
5.1 HAL_CAN_ConfigFilter() 미설정으로 인한 수신 콜백 미동작
- 증상: 오실로스코프로 버스 신호를 측정하면 정상 전송되고 있으나, STM32 MCU에서 HAL_CAN_RxFifo0MsgPendingCallback() 인터럽트 콜백 함수가 호출되지 않음.
- 원인: STM32 bxCAN 하드웨어는 Power-On Reset 시 모든 필터 은행이 비활성화(Disabled) 상태로 초기화되어 수신된 모든 프레임을 차단함.
- 해결책: HAL_CAN_ConfigFilter() 함수를 작성하고 FilterActivation = ENABLE 및 FilterMaskId를 설정하여 획득하고자 하는 ID 레지스터 값을 허용해야 함.
5.2 Bit Timing 및 Prescaler 오설정으로 인한 HAL_CAN_ERROR_BOF (Bus-Off)
- 증상: HAL_CAN_AddTxMessage() 호출 후 전송 실패가 반복되고, TEC(Transmit Error Counter) 레지스터가 255를 초과하여 MCU가 Bus-Off 상태로 진입함.
- 원인: STM32CubeMX에서 설정한 APB1 Clock 주파수를 기준으로 계산된 Sample Point가 ISO 표준(75% ~ 80%)을 벗어나 비트 동기화(Bit Synchronization)에 실패함.
- 해결책: 다음 공식을 만족하도록 Prescaler, BS1, BS2 레지스터를 재조정해야 함.
$$\text{Baud Rate} = \frac{\text{APB1 Clock}}{\text{Prescaler} \times (1 + \text{BS1} + \text{BS2})}$$
5.3 3.3V/5V 전압 레벨 불일치 및 종단 저항 미연결
- 증상: STM32F4(3.3V Logic)에 5V 전원 기반 트랜시버(TJA1050, MCP2551)를 직접 결선할 경우 RX 핀 전압 과부하 발생 또는 통신 불능 상태 진입.
- 원인: 트랜시버 VCC 공급 전압 기준에 따라 로직 레벨 불일치가 발생하거나, CAN 버스 양단 120Ω 종단 저항 미설치로 신호 반사파(Signal Reflection)가 발생함.
- 해결책: 3.3V MCU 전원 환경에서는 3.3V 전용 트랜시버인 SN65HVD230을 사용해야 함. 버스 양단 끝에는 총 60Ω의 병렬 종단 저항 상태를 확인해야 함.
6. 결론 및 하드웨어 가이드 요약 (마무리)
STM32 마이크로컨트롤러 기반 CAN 통신 네트워크를 성공적으로 구축하기 위해서는 내장 컨트롤러(STM32 bxCAN)의 특성을 이해하고, 구동 환경에 적합한 물리 트랜시버(SN65HVD230, TJA1050)를 선정해야 합니다.
소프트웨어 구현 시 비트 타이밍 레지스터 계산, 수신 필터 은행(Filter Bank) 활성화, 인터럽트 콜백 등록이 선행되어야 하며, can-utils 기반 디버깅 노하우를 접목하면 검증 공수를 단축할 수 있습니다.