最近在使用stm32F407的片子调试can通信,直接在正点原子的代码上修改调试,调试can1的时候,基本没啥问题,收发都正常,使用查询模式和中断模式都可以。但是当修改到can2的时候,可以正常发送数据,但是中断函数始终进不去。折腾了一两个小时终于搞定了。下面将解决过程分享给大家。
首先先上代码,这个代码是运行成功的代码,在后面我再详细介绍要注意的地方。
CAN_HandleTypeDef g_canx_handler; /* CANx句柄 */
CAN_TxHeaderTypeDef g_canx_txheader; /* 发送参数句柄 */
CAN_RxHeaderTypeDef g_canx_rxheader; /* 接收参数句柄 */
CAN_DATA_TypeDef g_can_rx_data; /* CAN接收数据句柄 */
CAN_DATA_TypeDef g_can_tx_data; /* CAN发送数据句柄 */
uint8_t can_init( uint32_t tsjw, uint32_t tbs2, uint32_t tbs1, uint16_t brp, uint32_t mode )
{
g_canx_handler.Instance = CANx; /* 使用CAN1/CAN2 */
g_canx_handler.Init.Prescaler = brp; /* 分频系数(Fdiv)为brp+1
g_canx_handler.Init.Mode = mode; /* 模式设置 */
g_canx_handler.Init.SyncJumpWidth = tsjw; /* 重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1TQ~CAN_SJW_4TQ */
g_canx_handler.Init.TimeSeg1 = tbs1; /* tbs1范围CAN_BS1_1TQ~CAN_BS1_16TQ */
g_canx_handler.Init.TimeSeg2 = tbs2; /* tbs2范围CAN_BS2_1TQ~CAN_BS2_8TQ */
g_canx_handler.Init.TimeTriggeredMode = DISABLE; /* 非时间触发通信模式 */
g_canx_handler.Init.AutoBusOff = DISABLE; /* 软件自动离线管理 */
g_canx_handler.Init.AutoWakeUp = DISABLE; /* 睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位) */
//报文自动传送开启后,当数据发送失败时,can芯片会自动重发数据,直到数据发送成功,会造成程序假死状态。
g_canx_handler.Init.AutoRetransmission = DISABLE; /* 禁止报文自动传送 */
g_canx_handler.Init.ReceiveFifoLocked = DISABLE; /* 报文不锁锁定,FIFO装满后新的覆盖旧的,如果设置报文锁定后,FIFO装满后新的就会被丢弃*/
g_canx_handler.Init.TransmitFifoPriority = DISABLE; /* 优先级由报文标识符决定 */
if ( HAL_CAN_Init( &g_canx_handler ) != HAL_OK )
{
return 1;
}
/* 使用中断接收 */
__HAL_CAN_ENABLE_IT( &g_canx_handler, CAN_IT_RX_FIFO0_MSG_PENDING ); /* FIFO0消息挂号中断允许 */
HAL_NVIC_EnableIRQ( CANx_RX0_IRQn ); /* 使能CAN中断 */
HAL_NVIC_SetPriority( CANx_RX0_IRQn, 7, 0 ); /* 抢占优先级7,子优先级0 */
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterBank = FILTER_ADDR; /* 过滤器地址 当只使用CAN1时,此地址范围为0--13,,当使用CAN2时,CAN1也会工作,相当于CAN2就是从机 */
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = 0x0000; /* 32位ID */
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000; /* 32位MASK */
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0; /* 过滤器0关联到FIFO0 */
sFilterConfig.FilterActivation = CAN_FILTER_ENABLE; /* 激活滤波器0 */
sFilterConfig.SlaveStartFilterBank = FILTER_ADDR; /* 从机滤波器起始地址,当只使用can1时,没有从机 此值无效*/
/* 过滤器配置 */
if ( HAL_CAN_ConfigFilter( &g_canx_handler, &sFilterConfig ) != HAL_OK )
{
return 2;
}
/* 启动CAN外围设备 */
if ( HAL_CAN_Start( &g_canx_handler ) != HAL_OK )
{
return 3;
}
return 0;
}
void HAL_CAN_MspInit( CAN_HandleTypeDef* hcan )
{
if ( CANx == hcan->Instance ) /* 如果地址为CANx 的地址 */
{
CAN_RX_GPIO_CLK_ENABLE(); /* CAN_RX脚时钟使能 */
CAN_TX_GPIO_CLK_ENABLE(); /* CAN_TX脚时钟使能 */
CANx_CLK_ENABLE(); /* 使能CAN时钟 */
GPIO_InitTypeDef gpio_init_struct;
gpio_init_struct.Pin = CAN_TX_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_AF_PP;
gpio_init_struct.Pull = GPIO_PULLUP;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
gpio_init_struct.Alternate = GPIO_AF9_CANx;
HAL_GPIO_Init( CAN_TX_GPIO_PORT, &gpio_init_struct ); /* CAN_TX脚 模式设置 */
gpio_init_struct.Pin = CAN_RX_GPIO_PIN;
HAL_GPIO_Init( CAN_RX_GPIO_PORT, &gpio_init_struct ); /* CAN_RX脚 模式设置 */
}
}
void CANx_RX0_IRQHandler( void )
{
HAL_CAN_IRQHandler( &g_canx_handler ); /* 调用HAL库 CAN 中断入口函数*/
}
void HAL_CAN_RxFifo0MsgPendingCallback( CAN_HandleTypeDef* hcan )
{
HAL_CAN_GetRxMessage( hcan, CAN_RX_FIFO0, &g_canx_rxheader, g_can_rx_data.buf ); /* 读取数据 */
}
下面是头文件
//#define USE_CAN1 1 /* 使用can1口,如果要使用can2,将此宏定义屏蔽 */
#ifdef USE_CAN1
/* CAN1 引脚 定义 */
#define CAN_RX_GPIO_PORT GPIOD
#define CAN_RX_GPIO_PIN GPIO_PIN_0
#define CAN_RX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0) /* PD口时钟使能 */
#define CAN_TX_GPIO_PORT GPIOD
#define CAN_TX_GPIO_PIN GPIO_PIN_1
#define CAN_TX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0) /* PD口时钟使能 */
#define CANx CAN1
#define CANx_RX0_IRQn CAN1_RX0_IRQn
#define CANx_CLK_ENABLE() __HAL_RCC_CAN1_CLK_ENABLE()
#define CANx_RX0_IRQHandler CAN1_RX0_IRQHandler
#define GPIO_AF9_CANx GPIO_AF9_CAN1
#define FILTER_ADDR 0 /* 滤波器地址 */
#else
/* CAN2 引脚 定义 */
#define CAN_RX_GPIO_PORT GPIOB
#define CAN_RX_GPIO_PIN GPIO_PIN_5
#define CAN_RX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PB口时钟使能 */
#define CAN_TX_GPIO_PORT GPIOB
#define CAN_TX_GPIO_PIN GPIO_PIN_6
#define CAN_TX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PB口时钟使能 */
#define CANx CAN2
#define CANx_RX0_IRQn CAN2_RX0_IRQn
#define CANx_CLK_ENABLE() __HAL_RCC_CAN1_CLK_ENABLE(); __HAL_RCC_CAN2_CLK_ENABLE()
#define CANx_RX0_IRQHandler CAN2_RX0_IRQHandler
#define GPIO_AF9_CANx GPIO_AF9_CAN2
#define FILTER_ADDR 14 /* 滤波器地址 */
#endif
这里通过一个宏定义来控制使用can1还是can2.
为了方便代码的编写,在头文件中将can1和can2配置时需要改动的地方都用宏定义来表示,这样切换can1和can2的时候,程序中的代码就不需要改动了。
can1和can2最大的区别首先就是 IO口不一样,can1使用的是PD0、PD1,can2使用的是PB5,、PB6,接下来不一样的就是中断源和中断函数入口。下面就就是can的时钟,这里要注意一个就是使用can1的时候,开启can1的时钟就行,但是使用can2的时候,也必须开启can1的时钟。大多数can2调试不通的原因就是这个。
在can的控制器中,存储访问控制器是由can1控制的,当使用can2的时候,can2要访问存储访问控制器时,必须通过can1才能访问,所以使用can2的时候,can1为主机,can2为从机。所以使用can2时,时钟使能的宏定义是将can1和can2的的时钟都开启的。
#define CANx_CLK_ENABLE() __HAL_RCC_CAN1_CLK_ENABLE(); __HAL_RCC_CAN2_CLK_ENABLE()
正常情况下,使用can2时,只需要开启can1的时钟,就能使用了,但是实际测试的时候发现,can2只能发送数据,不能接收数据。最后查找了找了半天原因才发现是滤波器地址选择引起的。
这个是正点原子默认的过滤器配置值。关于这两个值的含义可以直接在代码中看官方的解释。
通过注释大概可以知道当使用一个can时FilterBank
的值范围是0–13,当使用双路时它的范围是0–27。当使用一个can时 SlaveStartFilterBank
的值是没意义的,当使用双路时它的值是0–27。
看了这个解释之后其实还是不知道具体啥意思,是怎么用的。通过我实际测试之后,我对这两个值的理解如下:
1.当只使用can1时,FilterBank
的值必须在0到13之间,一旦这个值大于13,那么can1就不能进入接收中断。 SlaveStartFilterBank
这个值在使用can1时,系统内部是忽略这个值的。不管设置多少就可以,无所谓。
2.当使用can2时,由于can2读取数据需要依赖can1,所以要使用can2,就必须要使用can1,那么此时就是两路can,此时FilterBank
的值用来设置过滤器的地址,这个值从0到27都可以。接下来 SlaveStartFilterBank
的值就很关键了,这个值的含义是,从机滤波器的起始地址,那么当使用can2时,can2就是从机,那么这个值就是can2滤波器的起始地址,而上面FilterBank
这个值是要使用的滤波器地址。 这里要注意的是,SlaveStartFilterBank
的值一定要小于等于FilterBank
的值。也就是滤波器起始地址一定要比要使用的滤波器地址小。
如果按照上面代码中的设置,那么滤波器的地址设置为0
,滤波器的起始地址设置为14
,那么can2从滤波器起始地址14开始查找数据,它永远也找不到0号滤波器,所以就接收不到数据。
就是因为这两个值的原因,导致使用can2的时候,一直收不到数据。为了写代码时方便一点,这里直接将这两个值设置为一样的。滤波器起始地址和要使用的滤波器地址一样,这样不管怎么设置都不会错。
在头文件中直接用宏定义设置这个值,由于使用can1的时候,值不能超过13,所以can1的值直接设置为0。当使用can2的时候,直接将值设置为14。
这个滤波器的值设置好之后,can2使用起来就正常了。希望这个小小的坑大家都不要踩。为了一个地址值,折腾了好几个小时。