CAN通信协议

文章目录

  • STM32-CAN
    • 1. CAN基础知识
    • 2. CAN协议
      • 2.1. CAN协议与ISO/OSI基本参照模型的关系
      • 2.2. CAN协议及标准规格
      • 2.3. CAN协议
        • 2.3.1. 帧的种类
        • 2.3.2. 数据帧
        • 2.3.3. 遥控帧
        • 2.3.4. 错误帧
        • 2.3.5. 过载帧
        • 2.3.6. 间隔帧
      • 2.4. 优先级的决定
      • 2.5. 位填充
      • 2.6. 错误的种类
      • 2.7. 位时序
    • 3. CAN控制器
    • 4. CAN相关寄存器
    • 5. CAN相关HAL库驱动
    • 6. 代码实现

STM32-CAN

1. CAN基础知识

  • CAN简介:

    Controller Area Network,是ISO国际标准化的串行通信协议。在当前的汽车产业中,出于对安全性、舒适性、方便性、低公害、低成本的要求,各种各样的电子控制系统被开发了出来。由于这些系统之间通信所用的数据类型及对可靠性的要求不尽相同,由多条总线构成的情况很多,线束的数量也随之增加。为适应减少线束的数量”、“通过多个LAN,进行大量数据的高速通信”的需要,1986年德国电气商博世公司开发出面向汽车的CAN通信协议。此后,CAN通过ISO11898及ISO11519进行了标准。现在,CAN的高性能和可靠性已被认同,并被广泛地应用于工业自动化、船舶、医疗设备、工业设备等方面。现场总线是当今自动化领域技术发展的热点之一,被誉为自动化领域的计算机局域网。它的出现为分布式控制系统实现各节点之间实时、可靠的数据通信提供了强有力的技术支持。

  • CAN协议的特点:

    1. 多主控制: 在总线空闲时,所有单元都可以发送消息,而两个以上的单元同时发送消息时,根据标识符(ID)决定优先级。ID并不是表示发送的目的地址,而是表示访问总线的消息的优先级。两个以上的单元同时发送消息时,对各消息ID的每个位进行逐个仲裁比较。仲裁获胜的单元可继续发送消息,仲裁失利的单元则立即停止发送而进行接收工作。
    2. 系统的柔软性: 与总线相连的单元没有类似于“地址”的信息。因此在总线上增加单元时,连接在总线上的其它单元的软硬件及应用层都不需要改变。
    3. 通信速度快,距离远: 最高1Mbps(距离小于40m),最远可达10km(速率低于5Kbps)。
    4. 具有错误检测、错误通知和错误恢复功能: 所有单元都可以检测错误(错误检测功能),检测出错误的单元会立即同时通知其它所有单元(错误通知功能),正在发送消息的单元一旦检测出错误,会强制结束当前的发送。强制结束发送的单元会不断的重新发送此消息直到成功发送为止(错误恢复功能)。
    5. 故障封闭功能: CAN可以判断出错误的类型是总线上暂时的数据错误(如外部噪声)还是持续的数据错误(如单元内部故障、驱动器故障、断线等)。由此功能,当总线上发生持续错误时,可将引起此故障的单元从总线上隔离出去。
    6. 连接节点多: CAN总线是可以同时连接多个单元的总线,可连接的单元总数理论上是没有限制的,但实际上可连接的单元数受总线上的时间延迟及电气负载的限制。降低通信速度,可连接的单元数增加。
  • CAN应用场景:

    CAN总线协议已广泛应用在汽车电子、工业自动化、船舶、医疗设备、工业设备等方面。

    车载网络构想:
    在这里插入图片描述

  • CAN标准:

    ISO11898 标准(高速 CAN)和 ISO11519-2 标准(低速 CAN

    总线拓扑图:
    在这里插入图片描述在这里插入图片描述

  • CAN物理层:
    在这里插入图片描述

  • CAN收发器芯片:
    在这里插入图片描述

  • CAN错误状态:

    1. 主动错误状态: 主动错误状态是可以正常参加总线通信的状态。处于主动错误状态的单元检测出错误时,输出主动错误标志。

    2. 被动错误状态: 被动错误状态是易引起错误的状态。处于被动错误状态的单元虽能参加总线通信,但为不妨碍其它单元通信,接收时不能积极地发送错误通知。处于被动错误状态的单元即使检测出错误,而其它处于主动错误状态的单元如果没发现错误,整个总线也被认为是没有错误的。处于被动错误状态的单元检测出错误时,输出被动错误标志。另外,处于被动错误状态的单元在发送结束后不能马上再次开始发送。在开始下次发送前,在间隔帧期间内必须插入“延迟传送”(8个位的隐性位)。

    3. 总线关闭态: 总线关闭态是不能参加总线上通信的状态。信息的接收和发送均被禁止。

      这些状态依靠发送错误计数和接收错误计数来管理,根据计数值决定进入何种状态。
      在这里插入图片描述
      在这里插入图片描述

    • 错误计数值:
      在这里插入图片描述

2. CAN协议

2.1. CAN协议与ISO/OSI基本参照模型的关系

  • CAN协议与ISO/OSI基本参照模型:
    在这里插入图片描述

    数据链路层分为MAC子层和LLC子层,MAC子层是CAN协议的核心部分。数据链路层的功能是将物理层收到的信号组织成有意义的消息,并提供传送错误控制等传输控制的流程。具体地说,就是消息的帧化、仲裁、应答、错误的检测或报告。数据链路层的功能通常在CAN控制器的硬件中执行。

    在物理层定义了信号实际的发送方式、位时序、位的编码方式及同步的步骤。但具体地说,信号电平、通信速度、采样点、驱动器和总线的电气特性、连接器的形态等均未定义。这些必须由用户根据系统需求自行确定。

2.2. CAN协议及标准规格

在这里插入图片描述

2.3. CAN协议

2.3.1. 帧的种类

通信的实现依靠五种帧:数据帧遥控帧错误帧过载帧帧间隔

另外,数据帧和遥控帧有标准格式扩展格式两种格式。标准格式有 11 个位的标识符(ID),扩展格式有 29 个位的 ID。

帧类型帧作用
数据帧(Data Frame)用于发送单元向接收单元传输数据的帧
遥控帧(Remote Frame)用于接收单元向具有相同ID的发送单元请求数据的帧
错误帧(Error Frame)用于当检测出错误时向其他单元通知错误的帧
过载帧(Overload Frame)用于接收单元通知其尚未做好接收准备的帧
间隔帧(Inter Frame Space)用于将数据帧 及遥控帧与前面的帧分离开来的帧
2.3.2. 数据帧
  • 数据帧的构成:
    在这里插入图片描述

    数据帧由7段构成:

    帧起始:表示数据帧开始的段。
    在这里插入图片描述

    仲裁段:表示该帧优先级的段。
    在这里插入图片描述

    控制段:表示数据的字节数及保留位的段。
    在这里插入图片描述

    数据段:数据的内容,可发送 0~8 个字节的数据。
    在这里插入图片描述

    CRC段:检查帧的传输错误的段。
    在这里插入图片描述

    ACK段:表示确认正常接收的段。
    在这里插入图片描述

    帧结束:表示数据帧结束的段。
    在这里插入图片描述

2.3.3. 遥控帧
  • 遥控帧的构成:
    在这里插入图片描述

    遥控帧由6段组成:

    帧起始: 表示帧开始的段。

    仲裁段: 表示该帧优先级的段。

    控制段: 表示数据的字节数及保留位的段。

    CRC段: 表示帧传输错误的段。

    ACK段: 表示确认正常接收的段。

    帧结束: 表示遥控帧结束的段。

  • 数据帧与遥控帧的不同:

    • 遥控帧的RTR位为隐性位,没有数据段
    • 没有数据段的数据帧和遥控帧可通过RTR位区别
  • 遥控帧的数据长度码以所请求数据帧的数据长度码表示

  • 没有数据帧的数据段可用于各单元的定期连接确认/应答、或仲裁段本身带有实质性信息的情况

2.3.4. 错误帧

用于在接收和发送消息时检测出错误通知错误的帧。错误帧由错误标志错误界定符构成。
在这里插入图片描述

  • 错误标志
    • 主动错误标志(处于主动错误状态的单元检测出错误时输出的错误标志):6个位的显性位
    • 被动错误标志(处于被动错误状态的单元检测出错误时输出的错误标志):6个位的隐性位
  • 错误界定符
    • 错误界定符由8个位的隐性位构成。
2.3.5. 过载帧

用于接收单元通知其尚未完成接收准备的帧。过载帧由过载标志过载界定符构成。
在这里插入图片描述

  • 过载标志
    • 6个位的显性位。
    • 过载标志的构成与主动错误标志的构成相同。
  • 过载界定符:
    • 8个位的隐性位。
    • 过载界定符由8个位的隐性位构成。
2.3.6. 间隔帧

帧间隔是用于分隔数据帧和遥控帧的帧。数据帧和遥控帧可通过帧间隔将本帧与前面的任何帧分开。

过载帧和错误帧不能插入帧间隔。
在这里插入图片描述

  • 间隔:
    • 3个位的隐性位。
  • 延迟传送:
    • 8个位的隐性位。
    • 只在处于被动错误状态的单元刚发送一个消息后的帧间隔中包含的段。
  • 总线空闲:
    • 隐性电平,无长度限制。
    • 本状态下,可视为总线空闲,要发送的单元可开始访问总线。

2.4. 优先级的决定

在总线空闲态,最先开始发送消息的单元获得发送权。

多个单元同时开始发送时,各发送单元从仲裁段的第一位开始进行仲裁。连续输出显性电平最多的单元可继续发送。
在这里插入图片描述

  • 数据帧和遥控帧的优先级:

    具有相同ID的数据帧和遥控帧在总线上竞争时,仲裁段的最后一位(RTR)为显性位的数据帧具有优先权,可继续发送。
    在这里插入图片描述

  • 标准格式和拓展格式的优先级:

    标准格式ID与具有相同ID的遥控帧或者拓展格式的数据帧在总线上竞争时,标准格式的RTR位为显性的具有优先权,可继续发送。
    在这里插入图片描述

2.5. 位填充

位填充是为防止突发错误而设定的功能。当同样的电平持续5位时则添加一个位的反型数据。
在这里插入图片描述

  • 发送单元的工作:

    在发送数据帧和遥控帧时,SOF~CRC段间的数据,相同电平如果持续5位,在下一位(第6个位)则要插入1位与前5位反型的电平。

  • 接收单元的工作:

    在接收数据帧和遥控帧时,SOF~CRC段间的数据,相同电平如果持续5位,需要删除下一位(第6个位)再接收。如果这个第6个位的电平与前5位相同,将被视为错误并发送错误帧。

2.6. 错误的种类

一共有5种错误:位错误填充错误CRC错误格式错误ACK错误
在这里插入图片描述

  • 错误帧的输出位置:
    在这里插入图片描述

2.7. 位时序

由发送单元在非同步的情况下发送的每秒钟的位数称为位速率。一个位可分为4段。

  • 同步段(SS)
  • 传播时间段(PTS)
  • 相位缓冲段1(PBS1)
  • 相位缓冲段2(PBS2)

这些段又由可称为Time Quantum (Tq) 的最小时间单位构成。

1位分为4个段,每个段又由若干个Tq构成,这称为位时序。
在这里插入图片描述

1个位的构成:
在这里插入图片描述

  • CAN总线上取的同步的方法:

    CAN协议的通信方法为NRZ(Non-Return Zero)方法。各个位的开头或者结尾都没有附件同步信号。发送单元以与位时序同步的方式开始发送数据。接收单元根据总线上电平的变化进行同步接收工作。但是,发送单元和接收单元存在的时钟频率误差及传输路径上的相位延迟会引起同步偏差。因此,接收单元通过硬件同步再同步的方式调整时序进行接收。

    • 硬件同步:
      在这里插入图片描述

    • 再同步:
      在这里插入图片描述

    • 调整同步的规则:

      1. 1个位中只进行一次同步调整。
      2. 只有当上次采样点的总线值和边沿后的总线值不同时,该边沿才能用于调整同步。
      3. 在总线空闲且存在隐性电平到显性电平的边沿时,则一定要进行硬件同步。
      4. 在总线非空闲时检测到的隐性电平到显性电平的边沿如果满足条件(1)和(2),将进行再同步。但还要满足下面条件。
      5. 发送单元观测到自身输出的显性电平有延迟时不进行再同步。
      6. 发送单元在帧起始到仲裁段有多个单元同时发送的情况下,对延迟边沿不进行再同步。

3. CAN控制器

  • bxCAN介绍:
    在这里插入图片描述

  • CAN控制器工作模式:

    CAN控制器的工作模式有三种:初始化模式正常模式睡眠模式
    在这里插入图片描述

  • CAN控制器测试模式:

    CAN控制器的测试模式有三种:静默模式环回模式环回静默模式
    在这里插入图片描述

  • CAN控制器框图:
    在这里插入图片描述

    发送处理流程:
    在这里插入图片描述
    在这里插入图片描述

    接收处理流程:
    在这里插入图片描述

在这里插入图片描述

接收过滤器:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • CAN控制器位时序:
    在这里插入图片描述

4. CAN相关寄存器

寄存器名称作用
CAN_MCRCAN主控制寄存器主要负责CAN工作模式的配置
CAN_BTR位时序寄存器用来设置分频/TBS1/TBS2/TSWJ等参数,设置测试模式
CAN_(T/R)IxR标识符寄存器存放(待发送/接收)的报文ID、扩展ID、IDE位及RTR位
CAN_(T/R)DTxR数据长度和时间戳寄存器存放(待发送/接收)报文的DLC段
CAN_(T/R)DLxR低位数据寄存器存放 (待发送/接收)报文数据段的Data0~Data3的内容
CAN_(T/R)DHxR高位数据寄存器存放 (待发送/接收)报文数据段的Data4~Data7的内容
CAN_FM1R过滤器模式寄存器用于设置各过滤器组的工作模式
CAN_FS1R过滤器位宽寄存器用于设置各过滤器组的位宽
CAN_FFA1RFIFO关联寄存器用于设置报文通过过滤器后,被存入的FIFO
CAN_FA1R过滤器激活寄存器用于开启对应的过滤器组
CAN_FxR(1/2)过滤器组x寄存器根据位宽和模式设置不同,CAN_FxR1和FxR2功能不同

5. CAN相关HAL库驱动

  • CAN主控制寄存器 (CAN_MCR)
    在这里插入图片描述

  • CAN位时序寄存器 (CAN_BTR)
    在这里插入图片描述

  • CAN发送/接收邮箱标识符寄存器 (CAN_(T/R)IxR)
    在这里插入图片描述

  • CAN发送邮箱数据长度和时间截寄存器 (CAN_TDTxR)
    在这里插入图片描述

  • CAN发送邮箱低字节数据寄存器 (CAN_TDLxR)
    在这里插入图片描述

  • CAN过滤器模式寄存器 (CAN_FM1R)
    在这里插入图片描述

  • CAN过滤器位宽寄存器 (CAN_FS1R)
    在这里插入图片描述

  • CAN过滤器FIFO关联寄存器 (CAN_FFA1R)
    在这里插入图片描述

  • CAN过滤器激活寄存器 (CAN_FA1R)
    在这里插入图片描述

  • CAN过滤器组x寄存器 (CAN_FxR(1/2))
    在这里插入图片描述

6. 代码实现

  • 功能: 通过 KEY_UP 按键选择 CAN 的工作模式(正常模式/环回模式),然后通过 KEY0 控制数据发送,接着查询是否有数据接收到,假如接收到数据,就将接收到的数据显示在 LCD 模块上。如果是环回模式,我们不需要 2 个开发板。如果是正常模式,我们就需要 2 个战舰开发板,并且将他们的 CAN 接口对接起来,然后一个开发板发送数据,另外一个开发板将接收到的数据显示在 LCD 模块上。

  • CAN初始化函数

    void can_init(void)
    {CAN_FilterTypeDef can_filterconfig = {0};  // 初始化CAN过滤器配置结构体,将所有成员初始化为0g_can1_handler.Instance = CAN1;  // 选择CAN1作为CAN控制器实例g_can1_handler.Init.Mode = CAN_MODE_LOOPBACK;  // 设置CAN工作模式为环回模式(自发自收),用于调试g_can1_handler.Init.Prescaler = 4;  // 设置CAN分频系数为4,以降低输入时钟频率g_can1_handler.Init.TimeSeg1 = CAN_BS1_9TQ;  // 设置时间段1为9个时间量(TQ)g_can1_handler.Init.TimeSeg2 = CAN_BS2_8TQ;  // 设置时间段2为8个时间量(TQ)g_can1_handler.Init.SyncJumpWidth = CAN_SJW_1TQ;  // 设置重新同步跳跃宽度为1个时间量(TQ)g_can1_handler.Init.AutoBusOff = DISABLE;  // 禁止自动离线管理g_can1_handler.Init.AutoRetransmission = DISABLE;  // 禁止自动重发消息g_can1_handler.Init.AutoWakeUp = DISABLE;  // 禁止自动唤醒功能g_can1_handler.Init.ReceiveFifoLocked = DISABLE;  // 禁止接收FIFO锁定g_can1_handler.Init.TimeTriggeredMode = DISABLE;  // 禁止时间触发通信模式g_can1_handler.Init.TransmitFifoPriority = DISABLE;  // 禁止发送FIFO优先级HAL_CAN_Init(&g_can1_handler);  // 初始化CAN控制器can_filterconfig.FilterMode = CAN_FILTERMODE_IDMASK;  // 设置CAN过滤器模式为ID掩码模式can_filterconfig.FilterScale = CAN_FILTERSCALE_32BIT;  // 设置CAN过滤器规模为32位can_filterconfig.FilterIdHigh = 0;  // 设置过滤器ID高位为0can_filterconfig.FilterIdLow = 0;  // 设置过滤器ID低位为0can_filterconfig.FilterMaskIdHigh = 0;  // 设置过滤器掩码ID高位为0can_filterconfig.FilterMaskIdLow = 0;  // 设置过滤器掩码ID低位为0can_filterconfig.FilterBank = 0;  // 设置过滤器组为0can_filterconfig.FilterFIFOAssignment = CAN_FilterFIFO0;  // 设置过滤器FIFO分配为FIFO 0can_filterconfig.FilterActivation = CAN_FILTER_ENABLE;  // 启用过滤器can_filterconfig.SlaveStartFilterBank = 14;  // 设置从过滤器组起始号为14HAL_CAN_ConfigFilter(&g_can1_handler, &can_filterconfig);  // 配置CAN过滤器HAL_CAN_Start(&g_can1_handler);  // 启动CAN控制器
    }
    

    1. 配置CAN控制器实例

    g_can1_handler.Instance = CAN1;
    g_can1_handler.Init.Mode = CAN_MODE_LOOPBACK; // 工作模式设置:环回模式,自发自收
    
    • g_can1_handler.Instance = CAN1;:选择CAN1作为CAN控制器实例。
    • g_can1_handler.Init.Mode = CAN_MODE_LOOPBACK;:将CAN设置为环回模式,这意味着控制器会自发自收,用于调试。

    2. 配置CAN时间和同步参数

    g_can1_handler.Init.Prescaler = 4;                 // 分频系数
    g_can1_handler.Init.TimeSeg1 = CAN_BS1_9TQ;        // 时间段1
    g_can1_handler.Init.TimeSeg2 = CAN_BS2_8TQ;        // 时间段2
    g_can1_handler.Init.SyncJumpWidth = CAN_SJW_1TQ;   // 重新同步跳跃宽度
    
    • g_can1_handler.Init.Prescaler = 4;:设置分频系数为4,以降低输入时钟频率。
    • g_can1_handler.Init.TimeSeg1 = CAN_BS1_9TQ;:设置时间段1为9个时间量(TQ)。
    • g_can1_handler.Init.TimeSeg2 = CAN_BS2_8TQ;:设置时间段2为8个时间量(TQ)。
    • g_can1_handler.Init.SyncJumpWidth = CAN_SJW_1TQ;: 设置同步跳跃宽度为1个时间量(TQ)。

    3. 配置CAN功能

    g_can1_handler.Init.AutoBusOff = DISABLE;          // 禁止自动离线管理
    g_can1_handler.Init.AutoRetransmission = DISABLE;  // 禁止自动重发
    g_can1_handler.Init.AutoWakeUp = DISABLE;          // 禁止自动唤醒
    g_can1_handler.Init.ReceiveFifoLocked = DISABLE;   // 禁止接收FIFO锁定
    g_can1_handler.Init.TimeTriggeredMode = DISABLE;   // 禁止时间触发通信模式
    g_can1_handler.Init.TransmitFifoPriority = DISABLE;// 禁止发送FIFO优先级
    
    • g_can1_handler.Init.AutoBusOff = DISABLE;:禁止自动离线管理。
    • g_can1_handler.Init.AutoRetransmission = DISABLE;:禁止自动重发消息。
    • g_can1_handler.Init.AutoWakeUp = DISABLE;:禁止自动唤醒功能。
    • g_can1_handler.Init.ReceiveFifoLocked = DISABLE;:禁止接收FIFO锁定。
    • g_can1_handler.Init.TimeTriggeredMode = DISABLE;:禁止时间触发通信模式。
    • g_can1_handler.Init.TransmitFifoPriority = DISABLE;:禁止发送FIFO优先级。

    4. 初始化CAN控制器

    HAL_CAN_Init(&g_can1_handler); //调用HAL_CAN_Init函数来初始化CAN控制器
    

    5. 配置CAN过滤器

    can_filterconfig.FilterMode = CAN_FILTERMODE_IDMASK; //设置CAN过滤器模式为ID掩码模式
    can_filterconfig.FilterScale = CAN_FILTERSCALE_32BIT; //设置CAN过滤器规模为32位can_filterconfig.FilterIdHigh = 0;  // 设置过滤器ID高位为0
    can_filterconfig.FilterIdLow = 0;  // 设置过滤器ID低位为0
    can_filterconfig.FilterMaskIdHigh = 0;  // 设置过滤器掩码ID高位为0
    can_filterconfig.FilterMaskIdLow = 0;  // 设置过滤器掩码ID低位为0can_filterconfig.FilterBank = 0;  // 设置过滤器组为0
    can_filterconfig.FilterFIFOAssignment = CAN_FilterFIFO0;//设置过滤器FIFO分配为FIFO 0
    can_filterconfig.FilterActivation = CAN_FILTER_ENABLE;  // 启用过滤器
    can_filterconfig.SlaveStartFilterBank = 14;  // 设置从过滤器组起始号为14
    HAL_CAN_ConfigFilter(&g_can1_handler, &can_filterconfig);  // 配置CAN过滤器
    
    • can_filterconfig.FilterMode = CAN_FILTERMODE_IDMASK;:设置过滤器模式为ID掩码模式。
    • can_filterconfig.FilterScale = CAN_FILTERSCALE_32BIT;:设置过滤器规模为32位。
    • can_filterconfig.FilterIdHigh = 0;can_filterconfig.FilterIdLow = 0;:设置过滤器ID高位和低位为0。
    • can_filterconfig.FilterMaskIdHigh = 0;can_filterconfig.FilterMaskIdLow = 0;:设置过滤器掩码ID高位和低位为0。
    • can_filterconfig.FilterBank = 0;:设置过滤器组为0。
    • can_filterconfig.FilterFIFOAssignment = CAN_FilterFIFO0;:设置过滤器FIFO分配为FIFO 0。
    • can_filterconfig.FilterActivation = CAN_FILTER_ENABLE;:启用过滤器。
    • can_filterconfig.SlaveStartFilterBank = 14;:设置从过滤器组起始号为14。

    然后调用HAL_CAN_ConfigFilter函数来配置过滤器。

    6. 启动CAN控制器

    HAL_CAN_Start(&g_can1_handler); //调用HAL_CAN_Start函数来启动CAN控制器
    

    具体操作寄存器:在这里插入图片描述

  • MSP回调函数

    void HAL_CAN_MspInit(CAN_HandleTypeDef *hcan)
    {CAN_TX_GPIO_CLK_ENABLE();  // 启用CAN发送引脚的GPIO时钟CAN_RX_GPIO_CLK_ENABLE();  // 启用CAN接收引脚的GPIO时钟__HAL_RCC_CAN1_CLK_ENABLE();  // 启用CAN1控制器的时钟GPIO_InitTypeDef gpio_init_struct;  // 定义一个GPIO初始化结构体// 配置CAN发送引脚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;  // 高速HAL_GPIO_Init(CAN_TX_GPIO_PORT, &gpio_init_struct);  // 初始化CAN发送引脚的GPIO配置// 配置CAN接收引脚gpio_init_struct.Pin = CAN_RX_GPIO_PIN;  // 设置引脚号gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;  // 设置为复用输入模式HAL_GPIO_Init(CAN_RX_GPIO_PORT, &gpio_init_struct);  // 初始化CAN接收引脚的GPIO配置
    }
    

    1. 配置CAN发送引脚(TX):

    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;  // 高速
    HAL_GPIO_Init(CAN_TX_GPIO_PORT, &gpio_init_struct);  // 初始化CAN发送引脚的GPIO配置
    
    • gpio_init_struct.Pin = CAN_TX_GPIO_PIN:指定要配置的GPIO引脚为CAN发送引脚。
    • gpio_init_struct.Mode = GPIO_MODE_AF_PP:将引脚模式设置为复用推挽输出模式,用于CAN发送。
    • gpio_init_struct.Pull = GPIO_PULLUP:使能上拉电阻,保持引脚在空闲状态时为高电平。
    • gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH:将引脚速度设置为高频率,以支持高速数据传输。
    • HAL_GPIO_Init(CAN_TX_GPIO_PORT, &gpio_init_struct):使用以上配置初始化CAN发送引脚的GPIO。

    2. 配置CAN接收引脚(RX):

    gpio_init_struct.Pin = CAN_RX_GPIO_PIN;      // 设置引脚号
    gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;  // 设置为复用输入模式
    HAL_GPIO_Init(CAN_RX_GPIO_PORT, &gpio_init_struct);  // 初始化CAN接收引脚的GPIO配置
    
    • gpio_init_struct.Pin = CAN_RX_GPIO_PIN:指定要配置的GPIO引脚为CAN接收引脚。
    • gpio_init_struct.Mode = GPIO_MODE_AF_INPUT:将引脚模式设置为复用输入模式,用于CAN接收。
  • 发送消息数据函数

    void can_send_message(uint32_t id, uint8_t *buf, uint8_t len)
    {uint32_t tx_mail = CAN_TX_MAILBOX0;  // 定义并初始化发送邮箱为邮箱0g_can1_txheader.ExtId = id;          // 设置扩展ID为传入的id参数g_can1_txheader.DLC = len;           // 设置数据长度代码(DLC)为传入的len参数g_can1_txheader.IDE = CAN_ID_EXT;    // 设置标识符扩展(IDE)为扩展帧g_can1_txheader.RTR = CAN_RTR_DATA;  // 设置远程传输请求(RTR)为数据帧HAL_CAN_AddTxMessage(&g_can1_handler, &g_can1_txheader, buf, &tx_mail);  // 通过CAN控制器发送消息while(HAL_CAN_GetTxMailboxesFreeLevel(&g_can1_handler) != 3);  // 等待所有发送邮箱为空
    }
    

    1. 定义并初始化发送邮箱:

    uint32_t tx_mail = CAN_TX_MAILBOX0;  // 定义并初始化发送邮箱为邮箱0
    
    • 定义一个32位无符号整数tx_mail并将其初始化为CAN_TX_MAILBOX0,表示使用第一个发送邮箱。

    2. 设置CAN消息头:

    g_can1_txheader.ExtId = id;          // 设置扩展ID为传入的id参数
    g_can1_txheader.DLC = len;           // 设置数据长度代码(DLC)为传入的len参数
    g_can1_txheader.IDE = CAN_ID_EXT;    // 设置标识符扩展(IDE)为扩展帧
    g_can1_txheader.RTR = CAN_RTR_DATA;  // 设置远程传输请求(RTR)为数据帧
    
    • g_can1_txheader.ExtId = id:将传入的id参数设置为CAN消息的扩展ID。
    • g_can1_txheader.DLC = len:将传入的len参数设置为CAN消息的数据长度代码(DLC)。
    • g_can1_txheader.IDE = CAN_ID_EXT:将CAN消息的标识符扩展(IDE)设置为扩展帧,表示使用29位ID。
    • g_can1_txheader.RTR = CAN_RTR_DATA:将CAN消息的远程传输请求(RTR)设置为数据帧。

    3. 发送CAN消息:

    HAL_CAN_AddTxMessage(&g_can1_handler, &g_can1_txheader, buf, &tx_mail);  // 通过CAN控制器发送消息
    
    • 调用HAL_CAN_AddTxMessage函数,通过CAN控制器发送消息:
      • &g_can1_handler:指向CAN控制器的句柄。
      • &g_can1_txheader:指向消息头的指针。
      • buf:指向要发送的数据缓冲区。
      • &tx_mail:指向发送邮箱的指针。

    4. 等待发送完成:

    while(HAL_CAN_GetTxMailboxesFreeLevel(&g_can1_handler) != 3);  // 等待所有发送邮箱为空
    
    • 使用while循环等待所有发送邮箱为空:
      • HAL_CAN_GetTxMailboxesFreeLevel(&g_can1_handler):获取当前空闲的发送邮箱数量。
      • != 3:如果空闲的发送邮箱数量不等于3,则继续等待。这表示所有发送邮箱(CAN_TX_MAILBOX0, CAN_TX_MAILBOX1, CAN_TX_MAILBOX2)都空闲。

    总结:

    这段代码主要通过配置CAN消息的头部(包括ID、长度、帧类型等)并调用相应的库函数发送消息,最后等待所有发送邮箱都空闲,以确保消息成功发送。

  • 接收数据函数

    uint8_t can_receive_message(uint8_t *buf)
    {if(HAL_CAN_GetRxFifoFillLevel(&g_can1_handler, CAN_RX_FIFO0) == 0){return 0;}HAL_CAN_GetRxMessage(&g_can1_handler, CAN_RX_FIFO0, &g_can1_rxheader, buf);return g_can1_rxheader.DLC;
    }
    

    1. 检查接收FIFO队列是否有消息:

    if(HAL_CAN_GetRxFifoFillLevel(&g_can1_handler, CAN_RX_FIFO0) == 0)  // 检查接收FIFO0队列是否为空
    {return 0;  // 如果接收FIFO0队列为空,返回0,表示没有消息
    }
    
    • HAL_CAN_GetRxFifoFillLevel(&g_can1_handler, CAN_RX_FIFO0):调用库函数检查接收FIFO0队列的填充级别。接收FIFO0队列为空,即没有消息。
      • &g_can1_handler:指向CAN控制器的句柄。
      • CAN_RX_FIFO0:指定接收FIFO0队列。

    2. 从接收FIFO0队列中获取消息:

    HAL_CAN_GetRxMessage(&g_can1_handler, CAN_RX_FIFO0, &g_can1_rxheader, buf);  // 从接收FIFO0队列中获取消息,并存储在buf中
    
    • HAL_CAN_GetRxMessage(&g_can1_handler, CAN_RX_FIFO0, &g_can1_rxheader, buf):调用库函数从接收FIFO0队列中获取消息。
      • &g_can1_handler:指向CAN控制器的句柄。
      • CAN_RX_FIFO0:指定接收FIFO0队列。
      • &g_can1_rxheader:指向接收消息头部的指针。
      • buf:指向接收消息数据缓冲区的指针。

    3. 返回消息的长度代码(DLC):

    return g_can1_rxheader.DLC;  // 返回消息的长度代码(DLC)
    
    • g_can1_rxheader.DLC:获取接收消息头部的长度代码(DLC)。

    总结:

    这段代码用于接收通过CAN(控制器局域网络)发送的消息,并将其存储在指定的缓冲区中。它首先检查接收FIFO(先入先出)队列中是否有消息,如果有消息,函数从FIFO队列中提取消息,并返回消息的长度代码(DLC)。

声明:资料来源(战舰STM32F103ZET6开发板资源包)

  1. Cortex-M3权威指南(中文).pdf
  2. STM32F10xxx参考手册_V10(中文版).pdf
  3. STM32F103 战舰开发指南V1.3.pdf
  4. STM32F103ZET6(中文版).pdf
  5. 战舰V4 硬件参考手册_V1.0.pdf

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/34636.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙系统最简单安装谷歌服务及软件的方法

哈喽,各位小伙伴们好,我是给大家带来各类黑科技与前沿资讯的小武。 近日,华为开发者大会在东莞松山湖召开,发布了盘古大模型5.0和纯血版的鸿蒙 HarmonyOS NEXT 全场景智能操作系统,而根据研究机构 Counterpoint Resea…

ITSG、COST-G、Tongji和WHU Level-2数据产品读取绘图(Matlab)

数据介绍: ICGEM International Center for Global Gravity Field Models (gfz-potsdam.de) ITSG 2018:Institute of Geodesy at Graz University of Technolog(格拉茨理工大学大地测量研究所) 2018版本,最高60阶球谐…

Java导出excel合并行功能

导出的excel需要上下行相同的数据进行行合并的功能。如图显示 这里我使用的是项目框架自带的导出模板代码,是在这套模板基础之上做的修改。 // 我主要演示的就是mergeRows方法的操作,dataList是导出数据的集合。 workbook ExcelTools.expData(workbook…

大厂面试官问我:Redis中热key和大key是怎么解决的?【后端八股文五:Redis热key和大key八股文合集】

往期内容: 大厂面试官问我:Redis处理点赞,如果瞬时涌入大量用户点赞(千万级),应当如何进行处理?【后端八股文一:Redis点赞八股文合集】-CSDN博客 大厂面试官问我:布隆过滤…

振兴黄河新生力 打造文旅新地标——全国首家黄河会客厅在山东济南启幕

6月26日,由黄河文化发展工作站组织实施的全国首家黄河会客厅平台发布会暨山东基地启动仪式在济南成功召开。黄河会客厅以“民生黄河、生态动能、中华文明”为核心主题,融汇黄河智库、黄河文明、黄河产域、黄河金融、黄河科创、黄河物贸六大振兴赋能体系&…

计算机视觉:项目实战

目录 SSD1.安装ananconda2.安装cuda和cudnn3.配置Pytorch环境3.1 pytorch环境的配置与激活3.2 pytorch库的安装3.3 其它依赖库的安装 遇到的问题:1.EOFError: Ran out of input.2.No module named dlib. SSD 1.安装ananconda 见另一篇博文:https://blo…

OpenAI API一键搬家,天工推出开发者迁移计划

6月25日,OpenAI宣布称将于今年7月9日开始封锁来自非支持国家和地区的API流量。此后,来自中国大陆、中国香港等地的开发者将无法使用OpenAI API提供服务。 为了助力开发者高效切换至国内大模型,天工开放平台(https://model-platfo…

电脑突然提示dll文件丢失,怎么选择正确的恢复方法?

电脑突然提示dll文件丢失?其实当你的电脑使用久了,出现这种dll文件丢失是非常的正常的,毕竟你总会有不恰当的操作吧?这些操作都是会导致dll文件丢失的。丢失了,我们直接进行相关的修复就好了,还是比较简单的…

# linux 系统中,使用 “ ll “ 命令报错 “ bash ll command not found “ 解决方法:

linux 系统中,使用 " ll " 命令报错 " bash ll command not found " 解决方法: 一、错误描述: 报错原因: 1、这个错误表明你尝试在 bash shell 中执行 ll 命令,但是系统找不到这个命令。ll 通常…

C++之模板(二)

1、类模板 2、使用类模板 类模板在使用的时候要显示的调用是哪种类型&#xff0c;而不是像函数模板一样能够根据参数来推导出是哪种类型。 Stack.h #include <stdexcept>template <typename T> class Stack { public:explicit Stack(int maxSize);~Stack();void …

银河麒麟桌面操作系统V10SP1【FTP服务器】配置手册

简介: FTP是一个文件传输协议,主要是在互联网上提供文件储存和访问服务的计算机,一个FTP服务器可以对多个客户端提供服务。本文主要介绍在银河麒麟桌面操作系统V10SP1上如何搭建FTP服务器以及在客户端如何访问FTP服务器的操作方法。 正文: 一、操作环境 服务端:银河麒…

C# unknow column “p0.TaskTypeId‘ in ‘field list‘

这个问题就是数据库出现问题&#xff0c;去 日志中去看 &#xff0c;找个具体表去 看实体类&#xff0c;与数据库中的表&#xff0c;是否存在字段。

数字化采购管理革新:全过程数字化采购管理平台的架构与实施

摘要&#xff1a;在数字化转型的浪潮中&#xff0c;采购管理正逐步迈向全流程的数字化。本文将详细解析全过程数字化采购管理平台的技术架构和实施策略&#xff0c;探讨如何通过Spring Cloud、Spring Boot2、Mybatis等先进技术和服务框架&#xff0c;实现从供应商管理到采购招投…

示例:WPF中推荐一个支持折叠展开的GridSpliter自定义控件GridSplitterBox

一、目的&#xff1a;推荐一个支持折叠展开的GridSpliter自定义控件GridSplitterBox 二、效果 实现功能&#xff1a;设置菜单显示位置&#xff0c;最小宽度&#xff0c;最大宽度&#xff0c;位置持久化保存 三、环境 VS2022 Net7 四、使用方式 1、安装nuget包&#xff1a;H…

使用AI机器学习,轻松解决化合物配比优化问题

为什么需要化合物配比的优化&#xff1f; 在化合物制造行业中&#xff0c;化合物的配比是产品质量控制的关键环节。 化合物制造流程 目前&#xff0c;这一过程高度依赖于材料专家和工程技术人员的经验&#xff0c;通过反复试验来验证产品性能&#xff0c;确保其满足市场和客户的…

基于阿里云 OpenAPI 插件,让 Grafana 轻松实现云上数据可视化

作者&#xff1a;徽泠 引言 Grafana 作为市场上领先的开源监控解决方案之一&#xff0c;使得数据监控和可视化变得触手可及。作为一款开源的数据可视化和分析软件&#xff0c;Grafana 支持查询、可视化、提醒和探索您的各种数据&#xff0c;无论它们存储在何处。Grafana 通过…

AIGC笔记--U-ViT的简单代码实现

1--前言 原论文&#xff1a;All are Worth Words: A ViT Backbone for Diffusion Models 完整可debug的代码&#xff1a; 2--结构 3--简单代码 以视频作为输入&#xff0c;实现上图红色框的计算&#xff1a; import torch import torch.nn as nn from einops import rearran…

Linux-笔记 OverlayFS文件系统入门

目录 前言 主要概念 工作原理 特点特性 1、上下合并 2、同名文件覆盖 3、同名目录合并 4、写时拷贝 实操入门 内核配置 挂载文件系统 验证 1、同名文件覆盖 2、同名目录合并 3、写时拷贝 1&#xff09;验证新增文件或目录 2&#xff09;验证修改文件 3&…

昇思25天学习打卡营第3天|张量Tensor

张量Tensor 概念创建张量&#xff08;4种方式&#xff09;张量的属性张量索引张量运算Tensor与NumPy转换 概念 张量&#xff08;Tensor&#xff09;是一种特殊的数据结构&#xff0c;与数组和矩阵非常相似。张量是MindSpore网络运算中的基本数据结构。 创建张量&#xff08;4…

MySQL 7种Join的定义图解示范结果(所有join类型)

文章目录 MySQL 7种Join的定义&图解&示范&结果&#xff08;所有join类型&#xff09;基本知识笛卡尔积 建表&填充数据1-Join不带条件account筛选 1-Inner Join 内连接不带条件account相同where筛选玩点特殊的 2-Left Join 左连接不带条件account筛选 3-Right J…