打印
[研电赛技术支持]

GD32 CAN 应用深度剖析:从原理到实战项目

[复制链接]
1441|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Jiangxiaopi|  楼主 | 2025-1-2 09:00 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
一、CAN 总线概述
CAN(Controller Area Network)总线作为一种广泛应用于汽车电子、工业自动化等领域的串行通信协议,自 20 世纪 80 年代诞生以来,凭借其多主竞争式总线结构和差分信号传输方式,在复杂电磁环境下展现出强大的抗干扰能力和稳定的数据传输性能。CAN 总线通过显性电平(CAN_H 比 CAN_L 高,代表逻辑 0)和隐性电平(CAN_H 与 CAN_L 电压差极小,代表逻辑 1)两种状态进行通信,并规定了包括数据帧、远程帧、错误帧、过载帧等多种帧类型,其中数据帧的仲裁场用于确定总线访问权,CRC 场用于数据校验,保障了数据传输的准确性和可靠性。

二、GD32 的 CAN 外设特性
GD32 系列微控制器的 CAN 外设具备诸多优势:

灵活的波特率配置:能够支持从几十 kbps 到数 Mbps 的波特率范围,以满足不同应用场景的需求。例如在工业现场总线中,对于一些对实时性要求不高但对稳定性要求较高的设备连接,可能会采用较低的波特率,如 100 kbps 或 250 kbps;而在汽车电子的某些高速传感器数据传输场景中,则可能需要设置较高的波特率,如 500 kbps 甚至 1 Mbps 以上。
多邮箱机制:内置多个接收和发送邮箱,发送邮箱可提前存储待发数据,等待总线空闲时快速发送;接收邮箱则能有效缓存接收到的数据,方便 CPU 后续处理。这种多邮箱设计大大提高了数据处理的并行性,减少了数据堵塞的可能性,提升了通信效率。
强大的错误处理能力:具备完善的错误检测与处理机制,硬件能够自动监测位错误、填充错误、CRC 错误等多种总线错误,并通过中断或状态寄存器及时告知 CPU。同时,CAN 模块还可以自动重传出错数据,确保数据最终能够正确传输,这对于工业控制等长期稳定运行且不容许数据出错的场景至关重要。
三、GD32 CAN 硬件连接与引脚功能
GD32 的 CAN 接口与特定 GPIO 引脚复用,连接 CAN 总线时,需将 CAN_H 和 CAN_L 引脚对应连接到外部 CAN 收发器(如常见的 TJA1050)的相应引脚。不同系列的 GD32,其 CAN 引脚的端口分配有所差异,例如在某些 GD32F1 型号中,CAN1 的 CAN_H 可能对应 PA12,CAN_L 对应 PA11。此外,还可能涉及一些用于配置 CAN 模块工作模式(如睡眠模式、测试模式等)的控制引脚,在使用时需要正确配置这些引脚的复用功能和电气特性,确保 CAN 信号能够稳定传输,包括设置合适的输出驱动能力,防止信号过弱无法实现远距离传输或信号过强导致电磁辐射超标等问题。

四、GD32 CAN 初始化配置
时钟使能:通过特定的时钟控制寄存器,使用rcu_periph_clock_enable函数传入对应的 CAN 外设时钟参数,开启 CAN 外设的时钟供应,为后续的寄存器配置和模块运行提供必要的时钟基础。
rcu_periph_clock_enable(RCU_CAN0);
1
GPIO 引脚配置:将对应的 GPIO 引脚从通用输入输出模式切换为 CAN 功能引脚,这需要设置 GPIO 复用寄存器和电气特性寄存器。例如:
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12 | GPIO_PIN_11);


上述代码将 GPIOA 的 12 和 11 引脚配置为复用推挽输出模式,速度设置为 50MHz,使其能够作为 CAN 功能引脚正常工作,确保 CAN 信号的稳定传输。
\3. CAN 工作模式与波特率设置:

工作模式配置:可以将 CAN 模块配置为正常模式、环回模式(用于自测)或静默模式等,以满足不同的开发和测试需求。例如,在开发初期进行功能测试时,可能会先使用环回模式,方便调试代码逻辑,而在实际项目部署中,则会切换到正常模式进行正常的数据通信。
波特率设置:CAN 波特率的计算相对复杂,需要综合考虑系统时钟频率、CAN 外设的分频器设置以及位时间等参数。以下是详细的波特率配置过程:
首先,明确系统时钟频率(f_sys),假设我们使用的 GD32 芯片系统时钟频率为72 MHz。CAN 波特率的计算公式为:CAN Baud Rate = f_sys / ((tbs1 + tbs2 + 1) * (brp + 1)),其中tbs1和tbs2是时间量子(Time Quantum)的设置值,brp是波特率预分频器(Baud Rate Prescaler)的值。
时间量子分为tbs1和tbs2,它们共同决定了一个位时间(Bit Time)内的时间片段划分。例如,我们希望配置波特率为500 kbps,根据 CAN 协议规范,一个位时间通常由同步段(Sync_Seg)、传播时间段(Prop_Seg)、相位缓冲段 1(Phase_Seg1)和相位缓冲段 2(Phase_Seg2)组成,且它们的时间长度通常是由若干个时间量子构成。一般来说,同步段固定为 1 个时间量子,对于500 kbps的波特率,我们可以设置Prop_Seg为 5 个时间量子,Phase_Seg1为 7 个时间量子,Phase_Seg2为 2 个时间量子,那么tbs1 = Prop_Seg + Phase_Seg1 = 5 + 7 = 12,tbs2 = Phase_Seg2 = 2。

接下来计算brp的值,将上述参数代入波特率公式:500000 = 72000000 / ((12 + 2 + 1) * (brp + 1)),通过求解可得brp = 9。

在 GD32 的代码实现中,使用can_parameter_struct结构体来配置 CAN 的参数,示例如下:

can_parameter_struct can_initpara;
can_initpara.working_mode = CAN_MODE_NORMAL;
can_initpara.auto_retrans = ENABLE;
can_initpara.baudrate = CAN_BAUDRATE_500Kbps;
// 根据上述计算结果设置时间量子和波特率预分频器
can_initpara.time_quantum = (CAN_SJW_1TQ | CAN_BS1_12TQ | CAN_BS2_2TQ);
can_initpara.prescaler = 9;
can_init(CAN0, &can_initpara);


通过以上配置,就可以将 GD32 的 CAN 模块波特率设置为500 kbps,使其能够与其他 CAN 设备在相同波特率下进行稳定通信。
\4. 中断配置:

中断源选择:GD32 CAN 模块提供了多种中断源,包括发送完成中断、接收完成中断、错误中断等。例如,当我们希望在数据发送完成后立即执行其他操作,或者在接收到新数据时能够及时处理而不依赖于轮询方式,就可以开启相应的发送完成中断和接收完成中断。对于错误处理,如果希望在 CAN 通信出现错误(如位错误、CRC 错误等)时能够快速响应并采取相应措施(如尝试重传数据、记录错误信息等),则需要开启错误中断。
中断使能寄存器设置:通过配置 CAN 中断使能寄存器来开启所需的中断。例如,使用can_interrupt_enable函数来使能特定的中断源,如下代码片段展示了如何使能 CAN0 的发送完成中断和接收完成中断:
can_interrupt_enable(CAN0, CAN_INT_TBEIE | CAN_INT_RBNEIE);


上述代码中,CAN_INT_TBEIE表示发送缓冲区空中断使能,即当发送缓冲区为空(数据发送完成)时产生中断;CAN_INT_RBNEIE表示接收缓冲区非空中断使能,即当接收缓冲区有新数据时产生中断。

中断优先级设置:在一些复杂的系统中,可能存在多个中断源同时竞争 CPU 资源的情况。此时,需要根据任务的紧急程度和重要性为 CAN 中断设置合适的优先级。GD32 通常采用中断优先级分组机制,通过配置相关的优先级寄存器来确定 CAN 中断的优先级。例如,使用nvic_priority_group_config函数设置中断优先级分组,然后使用nvic_irq_enable函数结合相应的中断优先级值来使能 CAN 中断,并为其分配优先级。
中断服务函数编写:在开启中断后,需要编写相应的中断服务函数来处理中断事件。例如,对于 CAN0 的接收完成中断服务函数,示例如下:
void CAN0_RX_IRQHandler(void)
{
    if (can_interrupt_flag_get(CAN0, CAN_INT_FLAG_RBNE))
    {
        can_receive_message_struct receivemsg;
        can_message_receive(CAN0, CAN_FIFO0, &receivemsg);
        // 处理接收到的数据
        for (int i = 0; i < receivemsg.rx_dlen; i++)
        {
            received_data[i] = receivemsg.rx_data[i];
        }
        // 清除中断标志位,避免重复进入中断
        can_interrupt_flag_clear(CAN0, CAN_INT_FLAG_RBNE);
    }
}


在上述中断服务函数中,首先通过can_interrupt_flag_get函数检查是否是接收完成中断标志被置位,如果是,则读取接收邮箱中的数据,并进行相应的处理(如将数据存储到全局变量received_data中,以便主程序后续使用)。最后,使用can_interrupt_flag_clear函数清除接收完成中断标志位,确保中断系统能够正确响应下一次中断事件。

五、CAN ID 的应用
CAN ID 在 CAN 通信中起着至关重要的作用,它用于标识数据帧的优先级和来源 / 目标。在一个复杂的 CAN 网络中,多个节点可能同时向总线发送数据,CAN ID 的仲裁机制确保了高优先级的消息能够优先传输,避免数据冲突和混乱。

例如,在汽车电子系统中,发动机控制单元(ECU)发送的实时控制数据可能被分配一个较高优先级的 CAN ID,如0x100,而车内娱乐系统发送的音频数据可能具有较低优先级的 CAN ID,如0x300。当总线上同时存在这两种数据等待发送时,根据 CAN 总线的仲裁规则,具有较低数值 CAN ID 的发动机控制数据将优先获得总线访问权,确保关键的车辆运行数据能够及时、准确地传输,而娱乐系统数据则会在总线空闲时再进行发送,这样既保证了车辆的安全稳定运行,又兼顾了其他非关键功能的实现。

在代码实现中,当发送数据时,需要正确设置 CAN ID:

// 填充发送邮箱
can_transmit_message_struct transmitmsg;
transmitmsg.tx_sfid = 0x100;  // 设置高优先级的 CAN ID,例如用于发动机控制数据
transmitmsg.tx_efid = 0x00;
transmitmsg.tx_dlen = 8;
for (int i = 0; i < 8; i++) {
    transmitmsg.tx_data[i] = engine_data[i];  // engine_data 为发动机相关的数据数组
}
can_message_transmit(CAN0, &transmitmsg);


在接收数据时,也可以根据 CAN ID 来筛选特定的消息,只接收我们关注的数据帧,避免不必要的数据处理开销:

void CAN0_RX_IRQHandler(void)
{
    if (can_interrupt_flag_get(CAN0, CAN_INT_FLAG_RBNE))
    {
        can_receive_message_struct receivemsg;
        can_message_receive(CAN0, CAN_FIFO0, &receivemsg);
        if (receivemsg.rx_sfid == 0x100)  // 只处理发动机控制数据的 CAN ID
        {
            // 处理接收到的发动机数据
            for (int i = 0; i < receivemsg.rx_dlen; i++)
            {
                engine_received_data[i] = receivemsg.rx_data[i];
            }
        }
        // 清除中断标志位,避免重复进入中断
        can_interrupt_flag_clear(CAN0, CAN_INT_FLAG_RBNE);
    }
}




