STM32H743-ARM例程31-CAN

目录

实验平台

硬件:银杏科技GT7000双核心开发板-ARM-STM32H743XIH6,银杏科技iToolXE仿真器

软件:最新版本STM32CubeH7固件库STM32CubeMX v6.10.0,开发板环境MDK v5.35CANTest通信助手

CAN

CAN简介

CAN是Controller Area Network的缩写(简称为CAN),是由以研发和生产汽车电子产品著称的德国BOSCH公司开发的,并最终成为国际标准(ISO 11898),是国际上应用最广泛的现场总线之一。

CAN总线协议已经成为汽车计算机控制系统和嵌入式工业控制局域网的标准总线,并且拥有以CAN为底层协议专为大型货车和重工机械车辆设计的J1939协议。

CAN的可靠性已经得到广泛认可,被应用于工业自动化、船舶、医疗设备、工业设备等方面。
核心特点:

  1. 多主结构: 总线上所有节点(设备)没有主从之分,任何一个节点都可以在总线空闲时主动向其他节点发送信息。这极大地增强了系统的灵活性。

  2. 基于优先级的总线仲裁: 这是CAN总线最核心的机制。当多个节点同时发送数据时,优先级高的报文会"赢得"总线使用权,而优先级低的则会自动退出发送,没有任何数据损坏或延迟。优先级由报文的"标识符"决定,标识符值越小,优先级越高。

  3. 高可靠性:

    • 错误检测与处理: 拥有CRC循环冗余校验、位填充、应答、帧检查等多种错误检测机制。

    • 差分信号: 使用CAN_H和CAN_L两条线传输差分信号,能有效抑制共模干扰,抗噪声能力强。

    • 故障隔离: 故障节点会被自动关闭并脱离总线,避免影响整个网络。

  4. 广播通信: 理论上,一个节点发送的消息,总线上所有其他节点都能接收到。然后每个节点通过"标识符过滤"来决定是否处理该消息。

CAN物理层

CAN是一种异步通讯,只有CAN_High和CAN_Low两条信号线,共同构成一组差分信号线,以差分信号的形式进行通讯。

闭环总线网络:

CAN物理层的形式主要有两种,如下图是其中的一种"闭环网络",它允许总线最大长度为40m,最高速度为1Mbps,可以看到总线的两端各有一个120欧的电阻,这是规定。节点就是不同的设备,连接到一个闭环总线上面。

开环总线网络:

它的最大传输距离为1km, 最高通讯速率为125kbps,两根总线是独立的、不形成闭环,要求每根总线上各串联有一个"2.2千欧"的电阻。

讯节点:

CAN节点是指能够挂接在CAN总线上的单元,并能通过CAN总线实现各个节点间的通信,以实现复杂的控制过程。理论上CAN总线连接的节点可达110个,但实际上收到总线上的时间延迟及电气负载的限制。当连接多节点时,降低通信速率,可连接的节点增加;提高通信速率,则可连接的节点数减少。

CAN通讯节点由一个CAN控制器及CAN收发器,CAN控制器用于实现实现  CAN总线的协议底层以及数据链路层,用于生成CAN帧并以二进制码流的方式发送,在此过程中进行位填充、添加CRC校验、应答检测等操作;将接收到的二进制码流进行解析并接收,在此过程中进行收发比对、去位填充、执行CRC校验等操作。此外还需要进行冲突判断、错误处理等诸多任务。

CAN收发器(有时也叫做驱动器),用于将二进制码流转换为差分信号发送,将差分信号转换为二进制码流接收。

我们已经知道CAN控制器和收发器的概念,观察上面2张图,为接线方式,控制器和收发器通过普通的类似TTL逻辑信号来连接,CAN_Low和CAN_High是一对差分信号线。

当CAN节点需要发送数据时,控制器把要发送的二进制编码通过通过CAN_Tx线发送到收发器,然后由收发器把这个普通的逻辑电平信号转换成差分信号,通过差分线CAN_High和CAN_Low线输出到CAN总线网络。而通过收发器接收总线上的数据的到控制器时,是相反的过程。

CAN协议中差分信号

差分信号

差分信号是一种使用两根信号线来传输一个信号的方法。

  • 传统单端信号: 使用一根信号线(相对于一个公共地线)的电压变化来表示逻辑"0"和"1"。接收端通过判断信号线与地线之间的电压差来解读信息。

  • 差分信号:

    • 使用两根线:一根传输正相信号(V+),另一根传输反相信号(V-)。

    • 发送端将要发送的信号同时生成一个原信号和一个相位相反(大小相等、极性相反)的信号。

    • 接收端不关心它们对地的电压,只关心这两根线之间的电压差,即 V_diff = (V+) - (V-)。

差分信号的巨大优势主要来自于其对共模噪声的抑制能力。

卓越的抗噪声(抗干扰)能力

1.共模噪声:在传输过程中,两条紧邻的导线会像天线一样,同时捕捉到来自外部(如电机、电源线、无线电)的相同干扰噪声。这种同时出现在两条线上的噪声被称为"共模噪声"。

神奇的抵消过程:

  • 假设在传输中,一个瞬间的干扰 V_noise 同时叠加在了 V+ 和 V- 上。

  • 此时,接收器收到的信号变成了 (V+ + V_noise) 和 (V- + V_noise)。

  • 接收器计算新的电压差:V_diff_new = (V+ + V_noise) - (V- + V_noise) = V+ - V- = V_diff。

  • 看,噪声 V_noise 被完全抵消了! 最终的电压差与没有噪声时一模一样。

结论: 只要干扰对两根线的影响是近似相等的,差分信号就能将其有效消除。

2.更低的电磁辐射

  • 由于 V+ 和 V- 的电流大小相等、方向相反,它们产生的磁场也是方向相反的。

  • 在理想情况下,这些磁场会相互抵消,从而使得差分信号对外的电磁辐射远小于单端信号。

  • 这有助于产品通过EMC(电磁兼容性)认证,并减少系统内部各模块之间的相互干扰。

3.对共地要求不苛刻

在单端信号系统中,发送端和接收端的"地"电位必须非常一致。如果两地之间存在电位差 V_ground,这个差值会直接叠加在信号电压上,导致误判。

在差分系统中,接收端只关心两条信号线之间的相对差值,对地电位的轻微变化不敏感。只要共模电压在接收器的工作范围内,就不会影响信号的正确解读。

CAN协议中的差分信号

CAN协议中对它使用的CAN_High以及CAN_Low表示的差分信号做了规定,如下图,可以看到,显性电平对应逻辑"0",隐性电平对应逻辑"1"。当表示为隐性电平(逻辑"1")时,CAN_High和CAN_Low的电压均为2.5V,此时它们的电压差为0V;当表示显性电平(逻辑"0")时,CAN_High的电压为3.5V,CAN_Low的电压为1.5V,它们的电压差为2V。举个例子:当CAN收发器从CAN_Tx线接收到来自CAN控制器的低电平信号(逻辑"0")时,它会使CAN_High输出3.5V,同时CAN_Low输出1.5V。在总线上显性电平具有优先权,只要有一个单元(也就是节点)输出显性电平,总线上即为显性电平。而隐性电平则具有包容的含义,只有所有的单元都输出隐性电平,总线上才为隐性电平。那么可以知道,CAN通讯是半双工的,收发数据需要分开来进行,由于是公用总线,在整个网络中,同一时刻只能由一个通讯节点发送信号,其余的节点在该时刻只能接收。

CAN协议层

CAN的波特率及位同步

由于CAN属于异步通讯,没有时钟信号线,那么节点间就需要约定好波特率进行通讯。同时,为实现正确的总线电平采样,确保通讯正常,由此引出"位时序"的概念。

位时序的组成:

CAN协议将每一个数据位的时序分解成4段,即:SS段、PTS段、PBS1段、PBS2段,这4段加起来就是一个CAN数据位的长度。而分解后的最小时间单位是Tq,一个完整的位由8~25个Tq组成。如下图是总线电平下每个位分解。

SS段:称为同步段,用于使总线上的各个节点同步,要求有一个跳变沿位于此段内,该段的长度为1Tq;

PTS段:称为传播时间段,用于补偿网络内的物理延时,它是信号在总线上传播时间,输入比较器延时和输出驱动器延时之和的两倍,该段长可以为1~8Tq;

PBS1段:称为相位缓冲段,主要用来补偿变压阶段的误差,它的实际长度在重新同步的时候可以加长,该段大小可以为1~8Tq;

PBS2段:称为另一个相位缓冲段,也是用来补偿边沿阶段的误差,它的时间长度在重新同步的时候可以加长,该段大小可以为2~8Tq。

通讯的波特率:

总线上的各个通讯节点只要约好1个Tq的时间长度以及每一个数据位占据多少个Tq,就可以确定CAN通讯的波特率。

假设图30-4中1Tq=1us,而有19个Tq,则传输一位数据需要的实际为19us。这样每秒可以传输的数据位个数为:1*106/19=52631.6(bps),即为波特率。

CAN同步机制:

CAN规范定义了自己独有的同步方式:硬同步和重同步。同步与位时序密切相关。同步是由节点自身完成的,节点将检测到来自总线的沿与其自身的位时序相比较,并通过硬同步或重同步适配(调整)位时序。

CAN总线的位同步只有在节点检测到"隐性位(逻辑1)"到"显性位(逻辑0)"的跳变时才会产生,我们知道在同步段需要跳变沿,当跳变沿不处于此同步段内时,那么就会产生相位误差,该相位误差就是跳变沿与同步段结束位置直接的距离,产生误差的原因有多种,那么针对此种误差,也就是上面说到的硬同步和重同步。

硬同步只在总线空闲时通过一个下降沿(下面将讲到的帧起始)来完成,此时不管有没有相位误差,所有节点的位时序将重新开始。强迫引起硬同步的跳变沿位于重新开始的位时间的同步段之内。

重同步,在消息帧的随后位中,每当有从"隐性位"到"显性位"的跳变,并且该跳变段落在了同步段之外,就会引起一次重同步。重同步机制可以根据跳变沿增长或者缩短位时序以调整采样点的位置,保证正确采样。

CAN报文:

和SPI和串口协议不同的是,CAN使用两条差分信号线,因而只能表示一个信号,于是为了实现完整的传输信号功能,CAN协议便对数据、操作命令以及同步信号进行打包,打包后的内容便称为报文。

CAN报文格式:

CAN一共规定了5种类型的帧,分别是:数据帧、遥控帧、错误帧、过载帧、帧间隔,作用见下表。另外,数据帧和遥控帧有标准格式和扩展格式两个,唯一的区别是标识符(ID)不同,在下面数据帧的结构讲解中,我们会对两个格式进行介绍。

数据帧的结构

数据帧是CAN通信中最中意、最复杂的报文,以一个显性位(逻辑0)开始,以一个7个连续隐性位(逻辑1)结束。在它们之间分为仲裁段、控制段、数据段、CRC段和ACK段,加上起始和结束总共有7个段。

在下图中,在标准帧和扩展帧除了ID和DLC之外,还有RTR、IDE、SRR位。首先是RTR位,翻译为远程传输请求位,用显性电平和隐性电平来区分数据帧和遥控帧。然后是IDE位,为标识符扩展位,用显性电平和隐性电平来区分标准格式还是扩展格式。最后是SRR位,只存在于扩展格式,它用于替代标准格式中的RTR位,由于扩展帧中的SRR位为隐性位,RTR是数据帧为显性位,所以在两个ID相同的标准格式报文与扩展格式报文中,标准格式的优先级更高。

帧起始

SOF段(Start Of Frame),翻译为帧起始,标准帧和扩展帧都是通过一个显性电平表示帧起始。它用于通知各个节点将有数据传输,其它节点通过帧起始信号的电平跳变沿来进行硬同步。
仲裁段

