汽车CAN总线完全指南:从物理层到应用层的深度解析

入行十年,CAN总线是我接触最多的车载通信协议。从最早在4S店用诊断仪读故障码,到后来自己写飞控的CAN驱动,再到现在做整车电子架构,对CAN的理解也在不断加深。这篇文章我会把CAN总线的方方面面都讲透,争取让看完的人能真正理解CAN是怎么工作的,而不只是会用。

前言

CAN(Controller Area Network)是目前汽车电子中使用最广泛的通信协议,几乎所有的车载ECU都通过CAN总线互联。这篇文章会从最底层的物理信号讲起,一直到上层的应用协议,配合大量代码示例。


一、CAN总线概述与发展历史

1.1 为什么需要CAN总线

在CAN出现之前,汽车电子系统是怎样的?我见过80年代的老车,ECU之间通信靠的是点对点连线。一个信号从A传到B,就拉一根线;B再传给C,再拉一根线。

问题很明显:

  1. 线束爆炸:一辆现代汽车有上百个ECU,点对点连接需要的线束重量能达到几十公斤
  2. 可靠性差:接点多,故障点就多
  3. 扩展困难:加个新功能就得重新布线
  4. 成本高:铜线贵,工时也贵

80年代初,博世(Bosch)的工程师们开始琢磨这个问题。他们的思路是:能不能用一条总线把所有节点串起来,大家共享这条通信线路?

这就是CAN的由来。

1.2 发展历程

年份 里程碑
1983 博世开始CAN项目研发
1986 CAN协议在SAE大会首次公开发表
1987 Intel发布第一款CAN控制器芯片82526
1991 奔驰W140(S级)首次量产搭载CAN总线
1993 ISO 11898标准发布
2003 ISO 11898-1/2/3/4标准体系完善
2012 CAN FD(Flexible Data-rate)发布
2015 CAN FD写入ISO 11898-1

1.3 CAN的技术特点

CAN能成功不是偶然的,它的设计哲学很适合汽车这种应用场景:

多主架构(Multi-Master)

没有主从之分,任何节点都可以主动发送数据。这对汽车太重要了------你不能让发动机ECU等着某个主节点轮询才能报警。

消息导向(Message-Oriented)

CAN不关心"谁发的",只关心"发的是什么"。每条消息有个ID,接收方根据ID决定要不要收。

非破坏性仲裁(Non-Destructive Arbitration)

多个节点同时发送时,ID小的优先,ID大的自动退让。整个过程不会破坏任何数据。

强大的错误处理

CAN的错误检测能力覆盖率超过99.99%。


二、物理层深度解析

2.1 高速CAN(ISO 11898-2)

这是汽车上用得最多的CAN物理层标准。

电气特性

高速CAN用差分信号传输,两根线分别叫CAN_H(High)和CAN_L(Low):

复制代码
显性电平(Dominant,逻辑0):
    CAN_H = 3.5V (典型值,范围2.75V~4.5V)
    CAN_L = 1.5V (典型值,范围0.5V~2.25V)
    差分电压 Vdiff = CAN_H - CAN_L = 2.0V

隐性电平(Recessive,逻辑1):
    CAN_H = 2.5V (典型值,范围2.0V~3.0V)
    CAN_L = 2.5V (典型值,范围2.0V~3.0V)
    差分电压 Vdiff = 0V

共模电压 Vcom = (CAN_H + CAN_L) / 2 = 2.5V

为什么用差分信号?抗干扰能力强。外界干扰通常是共模的,差分接收时会被抵消。

总线拓扑

高速CAN要求线性拓扑(总线型),不支持星型或树型:

复制代码
正确的拓扑(线性):

    ┌─────┐   ┌─────┐   ┌─────┐   ┌─────┐
    │Node1│   │Node2│   │Node3│   │Node4│
    └──┬──┘   └──┬──┘   └──┬──┘   └──┬──┘
       │         │         │         │
    ═══╧═════════╧═════════╧═════════╧═══  CAN总线
    120Ω                              120Ω
    终端电阻                          终端电阻
终端电阻

高速CAN总线两端必须各接一个120Ω电阻。两个120Ω并联等于60Ω。

怎么判断终端电阻是否正确?用万用表测CAN_H和CAN_L之间的电阻:

  • 正常:约60Ω(两个120Ω并联)
  • 缺一个电阻:120Ω
  • 都没有:开路
  • 短接:接近0Ω
