HAL库 CAN

该文章已生成可运行项目,

CAN总线结构:

                                                图1  CAN总线结构

闭环结构:总线两端各接一个120欧姆的电阻,是高速短距离CAN网络,速率为125kbit/s - 1Mbit/s。1Mbit/s时通信距离最长是40m

开环结构:两根信号线独立,各自串一个2.2k电阻,低速远距离,最高到125kbit\s,在40kbit/s是最长距离是1KM。

CAN网络的逻辑电平和平时的不一致。

电平

高速CAN

低速CAN

显性电平(0)

UCAN_H – UCAN_L=  2V

UCAN_H – UCAN_L = 3V

隐性电平(1)

UCAN_H – UCAN_L = 0V

UCAN_H – UCAN_L = - 1.5V

                                                                 表1 显隐电平

位时序:

CAN网络上没有主从机之分,都是节点,同一时刻只能有一个节点发送数据其余接收,没有地址概念,按照帧发送数据,每个节点都可以接收数据,可以根据ID过滤数据。没有同步的时钟线,所以需要规定波特率。

位时序CAN总线上一个节点采集一个位数据的时序,通过位时序CAN总线可以位同步消除传输误差。

                                                                图2 位时序

同步段需要检测一个位跳变,固定时间是一个t_{q} 。BS1是m\times t_{q},定义了采样点位置,BS1结束时采样,m可以自动加长以补偿频率差异导致的正相位偏移。BS2一般是1-8个t_{q} ,定义了发送点的位置,可自动缩短补偿负相位偏移,CAN控制器可以自动对位时序实现再同步,此时可以修改图中的m和n,一般修改1-4个t_{q}。波特率是\frac{1}{(1+m+n)t_{q}}

STM32把传播时间段和相位缓冲段1合并在一起,形成了新的时间段1,在图2 中没有体现传播时间段;没有SS同步段的设置,因为SS段恒等于1

帧类型:

若干个位时序组成了帧。

数据帧、遥控帧、错误帧、过载帧、帧间空间。

重点学习数据帧和遥控帧。

数据帧:标准的数据帧和遥控帧都有11位的ID。数据帧传输带有ID的0-8字节数据,遥控帧只有ID,没有数据,用于请求数据。

                                                                  图3 数据帧

帧起始:SOF (Start Of Frame),显性电平0,图3中 SOF 标识的1是位数,不是实际的帧数据。

仲裁段:标准ID(11位)+RTR(Remote  Transmit Request)

        多个节点一同发送数据时根据仲裁段谁先出现显性电平(逻辑0)决定谁占用总线,如果两节点发送ID相同,则根据最后的RTR位(用于区分数据帧和遥控帧,数据帧为显性电平逻辑0,遥控帧为隐形电平逻辑1)裁决,所以具有相同ID的遥控帧和数据帧同时发送时,数据帧优先级高。

IDE(Identify Extern)是指示帧是否为标准格式,标准格式为显性电平逻辑0。RB0是保留位总为显性电平逻辑0,DLC Data Length Code是数据长度编码4位,遥控帧DLC总是0。

数据段:只有数据帧有。

CRC段前15位是检验码,最后一位总是隐形电平逻辑1,是CRC段的界定符。

ACK段包含一个ACK位和一个ACK段标识符

EOF连续7个隐形电平,表示帧结束。

扩展帧:

仲裁段的ID是分开的,两部分。

SRR是RTR占位符,隐形电平逻辑1,真正的RTR位在扩展ID后边。IDE在扩展帧中是隐形电平逻辑1。

RB1和RB0是保留位显性电平逻辑0。

总线仲裁规则:

1.空闲时谁先发送谁优先

2.同时ID显性电平先出现者优先

STM32CubeMX设置:

以72M主频的F103C8T6为例:APB1是36Mhz,选9分频后是4Mhz,一个时间片就是250ns。我们如果设波特率为500KBit/s,那么就设置BS1为4,BS2为3(这两个决定CAN的波特率)。其他时间默认即可。因为只有一个最小系统板,也没有CAN收发器,所以设置自发自收Test Mode:Loopback。

                                                                图4 Cube MX 设置

如果使用中断接收的话需要把中断打开:

                                                                图5 CAN中断

STM32F103C8T6只有一个CAN控制器,我们只关心接收时的中断所以使用RX0这个中断就行。CAN TX initerruputs是发送完成中断。CAN SCE interrupts是CAN错误中断。

代码:

在can.h中添加以下声明

//can.h
void CANFilter_Config(void);
void CAN1_Send_Test(void);

