CAN 통신 원리와 신호 표현
CAN(Controller Area Network)은 두 개의 차동 신호선인 CAN_H와 CAN_L로 구성됩니다. 이는 높은 신뢰성과 실시간성을 요구하는 자동차 및 산업 제어 시스템에 널리 사용됩니다.
통신 시 CAN_H와 CAN_L 사이의 전압 차이는 다음과 같은 논리 상태를 나타냅니다:
- 지배적 수준(Dominant): 논리 0
- 비지배적 수준(Recessive): 논리 1
ISO11898 표준(125kbps ~ 1Mbps)에서 CAN 신호 표현 방식은 차동 전압을 기반으로 하며, 지배적 수준은 양극성 전압 차이를 나타냅니다.
전송 과정
송신 과정:
- CAN 컨트롤러가 CPU로부터 받은 데이터를 논리 레벨(0 또는 1)로 변환
- CAN 트랜시버가 논리 레벨을 차동 신호로 변환하여 버스에 출력
수신 과정:
- CAN 트랜시버가 CAN_H/CAN_L에서 차동 신호를 받아 논리 레벨로 변환
- CAN 컨트롤러가 논리 레벨을 다시 CPU가 이해할 수 있는 데이터로 변환
CAN 프레임 구조
CAN 통신은 다섯 가지 프레임 유형을 지원합니다:
- 데이터 프레임: 실제 데이터 전송
- 원격 프레임: 데이터 요청
- 오류 프레임: 오류 알림
- 오버로드 프레임: 수신 준비 지연 알림
- 프레임 간격: 프레임 분리
표준 데이터 프레임은 다음으로 구성됩니다:
- 시작 비트(SOF): 1비트 지배적 신호
- 중재 필드: 식별자와 RTR 비트 포함
- 제어 필드: 확장 비트, 예약 비트, DLC 포함
- 데이터 필드: 최대 8바이트
- CRC 필드: 오류 검출용
- ACK 필드: 수신 확인
- 프레임 종료: 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Ω 종단 저항의 주요 목적:
- 간섭 저감: 고주파 노이즈 신속 제거
- 신속한 비지배적 상태 전환: 기생 커패시턴스 에너지 소산
- 신호 품질 향상: 반사파 감소
저항값 선정 근거
케이블의 특성 임피던스가 약 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 프레임은 다음 필드로 구성:
- FDF 비트: CAN/FD 구분
- BRS 비트: 변속률 제어
- 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);