传输距离与速率
波特率 最大总线长度 典型应用
1 Mbps 25m 实验室、短距离
500 kbps 100m 动力总成
250 kbps 250m 底盘
125 kbps 500m 车身

2.2 CAN收发器

收发器(Transceiver)是连接CAN控制器和物理总线的桥梁。

常用芯片:

芯片 厂商 类型 特点
TJA1050 NXP 高速 经典款,便宜
TJA1042 NXP 高速 带静默模式
MCP2551 Microchip 高速 兼容5V和3.3V
SN65HVD230 TI 高速 3.3V供电

三、数据链路层详解

3.1 帧类型

CAN定义了四种帧类型:

数据帧(Data Frame)

用于传输数据,是最常用的帧类型。

复制代码
数据帧结构(标准格式):

┌───┬──────────┬───┬───┬───┬──────┬────────────────┬──────────┬─────┬─────┬───────┐
│SOF│Identifier│RTR│IDE│r0 │ DLC  │   Data Field   │   CRC    │ ACK │ EOF │  IFS  │
│   │  11bit   │   │   │   │ 4bit │   0-8 bytes    │  15bit   │ 2bit│ 7bit│ 3bit  │
└───┴──────────┴───┴───┴───┴──────┴────────────────┴──────────┴─────┴─────┴───────┘
 1b     11b      1b  1b  1b   4b       0-64 bits       15+1b     2b    7b    ≥3b

SOF: Start of Frame,帧起始,固定为显性(0)
Identifier: 标识符,决定消息优先级和内容含义
RTR: Remote Transmission Request,0=数据帧,1=远程帧
IDE: Identifier Extension,0=标准格式,1=扩展格式
r0: 保留位,固定为显性(0)
DLC: Data Length Code,数据长度,0-8
Data: 数据字段,0-8字节
CRC: 循环冗余校验,15位+1位界定符
ACK: 应答域,2位(1位槽+1位界定符)
EOF: End of Frame,帧结束,7个隐性位
IFS: Interframe Space,帧间隔,至少3个隐性位

3.2 非破坏性仲裁

当多个节点同时发送时,CAN用"线与"机制进行仲裁:

  • 显性电平(0)会覆盖隐性电平(1)

  • 所有节点都发隐性时,总线才是隐性

    假设三个节点同时发送,ID分别是:
    Node A: 0x100 = 0001 0000 0000
    Node B: 0x123 = 0001 0010 0011
    Node C: 0x0FF = 0000 1111 1111

    仲裁过程(从高位到低位):

    位位置: 10 9 8 7 6 5 4 3 2 1 0
    ────────────────────────────────────────────
    Node A: 0 0 0 1 0 0 0 0 0 0 0
    Node B: 0 0 0 1 0 0 1 0 0 1 1
    Node C: 0 0 0 0 1 1 1 1 1 1 1
    ────────────────────────────────────────────
    总线: 0 0 0 0 ...(Node C获胜)

    Node A、B检测到自己发1但总线是0
    → 仲裁失败,停止发送,等待重试

    结果:ID最小的Node C(0x0FF)赢得仲裁

3.3 位填充(Bit Stuffing)

CAN用位填充来保证足够的信号跳变,用于时钟同步。

规则:发送5个连续相同的位后,自动插入一个相反的位

复制代码
原始数据:    11111 00000 111 00 1111111
填充后:      111110 000001 111 00 111110 11
                 ↑      ↑           ↑
               插入0   插入1       插入0

接收方自动去除填充位,恢复原始数据

3.4 CRC校验

CAN使用15位CRC,生成多项式是:x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1

c 复制代码
// CAN CRC-15计算
uint16_t can_crc15(uint8_t *data, int bits)
{
    uint16_t crc = 0;
    uint16_t polynomial = 0x4599;
    
    for(int i = 0; i < bits; i++) {
        int byte_idx = i / 8;
        int bit_idx = 7 - (i % 8);
        uint8_t bit = (data[byte_idx] >> bit_idx) & 0x01;
        
        uint16_t crc_next = ((crc << 1) | bit) & 0x7FFF;
        
        if(crc & 0x4000) {
            crc_next ^= polynomial;
        }
        
        crc = crc_next;
    }
    
    return crc;
}

四、位时序与同步机制

4.1 位时间结构