在can.c中需要配置过滤器、实现发送函数、实现接收中断回调函数

//can.c
static CAN_TxHeaderTypeDef TxMessage;//发送数据头,里边有ID信息
static CAN_RxHeaderTypeDef RxMessage;//接收数据头,里边有ID信息
//配置过滤器函数:(没有进行任何的过滤)
void CANFilter_Config(void)
{
  CAN_FilterTypeDef sFilterConfig;
  sFilterConfig.FilterBank = 0;
  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
  sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
  sFilterConfig.FilterIdHigh = 0x0000;
  sFilterConfig.FilterIdLow = 0x0000;
  sFilterConfig.FilterMaskIdHigh = 0x0000;
  sFilterConfig.FilterMaskIdLow = 0x0000;
  sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
  sFilterConfig.FilterActivation = ENABLE;
  sFilterConfig.SlaveStartFilterBank = 14;

  if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
  {
    /* Filter configuration Error */
         printf("ConfigFilter is Failed\n");
        Error_Handler();
  }
        printf("ConfigFilter is success\n");
}

void CAN1_Send_Test(void)
{
        uint32_t CAN_TX_BOX=0;//必须自己定义,用宏会硬件报错
        uint8_t data[8] ={0x01,0x02,0x03,0x04};
        TxMessage.IDE= CAN_ID_STD;//标准ID
        TxMessage.StdId = 0x2222;//自己定的ID
        TxMessage.RTR =CAN_RTR_DATA;//它是区别数据帧和远程帧的标志。数据帧显性
        TxMessage.TransmitGlobalTime = DISABLE;
        TxMessage.DLC = 4;//数据长度
        if (HAL_CAN_AddTxMessage(&hcan,&TxMessage, data,&CAN_TX_BOX) != HAL_OK){
                printf("CAN send test data fail! \r\n");
                Error_Handler();
        }
}
//接收回调函数
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) {
    uint8_t data[8];
    HAL_StatusTypeDef status;
    if (hcan->Instance == NULL){
        return;
    }
    //接收数据主要就是下边的函数,注意FIFO是在过滤器中定义的那个
    status = HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxMessage, data);
    if (status == HAL_OK) {
        printf("---> Data Received!\n");
        printf("RxMessage.StdId is 0x%08X\n", RxMessage.StdId);
        printf("data[0] is 0x%02X\n", data[0]);
        printf("data[1] is 0x%02X\n", data[1]);
        printf("data[2] is 0x%02X\n", data[2]);
        printf("data[3] is 0x%02X\n", data[3]);
        printf("<---\n");
    } else {
        printf("Failed to get CAN message, status: 0x%08X\n", status);
    }
}

将下面代码放到main.c文件中的MX_CAN_Init();函数之后,即可1s中接收到一次CAN信号,并且通过串口打印(假设printf已经重定向)

//main.c
 
CANFilter_Config();
if(HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK){
          printf("CAN_IT_RX_FIFO0_MSG_PENDING Fail\r\n");
          Error_Handler();
  }
  printf("CAN_IT_RX_FIFO0_MSG_PENDING success\r\n");
  //使能CAN总线

  if(HAL_CAN_Start(&hcan) !=HAL_OK){
          printf("CAN start Fail\r\n");
          Error_Handler();
  }
  printf("CAN start success\r\n");
  while (1)
  {
    CAN1_Send_Test();
    HAL_Delay(1000);
  }

过滤器:

cubeMX没有为我们生成过滤器代码,所以我们必须自己写一个,过滤器决定了你想收到哪个ID的信息。

FilterBank  是因为某些高端型号芯片会有两个CAN控制器芯片,主CAN控制器和从CAN控制器,丛CAN想用主CAN必须打开。我们F103C8T6就只有一个CAN控制器,所以就填0。

FilterMode 过滤器是工作在列表模式还是掩码模式。

        列表模式类似路由器的白名单模式。

        掩码模式类似子网掩码。下边两个FilterMask参数中的位如果是1则和FilterId严格匹配才可通过,如果FilterMask的位是0则被过滤ID对应位无论是什么均可通过(这个位不再关系不代表整个ID就通过过滤器了)。

FilterFIFOAssignment 需要指定FIFO0

sFilterConfig.SlaveStartFilterBank = 14;还是因为高端芯片的事,0-13是给主CAN的,14-27给从CAN,这里设成14代表主CAN使用0-13。

参考文献:

初学stm32 --- CAN_stm32 can-CSDN博客

https://blog.csdn.net/feixiaoliao/article/details/88981635

https://zhuanlan.zhihu.com/p/38299092

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值