简单来说就是表示优先级的段。仲裁段的内容主要为本数据帧的ID信息,数据帧具有标志格式和扩展格式两种,具体的区别就是ID信息的长度,标准格式的ID为11位,扩展格式的ID为29位,观察图30-5应该也发现了。

在CAN协议中,ID起着重要的作用,它决定着数据帧发送的优先级,也决定着其它节点是否会接收到这个数据帧。CAN的数据通信没有主从之分,任意一个节点可以向其他(一个或多个)节点发起数据通信,而对总线的占有权是由信息的重要性决定的,也就是说重要的信息,对它打包上一个优先级高的ID,使它能够及时地发送出去,也正是由于这样优先级的分配原则,使CAN的扩展性大大加强,在总线上增加或减少节点并不会影响其它的设备。

那么怎么仲裁呢?让我们联系前面的内容,显性电平的优先权和隐性电平的包容性,CAN正是利用这个特性进行仲裁。若两个节点同时竞争CAN总线的占有权,当它们发送报文时,若首先出现隐性电平,则会失去对总线的占有权,进入接收状态。
控制段

r1和r0为保留位,默认置为显性,它最主要的是由4位组成的DLC段,MSB先行,DLC段表示的数字为0~8。
数据段

为数据帧的核心内容,它是节点要发送的原始信息,由0~8个字节组成,MSB先行。
CRC段

为保证报文的正确传输,CAN的报文包含了一段15位的CRC校验码,若检验出错则向发送节点反馈出错信息,利用错误帧请求它重新发送。CRC计算一般由CAN控制器硬件完成,而出错时处理由软件控制最大的重发数。在CRC校验码之后,有一个CRC界定符,为隐性,将CRC校验码与后面的ACK段分隔开。
ACK段

包括一个ACK槽位和ACK界定符,在ACK槽位中,发送端发送的为隐性位,接收端在这一位中发送显性位以表示应答,在ACK界定槽和帧结束之间由ACK界定符分隔开。
帧结束段

由发送节点发送7个隐性位表示结束。

其它报文的结构

关于其它的CAN报文结构,不再展开讲解,其主要内容见图

有关CAN通信更多详情介绍可以参考CAN总线协议CAN-bus规范这两篇文档学习。

STM32的CAN外设简介

STM32具有bxCAN,是基本扩展CAN,它支持CAN协议2.0A和2.0B。它的设计目标是以最小的CPU负荷来高效处理大量接收到的报文。也支持报文发送的优先级要求(通过软件配置)。bxCAN提供所有支持时间触发通信模式所需的硬件功能。

该CAN控制器支持最高的通讯速率为1Mb/s;它可以自动地接收和发送CAN报文,支持使用标准ID和扩展ID的报文;外设中具有3个发送邮箱;具有2个3级深度的接收FIFO,可使用过滤功能只接收或不接收某些ID号的报文。具有双CAN,CAN1是主bxCAN,CAN2为从bxCAN。

STM32有两组CAN控制器,其中CAN1为主设备,负责管理bxCAN和512字节的SRAM存储器之间的通信;CAN2为从设备,它不能直接访问SRAM存储器。在使用CAN2的时候必须先使能CAN1外设的时钟。

CAN控制内核:

框图中标号①处的CAN控制内核包含了各种控制器及状态寄存器,通过这些寄存器可以配置CAN的参数,如波特率;请求发送报文;处理报文的接收;管理相关中断、获取诊断信息等。由于篇幅原因,这里主要讲解的是主控制器寄存器和位时序控制器。

主控制寄存器(CAN_MCR)

DBF调试冻结(DBF)

可设置为调试时的工作状态或者禁止收发的状态,禁止收发时仍然可以访问接收FIFO的中的数据。

时间触发通信模式(TTCM)

用于运行和禁止时间触发通信模式。在该模式下,CAN硬件的内部定时器被激活,并且产生时间戳,分别存储在CAN_RDTxR/CAN_TDTxR寄存器中。内部定时器在接收和发送的帧起始位的采样点位置被采样,并生成时间戳。

自动离线管理(ABOM)

