CAN 통신 설정 이전글 :
2023.01.27 - [DSP, MCU/STM32 (ARM Cortex-M)] - STM32 ] CAN 통신 설정 (bxCAN peripheral)
연결
내가 사용한건 SN65HVD230 CAN 트랜시버 모듈이다. CAN1과 CAN2 둘 다 사용하기 위해 모듈을 2개 사용했고, 보통 IC칩이 아니라 이런 모듈을 사면 120옴짜리 저항이 다 붙어서오기때문에 종단저항을 신경쓸 필요가 없다.
만약 CAN to USB 같은 다른 CAN 디바이스를 붙인다면, 중간에 붙이고 해당 디바이스에 디폴트 저항이 붙어있진 않은지 체크해 볼 필요가 있다.
Bus 종단저항이 올바르게 설치됐는지 확인하는 방법은 멀티미터로 체크하면 된다. 종단에 120옴 저항을 병렬로 설치했을 때 CAN bus 어디에서 측정하나 합성저항 60옴이 측정되어야 한다.
트랜시버가 없다면 루프백 모드로 bxCAN 송수신기능을 테스트하는 방법도 있다.
CAN Loop Back Mode
Self test를 위한 CAN Loop Back Mode이다.
그림과 같은 구조로 bxCAN 컨트롤러가 송신한 프레임은 bus 에 올라가고 RX를 통해 그대로 loop back 될테지만 RX line이 bus 와 연결되어 있지 않으니 bus에 있는 프레임을 수신할 수 는 없다.
bxCAN Transmission 요약
- 소프트웨어에서 message를 set up 하기 위해 3개의 transmit mailbox 가 제공된다.
- 어떤 메일박스를 먼저 송신할 지는 Transmission Scheduler 가 결정한다.
- 메시지를 송신하기 위해선 먼저 empty state 의 송신 메일박스를 선택하고 identifier, date length code, data 셋업한 뒤 송신을 요청해야 한다.
- 송신 요청은 control register 의 TXRQ bit 을 set 함으로써 이뤄진다.
- TXRQ bit 이 set 되면, 그 즉시 메일박스는 pending state에 들어가 highest priority 메일박스가 될 때까지 대기한다.
- 하나의 메일박스만 사용되는 경우, 그 메일박스가 highest priority one 이 될 것이다.
- 메일박스가 highest priority 를 가지는 순간 송신을 위한 전 단계인 scheduled state 에 들어간다.
- CAN bus 가 IDLE 상태가 되면 scheduled state 에 있던 메일박스는 transmit state 에 들어가 실제 전송이 이뤄진다.
- Scheduled state 에 들어간 메일박스가 있다고 하더라도, bus 가 IDLE 이 되기 전에 새로 pending state 에 들어온 메일박스가 더 높은 priority 를 가진다면 송신 순서가 바뀔 수 있다.
- 메일박스가 성공적으로 송신되고 나면 다시 empty state 가 된다.
- 전송이 성공하면 하드웨어적으로 CAN_TSR 레지스터의 RQCP 와 TXOK bit 가 set 된다.
- 전송이 실패하는 경우, 실패의 원인이 Arbitration Lost 면 CAN_TSR 레지스터의 ALST bit 가 set 되고, transmit error 가 detection 되는 경우 TERR bit 가 set 된다.
다음은 Transmit mailbox 의 상태도인데 이 그림을 이해하는 것이 매우 중요하다. STM 말고 다른 MCU를 써도 이런 CAN 컨트롤러 구조는 비슷할 것이다.
노란색이 성공적으로 송신했을 때의 흐름이다.
1. Empty state 메일박스에 메시지를 셋업한 이후 TXRQ bit 를 set 해 메일박스를 pending state 에 가져다 놓는다.
2. 메일박스가 highest priority one 이라면 Transmit scheduler 에 의해 scheduled state 에 들어갈 것이다.
3. CAN bus 가 IDLE 상태라면 transmit state 에 들어가 전송이 이뤄진다.
4. 전송 이후 메일박스는 다시 empty state 가 된다.
Pending state, Scheduled state 에 있는 메일박스를 ABRQ bit 를 set 함으로써 송신 중단시킬 수 있다.
Transmit 에 실패했을 때 CAN_MCR 의 NART 가 0인 경우 scheduled state 로 돌아가 재전송을 시도하고 NART 가 1인 경우 재전송 시도하지 않고 메일박스는 비워진다.
Transmit API, CAN_TxHeaderTypeDef 구조체
송신 API. 비어있는 메일박스에 메시지를 넣고 송신요청을 하는 함수
HAL_StatusTypeDef
HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *pHeader,
uint8_t aData[], uint32_t *pTxMailbox)
- CAN 핸들 포인터
- 아래에서 언급할 헤더 구조체변수의 포인터
- Frame 의 payload 가 될 데이터 배열
- uint32_t * 형으로 인자를 주면 어떤 메일박스가 쓰였는지 저장해주는 파라미터
CAN 메시지의 헤더의 구조체 정의
typedef struct
{
uint32_t StdId; /*!< Specifies the standard identifier.
This parameter must be a number between Min_Data = 0 and Max_Data = 0x7FF. */
uint32_t ExtId; /*!< Specifies the extended identifier.
This parameter must be a number between Min_Data = 0 and Max_Data = 0x1FFFFFFF. */
uint32_t IDE; /*!< Specifies the type of identifier for the message that will be transmitted.
This parameter can be a value of @ref CAN_identifier_type */
uint32_t RTR; /*!< Specifies the type of frame for the message that will be transmitted.
This parameter can be a value of @ref CAN_remote_transmission_request */
uint32_t DLC; /*!< Specifies the length of the frame that will be transmitted.
This parameter must be a number between Min_Data = 0 and Max_Data = 8. */
FunctionalState TransmitGlobalTime; /*!< Specifies whether the timestamp counter value captured on start
of frame transmission, is sent in DATA6 and DATA7 replacing pData[6] and pData[7].
@note: Time Triggered Communication Mode must be enabled.
@note: DLC must be programmed as 8 bytes, in order these 2 bytes are sent.
This parameter can be set to ENABLE or DISABLE. */
} CAN_TxHeaderTypeDef;
- StdId : Standad ID. 0~0x7FF
- ExtId : Extended ID. 0~0x1FFFFFFF
- IDE : Identifer type. Standard 인지 Extended 인지
- RTR : Frame type. Data Frame 인지 RTR frame 인지
- DLC : 데이터 length. 0~8
- TransmitGlobalTime : 옵션
위 구조체는 메시지의 헤더를 위한 구조체이므로 데이터를 담을 버퍼는 따로 만들어서 송신함수에 넘겨야 한다.
송신에 성공했는지 polling 방식으로 체크하는 함수
uint32_t HAL_CAN_IsTxMessagePending(CAN_HandleTypeDef *hcan, uint32_t TxMailboxes)
pending 상태인 송신요청이 있는지 확인하는 함수이다. 송신요청 후 송신이 잘 됐는지 체크하기 위해 쓸 수 있다.
2번째 파라미터로 넘어온 메일박스들 중 하나라도 pending 상태인 송신요청이 있으면 1을 리턴, 없으면 0을 리턴한다.
메일박스 인자는 하나를 넣어도 되고 여러 메일박스를 OR연산으로 넘겨도 된다.
// @defgroup CAN_Tx_Mailboxes CAN Tx Mailboxes
#define CAN_TX_MAILBOX0 (0x00000001U) /*!< Tx Mailbox 0 */
#define CAN_TX_MAILBOX1 (0x00000002U) /*!< Tx Mailbox 1 */
#define CAN_TX_MAILBOX2 (0x00000004U) /*!< Tx Mailbox 2 */
송신 예시
/* USER CODE BEGIN 0 */
void CAN1_tx(void)
{
CAN_TxHeaderTypeDef TxHeader;
uint8_t our_message[5] = {'H','E','L','L','O'};
uint32_t TxMailbox;
char msg[50];
TxHeader.DLC = 5;
TxHeader.StdId = 0x65D;
TxHeader.IDE = CAN_ID_STD;
TxHeader.RTR = CAN_RTR_DATA;
if(HAL_CAN_AddTxMessage(&hcan1, &TxHeader, our_message, &TxMailbox) != HAL_OK)
{
Error_Handler();
}
while(HAL_CAN_IsTxMessagePending(&hcan1, TxMailbox)){}
sprintf(msg, "Message Transmitted\r\n");
HAL_UART_Transmit(&huart3, (uint8_t *)msg, strlen(msg), HAL_MAX_DELAY);
}
/* USER CODE END 0 */
Tx Pin을 통해 본 파형
Receive Flow
CAN RX 핀을 통해 들어온 수신 메시지는 Acceptance Filter Banks 를 맨 처음 통과한다.
Filter 를 설정하는 것은 소프트웨어에서 가능하고, 수신메시지가 Filtering Rules 을 만족하는 지 체크하는 것과 만족하는 메시지를 FIFO1 또는 FIFO2 에 저장하는 것, 그리고 Filtering Rules 을 만족하지 않는 메시지(관심없는 메시지)를 통과시키지 않고 폐기하는 것은 하드웨어 단에서 이뤄진다.
CAN 은 Broadcast 시스템으로 각 node 가 bus 에 있는 모든 메시지를 듣기 때문에 Acceptance Filter 를 설정하는 것은 CPU 의 부하를 줄일 수 있다.
그리고 FIFO에 저장된 메시지는 Application 이 읽어갈 수 있도록 인터럽트를 트리거 한다.
bxCAN RX Features
- CAN1 과 CAN2 컨트롤러는 각각 수신 메시지를 저장하기 위해 두개의 receive FIFO 를 갖는다.
- 각 FIFO 는 3개의 메시지를 저장할 수 있다.
- FIFO 는 하드웨어 단에서 관리된다.
Acceptance Filter
- CAN1(Master) 과 CAN2(Slave) 가 공유하는 28개의 Filter banks 가 있다.
- 각 Filter Bank 는 2 * 32 bit Filter Resisters 를 가지고 있다. 각 레지스터는 FBx_R1, FBx_R2 로 부르며 아래와 같은 구조의 Filter bank 가 28개 있다.
Receive API, CAN_RxHeaderTypeDef 구조체
polling 방식 수신 API. Rx FIFO 로부터 메시지 램에 Receive Frame 을 가져온다.
HAL_StatusTypeDef
HAL_CAN_GetRxMessage(CAN_HandleTypeDef *hcan, uint32_t RxFifo,
CAN_RxHeaderTypeDef *pHeader, uint8_t aData[])
- hcan : CAN 핸들
- RxFifo : FIFO0 or FIFO1
- pHeader : 헤더 정보를 저장할 구조체 변수 포인터
- aData : 데이터를 저장할 버퍼
CAN_RxHeaderTypeDef 구조체 정의
typedef struct
{
uint32_t StdId; /*!< Specifies the standard identifier.
This parameter must be a number between Min_Data = 0 and Max_Data = 0x7FF. */
uint32_t ExtId; /*!< Specifies the extended identifier.
This parameter must be a number between Min_Data = 0 and Max_Data = 0x1FFFFFFF. */
uint32_t IDE; /*!< Specifies the type of identifier for the message that will be transmitted.
This parameter can be a value of @ref CAN_identifier_type */
uint32_t RTR; /*!< Specifies the type of frame for the message that will be transmitted.
This parameter can be a value of @ref CAN_remote_transmission_request */
uint32_t DLC; /*!< Specifies the length of the frame that will be transmitted.
This parameter must be a number between Min_Data = 0 and Max_Data = 8. */
uint32_t Timestamp; /*!< Specifies the timestamp counter value captured on start of frame reception.
@note: Time Triggered Communication Mode must be enabled.
This parameter must be a number between Min_Data = 0 and Max_Data = 0xFFFF. */
uint32_t FilterMatchIndex; /*!< Specifies the index of matching acceptance filter element.
This parameter must be a number between Min_Data = 0 and Max_Data = 0xFF. */
} CAN_RxHeaderTypeDef;
TxHeaderTypeDef 구조체와 비슷하다. Receive Frame 의 관련된 값들이 각 파라미터에 저장되게 된다.
Rx FIFO 에 메시지가 들어있는지 확인하는 함수
uint32_t HAL_CAN_GetRxFifoFillLevel(CAN_HandleTypeDef *hcan, uint32_t RxFifo)
두번째 파라미터로 @arg CAN_receive_FIFO_number 를 넣으면 해당 FIFO에 들어있는 메시지 개수를 리턴한다. 하나의 FIFO 는 3개의 메시지까지 저장할 수 있으므로 0~3 사이의 리턴값을 가질 것이다.
Acceptance Filter 예시와 사용법
Acceptance Filter 의 사용 예시
1. Standard Identifier 의 first 3 MSB 가 1 인 Frame 만 accept 한다. e.g. 111xxxxxxxx
2. Standard Identifier 의 first 3 MSB 가 0 이고 last 2 LSB 가 1 인 Frame 만 accept 한다. e.g. 000xxxxxx11
3. Standard Identifier 가 0x65D 또는 0x651 인 Frame 만 Accept 한다.
4. Remote Request Frame 만 Accept 한다.
5. Extended Identifier Frame 만 Accept 한다.
6. 모든 Frame 을 Accept 한다.
1. Standard Identifier 의 first 3 MSB 가 1 인 Frame 만 accept 한다. e.g. 111xxxxxxxx
- Mask Mode 를 사용한다.
- Mast Mode 를 사용하는 경우 R1 레지스터는 Identifier Register 가 되고 R2 레지스터는 Mask Register 가 된다.
- Mask Register STID 상위 3 bit 를 1로 하면 STD ID의 first 3 MSB 가 체크되고 나머지는 0으로 채우면 체크되지 않는다.
- Identifier Register STID 상위 3 bit 를 1로 한다. Mask Register 로 체크할 bit 의 위치를 결정한다면 Identifier Register는 실제 수신된 Frame 과 비교할 값이다. 수신 프레임의 STD ID 가 111xxxxxxxx 으로 시작하 Identifier Register 와 일치하면 Accept 되고 일치하지 않으면 discard 된다.
2. Standard Identifier 의 first 3 MSB 가 0 이고 last 2 LSB 가 1 인 Frame 만 accept 한다. e.g. 000xxxxxx11
- Mask Mode 를 사용한다.
- Mask Register 의 STID 상위 3 bit 를 1로 하고 하위 2 bit 를 1로 한다.
- Identifier Register STID 상위 3 bit 를 0으로 하고 하위 2 bit 를 1로 한다.
3. Standard Identifier 가 0x65D 또는 0x651 인 Frame 만 Accept 한다.
- List / ID Mode 를 사용한다.
- List / ID Mode 를 사용하는 경우 R1 은 Identifier Register-1 이 되고 R2 는 Identifier Register-2 가 된다. 각 Register 에 매치 여부를 체크할 Identifier 를 넣으면 된다.
- Identifier Register-1 에 0x65D 를 대입한다.
- Identifier Register-2 에 0x651 을 대입한다.
4. Remote Request Frame 만 Accept 한다.
- Mask Mode 를 사용한다.
- Mask Register 의 RTR bit 를 1로 한다.
- Identifier Register 의 RTR bit 를 1로 한다.
- RTR bit 가 Recessive(1)면 Remote Request Frame, Dominant(0)면 Data Frame 이니까 Remote Request Frame 만 Accept 할 수 있다.
5. Extended Identifier Frame 만 Accept 한다.
- Mask Mode 를 사용한다.
- Mask Register 의 IDE bit 를 1로 한다.
- Identifier Register 의 IDE bit 를 1로 한다.
- IDE bit 가 Recessive(1)면 29 bit Identifier Frame, Dominant(0)면 11 bit Identifier Frame 이니까 Extended ID Frame 만 Accept 할 수 있다.
6. 모든 Frame 을 Accept 한다.
- 이 경우 Acceptance Filter 설정을 안하고 모든 bit 가 default 값이 0이면 된다. 단, 어떤 FIFO를 메시지를 보낼지 정하기 위해서 쓰일 수 있다.
Filter Configuration API, CAN_FilterTypeDef 구조체
Filter 설정 API
HAL_StatusTypeDef
HAL_CAN_ConfigFilter(CAN_HandleTypeDef *hcan, CAN_FilterTypeDef *sFilterConfig)
두번째 파라미터가 필터 설정을 위한 CAN_FilterTypeDef 구조체 변수 주소이다.
CAN_FilterTypeDef 구조체 정의
typedef struct
{
uint32_t FilterIdHigh; /*!< Specifies the filter identification number (MSBs for a 32-bit
configuration, first one for a 16-bit configuration).
This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */
uint32_t FilterIdLow; /*!< Specifies the filter identification number (LSBs for a 32-bit
configuration, second one for a 16-bit configuration).
This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */
uint32_t FilterMaskIdHigh; /*!< Specifies the filter mask number or identification number,
according to the mode (MSBs for a 32-bit configuration,
first one for a 16-bit configuration).
This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */
uint32_t FilterMaskIdLow; /*!< Specifies the filter mask number or identification number,
according to the mode (LSBs for a 32-bit configuration,
second one for a 16-bit configuration).
This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */
uint32_t FilterFIFOAssignment; /*!< Specifies the FIFO (0 or 1U) which will be assigned to the filter.
This parameter can be a value of @ref CAN_filter_FIFO */
uint32_t FilterBank; /*!< Specifies the filter bank which will be initialized.
For single CAN instance(14 dedicated filter banks),
this parameter must be a number between Min_Data = 0 and Max_Data = 13.
For dual CAN instances(28 filter banks shared),
this parameter must be a number between Min_Data = 0 and Max_Data = 27. */
uint32_t FilterMode; /*!< Specifies the filter mode to be initialized.
This parameter can be a value of @ref CAN_filter_mode */
uint32_t FilterScale; /*!< Specifies the filter scale.
This parameter can be a value of @ref CAN_filter_scale */
uint32_t FilterActivation; /*!< Enable or disable the filter.
This parameter can be a value of @ref CAN_filter_activation */
uint32_t SlaveStartFilterBank; /*!< Select the start filter bank for the slave CAN instance.
For single CAN instances, this parameter is meaningless.
For dual CAN instances, all filter banks with lower index are assigned to master
CAN instance, whereas all filter banks with greater index are assigned to slave
CAN instance.
This parameter must be a number between Min_Data = 0 and Max_Data = 27. */
} CAN_FilterTypeDef;
아래 그림 참고.
- FilterIdHigh : FBx_R1 의 first 16 bit 설정
- FilterIdLow: FBx_R1 의 second 16 bit 설정
- FilterMaskIdHigh : FBx_R2 의 first 16 bit 설정
- FilterMaskIdLow : FBx_R2 의 second 16 bit 설정
- FilterFIFOAssignment : 해당 필터에 어떤 FIFO를 Assgin하여 사용할지 선택한다. 0 또는 1
- FilterBank : initialize 할 Filter bank. Single CAN 을 쓰는 경우 14개의 Filter bank 만 쓸 수 있으며 0~13 사이의 값이 온다. Dual CAN 을 쓰는 경우 0~27 사이의 값이 온다.
- FilterMode : Filter 를 Mask Mode 로 쓸 지 List Mode 로 쓸 지 선택한다.
- FilterScale : 16 bit scale 과 32 bit scale 이 있으며 32bit scale 을 선택하면 된다.
- FilterActivation : Filter Enable / Disable
- SlaveStartFilterBank : Single CAN 을 쓰는 경우 필요없는 옵션. Dual CAN 을 쓰는 경우 이 설정값보다 작은 index 의 Filter bank 는 Master CAN 이 사용하고 설정값보다 큰 index 의 Filter bank 는 Slave CAN 이 사용한다. (예를 들어 파라미터가 14면 Master 가 0-13을 쓰고, Slave 가 14-27을 쓴다.)
FilterScale 이 32bit 인 경우 레지스터 구조가 아래 그림에서 윗부분과 같고 16bit 인 경우 아랫부분과 같다.
Filter 설정 예시
먼저 Acceptance Filter 를 설정하고 Filtering Rules 를 만족하는 경우 어떤 FIFO로 보낼지 Guide 해주어야 한다. (FIFO0으로 보낼지 FIFO1로 보낼지)
그리고 Filter 설정은 normal 모드에 들어가기 전 initialization 모드에서 설정해주어야 하므로 반드시 HAL_CAN_Start() 함수 호출 전에 마쳐야한다.
void CAN_Filter_Config(void)
{
CAN_FilterTypeDef can1_filter_init;
can1_filter_init.FilterActivation = ENABLE;
can1_filter_init.FilterBank = 0;
can1_filter_init.FilterFIFOAssignment = CAN_RX_FIFO0;
can1_filter_init.FilterIdHigh = 0x0000;
can1_filter_init.FilterIdLow = 0x0000;
can1_filter_init.FilterMaskIdHigh = 0x0000;
can1_filter_init.FilterMaskIdLow = 0x0000;
can1_filter_init.FilterMode = CAN_FILTERMODE_IDMASK;
can1_filter_init.FilterScale = CAN_FILTERSCALE_32BIT;
if(HAL_CAN_ConfigFilter(&hcan1, &can1_filter_init) != HAL_OK)
{
Error_Handler();
}
}
수신 예시
int CAN1_rx(void)
{
int ret = 0;
CAN_RxHeaderTypeDef RxHeader;
uint8_t rcvd_msg[8];
char msg[50];
if(HAL_CAN_GetRxFifoFillLevel(&hcan1, CAN_RX_FIFO0) > 0)
{
if(HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &RxHeader, rcvd_msg) != HAL_OK)
{
Error_Handler();
}
else
{
ret = 1;
sprintf(msg, "Message Received : %s\r\n", rcvd_msg);
HAL_UART_Transmit(&huart3, (uint8_t *)msg, strlen(msg), HAL_MAX_DELAY);
}
}
return ret;
}
RxFIFO Fill Level 을 체크해 들어있는 메시지가 없으면 바로 리턴하고 메시가 있으면 수신 메시지를 터미널에 출력한 후 1을 리턴한다.
while (1)
{
if(CAN1_rx())
{
CAN1_tx();
}
}
참고.
PCAN-View 에서는 New Transmit Message 를 만들고 Cycle Time 을 설정해 Periodical 송신을 할 수 도 있고 Cycle Time을 0으로 설정한 경우 space 를 눌러 송신할 수 있다.
Reference : Fastbit Embedded Brain Academy
다음글 :
2023.01.31 - [DSP, MCU/STM32 (ARM Cortex-M)] - STM32 ] CAN Interrupt
'임베디드 개발 > STM32 (ARM Cortex-M)' 카테고리의 다른 글
STM32 ] Clock 설정, HSI, HSE, PLL, LSI, LSE (0) | 2023.02.05 |
---|---|
STM32 ] CAN Interrupt (3) | 2023.01.31 |
STM32 ] CAN 통신 설정 (bxCAN peripheral) (0) | 2023.01.27 |
STM32 ] 인터럽트 처리 과정 (0) | 2022.12.22 |
STM32 ] 무료 다이어그램 툴 draw.io 로 SW 설계해보기 (0) | 2022.12.22 |