CAN 버스 프로토콜 및 임베디드 구현 가이드

CAN 통신 원리와 신호 표현

CAN(Controller Area Network)은 두 개의 차동 신호선인 CAN_H와 CAN_L로 구성됩니다. 이는 높은 신뢰성과 실시간성을 요구하는 자동차 및 산업 제어 시스템에 널리 사용됩니다.

통신 시 CAN_H와 CAN_L 사이의 전압 차이는 다음과 같은 논리 상태를 나타냅니다:

  • 지배적 수준(Dominant): 논리 0
  • 비지배적 수준(Recessive): 논리 1

ISO11898 표준(125kbps ~ 1Mbps)에서 CAN 신호 표현 방식은 차동 전압을 기반으로 하며, 지배적 수준은 양극성 전압 차이를 나타냅니다.

전송 과정

송신 과정:

  1. CAN 컨트롤러가 CPU로부터 받은 데이터를 논리 레벨(0 또는 1)로 변환
  2. CAN 트랜시버가 논리 레벨을 차동 신호로 변환하여 버스에 출력

수신 과정:

  1. CAN 트랜시버가 CAN_H/CAN_L에서 차동 신호를 받아 논리 레벨로 변환
  2. CAN 컨트롤러가 논리 레벨을 다시 CPU가 이해할 수 있는 데이터로 변환

CAN 프레임 구조

CAN 통신은 다섯 가지 프레임 유형을 지원합니다:

  1. 데이터 프레임: 실제 데이터 전송
  2. 원격 프레임: 데이터 요청
  3. 오류 프레임: 오류 알림
  4. 오버로드 프레임: 수신 준비 지연 알림
  5. 프레임 간격: 프레임 분리

표준 데이터 프레임은 다음으로 구성됩니다:

  1. 시작 비트(SOF): 1비트 지배적 신호
  2. 중재 필드: 식별자와 RTR 비트 포함
  3. 제어 필드: 확장 비트, 예약 비트, DLC 포함
  4. 데이터 필드: 최대 8바이트
  5. CRC 필드: 오류 검출용
  6. ACK 필드: 수신 확인
  7. 프레임 종료: 7비트 비지배적 신호

CAN 프로토콜 기본 사항

CAN 물리 계층

CAN은 비동기 통신으로 클럭 신호 없이 동작하며, 두 개의 차동 신호선(CAN_H, CAN_L)을 사용합니다. 주요 특징은:

  • 고속 모드(ISO11898): 최대 1Mbps, 40m 이내, 양단에 120Ω 저항 필요
  • 저속 모드(ISO11519-2): 최대 125kbps, 1km까지, 개방형 네트워크

CAN 프로토콜 계층

비동기 통신 특성상 노드 간 미리 정의된 보레이트로 통신하며, 비트 동기화를 통해 간섭에 강한 특성을 갖습니다.

비트 타이밍 분해

CAN은 각 데이터 비트를 다음 네 부분으로 분해합니다:

  • SS 세그먼트: 동기화 점프 폭
  • PTS 세그먼트: 전송 지연 보상
  • PBS1 세그먼트: 샘플링 지점 전
  • PBS2 세그먼트: 샘플링 지점 후

최소 시간 단위 Tq(타이밍 쿼터)로 구성되며, 한 비트는 8~25개 Tq로 구성됩니다.

보레이트 계산

예시: Tq=1μs, 한 비트당 19Tq일 때

보레이트 = 1,000,000μs / 19μs = 52,632 bps

동기화 방식

  • 하드 동기화: 프레임 시작 시 동기화
  • 재동기화: 데이터 비트 전환 시 동기화

STM32 CAN 컨트롤러

STM32는 bxCAN 컨트롤러를 내장하고 있으며, CAN 2.0A/B 표준을 지원합니다. 최대 1Mbps 통신 속도를 제공하며:

  • 3개의 송신 메일박스
  • 2개의 3단계 수신 FIFO
  • ID 필터링 기능
  • 자동 재전송 지원