CAN是异步通信,没有独立的时钟线,靠位同步来保持收发一致。

一个位时间分成四段:

复制代码
           一个位时间(Bit Time)
├─────────────────────────────────────────────┤
│ SYNC_SEG │ PROP_SEG │ PHASE_SEG1 │ PHASE_SEG2 │
│   1 Tq   │ 1-8 Tq   │  1-8 Tq    │  1-8 Tq    │
│          │          │      ↑     │            │
│          │          │   采样点   │            │

采样点位置很关键,一般设在75%-80%。

4.2 波特率计算

波特率 = f_clk / (BRP × (SYNC + PROP + PHASE1 + PHASE2))

c 复制代码
// 波特率配置计算示例
// 假设CAN时钟 = 36MHz,目标波特率 = 500kbps

// Tq数 = 36MHz / 500kbps / BRP
// 设BRP = 4,则Tq数 = 36M / 500k / 4 = 18
// 分配:SYNC=1, PROP=5, PHASE1=6, PHASE2=6
// 采样点 = (1+5+6)/18 = 66.7%

typedef struct {
    uint16_t brp;       // 预分频 1-1024
    uint8_t  sjw;       // 同步跳转宽度 1-4
    uint8_t  ts1;       // PROP + PHASE1
    uint8_t  ts2;       // PHASE2
} CAN_BitTiming_t;

// 500kbps配置
CAN_BitTiming_t timing_500k = {
    .brp = 4,
    .sjw = 1,
    .ts1 = 11,   // PROP(5) + PHASE1(6)
    .ts2 = 6
};

五、错误处理机制

5.1 错误类型

CAN定义了五种错误:

错误类型 说明
位错误 发送的位和监测到的不一致
填充错误 检测到6个连续相同的位
CRC错误 CRC校验失败
格式错误 固定格式位域检测到非法值
ACK错误 没有收到应答

5.2 错误计数器

每个CAN控制器维护两个错误计数器:

  • TEC(Transmit Error Counter):发送错误计数
  • REC(Receive Error Counter):接收错误计数

5.3 错误状态

复制代码
                       TEC或REC > 127
        ┌──────────┐    ────────────────→    ┌───────────────┐
        │ Error    │                         │    Error      │
        │ Active   │    ←────────────────    │    Passive    │
        │          │     TEC和REC < 128      │               │
        └──────────┘                         └───────┬───────┘
                                                     │ TEC > 255
                                                     ↓
                                             ┌───────────────┐
                                             │   Bus Off     │
                                             └───────────────┘

六、CAN控制器软件实现

6.1 发送函数

c 复制代码
typedef struct {
    uint32_t id;
    uint8_t  ide;           // 0=标准帧,1=扩展帧
    uint8_t  rtr;           // 0=数据帧,1=远程帧
    uint8_t  dlc;
    uint8_t  data[8];
} CAN_TxMessage_t;

int CAN_Transmit(CAN_TypeDef *can, CAN_TxMessage_t *msg)
{
    // 查找空闲邮箱
    uint8_t mailbox = 0xFF;
    uint32_t tsr = can->TSR;
    
    if(tsr & CAN_TSR_TME0) mailbox = 0;
    else if(tsr & CAN_TSR_TME1) mailbox = 1;
    else if(tsr & CAN_TSR_TME2) mailbox = 2;
    else return -1;
    
    CAN_TxMailBox_TypeDef *txbox = &can->sTxMailBox[mailbox];
    
    // 设置ID
    if(msg->ide) {
        txbox->TIR = (msg->id << 3) | CAN_TI0R_IDE;
    } else {
        txbox->TIR = (msg->id << 21);
    }
    
    if(msg->rtr) txbox->TIR |= CAN_TI0R_RTR;
    
    // 设置DLC和数据
    txbox->TDTR = msg->dlc & 0x0F;
    
    txbox->TDLR = ((uint32_t)msg->data[3] << 24) |
                  ((uint32_t)msg->data[2] << 16) |
                  ((uint32_t)msg->data[1] << 8) |
                  msg->data[0];
                  
    txbox->TDHR = ((uint32_t)msg->data[7] << 24) |
                  ((uint32_t)msg->data[6] << 16) |
                  ((uint32_t)msg->data[5] << 8) |
                  msg->data[4];
    
    // 请求发送
    txbox->TIR |= CAN_TI0R_TXRQ;
    
    return mailbox;
}

