/* USER CODE BEGIN 2 */ if (HAL_CAN_Start(&hcan1) != HAL_OK) { Error_Handler(); }
TxHeader.StdId = 0x321;
TxHeader.ExtId = 0x01;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.IDE = CAN_ID_STD;
TxHeader.DLC = 2;
TxHeader.TransmitGlobalTime = DISABLE;
TxData[0] = 0xAC;
TxData[1] = 0xAD;
/* USER CODE END 2 */
/* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */
/\* USER CODE BEGIN 3 \*/
++TxData[1];
HAL\_CAN\_AddTxMessage(&hcan1, &TxHeader, TxData, &TxMailbox);
HAL\_Delay(10);
} /* USER CODE END 3 */
编译下载运行:

### CAN接收
CAN接收需要先设置滤波器, 这里设为0, 全部接收:
/* USER CODE BEGIN 0 */ void CAN1_Config(void) { /*## Configure the CAN Filter ##*/ CAN_FilterTypeDef sFilterConfig; sFilterConfig.FilterBank = 0; sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; sFilterConfig.FilterIdHigh = 0x0000; sFilterConfig.FilterIdLow = 0x0000; sFilterConfig.FilterMaskIdHigh = 0x0000; sFilterConfig.FilterMaskIdLow = 0x0000; sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; sFilterConfig.FilterActivation = ENABLE; sFilterConfig.SlaveStartFilterBank = 14; if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK) { Error_Handler(); /* Filter configuration Error */ }
/\*## Start the CAN peripheral ##\*/
if (HAL\_CAN\_Start(&hcan1) != HAL_OK) {
Error\_Handler();
}
/\*## Activate CAN RX notification ##\*/
if (HAL\_CAN\_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) {
Error\_Handler();
}
}
void CAN2_Config(void) { /*## Configure the CAN Filter ##*/ CAN_FilterTypeDef sFilterConfig; sFilterConfig.FilterBank = 14; sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; sFilterConfig.FilterIdHigh = 0x0000; sFilterConfig.FilterIdLow = 0x0000; sFilterConfig.FilterMaskIdHigh = 0x0000; sFilterConfig.FilterMaskIdLow = 0x0000; sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; sFilterConfig.FilterActivation = ENABLE; sFilterConfig.SlaveStartFilterBank = 14; if (HAL_CAN_ConfigFilter(&hcan2, &sFilterConfig) != HAL_OK) { Error_Handler(); /* Filter configuration Error */ }
/\*## Start the CAN peripheral ##\*/
if (HAL\_CAN\_Start(&hcan2) != HAL_OK) {
Error\_Handler();
}
/\*## Activate CAN RX notification ##\*/
if (HAL\_CAN\_ActivateNotification(&hcan2, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) {
Error\_Handler();
}
} /* USER CODE END 0 */
上面`CAN2`的`FilterBank`是从14开始的, 一共28个滤波器, `CAN1`和`CAN2`各占14个.
main函数中初始化调用:
/* USER CODE BEGIN 2 */ CAN1_Config(); CAN2_Config(); /* USER CODE END 2 */
然后是接收中断, 这里把接收到的中断通通回传, CAN1的传给CAN1, CAN2的回传给CAN2:
/* USER CODE BEGIN 4 */ /** * @brief Rx Fifo 0 message pending callback * @param hcan: pointer to a CAN_HandleTypeDef structure that contains * the configuration information for the specified CAN. * @retval None */ void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) { if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RxData) != HAL_OK) { Error_Handler(); }
TxHeader.StdId = RxHeader.StdId;
TxHeader.ExtId = RxHeader.ExtId;
TxHeader.RTR = RxHeader.RTR;
TxHeader.IDE = RxHeader.IDE;
TxHeader.DLC = RxHeader.DLC;
memcpy(TxData, RxData, RxHeader.DLC);
HAL\_CAN\_AddTxMessage(hcan, &TxHeader, TxData, &TxMailbox);
}
编译下载运行:

事实上, 我们上面没有考虑发送失败怎么办, 可以自己写一个发送函数, 发送失败延时重发, 失败超过限定次数放弃:
void CAN_SendStdMsg(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *txMsg, uint8_t sendData[]) { //txMsg->StdId = 0x321; txMsg->ExtId = 0x00; txMsg->RTR = CAN_RTR_DATA; txMsg->IDE = CAN_ID_STD; txMsg->DLC = 8; txMsg->TransmitGlobalTime = DISABLE;
uint32_t TxMailbox;
uint32_t j = 0;
while(1) {
if ((HAL\_CAN\_AddTxMessage(hcan, txMsg, sendData, &TxMailbox)!= HAL_ERROR) || (j++ > 10)) {
break; //success or fail\_num>10, stop send
}
for(__IO uint16_t k=0; k < 500; k++); //busy, delay some time
}
}
void CAN_SendExtMsg(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *txMsg, uint8_t sendData[]) { txMsg->StdId = 0x00; //txMsg->ExtId = 0x00; txMsg->RTR = CAN_RTR_DATA; txMsg->IDE = CAN_ID_EXT; txMsg->DLC = 8; txMsg->TransmitGlobalTime = DISABLE;
uint32_t TxMailbox;
uint32_t j = 0;
while(1) {
if ((HAL\_CAN\_AddTxMessage(hcan, txMsg, sendData, &TxMailbox)!= HAL_ERROR) || (j++ > 10)) {
break; //success or fail\_num>10, stop send
}
for(__IO uint16_t k=0; k < 500; k++); //busy, delay some time
}
}
### CAN3
`STM32F413 / 423, STM32F765 / 767 / 769 / 777 / 779` 系列有第三路CAN, 也就是`CAN3`, 上面`CAN1`是`Master`, `CAN2`是`Slave`, 28个滤波器组也用完了, 那`CAN3`怎么搞? 以STM32F767为例:

图中直接变成一个Master, 应该是可以和CAN1平起平坐的, 中断里面也没什么差别:

`CAN3`的滤波器设置与`CAN1`类似:
void CAN3_Config(void) { /*## Configure the CAN Filter ##*/ CAN_FilterTypeDef sFilterConfig; sFilterConfig.FilterBank = 0; sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; sFilterConfig.FilterIdHigh = 0x0000; sFilterConfig.FilterIdLow = 0x0000; sFilterConfig.FilterMaskIdHigh = 0x0000; sFilterConfig.FilterMaskIdLow = 0x0000; sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; sFilterConfig.FilterActivation = ENABLE; if (HAL_CAN_ConfigFilter(&hcan3, &sFilterConfig) != HAL_OK) { Error_Handler(); /* Filter configuration Error */ }
/\*## Start the CAN peripheral ##\*/
if (HAL\_CAN\_Start(&hcan3) != HAL_OK) {
Error\_Handler();
}
/\*## Activate CAN RX notification ##\*/
if (HAL\_CAN\_ActivateNotification(&hcan3, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) {
Error\_Handler();
}
}
接收中断函数也是一样的, 不必担心.
打开`HAL_CAN_ConfigFilter`函数定义, 里面有这几句话:
/* CAN1 and CAN2 are dual instances with 28 common filters banks */ /* Select master instance to access the filter banks */
/* CAN3 is single instance with 14 dedicated filters banks */
CAN3有自己独立的滤波器. 但HAL库函数用起来, 让你感觉和CAN1用法是一样的.
### CANFD板子
上面说到, `STM32G系列`/`H系列`/`L5系列` 有CANFD, 换一块自己手打的`STM32G474`的小板子:

FDCAN电平芯片在背面, 注意用到FDCAN功能时, CAN电平转换芯片也需要支持才行, 硬件连接上:
| FDCAN | STM32 |
| --- | --- |
| FDCAN1\_TX | PD1 |
| FDCAN1\_RX | PD0 |
| FDCAN2\_TX | PB6 |
| FDCAN2\_RX | PB5 |
| FDCAN3\_TX | PB4 |
| FDCAN3\_RX | PB3 |
外挂的12MHz晶振.
操作如下:
* MCU选择: 打开 `STM32CubeMX`, 点击 `ACCESS TO MCU SELECTOR`, 选择 `STM32G474VETx`
* 调试端口配置为SWD: `Pinout & Configuration` -> `System Core` -> `SYS` -> `Debug` 选择 `Serial Wire`
* `Pinout & Configuration` -> `System Core` -> `RCC` -> `HSE` 选择 `Crystal/Ceramic Resonator`
* Clock Configuration:

### CANFD配置
`Pinout & Configuration` -> `Connectivity` -> `FDCAN1`, `Mode` 可选`Classic Master`作为`普通CAN`用, 也可以选择`FD`, 由于我最近没有`CANFD`的应用, 就选`Classic Master`先当`普通CAN`用(至于`FD`的可参考<http://bbs.eeworld.com.cn/thread-1080702-1-1.html>这篇文章), 然后手动把引脚从`PA11/PA12`挪到`PD0/PD1`:

回到 `Clock Configuration` 时钟树里, 可以看到`FDCAN`的时钟是独立可选的, 先用默认的PCLK1, 170MHz:

CANFD的速率可变, 仲裁比特率最高1Mbps(与CAN相同), 数据比特率可以很高(8Mbps?),这就牵涉到两个波特率的配置, 如下图所示:

红色框中的`Nominal`可以当成仲裁波特率, 蓝色框中的`Data`当成数据波特率, 由于这里只当`CAN`用, 设成一样的500K波特率就可以了, 计算方法: `FDCAN_CLK / Prescaler / (seg1 + seg2 + 1) = 170M/17/(9+10+1)=500K`.
`NVIC`中勾选`FDCAN1 interrupt 0`, 优先级先默认不配置:

参考上面的`生成代码`一小节, 打开工程.
### CANFD发送
`main.c`代码如下:
/* USER CODE BEGIN PV */ FDCAN_TxHeaderTypeDef TxHeader; uint8_t TxData[8] = {0}; /* USER CODE END PV */
/* USER CODE BEGIN 2 */ if (HAL_FDCAN_Start(&hfdcan1) != HAL_OK) { Error_Handler(); } /* USER CODE END 2 */
/* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */
/\* USER CODE BEGIN 3 \*/
TxHeader.Identifier = 0x1234567;
TxHeader.IdType = FDCAN_EXTENDED_ID;
TxHeader.TxFrameType = FDCAN_DATA_FRAME;
TxHeader.DataLength = FDCAN_DLC_BYTES_8; //not 8
TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
TxHeader.BitRateSwitch = FDCAN_BRS_OFF;
TxHeader.FDFormat = FDCAN_CLASSIC_CAN;
TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
TxHeader.MessageMarker = 0;
++TxData[7];
if(HAL\_FDCAN\_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, TxData) != HAL_OK) {
Error\_Handler();
}
HAL\_Delay(1000);
} /* USER CODE END 3 */
### CANFD接收
`main.c`代码如下, 滤波器设置了全接收(标准帧未测试), 中断函数中回传接收到的CAN帧:
/* USER CODE BEGIN PV */ FDCAN_TxHeaderTypeDef TxHeader; FDCAN_RxHeaderTypeDef RxHeader; uint8_t TxData[8] = {0}; uint8_t RxData[8]; /* USER CODE END PV */
/* USER CODE BEGIN 0 */ void FDCAN1_Config(void) { FDCAN_FilterTypeDef sFilterConfig;
/* Configure Rx filter */ sFilterConfig.IdType = FDCAN_STANDARD_ID; sFilterConfig.FilterIndex = 0; sFilterConfig.FilterType = FDCAN_FILTER_RANGE; sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; sFilterConfig.FilterID1 = 0x00; sFilterConfig.FilterID2 = 0x7FF; if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK) { Error_Handler(); }
sFilterConfig.IdType = FDCAN_EXTENDED_ID;
sFilterConfig.FilterIndex = 0; sFilterConfig.FilterType = FDCAN_FILTER_RANGE; sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; sFilterConfig.FilterID1 = 0x00; sFilterConfig.FilterID2 = 0x1FFFFFFF; if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK) { Error_Handler(); }
/* Configure global filter: Filter all remote frames with STD and EXT ID Reject non matching frames with STD ID and EXT ID */ if (HAL_FDCAN_ConfigGlobalFilter(&hfdcan1, FDCAN_REJECT, FDCAN_REJECT, FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK) { Error_Handler(); }
/* Start the FDCAN module */ if (HAL_FDCAN_Start(&hfdcan1) != HAL_OK) { Error_Handler(); }
if (HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0) != HAL_OK) { Error_Handler(); } } /* USER CODE END 0 */
收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!