【背景】
各位,我现在要用树莓派和STM32的板子进行通信,一个是用串口,但是,考虑到串口的速度,通信距离和可靠性,还是用CAN比较合适。所以,我要做一个STM32的CAN通信接口。下面就是保姆级教程。
【保姆级教程】
先来看看CAN接口的硬件怎么设计。
【CAN接口硬件】
不多说,直接上电路图。
看到没有,CAN的接口电路就是这么简单。用的收发芯片是NXP的TJA1044GT/3Z, 为什么用这个芯片呢?因为它的VCC是5V,而IO口的电压可以用3.3V,STM32的IO的电压就是3.3V,那么就不需要电压转换电路,就可以直接接STM32的IO口了。
记得这个120欧的终端电阻一定要加上。
PD1,PD0, 在我的STM32G474VET6上,就是FDCAN_TX和FDCAN_RX。
【STM32CubeMX配置】
这个配置其实挺直观的,我的配置是这样的。
我的配置就是将FDCAN配置成经典的CAN2.0, 波特率配置成1Mbps。
(这里的配置有一个问题,后面讲软件的时候再讲)
中断配置成这样的。
我的时钟配置成170Mhz
其他没啥了。直接生成代码。
【软件代码】
cubeMX生成的代码是这样的。
要使用CAN,自己在main文件里还要加入下列变量。
我这里配置的CAN发送和接收的,都是8byte长度,这里变量,为了充足一点,都是声明的16bytes. 用8bytes应该也是没有问题的。
【fdcan.h】
这头文件是这样的。
【fdcan.c】
这C文件是这样的。
#include "fdcan.h"
#include "BSP_USB.h"
#include "gpio.h"
void my_FDCAN1_init(void)
{
// 设置接收过滤器
FDCAN_FilterTypeDef sFilterConfig;
sFilterConfig.IdType = FDCAN_STANDARD_ID;
sFilterConfig.FilterIndex = 0;
sFilterConfig.FilterType = FDCAN_FILTER_MASK;
sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
sFilterConfig.FilterID1 = 0x002; // 设置过滤器ID1
sFilterConfig.FilterID2 = 0x7ff; // 设置过滤器ID2
if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
// 设置发送头
fdcan1TxHeader.Identifier = 0x001; // 设置标识符
fdcan1TxHeader.IdType = FDCAN_STANDARD_ID; // 设置ID类型
fdcan1TxHeader.TxFrameType = FDCAN_DATA_FRAME; // 设置帧类型
fdcan1TxHeader.DataLength = FDCAN_DLC_BYTES_8; // 设置数据长度
fdcan1TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE; // 设置错误状态指示符
fdcan1TxHeader.BitRateSwitch = FDCAN_BRS_OFF; // 设置比特率切换
fdcan1TxHeader.FDFormat = FDCAN_CLASSIC_CAN; // 设置格式
fdcan1TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS; // 设置事件FIFO控制
fdcan1TxHeader.MessageMarker = 0; // 设置消息标记器
// 启动 FDCAN1
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();
}
CAN_STB_normal(); // 设置 FDCAN1 进入正常工作模式
HAL_Delay(5);
fdcan1_RXFlag = 0; // 初始化接收标志
usb_printf("FDCAN1 initialized successfully\r\n");
}
void my_FDCAN1_Transmit(uint8_t *TxData)
{
// 发送数据
if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &fdcan1TxHeader, TxData) != HAL_OK)
{
usb_printf("HAL_FDCAN_AddMessageToTxFifoQ failed\r\n");
Error_Handler();
}
}
// FDCAN1 接收回调函数
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
if ((hfdcan->Instance == FDCAN1) && ((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != RESET))
{
// 从接收 FIFO 中读取数据
if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &fdcan1RxHeader, fdcan1RxData) != HAL_OK)
{
usb_printf("HAL_FDCAN_GetRxMessage failed\r\n");
Error_Handler();
}
// 重启接收 FIFO
if (HAL_FDCAN_ActivateNotification(hfdcan, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0) != HAL_OK)
{
usb_printf("HAL_FDCAN_ActivateNotification failed\r\n");
Error_Handler();
}
// 设置接收标志
fdcan1_RXFlag = 1;
}
}
【main.c】
main函数里,CAN接收是这样处理的。
// fdcan1 test
if(fdcan1_RXFlag == 1)
{
fdcan1_RXFlag = 0; // Clear the receive flag
for(int i = 0; i < 8; i++)
{
fdcan1TxData[i] = fdcan1RxData[i]+1; // Copy received data to transmit buffer
}
my_FDCAN1_Transmit(fdcan1TxData); // Echo back the received data
usb_printf("FDCAN1 Received data: ");
HAL_Delay(5);
for (int i = 0; i < 8; i++)
{
usb_printf("%02X ", fdcan1RxData[i]);
HAL_Delay(1);
}
usb_printf("\r\n");
}
这个很简单,就是如果CAN接收到数据了,那么将数据+1,给到发送buffer。然后,用CAN发出去。再把接收到的数据打印出来。
【结果检验】
那么,编译代码后,烧写到板子里去,将CAN用2根线接到CANH,CANL的对手CAN设备的端子上。(我是用的USB转CAN的canable2.0s)。 然后,用windows上的CAN上位机来测试。
我用的cangaroo-win32-ccdcb64. 结果如下。
已经可以发送和接收了。
但是,我想我的CAN设备只接收CANID 0x002的消息。也就是我的代码里设定的。
sFilterConfig.FilterType = FDCAN_FILTER_MASK;
sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
sFilterConfig.FilterID1 = 0x002; // 设置过滤器ID1
sFilterConfig.FilterID2 = 0x7ff; // 设置过滤器ID2
但是,我测试下来,是这个过滤好像没啥用,还是所有的CANID的消息都能收到。那么要实现CANID过滤功能怎么办呢?
这个过滤器的代码要改成这样的。
// 设置接收过滤器
FDCAN_FilterTypeDef sFilterConfig;
sFilterConfig.IdType = FDCAN_STANDARD_ID;
sFilterConfig.FilterIndex = 0;
sFilterConfig.FilterType = FDCAN_FILTER_MASK;
sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
sFilterConfig.FilterID1 = 0x002; // 设置过滤器ID1
sFilterConfig.FilterID2 = 0x7ff; // 设置过滤器ID2
if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
if(HAL_FDCAN_ConfigGlobalFilter(&hfdcan1, FDCAN_REJECT, FDCAN_REJECT, FDCAN_REJECT_REMOTE, FDCAN_REJECT_REMOTE) != HAL_OK)
{
Error_Handler();
}
同时,main里的MX_FDCAN1_Init(void)也要改。这里是cubeMX生成的,有一行要改成这样。
hfdcan1.Init.StdFiltersNbr = 1;
这就是我在cubeMX配置时提过的。
那么这样修改之后,就能够实现只有自己需要的CANID的消息才收到。
看看上面的cangaroo的图片,可以看到,只有CANID是0x002的消息才收到了。
【结果】
好了,大功告成,亲个嘴儿。