全部例程

CAN - 控制器局域网络接口

W55MH32

更新于 2026年8月12日

本篇总结

使用 CAN,收发数据帧并通过中断打印报文。

🔑 KEIL MDK、UART调试、CAN

W55MH32L 内置 1 个 CAN 控制器,支持 CAN 2.0A/B 协议标准,最高速率 1Mbps。本示例演示 CAN 控制器的初始化、过滤器配置,每秒发送数据帧,接收中断打印报文内容。

前置工具准备

开始本例程前,请确认以下工具已安装:

  • WIZ UartTool V1.0:串口收发测试工具:点击下载

硬件连接

本例程使用 CAN1 的 TX(PA12) 和 RX(PA11) 引脚。在环回模式下无需外部 CAN 收发器即可自测。如需连接外部 CAN 总线,需通过 CAN 收发器芯片(如 TJA1050)连接到 CANH/CANL 总线。USART1 用于输出调试信息。

引脚功能方向模式
PA12CAN1_TX开发板 → 收发器复用推挽输出 (AF_PP)
PA11CAN1_RX收发器 → 开发板上拉输入 (IPU)
PA9USART1_TX开发板 → PC复用推挽输出 (AF_PP)
PA10USART1_RXPC → 开发板浮空输入 (IN_FLOATING)

工程结构

工程位于 SDK 内 1.SDK\ModuleDemo\CAN\,核心文件如下:

CAN/
├── USER/
│   ├── main.c            ← CAN初始化 + 收发测试 + printf重定向
│   ├── w55mh32_conf.h    ← 外设头文件集中包含
│   ├── w55mh32_it.c/h    ← 中断服务函数(CAN接收中断)
│   ├── system_w55mh32.c/h← 系统初始化(时钟配置)
│   └── startup_w55mh32.s ← 启动文件
└── SYSTEM/
    └── delay/
        └── delay.c/h      ← SysTick 延时函数

步骤1:CAN 初始化与过滤器配置

函数:CAN_Configuration() — 配置 PA12(TX)/PA11(RX) 引脚、CAN 波特率为 1Mbps、过滤器接收所有标准帧。


void CAN_Configuration(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    CAN_InitTypeDef   CAN_InitStructure;
    CAN_FilterInitTypeDef CAN_FilterInitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);

    /* PA12: CAN_TX → 复用推挽输出 */
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* PA11: CAN_RX → 上拉输入 */
    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* CAN 基本参数:1Mbps / 正常模式 */
    CAN_InitStructure.CAN_TTCM      = DISABLE;
    CAN_InitStructure.CAN_ABOM      = DISABLE;
    CAN_InitStructure.CAN_AWUM      = DISABLE;
    CAN_InitStructure.CAN_NART      = DISABLE;
    CAN_InitStructure.CAN_RFLM      = DISABLE;
    CAN_InitStructure.CAN_TXFP      = DISABLE;
    CAN_InitStructure.CAN_Mode      = CAN_Mode_Normal;
    /* 波特率 = 36MHz / (6*(1+3+2)) = 36MHz/36 = 1Mbps */
    CAN_InitStructure.CAN_SJW       = CAN_SJW_1tq;
    CAN_InitStructure.CAN_BS1       = CAN_BS1_3tq;
    CAN_InitStructure.CAN_BS2       = CAN_BS2_2tq;
    CAN_InitStructure.CAN_Prescaler = 6;
    CAN_Init(CAN1, &CAN_InitStructure);

    /* 过滤器:接收所有标准帧 */
    CAN_FilterInitStructure.CAN_FilterNumber         = 1;
    CAN_FilterInitStructure.CAN_FilterMode           = CAN_FilterMode_IdMask;
    CAN_FilterInitStructure.CAN_FilterScale          = CAN_FilterScale_32bit;
    CAN_FilterInitStructure.CAN_FilterIdHigh         = 0x0000;
    CAN_FilterInitStructure.CAN_FilterIdLow          = 0x0000;
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh     = 0x0000;
    CAN_FilterInitStructure.CAN_FilterMaskIdLow      = 0x0000;
    CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0;
    CAN_FilterInitStructure.CAN_FilterActivation     = ENABLE;
    CAN_FilterInit(&CAN_FilterInitStructure);
}
  

波特率计算公式:CAN_BaudRate = APB1_CLK / (Prescaler × (1 + BS1 + BS2))。APB1 = 36MHz,Prescaler=6,BS1=3tq,BS2=2tq,则波特率 = 36M / (6 × (1+3+2)) = 36M / 36 = 1Mbps。过滤器设置为 IdMask 模式且掩码全 0,表示接收所有 ID 的报文。

如果你想自定义

  • 改波特率 → 修改 CAN_PrescalerCAN_BS1CAN_BS2 参数
  • 改工作模式CAN_Mode_LoopBack(环回自测)、CAN_Mode_Silent(静默监听)、CAN_Mode_Normal(正常通信)
  • 过滤特定ID → 设置 CAN_FilterIdHigh/Low 和掩码,只接收指定 ID 的报文

