CAN电气特性属性

描述

 

 

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 网络中的所有单元都通过 CAN_H 和CAN_L 这两根线连接在一起,如图所示:

 

CAN

 

途中所有的 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 的源码来分析。

表不同模式下各结构体成员的内容

CAN

对这些结构体成员赋值的时候,还要注意寄存器位的映射,即注意哪部分代表 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

问题:怎么配置波特率呢?

答案:用我上面贴的工具(CAN波特率计算 f103AHP1_36M  f407AHP1_42M  采样点软件有说明.rar)直接配置,举两个个例子

例子1:我们要配置成500KHz,那么我们这样配置

CANCAN

我们用采集点为80%,所以BS1为4tq,BS2为2tq,分频系数为12,代进公式Fpclk1/((CAN_BS1+CAN_BS2+1)*CAN_Prescaler)=42M/(4+2+1)/12=500kHz

例子2:我们要配置成1M Hz,那么我们这样配置

CAN

我们用采集点为75%,所以BS1为3tq,BS2为2tq,分频系数为7,代进公式Fpclk1/((CAN_BS1+CAN_BS2+1)*CAN_Prescaler)=42M/(3+2+1)/7=1MHz

CAN

问题:Basic Parameter分别是啥意思呢?

CAN

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 的初始化

问题:为啥CAN分为RX0,RX1中断呢?

CAN

答案:STM32有2个3级深度的接收缓冲区:FIFO0和FIFO1,每个FIFO都可以存放3个完整的报文,它们完全由硬件来管理。如果是来自FIFO0的接收中断,则用CAN1_RX0_IRQn中断来处理。如果是来自FIFO1的接收中断,则用CAN1_RX1_IRQn中断来处理,如图:CAN

问题:CAN SCE中断时什么?

CAN

答案:status chanege error,错误和状态变化中断!

     四、CAN分析工具的使用      

下面我们会用到CAN分析工具,还是比较好用的,此部分使用作为自己使用

https://www.zhcxgd.com/h-col-112.html

     五、实验      

1.Normal模式测试500K 波特率(定时发送,轮询接收)

1.1 CubeMx配置

CANCAN

1.2 设置Filter过滤,我们只使能FIFO0,并且不过滤任何消息

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;
}

1.3 开启CAN(注意,默认Cubemx生成的代码并没有can start)

HAL_CAN_Start(&hcan1);

1.4 编写发送函数

我们开出了几个参数,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协议分析仪来抓下结果

CAN

1.5 编写轮询接收函数

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.1 CubeMx配置

CANCAN

步骤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(" ");

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分