该位决定CAN硬件在什么条件下可以退出离线状态,为"1"时,一旦硬件检测到128次11位的连续隐性位,则自动退出离线状态;为"0"时,软件对CAN_MCR寄存器的INRQ位进行置'1'随后清'0'后,一旦硬件检测到128次11位连续的隐性位,则退出离线状态。

自动唤醒(AWUM)

CAN外设可以使用软件进入低功耗的睡眠模式,使能此功能后,当CAN检测到总线活动的时候,会自动唤醒。

自动重传(NART)

设置为"0"时,CAN硬件发送报文时会一直重传,直达成功为止;为"1"时,CAN报文只被发送一次,不管有没成功。

接收FIFO锁定模式(RFLM)

该功能用于锁定接收FIFO,锁定时,接收FIFO溢出时,会丢弃下一个接收到的报文;不锁定,则下一个接收到的报文会覆盖原报文。

发送FIFO优先级(TXFP)

当发送邮箱中有多个报文同时在等待发送时,该位控制是报文的ID来决定还是发送请求的顺序来决定。

CAN位时序寄存器(CAN_BTR)

为了方便用于调试,CAN通过设置SILM和LBKM位可以将模式设置为正常模式、静默模式、回环模式和禁止回环模式。

CAN发送邮箱

在上图中的标号②是CAN外设的发送邮箱,它一共有3个发送邮箱,即可以缓存3个待发送的报文。由发送跳段根据优先级决定哪个邮箱的报文先被发送。

每个发送邮箱含有标记符寄存器(CAN_TIxR)、数据长度控制寄存器(CAN_TDTxR)和两个数据寄存器CAN_TDLxR、CAN_TDHxR。

当我们要发送报文时,把报文的各个段分解,按位置写入到这些寄存器中,并将CAN_TIxR中的发送请求寄存器位TMIDxR_TXRQ置1,即可完成发送。

CAN接收FIFO

CAN外设有2个接收FIFO,每个FIFO有3个邮箱,即可以缓存6个接收到的报文。当接收到报文时,FIFO的报文计数器会自增,而STM32内部读取FIFO数据之后,报文计数器会自减,我们便可以通过状态寄存器获知报文计数器的值。

和发送邮箱类似,也有4个寄存器,类型也是一样的。通过中断或者状态寄存器知道接收FIFO有数据后,再读取这些寄存器的值即可把接收到的报文加载到STM32的内存中。

接收过滤器

在CAN协议中,消息的标识符与节点地址无关,但是和消息的内容有关。因此,接收节点会根据标识符的值来确定软件是否需要该消息。STM32在的CAN外设接收报文前会嫌使用接收过滤器进行检查,只接收需要的报文到FIFO中。

CAN过滤器的寄存器有多个,可以自己看手册进行相应的配置,这里就不多讲解了。过滤的方法有两种模式分别是ID列表模式和掩码模式。

在本实验中,GT7000使用SN65HVD230作为CAN驱动,通过CAN助手发送数据并能接收数据。

原理图:

STM32CubeMX生成工程

我们参考前面章节STM32H743-结合CubeMX新建HAL库MDK工程,打开CubeMX软件,重复步骤不再展示。我们来看配置FDCAN1部分配置如下图所示:


实验代码

1. 主函数

c 复制代码
int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_FDCAN1_Init();
    can.initialize();
    while (1)
    {
        if(can.receive_ok_flag == 1)
        {
            can.receive_ok_flag = 0;
            HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, RxData);
        }
/*
        for(int i=0; i<8; i++)
            RxData[i] = i; 
        for(int i=0; i<1000000; i++)
            for(int j=0; j<1000; j++)
                HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, RxData);
*/
    }
}

2. CAN初始化

