一、CAN总线波特率计算
CAN总线通信的各节点通信时会产生相位差,所以要进行位同步,两个节点保持步调一致。
CAN_SJW:重新同步跳跃宽度(SJW) 。定义了在每位中可以延长或缩短多少个时间单元的上限。其值可以编程为1到4个时间单元。
CAN_BS1:时间段1(BS1):定义采样点的位置。其值可以编程为1到16个时间单元,但也可以被自动延长,以补偿因为网络中不同节点的频率差异所造成的相位的正向漂移。
CAN_BS2:时间段2(BS2):定义发送点的位置。其值可以编程为1到8个时间单元,但也可以被自动缩短以补偿相位的负向漂移。
CAN_Prescaler:直观理解就是分频率。
CAN总线的波特率是取自于总线APB1(PCLK1),通过函数RCC_PCLK1Config给PCLK1配置频率。设置了以上的四个值之后,
CAN总线的波特率=PCLK1/((CAN_SJW +CAN_BS1 + CAN_BS2)*CAN_Prescaler)
假设PCLK1=36MHz、CAN_SJW=1、CAN_BS1=8、CAN_BS2=7、CAN_Prescaler=9
则CAN总线的波特率=PCLK1/((1 + 8 + 7) * 9) = 36MHz / 16 / 9 = 250Kbits
二、查看数据手册(stm32F407ZG),看CAN挂载在哪条总线上面,以及对应的引脚
三、CAN过滤器介绍
typedef struct
{
__IO uint32_t FR1; //标识符屏蔽位模式中标识符寄存器,配置ID
__IO uint32_t FR2; //标识符屏蔽位模式中屏蔽寄存器
}CAN_FilterRegister_TypeDef; //标识符列表模式中为2个标识符寄存器,配置ID
例如:屏蔽位寄存器的bit15=1,标识符寄存器的bit15=0,那么接受的Message里面的标识符的bit15必须为0才可能被硬件接受。如果屏蔽位寄存器的bit15=0,Message里面的标识符的bit15无论为什么值,bit15都能匹配通过。
32位屏蔽位模式下:1个过滤器。FR2指定需要关心哪些位,FR1指定这些位的标准值
32位列表模式下:2个过滤器,FR1指定过滤器0的标准值,FR2指定过滤器1的标准值
16位屏蔽位模式下:2个过滤器。FR1高位配置过滤器0标准值,低位配置给过滤器1的标准值。FR2高位配置过滤器0关心的位,低位配置给过滤器1的关心的位。
16位列表模式下:4个过滤器。FR1低位配置过滤器0,高位配置过滤器1。FR2配置过滤器2和过滤器3
IDE=0为标准ID,IDE=1为扩展ID。
RTR=0代表数据帧,RTR=1代表远程帧。
MASKID设置屏蔽码,ID设置标准值。
四、STM32CubeMX配置CAN通信
(1)通过配置分频值和三个时间的比例,可以得出CAN的波特率
(2)针对CAN基本参数设置,具体含义如下
这些是配置基本参数用的:
- 时间触发模式:会自动为报文生成时间戳,没有研究过,暂时默认关闭。
- 自动总线关闭管理:没有研究过,暂时默认关闭。
- 自动唤醒模式:使能后,当CAN外设在休眠状态时如果CAN总线有数据,则自动唤醒CAN外设。
- 自动重发:使能后,如果因为仲裁失败(总线冲突)或是其他原因导致发送失败,会自动重发。建议使能。
- 接收FIFO锁定模式:如果使能,当接收FIFO满时,下一条数据会被丢失。如果不使能,则覆盖前面的数据。
- 发送FIFO优先级:当发送邮箱中同时有多个帧,是按照先进先出的顺序发送还是按照ID的优先级发送。如果不使能,则按照ID优先级发送。如果使能,则按照先进先出优先级发送。
使能CAN接收中断
(3)CAN几种模式
Advanced Parameters
这一项主要是用来调试用的,如果只是正常使用,选择 Normal 模式。
- 正常模式:CAN外设正常地向CAN总线发送数据并从CAN总线上接收数据。
- 回环模式:CAN外设正常向CAN总线发送数据,同时接收自己发送的数据,但不从CAN总线上接收数据。
- 静默模式:CAN外设不向CAN总线发送数据,仅从CAN总线上接收数据,但不会应答。一般用于检测CAN总线的流量。
- 静默回环模式:CAN外设不会往CAN总线收发数据,仅给自己发送。一般用于自检。
五、使能CAN通信
HAL_CAN_Start(&hcan1);
六、发送数据
写法一:测试时直接发送
CAN_TxHeaderTypeDef can_Tx;
uint8_t sendBuf[5] = {"hello"};
uint32_t box;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_CAN_Init();
HAL_CAN_Start(&hcan1);//使能CAN通信
can_Tx.StdId = 0x123;
can_Tx.ExtId = 0;
can_Tx.IDE = CAN_ID_STD;
can_Tx.RTR = CAN_RTR_DATA;
can_Tx.DLC = 5;
can_Tx.TransmitGlobalTime = DISABLE;
while (1)
{
HAL_CAN_AddTxMessage(&hcan1, &can_Tx, sendBuf, &box);
HAL_Delay(100);
}
}
写法二:测试成功后可以封装成发送函数,下面之所以有while循环等待发送完成是为了避免出现发送5条数据,只能接受到一条数据的情况
uint8_t can1_send_msg(uint32_t id, uint8_t *msg, uint8_t len)
{
uint16_t t = 0;
uint32_t TxMailbox = CAN_TX_MAILBOX0;
g_canx_txheader.StdId = id; /* 标准标识符 */
g_canx_txheader.ExtId = id; /* 扩展标识符(29位) */
g_canx_txheader.IDE = CAN_ID_STD; /* 使用标准帧 */
g_canx_txheader.RTR = CAN_RTR_DATA; /* 数据帧 */
g_canx_txheader.DLC = len;
if (HAL_CAN_AddTxMessage(&hcan1, &g_canx_txheader, msg, &TxMailbox) != HAL_OK) /* 发送消息 */
{
return 1;
}
while (HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) != 3) /* 等待发送完成,所有邮箱为空 */
{
t++;
if (t > 0xFFF)
{
HAL_CAN_AbortTxRequest(&hcan1, TxMailbox); /* 超时,直接中止邮箱的发送请求 */
return 1;
}
}
return 0;
}
七、配置过滤器接收数据
写法一:直接主函数测试接收是否配置成功
#include <stdio.h>
CAN_RxHeaderTypeDef can_Rx;
uint8_t recvBuf[8];
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_CAN_Init();
MX_USART1_UART_Init()
CAN_FilterTypeDef can_Filter = {0};
can_Filter.FilterIdHigh = 0;
can_Filter.FilterIdLow = 0;
can_Filter.FilterMaskIdHigh = 0;
can_Filter.FilterMaskIdLow = 0;
can_Filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
can_Filter.FilterBank = 0;
can_Filter.FilterMode = CAN_FILTERMODE_IDMASK;
can_Filter.FilterScale = CAN_FILTERSCALE_32BIT;
can_Filter.FilterActivation = CAN_FILTER_ENABLE;
HAL_CAN_ConfigFilter(&hcan1, &can_Filter);
HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
HAL_CAN_Start(&hcan1);
while (1)
{
}
}
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &can_Rx, recvBuf);
}
写法二:让CAN接收更加系统化
(1) 直接加在CAN1初始化的下面即可,配置过滤器,只有配置了过滤器之后才能接收数据
/* 配置CAN过滤器 */
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterBank = 0; /* 过滤器0 */
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.FilterActivation = CAN_FILTER_ENABLE; /* 激活滤波器0 */
sFilterConfig.SlaveStartFilterBank = 14; //设置CAN2的起始过滤器组(对于单CAN的CPU或从CAN此参数无效;对于双CAN的CPU此参数为从CAN的起始过滤器组编号)
HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig);/* 过滤器配置 */
(2) 除开之前在cubemx配置时使能了CAN接收总中断,还需要打开CAN的FIFO消息挂起中断请求(也就是CAN外设的中断使能位)。
HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
(3)重写CAN接收中断回调函数
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &can_Rx, recvBuf);
/*下面是你用来处理收到数据的代码,
可以通过串口把内容发送出来,
也可以用来控制某些外设*/
}
八、CAN调试心得
(1)如果使用CAN盒调试CAN通信时,出现发送或者接收数据失败,将CAN配置为静默回环模式,先排除硬件文件,保证软件无误
(2)发送5帧数据只能接收到一帧数据,说明CAN发送较快,发送完一帧数据之后可以等待发送完成在发送下一帧数据,修改CAN配置
(3)如果静默回环模式配置正确,使用CAN盒通信时只能接收错误帧,检查CAN波特率是否正确。有两种可能性,第一种就是CAN的波特率计算错误,第二种就是系统时钟配置错误,倒是CAN时钟不是默认时钟
九、配置两个CAN通信
如果配置两个CAN通信,他们的过滤器配置需要稍作修改
CAN1
/* 配置CAN过滤器 */
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterBank = 0; /* 过滤器0 */
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.FilterActivation = CAN_FILTER_ENABLE; /* 激活滤波器0 */
sFilterConfig.SlaveStartFilterBank = 14; 设置CAN2的起始过滤器组(对于单CAN的CPU或从CAN此参数无效;对于双CAN的CPU此参数为从CAN的起始过滤器组编号)
HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig);/* 过滤器配置 */
CAN2
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterBank = 14; /* 过滤器14 */
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.FilterActivation = CAN_FILTER_ENABLE; /* 激活滤波器0 */
sFilterConfig.SlaveStartFilterBank = 28; //无效
HAL_CAN_ConfigFilter(&hcan2, &sFilterConfig);/* 过滤器配置 */