六、数据传输原理与代码实现
发送数据:当有数据需要发送时,首先将数据填充到 CAN 发送邮箱中,同时设置好目标地址(由 CAN 标识符确定接收节点)和数据长度等信息。例如:
// 填充发送邮箱
can_transmit_message_struct transmitmsg;
transmitmsg.tx_sfid = 0x200;  // 目标标识符,根据实际接收节点设置
transmitmsg.tx_efid = 0x00;
transmitmsg.tx_dlen = 8;     // 数据长度,假设要发送 8 个字节的数据
for (int i = 0; i < 8; i++) {
    transmitmsg.tx_data[i] = data[i];  // data 为待发送的数据数组
}
can_message_transmit(CAN0, &transmitmsg);


填充完发送邮箱后,启动发送请求,CAN 硬件会依据总线仲裁规则,在总线空闲时将数据发送出去。总线仲裁机制确保了在多个节点同时竞争总线发送权时,优先级高的节点能够优先发送数据,保证了通信的有序性和高效性。
\2. 接收数据:CAN 接收邮箱会实时监听总线,一旦检测到有匹配自身标识符的数据帧到达,就会将数据存入邮箱中。CPU 可以通过查询接收标志位或者利用中断通知机制,得知有新数据到来,然后读取邮箱中的数据进行后续处理。例如:

// 检查接收标志
if (can_flag_get(CAN0, CAN_FLAG_RFNE0)) {
    can_receive_message_struct receivemsg;
    can_message_receive(CAN0, CAN_FIFO0, &receivemsg);
    // 处理接收到的数据
    for (int i = 0; i < receivemsg.rx_dlen; i++) {
        received_data[i] = receivemsg.rx_data[i];
    }
}


在上述代码中,首先通过can_flag_get函数检查 CAN0 的接收 FIFO0 是否有新数据(CAN_FLAG_RFNE0标志位表示接收 FIFO0 非空),如果有新数据,则调用can_message_receive函数读取接收邮箱中的数据,并将其存储到received_data数组中,以便后续对数据进行进一步的分析和处理,例如在一个工业自动化控制系统中,接收到的数据可能是传感器采集的温度、压力等参数值,CPU 可以根据这些数据进行相应的控制决策,如调节电机转速、控制阀门开度等。

七、实战项目应用
汽车仪表盘数据采集:在汽车电子系统中,多个传感器(如车速传感器、水温传感器、油压传感器等)需要将采集到的数据实时传输到仪表盘进行显示。利用 GD32 的 CAN 总线,各传感器作为 CAN 节点,将采集的数据按照 CAN 协议封装成数据帧发送出去。例如,车速传感器可能每隔 100ms 发送一次包含车速信息的 CAN 数据帧,帧中数据字段包含当前车速的数值信息,标识符设置为特定值,如0x400,以便仪表盘上的 GD32 接收端能够识别并接收该数据帧。GD32 作为接收端,接收到这些数据帧后,通过解析数据帧中的数据字段,提取出车速、水温、油压等数值,并在仪表盘上进行精准显示,为驾驶员提供车辆运行状态的实时信息,确保驾驶安全和车辆正常运行。
以下是接收车速数据的代码示例:

void CAN0_RX_IRQHandler(void)
{
    if (can_interrupt_flag_get(CAN0, CAN_INT_FLAG_RBNE))
    {
        can_receive_message_struct receivemsg;
        can_message_receive(CAN0, CAN_FIFO0, &receivemsg);
        if (receivemsg.rx_sfid == 0x400)  // 检查是否为车速数据的 CAN ID
        {
            // 提取车速数据
            speed_data = (receivemsg.rx_data[0] << 8) | receivemsg.rx_data[1];
            // 更新仪表盘显示(假设存在更新显示的函数 update_dashboard)
            update_dashboard(SPEED_DISPLAY_INDEX, speed_data);
        }
        // 清除中断标志位,避免重复进入中断
        can_interrupt_flag_clear(CAN0, CAN_INT_FLAG_RBNE);
    }
}