6.2 接收滤波配置

c 复制代码
typedef struct {
    uint32_t filter_id;
    uint32_t filter_mask;
    uint32_t filter_fifo;
    uint8_t  filter_bank;
    uint8_t  filter_mode;      // 0=掩码模式,1=列表模式
    uint8_t  filter_scale;     // 0=16位,1=32位
    uint8_t  filter_activation;
} CAN_FilterConfig_t;

void CAN_ConfigFilter(CAN_TypeDef *can, CAN_FilterConfig_t *config)
{
    // 进入滤波器初始化模式
    can->FMR |= CAN_FMR_FINIT;
    
    // 关闭滤波器
    can->FA1R &= ~(1 << config->filter_bank);
    
    // 设置滤波器模式
    if(config->filter_mode)
        can->FM1R |= (1 << config->filter_bank);
    else
        can->FM1R &= ~(1 << config->filter_bank);
    
    // 设置滤波器位宽
    if(config->filter_scale)
        can->FS1R |= (1 << config->filter_bank);
    else
        can->FS1R &= ~(1 << config->filter_bank);
    
    // 设置滤波器关联的FIFO
    if(config->filter_fifo)
        can->FFA1R |= (1 << config->filter_bank);
    else
        can->FFA1R &= ~(1 << config->filter_bank);
    
    // 设置滤波器值
    can->sFilterRegister[config->filter_bank].FR1 = config->filter_id;
    can->sFilterRegister[config->filter_bank].FR2 = config->filter_mask;
    
    // 启用滤波器
    if(config->filter_activation)
        can->FA1R |= (1 << config->filter_bank);
    
    // 退出滤波器初始化模式
    can->FMR &= ~CAN_FMR_FINIT;
}

6.3 接收处理

c 复制代码
typedef struct {
    uint32_t id;
    uint8_t  ide;
    uint8_t  rtr;
    uint8_t  dlc;
    uint8_t  data[8];
    uint8_t  fmi;
    uint32_t timestamp;
} CAN_RxMessage_t;

int CAN_Receive(CAN_TypeDef *can, uint8_t fifo, CAN_RxMessage_t *msg)
{
    CAN_FIFOMailBox_TypeDef *rxbox;
    uint32_t rfr;
    
    if(fifo == 0) {
        rfr = can->RF0R;
        rxbox = &can->sFIFOMailBox[0];
    } else {
        rfr = can->RF1R;
        rxbox = &can->sFIFOMailBox[1];
    }
    
    // 检查FIFO是否有消息
    if((rfr & 0x03) == 0) return -1;
    
    // 读取ID
    uint32_t rir = rxbox->RIR;
    msg->ide = (rir & CAN_RI0R_IDE) ? 1 : 0;
    msg->rtr = (rir & CAN_RI0R_RTR) ? 1 : 0;
    
    if(msg->ide)
        msg->id = (rir >> 3) & 0x1FFFFFFF;
    else
        msg->id = (rir >> 21) & 0x7FF;
    
    // 读取DLC和数据
    uint32_t rdtr = rxbox->RDTR;
    msg->dlc = rdtr & 0x0F;
    msg->fmi = (rdtr >> 8) & 0xFF;
    msg->timestamp = (rdtr >> 16) & 0xFFFF;
    
    uint32_t rdlr = rxbox->RDLR;
    uint32_t rdhr = rxbox->RDHR;
    
    msg->data[0] = rdlr & 0xFF;
    msg->data[1] = (rdlr >> 8) & 0xFF;
    msg->data[2] = (rdlr >> 16) & 0xFF;
    msg->data[3] = (rdlr >> 24) & 0xFF;
    msg->data[4] = rdhr & 0xFF;
    msg->data[5] = (rdhr >> 8) & 0xFF;
    msg->data[6] = (rdhr >> 16) & 0xFF;
    msg->data[7] = (rdhr >> 24) & 0xFF;
    
    // 释放FIFO
    if(fifo == 0)
        can->RF0R |= CAN_RF0R_RFOM0;
    else
        can->RF1R |= CAN_RF1R_RFOM1;
    
    return 0;
}

七、消息调度器实现

c 复制代码
#define MAX_TX_MESSAGES     32
#define MAX_RX_MESSAGES     64