CAN 컨트롤 코어

주요 레지스터:

  • CAN_MCR: 작동 모드 관리(INRQ 비트로 초기화 제어)
  • CAN_BTR: 테스트 모드, 보레이트, 타이밍 파라미터 설정

보레이트 계산식:

보레이트 = APB1_CLK / ((BRP+1) * (TS1+TS2+1))

예: TS1=9, TS2=5, BRP=6, APB1=45MHz

보레이트 = 45,000,000 / (7 * 15) = 500,000 bps

송신 메일박스

3개의 메일박스로 최대 3개의 프레임을 버퍼링할 수 있습니다. 각 메일박스는 다음 레지스터로 구성:

  • CAN_TIxR: 식별자 레지스터
  • CAN_TDTxR: 데이터 길이 제어 레지스터
  • CAN_TDLxR/CAN_TDHxR: 데이터 레지스터

수신 FIFO

2개의 FIFO, 각각 3개의 슬롯으로 총 6개 프레임 저장 가능:

  • CAN_RIxR: 식별자 레지스터
  • CAN_RDTxR: 데이터 길이 제어 레지스터
  • CAN_RDLxR/CAN_RDHxR: 데이터 레지스터

ID 필터링

28개의 필터 그룹으로 특정 ID만 수신 가능:

  • 마스크 모드: 특정 비트 패턴 매칭
  • 리스트 모드: 정확한 ID 매칭

필터 스케일:

  • 32비트: STDID[10:0], EXTID[17:0], IDE, RTR 포함
  • 16비트: STDID[10:0], RTR, IDE, EXTID[17:15] 포함

CAN 네트워크 설계

종단 저항의 역할

120Ω 종단 저항의 주요 목적:

  1. 간섭 저감: 고주파 노이즈 신속 제거
  2. 신속한 비지배적 상태 전환: 기생 커패시턴스 에너지 소산
  3. 신호 품질 향상: 반사파 감소

저항값 선정 근거

케이블의 특성 임피던스가 약 120Ω이므로, 무한 긴 전송선로를 시뮬레이션하기 위해 120Ω 저항 사용.

전력 소모 계산

CANH가 18V에 단락될 경우:

P = I² × R = (0.05A)² × 120Ω = 0.3W

고온 환경 고려 시 0.5W 등급 권장

CAN FD 프로토콜

CAN FD의 등장 배경

CAN 2.0의 한계:

  • 데이터 길이 제한: 최대 8바이트
  • 속도 제한: 최대 1Mbps
  • 대역폭 효율성: 약 40-50%

CAN FD는 2015년 ISO11898-1에 통합되어 다음을 개선:

  • 변속률 지원: 최대 8Mbps
  • 확장 데이터 길이: 최대 64바이트
  • 개선된 CRC 알고리즘

CAN FD vs CAN 2.0 차이점

항목 CAN 2.0 CAN FD
최대 속도 1Mbps 8Mbps
데이터 길이 8바이트 64바이트
ID 길이 11비트 12비트
CRC 길이 15비트 21비트

CAN FD 프레임 구조

CAN FD 프레임은 다음 필드로 구성:

  1. FDF 비트: CAN/FD 구분
  2. BRS 비트: 변속률 제어
  3. ESI 비트: 오류 상태 표시

STM32 CAN 통신 구현

기본 설정

#include "stm32f10x.h"

typedef struct {
    uint32_t StdId;
    uint32_t ExtId;
    uint8_t IDE;
    uint8_t RTR;
    uint8_t DLC;
    uint8_t Data[8];
} CanTxMsg;

