STM32 HAL CAN通信 CAN通信模块 野火小智使用避坑分享
首先利用江科大的代码进行测试,CAN盒子可以接收到核心板发送的报文,之后用STM32CUBEMX搭建CAN工程,配置好后,发送三次报文后就开始报错,且CAN盒一帧报文都未收到,调试模式下,发现三个邮箱都满了,TSR寄存器的值为0x8200 0000,刚开始以为是哪里配置出现了问题,反复查看代码,始终没找到问题,后续烧录回标准库代码后,依然无法通信,以为期间不小心改动了某些地方的代码,排查了一遍依然
野火小智模块
该模块是从优信电子店铺购买,宝贝详情中对SIT1042收发器芯片、模块的外部引脚进行了说明,文中描述芯片的VIO引脚为EN引脚,可悬空,收到的模块EN引脚的丝印为VIO,因为VIO引脚和EN连着的,当时觉得可能是新旧模块混发,也没在意。因此后续开发中EN/VIO引脚悬空。
在调试中遇到的问题
采用STM32F103核心板和野火小智CAN通信模块,用千目电子的USB-CAN转换器B1进行通信的调试。
首先利用江科大的代码进行测试,CAN盒子可以接收到核心板发送的报文,之后用STM32CUBEMX搭建CAN工程,配置好后,发送三次报文后就开始报错,且CAN盒一帧报文都未收到,调试模式下,发现三个邮箱都满了,TSR寄存器的值为0x8200 0000,刚开始以为是哪里配置出现了问题,反复查看代码,始终没找到问题,后续烧录回标准库代码后,依然无法通信,以为期间不小心改动了某些地方的代码,排查了一遍依然没有发现问题,始终无法通信。
将CAN模式改为环回模式后,32自身是可以收到自己发送的代码的,因此怀疑CAN模块、CAN盒的问题。
之后用示波器抓CAN盒发送的波形,波形没问题,说明CAN盒正确发送了报文,看来问题出在野火小智模块上。
查论坛中有个网友的现象和我这个一致,网友最后是因为核心板供电电压不够,导致的收发器模块无法正常工作,我使用直流电源单独给野火小智模块供电,并共地,问题依然没有解决,但是问题应该在模块上
硬件排查过程
CAN盒定时发送报文,用示波器抓收发器模块的RX引脚,RX引脚无波形。


此时才反应过来是不是VIO引脚的问题,因此将VIO引脚连接到核心板的3V3上,示波器抓到了RX输出的波形。

之后去野火官方旗舰店查看该手册说明,引脚说明中明确说明VIO引脚接3V3。
附STM32 CAN代码配置
基于STM32f103,代码包含标准库和hal库,标准库移植江科大CAN封装函数,HAL库基于CUBEMX创建。
标准库:
void MyCAN_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_Init(GPIOA, &GPIO_InitStructure);
CAN_InitTypeDef CAN_InitStructure;
CAN_InitStructure.CAN_Prescaler = 48;//波特率 = 36M/48/(1+2+3)=125k
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;//环回、静默、静默环回三种,非初始化、睡眠、标准模式选择
CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;//间隔位时间
CAN_InitStructure.CAN_BS1 = CAN_BS1_2tq;//
CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq;
CAN_InitStructure.CAN_TTCM = DISABLE;//
CAN_InitStructure.CAN_ABOM = DISABLE;//自动离线恢复
CAN_InitStructure.CAN_AWUM = DISABLE;//自动唤醒模式
CAN_InitStructure.CAN_NART = DISABLE;//不自动重发模式
CAN_InitStructure.CAN_RFLM = DISABLE;//FIFO锁定模式,ENABLE的话,FIFO溢出,FIFO会丢弃当前接收的数据;DISABLE FIFO溢出,会覆盖原数据
CAN_InitStructure.CAN_TXFP = DISABLE;//发送FIFO优先级,DISABLE由报文的标识符决定优先级,ENABLE由
CAN_Init(CAN1, &CAN_InitStructure);//初始化CAN
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;//所有ID均可通过
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;//选择FIFO0
CAN_FilterInitStructure.CAN_FilterNumber = 0;//过滤器编号,选择0号过滤器
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;//32位屏蔽模式
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;//过滤器使能
CAN_FilterInit(&CAN_FilterInitStructure);
}
void MyCAN_Transmit(uint32_t ID, uint8_t Length, uint8_t *Data)
{
CanTxMsg TxMessage;
TxMessage.StdId = ID;//标准ID
TxMessage.ExtId = ID;//扩展ID
TxMessage.IDE = CAN_Id_Standard;//标准
TxMessage.RTR = CAN_RTR_Data;//数据帧 数据或遥测帧进行选择
TxMessage.DLC = Length;//数据长度,0~8
for (uint8_t i = 0; i < Length; i ++)
{
TxMessage.Data[i] = Data[i];
}
uint8_t TransmitMailbox = CAN_Transmit(CAN1, &TxMessage);//将结构体数据发送给CAN1,函数返回当前邮箱编号
while (CAN_TransmitStatus(CAN1, TransmitMailbox) != CAN_TxStatus_Ok);//判断该邮箱状态,是否发送完毕
}
uint8_t MyCAN_ReceiveFlag(void)
{
if (CAN_MessagePending(CAN1, CAN_FIFO0) > 0)
{
return 1;
}
return 0;
}
void MyCAN_Receive(uint32_t *ID, uint8_t *Length, uint8_t *Data)
{
CanRxMsg RxMessage;
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//接收CAN1、FIFO0中的数据
if (RxMessage.IDE == CAN_Id_Standard)
{
*ID = RxMessage.StdId;//判断是否为标准ID帧,是的话,将ID赋值给形参
}
else
{
//...
}
if (RxMessage.RTR == CAN_RTR_Data)//判断是否为数据帧,是的话就将接收结构体中的数据赋值到数组Data中
{
*Length = RxMessage.DLC;
for (uint8_t i = 0; i < *Length; i ++)
{
Data[i] = RxMessage.Data[i];
}
}
else
{
//...
}
}
HAL库:
void MX_CAN_Init(void)
{
/* USER CODE BEGIN CAN_Init 0 */
/* USER CODE END CAN_Init 0 */
/* USER CODE BEGIN CAN_Init 1 */
/* USER CODE END CAN_Init 1 */
hcan.Instance = CAN1;
hcan.Init.Prescaler = 48;
hcan.Init.Mode = CAN_MODE_NORMAL;
hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan.Init.TimeSeg1 = CAN_BS1_2TQ;
hcan.Init.TimeSeg2 = CAN_BS2_3TQ;
hcan.Init.TimeTriggeredMode = DISABLE;
hcan.Init.AutoBusOff = DISABLE;
hcan.Init.AutoWakeUp = DISABLE;
hcan.Init.AutoRetransmission = ENABLE;
hcan.Init.ReceiveFifoLocked = DISABLE;
hcan.Init.TransmitFifoPriority = DISABLE;
if (HAL_CAN_Init(&hcan) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN CAN_Init 2 */
while(CanSetFilter(&hcan) != HAL_OK);
HAL_CAN_Start(&hcan);
/* USER CODE END CAN_Init 2 */
}
void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(canHandle->Instance==CAN1)
{
/* USER CODE BEGIN CAN1_MspInit 0 */
/* USER CODE END CAN1_MspInit 0 */
/* CAN1 clock enable */
__HAL_RCC_CAN1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**CAN GPIO Configuration
PA11 ------> CAN_RX
PA12 ------> CAN_TX
*/
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* CAN1 interrupt Init */
HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(CAN1_RX1_IRQn);
/* USER CODE BEGIN CAN1_MspInit 1 */
/* USER CODE END CAN1_MspInit 1 */
}
}
void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle)
{
if(canHandle->Instance==CAN1)
{
/* USER CODE BEGIN CAN1_MspDeInit 0 */
/* USER CODE END CAN1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_CAN1_CLK_DISABLE();
/**CAN GPIO Configuration
PA11 ------> CAN_RX
PA12 ------> CAN_TX
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12);
/* CAN1 interrupt Deinit */
HAL_NVIC_DisableIRQ(CAN1_RX1_IRQn);
/* USER CODE BEGIN CAN1_MspDeInit 1 */
/* USER CODE END CAN1_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
HAL_StatusTypeDef CanSetFilter(CAN_HandleTypeDef *hcan)
{
HAL_StatusTypeDef Status;
CAN_FilterTypeDef CAN_FilterStruct;
CAN_FilterStruct.FilterIdHigh=0x0000;
CAN_FilterStruct.FilterIdLow=0x0000;
CAN_FilterStruct.FilterMaskIdHigh=0x0000;
CAN_FilterStruct.FilterMaskIdLow=0x0000;
CAN_FilterStruct.FilterBank=0;
CAN_FilterStruct.FilterFIFOAssignment=CAN_FILTER_FIFO0;
CAN_FilterStruct.FilterMode=CAN_FILTERMODE_IDMASK;
CAN_FilterStruct.FilterScale=CAN_FILTERSCALE_32BIT;
CAN_FilterStruct.FilterActivation=ENABLE;
Status= HAL_CAN_ConfigFilter(hcan, &CAN_FilterStruct);
return Status;
}
void CanTransmit(CAN_HandleTypeDef *hcan,uint32_t ID,uint8_t Data[],uint32_t Length)
{
uint32_t TxMail;
CAN_TxHeaderTypeDef CAN_TxStructure;
HAL_StatusTypeDef Status;
CAN_TxStructure.StdId=ID;
CAN_TxStructure.ExtId=ID;
CAN_TxStructure.IDE=CAN_ID_STD;
CAN_TxStructure.RTR=CAN_RTR_DATA;
CAN_TxStructure.DLC=Length;
CAN_TxStructure.TransmitGlobalTime=DISABLE;
Status=HAL_CAN_AddTxMessage(hcan, &CAN_TxStructure, Data, &TxMail);
if(Status!=HAL_OK)
{
printf("Send Error:");
printf("%d\r\n",Status);
}
}
HAL库中:
HAL_StatusTypeDef CanSetFilter(CAN_HandleTypeDef hcan);
void CanTransmit(CAN_HandleTypeDef hcan,uint32_t ID,uint8_t Data[],uint32_t Length);
滤波器配置函数,发送及接受函数需要自己创建,其余为MX配置生成,其中需要注意的一点是在CAN_Init配置完后要添加HAL_CAN_Start(&hcan);**函数,在stm32fxxx_hal_can.c库文件中有详细说明。
存在的一些疑问
VIO引脚连接3V3 后,通信可正常进行,之后将VIO引脚断开,通信依然正常,模块重新上电后无法通信,需要对VIO引脚重新供电。
勘误
20250515
HAL库代码中使能的是CAN_RX1中断,滤波器中配置的为FIFO0,这里有错误注意,应改成FIFO1,不然进不了FIFO1接收中断回调函数。
更多推荐



所有评论(0)