사용 MCU: STM32F407ZGT
사용 IDE: STM32CubeIDE
CubeMX버전: 6.0.1
사용 HAL 버전: STM32Cube FW_F4 V1.25.2
개요[1]
- Bosch 사에서 차량 내에서 호스트 컴퓨터 없이 마이크로 콘트롤러나 장치들이 서로 통신하기 위해 설계된 통신 규격
- 1993년에 처음 ISO 11898 국제 표준으로 제정
특징[2]
- Multi Master 구조
- 최대 1Mbps 통신 속도
- 최대 8byte 통신 데이터 수
- 통신 프로토콜 / 에러 처리를 하드웨어적으로 처리
- 다수의 장치(Standard: 11bit, Extended 29bit ID구분)간 통신 가능
- Twist pair wire와 차동신호를 사용하여 노이즈 환경에 강함
- 비용이 경제적
프로토콜[3]
- ISO 11898:2003 표준에서 Standard 11bit 식별자를 사용하여 125kbps ~ 1Mbps의 통신 속도를 제공합니다.
이 표준은 나중에 Extended 29bit 식별자로 수정되었습니다.
- Standard 11bit: 2^11, 2048개의 메세지 식별자
- Extended 29bit: 2^29, 536,870,912개의 메세지 식별자
Standard CAN: 11-bit 식별자 Bit Fields
SOF | 11-bit Identifier |
RTR | IDE | r0 | DLC | 0~8 bytes Data | CRC | ACK | EOF | IFS |
SOF(Start of frame): 프레임의 시작 비트
Identifier: 메세지의 우선순위를 설정하는 11bit 식별자, 값이 낮을수록 우선 순위가 높다
RTR(single remote transmission request): 다른 노드로부터 정보가 필요할 때 정의. (Data Frame: 0, Remote Frame:1)
IDE(dominant single identifier extension): CAN 식별자 타입 정의 (Standard: 0, Extended: 1)
r0: Reserved bit
DLC(data length code): 송신하는 데이터의 byte 수로 4bit
Data: 송신 데이터로 최대 8byte
CRC(Cyclic redundancy check): 16bit checksum (15bit + 1bit 구분기호), 오류 감지를 위한 비트
EOF(End of Frame): 프레임 끝
IFS(interframe space): 7bit
Extended CAN: 29-bit 식별자 Bit Fields
SOF | 11-bit Identifier |
SRR | IDE | 18-bit Identifier |
RTR | r1 | r0 | DLC | 0~8 bytes Data | CRC | ACK | EOF | IFS |
SRR(substitute remote request): Extension format의 자리를 표시
IDE(identifier extension): 더 많은 식별자 비트가 뒤따른다는 것을 나타냄
r1: Reserved bit
회로
우선 제가 가지고 있는 보드의 회로도는 아래와 같습니다.
- R26, R27은 사용하고자 하는 CAN Transceiver 모듈에 따라 공급전압을 선택하기 위한 저항으로 사용되었습니다.
- R40은 임피던스 매칭을 위한 저항으로 사용되었습니다.
Transceiver
CAN 통신을 하기 위해서는 MCU에 CAN 핀이 있더라도 별도의 Transceiver를 달아주어야 합니다.
그 이유는 CAN 통신은 CANH와 CANL 라인으로 차동 신호로 통신을 하는데
MCU의 신호를 차동신호로, 차동 신호를 MCU가 받아들일 수 있는 디지털 신호로 변환시켜주는 역할을 하기 때문입니다. 또 정전기(ESD)같은 왜란에 의해 Transceiver가 데미지를 입어 MCU가 손상되는 것을 막아주는 역할이 될 수 있습니다.
종단저항
또 CANH와 CANL 양 종단에 120옴 정도의 저항을 달아주어야 합니다(거리가 가까우면 달아주지 않아도 괜찮다고 하기도 하는것 같습니다.) 120옴을 달아주어야 하는 이유는 신호 왜곡을 보호하기 위한 임피던스 매칭을 위해 ISO 11898 표준으로 정의했기 때문입니다.
퀀텀(Quantum, tq)[4][5]: bit time의 기본 시간 단위
* Synchronization Segment(Sync_Seg): 1 Time Quantum 길이, 다양한 버스 노드를 동기화에 사용
* Propagation Time Segment(Prop_Seg): CAN 네트워크 내에서 물리적 지연 시간을 보상에 사용
* Phase Buffer Segment 1(Phase_Seg1): 에지 위상 오류를 보상하는 데 사용
* Phase Buffer Segment 2(Phase_Seg2): 에지 위상 오류를 보상하는 데 사용
CubeMX 설정
우선 STM32F407 데이터시트를 보면, CAN은 APB1을 사용하고 최대 42MHz의 클럭을 가지는 것을 확인할 수 있습니다.
HCLK 클럭 설정을 아래와 같이 최대로 설정하고 APB1의 클럭이 42MHz라는 것을 확인하였습니다.
Can을 활성화 하고 각 파라미터에 대해 알아보겠습니다.
Prescaler: 통신의 속도를 설정, (1/APB1 * Prescaler = Time Quantum, 1/42M * 16 = 380ns)
Quanta 설정
CAN Bit Time Calculation (can-wiki.info) 사이트에 들어가서 파라미터를 얻습니다.
1. 사용 MCU 선택: ST Microelectronics bxCAN
2. Sample-Point 입력: 50~90%, (CANopen와 DeviceNet에서는 일반적으로 87.5%, ARINC 825에서는 75% 사용)
3. SJW: 1 고정
4. Baud rate 입력 (125 ~ 1000)
다음과 같은 값을 얻어 아래와 같이 입력하였습니다.
NVIC에서 CAN1에 대한 설정도 Enable 시켜줍니다.
프로젝트를 생성하고 다음과 같이 예제 프로그램을 작성하였습니다.
// 데이터를 송신예제
/* USER CODE BEGIN PV */
CAN_TxHeaderTypeDef TxHeader;
uint8_t TxData[8];
uint32_t TxMailbox;
/* USER CODE END PV */
/* USER CODE BEGIN 0 */
// printf 를 사용하기 위한 함수
int _write(int file, char *ptr, int len)
{
HAL_UART_Transmit(&huart1, (uint8_t *)ptr, (uint16_t)len, 100);
return (len);
}
/* USER CODE END 0 */
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_CAN1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
printf("Start\r\n");
/* Can Start */
if (HAL_CAN_Start(&hcan1) != HAL_OK)
{
/* Start Error */
Error_Handler();
}
/* Configure Transmission process */
TxHeader.StdId = 0x321; // Standard Identifier, 0 ~ 0x7FF
TxHeader.ExtId = 0x01; // Extended Identifier, 0 ~ 0x1FFFFFFF
TxHeader.RTR = CAN_RTR_DATA; // 전송하는 메세지의 프레임 타입, DATA or REMOTE
TxHeader.IDE = CAN_ID_STD; // 전송하는 메세지의 식별자 타입, STD or EXT
TxHeader.DLC = 8; // 송신 프레임 길이, 0 ~ 8 byte
TxHeader.TransmitGlobalTime = DISABLE; // 프레임 전송 시작될 때 timestamp counter 값을 capture.
/* Set the data to be transmitted */
TxData[0] = 1;
TxData[1] = 2;
TxData[2] = 3;
TxData[3] = 4;
TxData[4] = 5;
TxData[5] = 6;
TxData[6] = 7;
TxData[7] = 8;
/* Start the Transmission process */
if (HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, &TxMailbox) != HAL_OK)
{
printf("Can Send Fail\r\n");
Error_Handler();
}
printf("Can Send Success\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
// CAN 수신 예제
/* USER CODE BEGIN PV */
CAN_FilterTypeDef sFilterConfig; // 필터 설정 구조체 변수
CAN_RxHeaderTypeDef RxHeader;
uint8_t RxData[8];
/* USER CODE END PV */
/* USER CODE BEGIN 0 */
// printf 를 사용하기 위한 함수
int _write(int file, char *ptr, int len)
{
HAL_UART_Transmit(&huart1, (uint8_t *)ptr, (uint16_t)len, 100);
return (len);
}
// CAN 수신 인터럽트 콜백
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *CanHandle)
{
printf("%s\r\n", __FUNCTION__);
/* Get RX message */
if (HAL_CAN_GetRxMessage(CanHandle, CAN_RX_FIFO0, &RxHeader, RxData) != HAL_OK)
{
/* Reception Error */
Error_Handler();
}
printf("StdID: %04lx, IDE: %ld, DLC: %ld\r\n", RxHeader.StdId, RxHeader.IDE, RxHeader.DLC);
printf("Data: %d %d %d %d %d %d %d %d\r\n", RxData[0], RxData[1], RxData[2], RxData[3], RxData[4], RxData[5], RxData[6], RxData[7]);
}
// CAN Error 콜백
void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan)
{
printf("%s\r\n", __FUNCTION__);
}
/* USER CODE END 0 */
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_CAN1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
printf("Start\r\n");
/* CAN Filter 설정 */
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = 0x0000;
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000; // 0x00000000 = 모든 ID를 받아들이겠다
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.SlaveStartFilterBank = 14; // CAN2의 FilterBank시작 위치, CAN2를 사용한다면 FilterBank를 SlaveStartFilterBank보다 크게 설정해야 함.
if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
{
/* Filter configuration Error */
Error_Handler();
}
/* Can Start */
if (HAL_CAN_Start(&hcan1) != HAL_OK)
{
/* Start Error */
Error_Handler();
}
/* Activate CAN RX notification */
if (HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
{
/* Notification Error */
Error_Handler();
}
printf("Can Ready!!\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
Reference
[1] CAN 버스 - 위키백과, 우리 모두의 백과사전
[3] Introduction to the Controller Area Network - TI Application Report
'MCU > STM32:HAL' 카테고리의 다른 글
[STM32CubeProgrammer]UART Bootloader로 펌웨어 다운로드 하기 (2) | 2023.06.21 |
---|---|
[STM32F][Teraterm]YMODEM 프로토콜로 헥사 전송시 버그 (0) | 2023.03.24 |
[STM32][DSP] DSP 라이브러리 설정 (2) | 2022.06.17 |
[HAL][STM32F]USART와 Queue (2) | 2021.03.24 |
[STM32F]FSMC와 LCD (0) | 2021.02.28 |
[STM32F]FSMC로 LCD(SSD1963) 제어하기 (2) | 2021.02.07 |
[STM32F][HAL] USART Interrupt (0) | 2020.12.23 |
[STM32F][HAL] DAC (Digital to Analog Converter) (0) | 2020.12.22 |