步骤2:CAN 发送与接收中断

CAN_Send() 构造标准数据帧并发送,USB_LP_CAN1_RX0_IRQHandler() 在接收 FIFO0 有报文时触发中断,读取并打印报文内容。


/* CAN 发送函数 */
void CAN_Send(uint8_t *data, uint8_t len)
{
    CanTxMsg TxMessage;
    TxMessage.StdId = 0x321;
    TxMessage.IDE   = CAN_ID_STD;
    TxMessage.RTR   = CAN_RTR_DATA;
    TxMessage.DLC   = len;
    for (uint8_t i = 0; i < len; i++)
        TxMessage.Data[i] = data[i];
    CAN_Transmit(CAN1, &TxMessage);
}

/* CAN 接收中断服务函数 */
void USB_LP_CAN1_RX0_IRQHandler(void)
{
    CanRxMsg RxMessage;
    if (CAN_GetITStatus(CAN1, CAN_IT_FMP0) != RESET)
    {
        CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
        printf("CAN Recv: ID=0x%X, DLC=%d, Data=", RxMessage.StdId, RxMessage.DLC);
        for (uint8_t i = 0; i < RxMessage.DLC; i++)
            printf("%02X ", RxMessage.Data[i]);
        printf("\n");
    }
}
  

发送时构造 CanTxMsg 结构体设置 ID(0x321)、帧类型(标准数据帧)、数据长度(8字节)和数据内容,调用 CAN_Transmit() 发送。接收中断通过 CAN_Receive() 读取 FIFO0 中的报文。

步骤3:主函数逻辑

函数:main() — 初始化 CAN 和串口,使能接收中断,每秒发送一帧数据并递增首字节。


int main(void)
{
    RCC_ClocksTypeDef clocks;
    delay_init();
    UART_Configuration(115200);
    RCC_GetClocksFreq(&clocks);
    printf("CAN LoopBack Test.\n");

    CAN_Configuration();
    CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);

    /* NVIC 配置 */
    NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);

    uint8_t sendData[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
    while (1)
    {
        CAN_Send(sendData, 8);
        printf("CAN Send: ID=0x321, Data=");
        for (uint8_t i = 0; i < 8; i++)
            printf("%02X ", sendData[i]);
        printf("\n");
        delay_ms(1000);
        sendData[0]++;
    }
}
  

主循环每秒发送一帧 8 字节数据帧(ID=0x321),首字节递增。在环回模式下,发送的报文会被自身接收,触发接收中断打印收到的数据。

编译与下载

Keil MDK 中打开 CAN.uvprojxF7 编译,确认 0 Error(s)。连接 DEBUG USB 口到 PC,F8 下载。

运行验证

打开 WIZ UartTool V1.0,波特率 115200 / 8N1。下载固件并按 RESET 后,串口每秒打印发送和接收的 CAN 报文。在环回模式下,发送的数据会被自身接收,发送和接收内容一致。如使用 CAN 分析仪软件,可看到总线上的数据帧。

CAN通信串口输出及分析仪界面
CAN 通信测试结果:串口打印发送/接收报文,CAN 分析仪显示总线数据帧

常见问题

Q: CAN 通信无数据收发

确认 CAN 模式设置正确。环回模式(LoopBack)下无需外部收发器即可自测。正常模式(Normal)下必须连接 CAN 收发器芯片和终端电阻(120Ω)。检查波特率参数是否与总线一致。

Q: 接收中断不触发

确认过滤器配置正确(掩码全 0 表示接收所有帧)。检查 NVIC 中断是否使能(CAN_IT_FMP0),以及中断服务函数名是否正确(USB_LP_CAN1_RX0_IRQHandler)。

Q: 波特率不匹配导致通信失败

CAN 波特率 = APB1_CLK / (Prescaler × (1 + BS1 + BS2))。确保所有节点使用相同的波特率。W55MH32L 的 APB1 时钟默认 36MHz,可通过 RCC_GetClocksFreq 确认。

总结

本文基于 W55MH32L 的 CAN 控制器实现了数据帧的发送与中断接收:

  • CAN 初始化:配置 PA12(TX)/PA11(RX) 引脚,波特率 1Mbps,正常/环回模式可切换
  • 过滤器配置:IdMask 模式,掩码全 0 接收所有 ID 的标准数据帧
  • 每秒发送 8 字节数据帧(ID=0x321),接收中断中读取并打印报文内容
  • 支持 CAN 2.0A/B 协议标准,适用于汽车电子和工业控制领域的高可靠性通信

下载本章例程

我们提供完整的工程文件以及配套开发板,方便你随时测试,快速完成产品开发:

开发环境: Keil MDK5 配套开发板

Andy

W5500 技术支持专家