STM32CubeMX_CAN_CAN3_FDCAN_cubemx dcan,2024年春招物联网嵌入式开发面试题

126 阅读5分钟

/* 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 */


编译下载运行:  
 ![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/844a98ba9d1c4638986859467a97f167~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1770653569&x-signature=HZChwrhEA4btuZOSldhh0a2UZyw%3D)


### 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);

}


编译下载运行:  
 ![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/02a6494dfc22475eb983ab0f87cfb5c6~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1770653569&x-signature=AA3B4%2FRsKJIbx3bR9J%2Bg%2Fo514nc%3D)  
 事实上, 我们上面没有考虑发送失败怎么办, 可以自己写一个发送函数, 发送失败延时重发, 失败超过限定次数放弃:



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为例:  
 ![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/a392cea14a7f464ab8ad5dd6d25c05b0~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1770653569&x-signature=ETjIx116lDuoBgD8tLJ6e1ZYozo%3D)  
 图中直接变成一个Master, 应该是可以和CAN1平起平坐的, 中断里面也没什么差别:  
 ![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/741ca0453f004868925acf21a8594834~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1770653569&x-signature=emh9e%2BKiiVcA8iIOEMLBgkgNnYA%3D)  
 `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`的小板子:  
 ![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/6bb8636a5b85452eb69e46d5186e1828~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1770653569&x-signature=GYowPieAW4rOPzpThNBloV%2BlmQE%3D)  
 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:  
 ![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/21b67bd226d145a78b14072e283ccf66~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1770653569&x-signature=UkwEnoqrRbz7N0J5NcuhW%2BxK4Mc%3D)


### 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`:  
 ![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/5279287d18ec425e836efea5465a3427~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1770653569&x-signature=%2BFGMm7zHq3u6sz6SnYGXaAQ163Y%3D)  
 回到 `Clock Configuration` 时钟树里, 可以看到`FDCAN`的时钟是独立可选的, 先用默认的PCLK1, 170MHz:  
 ![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/f96268db610c4f62a0c68feefc6104cc~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1770653569&x-signature=2cq1f7sV6egmttVvB3ZSdIhp4l4%3D)  
 CANFD的速率可变, 仲裁比特率最高1Mbps(与CAN相同), 数据比特率可以很高(8Mbps?),这就牵涉到两个波特率的配置, 如下图所示:  
 ![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/cfb6d4bbeefa44fcbc00fdf996e42d16~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1770653569&x-signature=2ANk9e9Ke79jjM1oY8y19xThQ6A%3D)  
 红色框中的`Nominal`可以当成仲裁波特率, 蓝色框中的`Data`当成数据波特率, 由于这里只当`CAN`用, 设成一样的500K波特率就可以了, 计算方法: `FDCAN_CLK / Prescaler / (seg1 + seg2 + 1) = 170M/17/(9+10+1)=500K`.


`NVIC`中勾选`FDCAN1 interrupt 0`, 优先级先默认不配置:  
 ![在这里插入图片描述](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/f9a10c6b460146bd889c673f03c1ce89~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1770653569&x-signature=Z8j6OPX7c31pFReP5FJ39SD92fg%3D)


参考上面的`生成代码`一小节, 打开工程.


### 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年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。 img img

如果你需要这些资料,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!