工业自动化生产线监控:在自动化生产线上,分布着众多的电机、传感器、执行机构等设备。GD32 通过 CAN 总线与这些设备进行通信,收集电机的转速信息、设备的故障信号、物料的位置信息等关键数据。例如,电机控制器可以将电机的实时转速数据通过 CAN 总线发送给 GD32,当电机出现堵转等故障时,发送故障信号帧,其 CAN ID 可以设置为0x500;传感器检测到物料的位置变化后,也将位置信息通过 CAN 总线传输给 GD32,其 CAN ID 为0x600。GD32 接收到这些数据后会立即对数据进行解析和处理。
对于电机转速数据,先从接收到的数据帧中提取出代表转速的数值信息,假设数据存储在received_data数组中,电机转速数据存放在前两个字节,通过以下代码提取:

motor_speed = (received_data[0] << 8) | received_data[1];


然后将转速值与预设的正常转速范围进行比较。若转速超出正常范围,可能意味着电机存在过载、故障或工艺异常等情况,GD32 会通过 CAN 总线向控制中心发送警报信息,其 CAN ID 设为0x700,同时点亮与该电机对应的故障指示灯(假设通过set_led_status函数控制指示灯状态):

if (motor_speed < min_normal_speed || motor_speed > max_normal_speed) {
    // 填充发送邮箱发送警报信息
    can_transmit_message_struct transmitmsg;
    transmitmsg.tx_sfid = 0x700;
    transmitmsg.tx_efid = 0x00;
    transmitmsg.tx_dlen = 8;
    transmitmsg.tx_data[0] = (motor_id << 4) | 0x01;  // 假设电机有唯一 ID,0x01 表示故障类型为转速异常
    can_message_transmit(CAN0, &transmitmsg);
    // 点亮故障指示灯
    set_led_status(motor_id, LED_RED);
}


当接收到物料位置信息时,同样先解析数据,判断物料是否到达预定的加工位置或存储位置。如果物料位置出现偏差,GD32 会向搬运设备(如机械臂或传送带控制器)发送调整指令,通过特定的 CAN ID(例如0x800)和数据格式告知其应如何移动以纠正物料位置:

// 解析物料位置数据
material_position = (received_data[2] << 16) | (received_data[3] << 8) | received_data[4];
if (material_position!= target_position) {
    // 计算位置偏差量
    position_error = material_position - target_position;
    // 填充发送邮箱发送调整指令
    can_transmit_message_struct transmitmsg;
    transmitmsg.tx_sfid = 0x800;
    transmitmsg.tx_efid = 0x00;
    transmitmsg.tx_dlen = 8;
    transmitmsg.tx_data[0] = (direction << 4) | (movement_distance & 0x0F);  // 假设前 4 位表示移动方向,后 4 位表示移动距离编码
    can_message_transmit(CAN0, &transmitmsg);
}


此外,GD32 还会对各类设备的数据进行实时统计和分析,例如计算一段时间内电机的平均转速、物料的平均传输时间等,这些统计数据可以用于优化生产线的整体性能,通过 CAN 总线定期上传至生产管理系统(以0x900为 CAN ID 发送统计报告),为管理人员提供决策依据,以便他们及时调整生产参数、安排设备维护计划等:

// 假设在一个数据采集周期内收集了多个电机转速数据,存储在 motor_speed_array 数组中
average_motor_speed = calculate_average(motor_speed_array, num_speed_samples);
// 填充发送邮箱发送统计报告
can_transmit_message_struct transmitmsg;
transmitmsg.tx_sfid = 0x900;
transmitmsg.tx_efid = 0x00;
transmitmsg.tx_dlen = 8;
transmitmsg.tx_data[0] = (average_motor_speed >> 8) & 0xFF;
transmitmsg.tx_data[1] = average_motor_speed & 0xFF;
// 其他统计数据填充...
can_message_transmit(CAN0, &transmitmsg);


在整个过程中,GD32 充分利用 CAN 总线的高效通信能力和自身强大的处理性能,确保生产线的各个环节能够协调、稳定地运行,及时发现并解决潜在的问题,提高生产效率和产品质量,降低因设备故障或物料流转不畅导致的生产停滞风险,从而实现工业自动化生产线的智能化监控与管理。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/pigliuxu/article/details/144752452

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

7

主题

30

帖子

0

粉丝