void CAN_GPIO_Config(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    
    // CAN_RX (PB8) - Pull-up input
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    // CAN_TX (PB9) - Alternate function push-pull
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

void CAN_NVIC_Config(void) {
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
    
    NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

void CAN_Config(void) {
    CAN_InitTypeDef CAN_InitStructure;
    CAN_FilterInitTypeDef CAN_FilterInitStructure;
    
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
    
    CAN_DeInit(CAN1);
    CAN_StructInit(&CAN_InitStructure);
    
    // CAN parameters
    CAN_InitStructure.CAN_TTCM = DISABLE;
    CAN_InitStructure.CAN_ABOM = DISABLE;
    CAN_InitStructure.CAN_AWUM = DISABLE;
    CAN_InitStructure.CAN_NART = DISABLE;
    CAN_InitStructure.CAN_RFLM = DISABLE;
    CAN_InitStructure.CAN_TXFP = DISABLE;
    CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
    CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
    CAN_InitStructure.CAN_BS1 = CAN_BS1_3tq;
    CAN_InitStructure.CAN_BS2 = CAN_BS2_2tq;
    CAN_InitStructure.CAN_Prescaler = 60;
    CAN_Init(CAN1, &CAN_InitStructure);
    
    // Filter configuration
    CAN_FilterInitStructure.CAN_FilterNumber = 0;
    CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
    CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
    CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
    CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
    CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
    CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0;
    CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
    CAN_FilterInit(&CAN_FilterInitStructure);
    
    CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
}

송신 함수

uint8_t CAN_TransmitData(uint32_t extId, uint8_t* data, uint8_t length) {
    CanTxMsg TxMessage;
    uint8_t mailbox;
    uint16_t timeout = 0;
    
    TxMessage.StdId = 0x00;
    TxMessage.ExtId = extId;
    TxMessage.IDE = CAN_ID_EXT;
    TxMessage.RTR = CAN_RTR_DATA;
    TxMessage.DLC = length > 8 ? 8 : length;
    
    for(int i = 0; i < TxMessage.DLC; i++) {
        TxMessage.Data[i] = data[i];
    }
    
    mailbox = CAN_Transmit(CAN1, &TxMessage);
    
    while((CAN_TransmitStatus(CAN1, mailbox) == CAN_TxStatus_Failed) && (timeout < 0xFFF)) {
        timeout++;
    }
    
    if(timeout >= 0xFFF) {
        return 0; // Transmission failed
    }
    
    return 1; // Success
}

수신 인터럽트 핸들러

void USB_LP_CAN1_RX0_IRQHandler(void) {
    CanRxMsg RxMessage;
    
    CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
    
    // Process received data
    if(RxMessage.IDE == CAN_ID_EXT) {
        switch(RxMessage.ExtId) {
            case 0x1314: // Master node
                // Handle master data
                break;
            case 0x1311: // Slave node
                // Handle slave data
                break;
        }
    }
}

CAN 필터 설정 예제

특정 ID만 수신

// Filter to receive only ID 0x12345678
CAN_FilterInitTypeDef CAN_FilterConfig;

CAN_FilterConfig.CAN_FilterNumber = 0;
CAN_FilterConfig.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterConfig.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterConfig.CAN_FilterIdHigh = 0x1234;  // Upper 16 bits
CAN_FilterConfig.CAN_FilterIdLow = 0x5678;   // Lower 16 bits
CAN_FilterConfig.CAN_FilterMaskIdHigh = 0xFFFF;
CAN_FilterConfig.CAN_FilterMaskIdLow = 0xFFFF;
CAN_FilterConfig.CAN_FilterFIFOAssignment = CAN_FIFO0;
CAN_FilterConfig.CAN_FilterActivation = ENABLE;

CAN_FilterInit(&CAN_FilterConfig);

범위 기반 필터링

// Filter to receive IDs starting with 0x12xxxxxx
CAN_FilterConfig.CAN_FilterIdHigh = 0x1200;  // Match first byte
CAN_FilterConfig.CAN_FilterIdLow = 0x0000;
CAN_FilterConfig.CAN_FilterMaskIdHigh = 0xFF00; // Mask first byte
CAN_FilterConfig.CAN_FilterMaskIdLow = 0x0000;

CAN_FilterInit(&CAN_FilterConfig);

태그: CAN bus embedded systems STM32 Microcontroller CAN protocol

6월 25일 00:20에 게시됨