STM32CubeMX_CAN_CAN3_FDCAN
前言
STM32CubeMX_环境搭建_GPIO_外部中断
STM32CubeMX_定时器中断_PWM
STM32CubeMX_UART_printf_接收中断_DMA空闲中断_LPUART
STM32CubeMX_以太网_RMII_LwIP_UDP
前四节简单的总结了GPIO, EXTI, TIMER, UART, 以太网的相关用法, 本节总结一下STM32的CAN, FDCAN, CAN3的用法. STM32的CAN很多系列都有: F0/1/2/3/4/7, L4/L4+等都能找到CAN的身影, 分别有1路到3路不等. 而FDCAN在STM32G系列/H系列/L5系列中比较常见. 本节我们先从CAN开始, 原理性的东西自己查去, 主要看操作.
用的是下面这个自制的STM32F405RGT6的板子:

CAN的引脚为:
| CAN | STM32 |
|---|---|
| CAN1_TX | PB9 |
| CAN1_RX | PB8 |
| CAN2_TX | PB13 |
| CAN2_RX | PB12 |
然后通过CAN电平转换芯片TJA1051连接到CAN分析仪上去:

MCU本身外接的12MHz的晶振, 硬件连接介绍完毕, 下面开始操作.
STM32CubeMX新建工程
步骤如下:
- MCU选择: 打开
STM32CubeMX, 点击ACCESS TO MCU SELECTOR, 选择STM32F405RGTx - 调试端口配置为SWD:
Pinout & Configuration->System Core->SYS->Debug选择Serial Wire Pinout & Configuration->System Core->RCC->HSE选择Crystal/Ceramic Resonator- Clock Configuration:

注意CAN时钟属于APB1 peripheral clocks(MHz), 所以是42MHz.
CAN配置
Pinout & Configuration -> Connectivity -> CAN1, Mode里面勾选Master Mode, 发现默认的CAN引脚是PA11, PA12, 我们修改为PB8, PB9:

Pinout & Configuration -> Connectivity -> CAN2, 勾选Slave Mode, 默认的就是PB12, PB13, 不用手动修正:

CAN1 和 CAN2 的 Configuration 中, 预分配Prescaler设置为7, BS1设为5, BS2设为6, 这样波特率为42M/7/(5+6+1)=500K, 1是固定的, 与RJW的关系不大:

NVIC Setting 中勾选CAN1, CAN2的RX0中断, 优先级默认不改:


生成代码
Project Manager -> Project -> Browse 选择工程位置(Project Location), 填入工程名(Project Name), Toolchain/IDE 选择 MDK-ARM.
Project Manager -> Code Generator -> 勾选Copy only the necessary library files, 还有Generate peripheral initialization as a pair of .c/.h files per periphral
点击右上角 GENERATE CODE 按钮生成代码, 打开工程.
Keil 点击魔术棒或者Project -> Options for Target ..., 默认配置Debug为ST-link Debugger, 点击Setting -> Flash Download -> 勾选Reset and Run, 这样下载后可以自动复位运行.
如有疑问, 仍然去参考 STM32CubeMX_环境搭建_GPIO_外部中断 一节.
CAN发送
CAN1为例, main.c中添加:
/* USER CODE BEGIN PV */
CAN_TxHeaderTypeDef TxHeader;
uint8_t TxData[8] = {0};
uint32_t TxMailbox;
/* USER CODE END PV */
/* 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 */
/* USER CODE BEGIN 2 */
FDCAN1_Config();
/* USER CODE END 2 */
/* USER CODE BEGIN 4 */
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
if((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != RESET)
{
/* Retrieve Rx messages from RX FIFO0 */
if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &RxHeader, RxData) != HAL_OK)
{
Error_Handler();
if(hfdcan == &hfdcan1) {
TxHeader.Identifier = RxHeader.Identifier;
TxHeader.IdType = RxHeader.IdType; //FDCAN_STANDARD_ID, FDCAN_EXTENDED_ID
TxHeader.TxFrameType = RxHeader.RxFrameType; //FDCAN_DATA_FRAME, FDCAN_REMOTE_FRAME
TxHeader.DataLength = RxHeader.DataLength; //FDCAN_DLC_BYTES_8
TxHeader.ErrorStateIndicator = RxHeader.ErrorStateIndicator; //FDCAN_ESI_ACTIVE
TxHeader.BitRateSwitch = RxHeader.BitRateSwitch; //FDCAN_BRS_OFF
TxHeader.FDFormat = RxHeader.FDFormat; //FDCAN_CLASSIC_CAN, FDCAN_FD_CAN
TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
TxHeader.MessageMarker = 0;
memcpy(TxData, RxData, TxHeader.DataLength);
if(HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, TxData) != HAL_OK) {
Error_Handler();
}
}
}
}
}
/* USER CODE END 4 */
工程代码
https://download.csdn.net/download/weifengdq/11967173
微信公众号
欢迎扫描二维码关注本人微信公众号, 及时获取或者发送给我最新消息:
