CAN BUS ON STM32
Can Bus接線
- CAN 物理層的形式主要有兩種,左圖中的 CAN 通訊網路是一種遵循 ISO11898 標準的高速、短距離「閉環網路」,它的匯流排最大長度為 40m,通訊速度最高為 1Mbps,匯流排的兩端各要求有一個「120 歐」的電阻。
- 右圖中的是遵循 ISO11519-2 標準的低速、遠距離「開環網路」,它的最大傳輸距離為 1km,最高通訊速率為 125kbps,兩根匯流排是獨立的、不形成閉環,要求每根匯流排上各串聯有一個「2.2千歐」的電阻。
PS:這邊硬體接線請注意必加電阻與CAN transceiver ,千萬不要傻傻的MCU的Can腳位直接接出來!!!!
MESSAGE TRANSFER(CAN通訊的資料格式)
CAN2.0有兩種版本,CAN2.0A(Standard),CAN2.0B(Extended)。
- Start of Frame(SOF):送出1位元dominant(0),用來同步。
- Arbitration Field:表示節點的優先權,用來判別優先權。若多個節點同時傳送,在仲裁欄位逐一比對優先權,
位元為dominant(0)可優先傳送。長度有兩種11bits(Standard identifier)和29bits(Extended Identifier)。 - RTR :為優先判斷與資料接收與否的識別
RTR=dominant(0),表資料要傳出,RTR=recessive(1),表要接收資料。 - IDE: 標準格式(Standard identifier)為dominant(0);延伸格式(Extended Identifier)為 recessive(1)。
- R0:保留
CAN Bus上的傳輸圖解
可以看到下圖分別有3個傳輸裝置分別是A、B、C會把資料同時放到Bus線路上這邊只要留意每個裝置傳輸時候給予微小delay時間即可避免資料傳輸重疊衝突
STM32cubeMX設置與範例程式
怎麼設定波特率呢?
例子1:我們要設定成500KHz,那麼我們這樣設定(公式如下)
Fpclk1/((CAN_BS1+CAN_BS2+1)*CAN_Prescaler)=42M/(4+2+1)/12=500kHz
Basic Parameter設置
- Timer Triggered Communication Mode:否使用時間觸發功能 (ENABLE/DISABLE),時間觸發功能在某些CAN 標準中會使用到。
- Automatic Bus-Off Management:用於設定是否使用自動離線管理功能 (ENABLE/DISABLE),
使用自動離線管理可以在出錯時離線後適時自動恢復,不需要軟體干預。 - Automatic Wake-Up Mode:用於設定是否使用自動喚醒功能 (ENABLE/DISABLE),
使能自動喚醒功能後它會在監測到匯流排活動後自動喚醒。 - Automatic Retransmission:用於設定是否使用自動重傳功能 (ENABLE/DISABLE),
使用自動重傳功能時,會一直傳送報文直到成功為止。 - Receive Fifo Locked Mode:用於設定是否使用鎖定接收 FIFO(ENABLE/DISABLE),
鎖定接收 FIFO 後,若FIFO 溢位時會丟棄新資料,否則在 FIFO 溢位時以新資料覆蓋舊資料。 - Transmit Fifo Priority:用於設定傳送報文的優先順序判定方法 (ENABLE/DISABLE),使能時,以報文存入傳送郵箱的先後順序來傳送,否則按照報文 ID 的優先順序來傳送。
中斷種類
- RX0,RX1中斷:STM32有2個3級深度的接收緩衝區:FIFO0和FIFO1,每個FIFO都可以存放3個完整的報文,它們完全由硬體來管理。
如果是來自FIFO0的接收中斷,則用CAN1_RX0_IRQn中斷來處理。
如果是來自FIFO1的接收中斷,則用CAN1_RX1_IRQn中 斷來處理 - SCE中斷:status chanege error,錯誤和狀態變化中斷
範例程式與涵式庫
這邊以F4系列為例子其他可以參考各系列的User_manual
開啟CAN Bus(必加)
HAL_CAN_Start(&hcan1);
Send Data
uint8_t bsp_can1_send_msg(uint32_t id_type,uint32_t basic_id,uint32_t ex_id,uint8_t *data,uint32_t data_len)
{
uint8_t index = 0;
uint32_t *msg_box;
uint8_t send_buf[8] = {0};
CAN_TxHeaderTypeDef send_msg_hdr;
send_msg_hdr.StdId = basic_id;
send_msg_hdr.ExtId = ex_id;
send_msg_hdr.IDE = id_type;
send_msg_hdr.RTR = CAN_RTR_DATA;
send_msg_hdr.DLC = data_len;
send_msg_hdr.TransmitGlobalTime = DISABLE;
for(index = 0; index < data_len; index++)
send_buf[index] = data[index];
HAL_CAN_AddTxMessage(&hcan1,&send_msg_hdr,send_buf,msg_box);
return BSP_CAN_OK;
}
Get Data
uint8_t bsp_can1_polling_recv_msg(uint32_t *basic_id,uint32_t *ex_id,uint8_t *data,uint32_t *data_len)
{
uint8_t index = 0;
uint8_t recv_data[8];
CAN_RxHeaderTypeDef header;
while (HAL_CAN_GetRxFifoFillLevel(&hcan1, CAN_RX_FIFO0) != 0)
{
if (__HAL_CAN_GET_FLAG(&hcan1, CAN_FLAG_FOV0) != RESET)
printf("[CAN] FIFO0 overrun!\n");
HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &header, recv_data);
if(header.IDE == CAN_ID_STD)
{
printf("StdId ID:%d\n",header.StdId);
}
else
{
printf("ExtId ID:%d\n",header.ExtId);
}
printf("CAN IDE:0x%x\n",header.IDE);
printf("CAN RTR:0x%x\n",header.RTR);
printf("CAN DLC:0x%x\n",header.DLC);
printf("RECV DATA:");
for(index = 0; index < header.DLC; index++)
{
printf("0x%x ",recv_data[index]);
}
printf("\n");
}
}
其他涵式庫
PS:舊版的會有HAL_CAN_Transmit和HAL_CAN_Receive較新的MCU都替換成新涵式庫可以看下圖