typedef struct {
    uint32_t id;
    uint8_t  dlc;
    uint16_t cycle_ms;
    uint16_t offset_ms;
    bool     enabled;
    void     (*pack_func)(uint8_t *data);
} TxMessageConfig_t;

typedef struct {
    uint32_t id;
    uint16_t timeout_ms;
    bool     enabled;
    void     (*unpack_func)(const uint8_t *data, uint8_t dlc);
    void     (*timeout_func)(void);
} RxMessageConfig_t;

typedef struct {
    TxMessageConfig_t config;
    uint32_t last_tx_time;
    uint32_t tx_count;
} TxMessageState_t;

typedef struct {
    RxMessageConfig_t config;
    uint32_t last_rx_time;
    uint32_t rx_count;
    bool     timeout_flag;
    bool     received_once;
} RxMessageState_t;

typedef struct {
    TxMessageState_t tx_messages[MAX_TX_MESSAGES];
    uint8_t tx_count;
    
    RxMessageState_t rx_messages[MAX_RX_MESSAGES];
    uint8_t rx_count;
    
    uint32_t current_time;
} CAN_Scheduler_t;

// 周期调度
void CAN_Scheduler_Tick(CAN_Scheduler_t *scheduler, uint32_t current_time_ms)
{
    scheduler->current_time = current_time_ms;
    
    // 处理发送
    for(int i = 0; i < scheduler->tx_count; i++) {
        TxMessageState_t *tx = &scheduler->tx_messages[i];
        
        if(!tx->config.enabled) continue;
        
        uint32_t elapsed = current_time_ms - tx->last_tx_time;
        if(elapsed >= tx->config.cycle_ms) {
            uint8_t data[8] = {0};
            
            if(tx->config.pack_func)
                tx->config.pack_func(data);
            
            CAN_TxMessage_t msg = {
                .id = tx->config.id,
                .ide = 0,
                .rtr = 0,
                .dlc = tx->config.dlc
            };
            memcpy(msg.data, data, tx->config.dlc);
            
            if(CAN_Transmit(CAN1, &msg) >= 0) {
                tx->last_tx_time = current_time_ms;
                tx->tx_count++;
            }
        }
    }
    
    // 处理接收超时
    for(int i = 0; i < scheduler->rx_count; i++) {
        RxMessageState_t *rx = &scheduler->rx_messages[i];
        
        if(!rx->config.enabled || !rx->received_once) continue;
        
        uint32_t elapsed = current_time_ms - rx->last_rx_time;
        if(elapsed > rx->config.timeout_ms && !rx->timeout_flag) {
            rx->timeout_flag = true;
            if(rx->config.timeout_func)
                rx->config.timeout_func();
        }
    }
}

八、ISO-TP传输层

ISO 15765-2定义了CAN上的传输层协议,用于传输超过8字节的数据:

c 复制代码
// 帧类型
typedef enum {
    ISOTP_SF = 0,       // Single Frame
    ISOTP_FF = 1,       // First Frame
    ISOTP_CF = 2,       // Consecutive Frame
    ISOTP_FC = 3        // Flow Control
} ISOTP_FrameType_t;

// 发送数据
int ISOTP_Transmit(ISOTP_Session_t *session, const uint8_t *data, uint16_t len)
{
    if(len == 0 || session->state != ISOTP_IDLE) return -1;
    
    memcpy(session->tx_buffer, data, len);
    session->tx_len = len;
    session->tx_offset = 0;
    session->tx_sn = 1;
    
    uint8_t frame[8] = {0};
    
    if(len <= 7) {
        // 单帧
        frame[0] = (ISOTP_SF << 4) | len;
        memcpy(&frame[1], data, len);
        session->can_send(session->config.tx_id, frame, 8);
    } else {
        // 首帧
        frame[0] = (ISOTP_FF << 4) | ((len >> 8) & 0x0F);
        frame[1] = len & 0xFF;
        memcpy(&frame[2], data, 6);
        
        session->tx_offset = 6;
        session->state = ISOTP_WAIT_FC;
        
        session->can_send(session->config.tx_id, frame, 8);
    }
    
    return 0;
}

九、CAN FD简介

CAN FD是CAN 2.0的升级版:

特性 CAN 2.0 CAN FD
数据场波特率 最高1Mbps 最高8Mbps
数据长度 0-8字节 0-64字节
CRC 15位 17/21位
c 复制代码
// CAN FD DLC映射
uint8_t dlc_to_bytes(uint8_t dlc)
{
    const uint8_t map[] = {0,1,2,3,4,5,6,7,8,12,16,20,24,32,48,64};
    return map[dlc & 0x0F];
}

uint8_t bytes_to_dlc(uint8_t bytes)
{
    if(bytes <= 8) return bytes;
    if(bytes <= 12) return 9;
    if(bytes <= 16) return 10;
    if(bytes <= 20) return 11;
    if(bytes <= 24) return 12;
    if(bytes <= 32) return 13;
    if(bytes <= 48) return 14;
    return 15;
}

十、调试与故障排查

10.1 常见问题排查

问题 可能原因 排查方法
完全不通信 接线错误、终端电阻、波特率 万用表测电阻,示波器看波形
偶发丢帧 干扰、采样点、总线负载 调整采样点,计算负载率
Bus Off 严重错误累积 检查物理层,实现自动恢复

10.2 调试代码

c 复制代码
void CAN_DebugPrint(const char *prefix, uint32_t id, bool is_ext,
                    const uint8_t *data, uint8_t dlc)
{
    printf("%s %s 0x%08X [%d] ", prefix, is_ext ? "EXT" : "STD", id, dlc);
    for(int i = 0; i < dlc; i++)
        printf("%02X ", data[i]);
    printf("\n");
}

void CAN_PrintStatus(CAN_TypeDef *can)
{
    uint32_t esr = can->ESR;
    uint8_t tec = (esr >> 16) & 0xFF;
    uint8_t rec = (esr >> 24) & 0xFF;
    
    printf("TEC: %d, REC: %d, State: ", tec, rec);
    
    if(esr & CAN_ESR_BOFF) printf("BUS OFF\n");
    else if(esr & CAN_ESR_EPVF) printf("Error Passive\n");
    else if(esr & CAN_ESR_EWGF) printf("Error Warning\n");
    else printf("OK\n");
}

总结

这篇文章从物理层到应用层,把CAN总线的方方面面都讲了一遍。核心要点:

  1. 物理层:差分信号、终端电阻、收发器选型
  2. 数据链路层:帧结构、非破坏性仲裁、位填充
  3. 位时序:采样点配置是通信稳定的关键
  4. 错误处理:五种错误类型、三种错误状态
  5. 软件协议栈:分层架构、消息调度、ISO-TP传输

CAN虽然是30多年前的技术了,但在汽车电子领域依然是主力。希望这篇文章对你有帮助。


参考资料

  1. ISO 11898-1:2015 CAN数据链路层和物理信令
  2. ISO 11898-2:2016 CAN高速物理层
  3. ISO 15765-2 诊断通信传输层
  4. Bosch CAN Specification 2.0
  5. STM32F4 参考手册 - bxCAN章节

这篇文章写了好几个晚上,希望能帮到正在学习CAN的朋友。有问题欢迎评论区讨论!

相关推荐
ws20190716 小时前
智行未来,科技驱动:AUTO TECH China 2026广州展将于11月27日举办!
人工智能·科技·汽车
寰宇视讯1 天前
邦邦汽服携手吉利循环产业中心解锁汽车后市场绿色循环新路径
汽车
中科米堆1 天前
汽车制造厂采用自动化三维扫描系统,将抽检升级为全检-中科米堆CASAIM
运维·自动化·汽车·3d全尺寸检测
IAR Systems1 天前
SiFive车规级RISC-V IP获IAR最新版嵌入式开发工具全面支持,加速汽车电子创新
汽车·risc-v
应用市场1 天前
汽车CAN总线隔离设计与故障诊断:从原理到代码实战
开发语言·汽车·无人机
ASD123asfadxv2 天前
基于YOLO11的汽车车灯状态识别与分类_C3k2-wConv改进_1
分类·数据挖掘·汽车
雨大王5122 天前
汽车零部件检测的未来:全尺寸、全链条、全生命周期管理
汽车
LCG米2 天前
高可靠性汽车电子设计:基于英飞凌AURIX TC4x的ASIL-D域控制器开发实战
汽车
虹科Pico汽车示波器2 天前
汽车免拆诊断案例 | 本田Insight混合动力系统冷却风扇故障深度解析
汽车·三相电机·汽车示波器·本田insight·混动车·冷却风扇故障·毫欧与电机测试仪