c 复制代码
static int initialize(void)
{
    FDCAN_FilterTypeDef sFilterConfig;

    /* 配置接收过滤器 */
    sFilterConfig.IdType = FDCAN_STANDARD_ID;         // 标准ID格式(11位)[1,2](@ref)
    sFilterConfig.FilterIndex = 0; // 使用过滤器索引0(标准ID最多支持128个过滤器)[1](@ref)
    sFilterConfig.FilterType = FDCAN_FILTER_RANGE;    // 范围过滤器模式[2,6](@ref)
sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; 
// 匹配的帧存入RX FIFO0[1,6](@ref)
    sFilterConfig.FilterID1 = 0x000;                 // 起始ID范围:0x000
sFilterConfig.FilterID2 = 0x7FF;                 
// 结束ID范围:0x7FF(接收所有标准ID帧)[2](@ref)    
    if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
    {
        /* Filter configuration Error */
        Error_Handler();
    }

    /* 全局过滤器配置:拒绝所有不匹配的帧 */
    HAL_FDCAN_ConfigGlobalFilter(&hfdcan1, 
            FDCAN_REJECT,             // 拒绝非匹配标准帧
            FDCAN_REJECT,             // 拒绝非匹配扩展帧
            FDCAN_REJECT_REMOTE,    // 拒绝远程标准帧
            FDCAN_REJECT_REMOTE);   // 拒绝远程扩展帧[4](@ref)

    /* Start the FDCAN module */
    if (HAL_FDCAN_Start(&hfdcan1) != HAL_OK)
    {
        /* Start Error */
        Error_Handler();
    }

    if (HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0) != HAL_OK)
    {
        /* Notification Error */
        Error_Handler();
    }

    /* Prepare Tx Header */
    /* 发送帧头配置 */
    /* 发送帧头配置 */
    TxHeader.Identifier = 0x321;                     // 发送ID为0x321(标准ID)
    TxHeader.IdType = FDCAN_STANDARD_ID;      // 使用标准ID格式
    TxHeader.TxFrameType = FDCAN_DATA_FRAME;  // 数据帧(非远程帧)
    TxHeader.DataLength = FDCAN_DLC_BYTES_8;    // 数据长度8字节[5](@ref)
    TxHeader.BitRateSwitch = FDCAN_BRS_OFF;       // 禁用比特率切换(经典CAN模式)[5](@ref)
    TxHeader.FDFormat = FDCAN_CLASSIC_CAN;     // 使用传统CAN格式(非FD模式)[5](@ref)
    TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
    TxHeader.MessageMarker = 0;
    
    return 0;
}

3. HAL_FDCAN_RxFifo0Callback函数

c 复制代码
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
    if((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != RESET)
    {
        /* Retreive Rx messages from RX FIFO0 */
        if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &RxHeader, RxData) != HAL_OK)
        {
            /* Reception Error */
            Error_Handler();
        }       
        if (HAL_FDCAN_ActivateNotification(hfdcan, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0) != HAL_OK)
        {
            /* Notification Error */
            Error_Handler();
        }   
        /* Receive OK */
        can.receive_ok_flag = 1;
    }
}

实验现象

本章实验需要CAN通信分析仪,读者需要自行准备,将分析仪的H接口与GT7000开发板上CAN接口H连接,L与L连接如下图所示

运行程序,并打开CANTest助手工具:


点击发送,GT7000会将接收到的CAN数据返回来。

相关推荐
切糕师学AI3 小时前
MCU的时钟系统
嵌入式硬件·mcu·芯片
无限进步_4 小时前
深入理解C语言scanf函数:从基础到高级用法完全指南
c语言·开发语言·c++·后端·算法·visual studio
清风6666665 小时前
基于单片机的鱼缸监测与远程管理系统设计
单片机·毕业设计·课程设计·1024程序员节·期末大作业
逆小舟5 小时前
【STM32】工程文件管理
stm32·单片机·嵌入式硬件
yuuki2332335 小时前
【数据结构】顺序表+回调函数
c语言·数据结构·后端
xiaobobo33305 小时前
c语言中const关键字和枚举enum的新认识
c语言·开发语言·const·enum
hazy1k7 小时前
51单片机基础-DS1302时钟
stm32·单片机·嵌入式硬件·51单片机·1024程序员节
不语n9 小时前
点亮LED
单片机·嵌入式硬件
终焉代码9 小时前
【C++】C++11特性学习(1)——列表初始化 | 右值引用与移动语义
c语言·c++·学习·1024程序员节