CAN 电气属性
CAN 总线使用两根线来连接各个单元:CAN_H 和 CAN_L,CAN 控制器通过判断这两根线上的电位差来得到总线电平,CAN总线电平分为显性电平和隐性电平两种。
显性电平表示逻辑“0”,此时 CAN_H 电平比 CAN_L 高,分别为 3.5V 和 1.5V,电位差为2V。隐形电平表示逻辑“1”,此时 CAN_H 和 CAN_L 电压都为 2.5V 左右,电位差为 0V。CAN总线就通过显性和隐形电平的变化来将具体的数据发送出去,如图所示:
CAN 总线上没有节点传输数据的时候一直处于隐性状态,也就是说总线空闲状态的时候一直处于隐性。CAN 网络中的所有单元都通过 CAN_H 和CAN_L 这两根线连接在一起,如图所示:
途中所有的 CAN 节点单元都采用 CAN_H 和 CAN_L 这两根线连接在一起,CAN_H 接CAN_H、CAN_L 接 CAN_L,CAN总线两端要各接一个 120Ω的端接电阻,用于匹配总线阻抗,吸收信号反射及回拨,提高数据通信的抗干扰能力以及可靠性。
CAN 总线传输速度可达 1Mbps/S,最新的 CAN-FD 最高速度可达 5Mbps/S,甚至更高,感兴趣的可以自行查阅相关资料。CAN传输速度和总线距离有关,总线距离越短,传输速度越快。
uint32_t FilterScale; /* 设置筛选器的尺度 */
uint32_t FilterActivation; /* 是否使能本筛选器 */
uint32_t SlaveStartFilterBank;
} CAN_FilterTypeDef;
这些结构体成员都是“41.2.14 验收筛选器”小节介绍的内容,可对比阅读,各个结构体成员的介绍如下:
(1) FilterIdHigh
FilterIdHigh 成员用于存储要筛选的 ID,若筛选器工作在 32 位模式,它存储的是所筛选 ID 的高 16 位;若筛选器工作在 16 位模式,它存储的就是一个完整的要筛选的 ID。
(2) FilterIdLow
类似地,FilterIdLow 成员也是用于存储要筛选的 ID,若筛选器工作在 32 位模式,它存储的是所筛选 ID 的低 16 位;若筛选器工作在 16 位模式,它存储的就是一个完整的要筛选的 ID。
(3) FilterMaskIdHigh
FilterMaskIdHigh 存储的内容分两种情况,当筛选器工作在标识符列表模式时,它的功能与 FilterIdHigh 相同,都是存储要筛选的 ID;而当筛选器工作在掩码模式时,它存储的是 FilterIdHigh 成员对应的掩码,与 FilterIdLow 组成一组筛选器。
(4) FilterMaskIdLow
类似地, FilterMaskIdLow 存储的内容也分两种情况,当筛选器工作在标识符列表模式时,它的功能与 FilterIdLow 相同,都是存储要筛选的 ID;而当筛选器工作在掩码模式时,它存储的是 FilterIdLow 成员对应的掩码,与 FilterIdLow 组成一组筛选器。上面四个结构体的存储的内容很容易让人糊涂,请结合前面的图 39_0_15 和下面的表 39‑7 理解,如果还搞不清楚,再结合库函数 FilterInit 的源码来分析。
表不同模式下各结构体成员的内容
对这些结构体成员赋值的时候,还要注意寄存器位的映射,即注意哪部分代表 STID,哪部分代表 EXID 以及 IDE、RTR 位。
(5) FilterFIFOAssignment
本成员用于设置当报文通过筛选器的匹配后,该报文会被存储到哪一个接收 FIFO,它的可选值为 FIFO0 或 FIFO1(宏 CAN_FILTER_FIFO0/1)。
(6) FilterBank
本成员用于设置筛选器的编号,即本过滤器结构体配置的是哪一组筛选器,CAN 一共有 28 个筛选器,所以它的可输入参数范围为 0-27。
(7) FilterMode
本 成 员 用 于 设 置 筛 选 器 的 工 作 模 式, 可 以 设 置 为 列 表 模 式 (宏CAN_FILTERMODE_IDLIST) 及掩码模式 (宏 CAN_FILTERMODE_IDMASK)。
(8) FilterScale
本成员用于设置筛选器的尺度,可以设置为 32 位长 (宏 CAN_FILTERSCALE_32BIT)及 16 位长 (宏 CAN_FILTERSCALE_16BIT)。
(9) FilterActivation
本成员用于设置是否激活这个筛选器 (宏 ENABLE/DISABLE)。
三、CAN Cubemx配置我们通过问题来熟悉下cubemx配置,你熟悉了这些问题基本就知道怎么配置了!
问题:Parameter Settings分别都是设置什么的?答案:如图
答案:用我上面贴的工具(CAN波特率计算 f103AHP1_36M f407AHP1_42M 采样点软件有说明.rar)直接配置,举两个个例子
例子1:我们要配置成500KHz,那么我们这样配置
我们用采集点为80%,所以BS1为4tq,BS2为2tq,分频系数为12,代进公式Fpclk1/((CAN_BS1+CAN_BS2+1)*CAN_Prescaler)=42M/(4+2+1)/12=500kHz
例子2:我们要配置成1M Hz,那么我们这样配置
我们用采集点为75%,所以BS1为3tq,BS2为2tq,分频系数为7,代进公式Fpclk1/((CAN_BS1+CAN_BS2+1)*CAN_Prescaler)=42M/(3+2+1)/7=1MHz
Timer Triggered Communication Mode:否使用时间触发功能 (ENABLE/DISABLE),时间触发功能在某些CAN 标准中会使用到。
Automatic Bus-Off Management:用于设置是否使用自动离线管理功能 (ENABLE/DISABLE),使用自动离线管理可以在出错时离线后适时自动恢复,不需要软件干预。
Automatic Wake-Up Mode:用于设置是否使用自动唤醒功能 (ENABLE/DISABLE),使能自动唤醒功能后它会在监测到总线活动后自动唤醒。
Automatic Retransmission:用于设置是否使用自动重传功能 (ENABLE/DISABLE),使用自动重传功能时,会一直发送报文直到成功为止。
Receive Fifo Locked Mode:用于设置是否使用锁定接收 FIFO(ENABLE/DISABLE),锁定接收 FIFO 后,若FIFO 溢出时会丢弃新数据,否则在 FIFO 溢出时以新数据覆盖旧数据。
Transmit Fifo Priority:用于设置发送报文的优先级判定方法 (ENABLE/DISABLE),使能时,以报文存入发送邮箱的先后顺序来发送,否则按照报文 ID 的优先级来发送。配置完这些结构体成员后,我们调用库函数 HAL_CAN_Init 即可把这些参数写入到 CAN 控制寄存器中,实现 CAN 的初始化
答案:STM32有2个3级深度的接收缓冲区:FIFO0和FIFO1,每个FIFO都可以存放3个完整的报文,它们完全由硬件来管理。如果是来自FIFO0的接收中断,则用CAN1_RX0_IRQn中断来处理。如果是来自FIFO1的接收中断,则用CAN1_RX1_IRQn中断来处理,如图:
答案:status chanege error,错误和状态变化中断!
四、CAN分析工具的使用下面我们会用到CAN分析工具,还是比较好用的,此部分使用作为自己使用
五、实验https://www.zhcxgd.com/h-col-112.html
uint8_t bsp_can1_filter_config(void)
{
CAN_FilterTypeDef filter = {0};
filter.FilterActivation = ENABLE;
filter.FilterMode = CAN_FILTERMODE_IDMASK;
filter.FilterScale = CAN_FILTERSCALE_32BIT;
filter.FilterBank = 0;
filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
filter.FilterIdLow = 0;
filter.FilterIdHigh = 0;
filter.FilterMaskIdLow = 0;
filter.FilterMaskIdHigh = 0;
HAL_CAN_ConfigFilter(&hcan1, &filter);
return BSP_CAN_OK;
}
HAL_CAN_Start(&hcan1);
我们开出了几个参数,id_type是扩展帧还是标准帧,basic_id标准帧ID(在标准帧中有效),ex_id扩展帧ID(在扩展帧中有效),data要发送的数据,data_len要发送的数据长度
uint8_t bsp_can1_send_msg(uint32_t id_type,uint32_t basic_id,uint32_t ex_id,uint8_t *data,uint32_t data_len)
{
uint8_t index = 0;
uint32_t *msg_box;
uint8_t send_buf[8] = {0};
CAN_TxHeaderTypeDef send_msg_hdr;
send_msg_hdr.StdId = basic_id;
send_msg_hdr.ExtId = ex_id;
send_msg_hdr.IDE = id_type;
send_msg_hdr.RTR = CAN_RTR_DATA;
send_msg_hdr.DLC = data_len;
send_msg_hdr.TransmitGlobalTime = DISABLE;
for(index = 0; index < data_len; index++)
send_buf[index] = data[index];
HAL_CAN_AddTxMessage(&hcan1,&send_msg_hdr,send_buf,msg_box);
return BSP_CAN_OK;
}
我们在main函数中1s发送一帧,标准帧跟扩展帧交叉调用,代码如下:
send_data[0]++;
send_data[1]++;
send_data[2]++;
send_data[3]++;
send_data[4]++;
send_data[5]++;
send_data[6]++;
send_data[7]++;
if(id_type_std == 1)
{
bsp_can1_send_msg(CAN_ID_STD,1,2,send_data,8);
id_type_std = 0;
}
else
{
bsp_can1_send_msg(CAN_ID_EXT,1,2,send_data,8);
id_type_std = 1;
}
HAL_Delay(1000);
我们通过CAN协议分析仪来抓下结果
uint8_t bsp_can1_polling_recv_msg(uint32_t *basic_id,uint32_t *ex_id,uint8_t *data,uint32_t *data_len)
{
uint8_t index = 0;
uint8_t recv_data[8];
CAN_RxHeaderTypeDef header;
while (HAL_CAN_GetRxFifoFillLevel(&hcan1, CAN_RX_FIFO0) != 0)
{
if (__HAL_CAN_GET_FLAG(&hcan1, CAN_FLAG_FOV0) != RESET)
printf("[CAN] FIFO0 overrun!
");
HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &header, recv_data);
if(header.IDE == CAN_ID_STD)
{
printf("StdId ID:%d
",header.StdId);
}
else
{
printf("ExtId ID:%d
",header.ExtId);
}
printf("CAN IDE:0x%x
",header.IDE);
printf("CAN RTR:0x%x
",header.RTR);
printf("CAN DLC:0x%x
",header.DLC);
printf("RECV DATA:");
for(index = 0; index < header.DLC; index++)
{
printf("0x%x ",recv_data[index]);
}
printf("
");
}
}
实验一总结:
1.没用调用HAL_CAN_Start(&hcan1);使能CAN
2.没有编写Filter函数,我开始自认为不设置就默认不过滤,现在看来是我想多了,其实想想也合理,你如果不过滤分配FIFO,STM32怎么决定把收到的放到哪个FIFO中
待提升:
1.目前只用到FIFO0,待把FIFO1使用起来2.Normal模式测试500K 波特率(定时发送,中断接收)
步骤2,3,4跟polling完全一致,我们来直接说下中断怎么用(主要是使能notifity就行了)
static void MX_CAN1_Init(void)
{
/* USER CODE BEGIN CAN1_Init 0 */
/* USER CODE END CAN1_Init 0 */
/* USER CODE BEGIN CAN1_Init 1 */
/* USER CODE END CAN1_Init 1 */
hcan1.Instance = CAN1;
hcan1.Init.Prescaler = 12;
hcan1.Init.Mode = CAN_MODE_NORMAL;
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan1.Init.TimeSeg1 = CAN_BS1_4TQ;
hcan1.Init.TimeSeg2 = CAN_BS2_2TQ;
hcan1.Init.TimeTriggeredMode = DISABLE;
hcan1.Init.AutoBusOff = ENABLE;
hcan1.Init.AutoWakeUp = ENABLE;
hcan1.Init.AutoRetransmission = DISABLE;
hcan1.Init.ReceiveFifoLocked = DISABLE;
hcan1.Init.TransmitFifoPriority = DISABLE;
if (HAL_CAN_Init(&hcan1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN CAN1_Init 2 */
bsp_can1_filter_config();
HAL_CAN_Start(&hcan1);
HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);
/* USER CODE END CAN1_Init 2 */
}
下面我们来编写下中断函数
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) { uint8_t index = 0; uint8_t recv_data[8]; CAN_RxHeaderTypeDef header; HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &header, recv_data); if(header.IDE == CAN_ID_STD) { printf("StdId ID:%d ",header.StdId); } else { printf("ExtId ID:%d ",header.ExtId); } printf("CAN IDE:0x%x ",header.IDE); printf("CAN RTR:0x%x ",header.RTR); printf("CAN DLC:0x%x ",header.DLC); printf("RECV DATA:"); for(index = 0; index < header.DLC; index++) { printf("0x%x ",recv_data[index]); } printf(" ");
全部0条评论
快来发表一下你的评论吧 !