STM32F107 CAN 接收发送中断函数

一、CAN 外设配置(STM32F107)

1.1 引脚配置

引脚 功能 模式 备注
PA11 CAN_RX 浮空输入/上拉输入 连接到 CAN 收发器 RX
PA12 CAN_TX 复用推挽输出 连接到 CAN 收发器 TX
PD0 CAN_RX_Remap 重映射 RX 备用引脚
PD1 CAN_TX_Remap 重映射 TX 备用引脚

1.2 时钟配置

// 系统时钟配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);

// 如果需要重映射
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap1_CAN1, ENABLE);  // 重映射到 PD0/PD1

二、完整 CAN 中断配置代码

2.1 头文件定义

// can.h
#ifndef __CAN_H
#define __CAN_H

#include "stm32f10x.h"
#include <stdint.h>
#include <stdbool.h>

// CAN 消息结构
typedef struct {
    uint32_t id;        // 帧 ID
    uint8_t  data[8];   // 数据
    uint8_t  len;       // 数据长度
    uint8_t  format;    // 0=标准帧, 1=扩展帧
    uint8_t  type;      // 0=数据帧, 1=远程帧
} CAN_Message;

// CAN 初始化结构
typedef struct {
    uint32_t baudrate;   // 波特率
    uint8_t  mode;       // 工作模式
    uint8_t  sjw;        // 同步跳转宽度
    uint8_t  bs1;        // BS1 时间段
    uint8_t  bs2;        // BS2 时间段
} CAN_Config;

// 波特率定义
#define CAN_BAUDRATE_125K  125000
#define CAN_BAUDRATE_250K  250000
#define CAN_BAUDRATE_500K  500000
#define CAN_BAUDRATE_1M    1000000

// 工作模式
#define CAN_MODE_NORMAL    0
#define CAN_MODE_LOOPBACK  1
#define CAN_MODE_SILENT    2
#define CAN_MODE_SILENT_LOOPBACK 3

// 错误代码
#define CAN_ERROR_NONE     0
#define CAN_ERROR_INIT     1
#define CAN_ERROR_TX       2
#define CAN_ERROR_RX       3
#define CAN_ERROR_BUSOFF   4

// 函数声明
void CAN_Init(uint32_t baudrate, uint8_t mode);
void CAN_DeInit(void);
uint8_t CAN_SendMessage(CAN_Message *msg);
uint8_t CAN_ReceiveMessage(CAN_Message *msg);
void CAN_SetFilter(uint32_t filter_id, uint32_t mask);
void CAN_EnableInterrupts(void);
void CAN_DisableInterrupts(void);
void CAN_IRQHandler(void);
uint8_t CAN_GetStatus(void);
void CAN_Reset(void);

// 回调函数类型
typedef void (*CAN_RxCallback_t)(CAN_Message *msg);
typedef void (*CAN_TxCallback_t)(uint8_t status);
typedef void (*CAN_ErrorCallback_t)(uint8_t error);

// 回调函数设置
void CAN_SetRxCallback(CAN_RxCallback_t callback);
void CAN_SetTxCallback(CAN_TxCallback_t callback);
void CAN_SetErrorCallback(CAN_ErrorCallback_t callback);

#endif

2.2 CAN 初始化函数

// can.c
#include "can.h"
#include "gpio.h"
#include "nvic.h"

// 全局变量
volatile uint8_t can_error = CAN_ERROR_NONE;
volatile uint8_t tx_mailbox_full = 0;
volatile uint8_t rx_message_available = 0;

// 接收/发送缓冲区
static CAN_Message rx_buffer[10];
static uint8_t rx_head = 0, rx_tail = 0;
static uint8_t rx_count = 0;

// 回调函数
static CAN_RxCallback_t rx_callback = NULL;
static CAN_TxCallback_t tx_callback = NULL;
static CAN_ErrorCallback_t error_callback = NULL;

// CAN 初始化
void CAN_Init(uint32_t baudrate, uint8_t mode)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    CAN_InitTypeDef CAN_InitStructure;
    CAN_FilterInitTypeDef CAN_FilterInitStructure;
    
    // 1. 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
    
    // 2. 配置 CAN 引脚
    // PA11 -> CAN_RX
    // PA12 -> CAN_TX
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;  // 上拉输入
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 3. CAN 参数初始化
    CAN_DeInit();
    CAN_StructInit(&CAN_InitStructure);
    
    // 工作模式
    switch(mode) {
        case CAN_MODE_NORMAL:
            CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
            break;
        case CAN_MODE_LOOPBACK:
            CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack;
            break;
        case CAN_MODE_SILENT:
            CAN_InitStructure.CAN_Mode = CAN_Mode_Silent;
            break;
        case CAN_MODE_SILENT_LOOPBACK:
            CAN_InitStructure.CAN_Mode = CAN_Mode_Silent_LoopBack;
            break;
        default:
            CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
    }
    
    // 波特率计算
    // 系统时钟 36MHz, APB1 36MHz, CAN 时钟 36MHz
    // 波特率 = 1 / (Tsync_seg + Tseg1 + Tseg2) * 分频系数
    // 这里使用 500kbps
    uint8_t prescaler = 0;
    
    switch(baudrate) {
        case 125000:  // 125kbps
            prescaler = 12;
            break;
        case 250000:  // 250kbps
            prescaler = 6;
            break;
        case 500000:  // 500kbps
            prescaler = 3;
            break;
        case 1000000:  // 1Mbps
            prescaler = 2;
            break;
        default:  // 默认 500kbps
            prescaler = 3;
    }
    
    CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;        // 同步跳转宽度 1Tq
    CAN_InitStructure.CAN_BS1 = CAN_BS1_9tq;        // BS1 时间段 9Tq
    CAN_InitStructure.CAN_BS2 = CAN_BS2_4tq;        // BS2 时间段 4Tq
    CAN_InitStructure.CAN_Prescaler = prescaler;    // 分频系数
    CAN_InitStructure.CAN_TTCM = DISABLE;           // 禁止时间触发通信模式
    CAN_InitStructure.CAN_ABOM = ENABLE;            // 自动离线管理
    CAN_InitStructure.CAN_AWUM = ENABLE;            // 自动唤醒模式
    CAN_InitStructure.CAN_NART = DISABLE;           // 禁止报文自动重传
    CAN_InitStructure.CAN_RFLM = DISABLE;           // 接收 FIFO 不锁定
    CAN_InitStructure.CAN_TXFP = DISABLE;           // 发送 FIFO 优先级由标识符决定
    
    if (CAN_Init(CAN1, &CAN_InitStructure) != CAN_InitStatus_Failed) {
        can_error = CAN_ERROR_NONE;
    } else {
        can_error = CAN_ERROR_INIT;
        if (error_callback) error_callback(CAN_ERROR_INIT);
        return;
    }
    
    // 4. 配置过滤器
    CAN_FilterInitStructure.CAN_FilterNumber = 0;           // 过滤器0
    CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;  // 掩码模式
    CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; // 32位
    CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;      // ID 高16位
    CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;       // ID 低16位
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;  // 掩码高16位
    CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;   // 掩码低16位
    CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0;  // 使用 FIFO0
    CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;  // 激活过滤器
    CAN_FilterInit(&CAN_FilterInitStructure);
    
    // 5. 清空 FIFO
    CAN_FIFOMessagePending(CAN1, CAN_FIFO0) == 0;
    
    // 6. 初始化接收缓冲区
    rx_head = 0;
    rx_tail = 0;
    rx_count = 0;
    
    // 7. 配置 NVIC
    NVIC_Config();
    
    printf("CAN Initialized: %d bps, Mode: %d\r\n", baudrate, mode);
}

// NVIC 配置
void NVIC_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    
    // 配置 CAN 接收中断
    NVIC_InitStructure.NVIC_IRQChannel = USB_HP_CAN1_TX_IRQn;  // CAN1 TX 中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    // 配置 CAN 发送中断
    NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;  // CAN1 RX0 中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    // 配置 CAN 错误中断
    NVIC_InitStructure.NVIC_IRQChannel = CAN1_SCE_IRQn;  // CAN1 错误中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

// 使能中断
void CAN_EnableInterrupts(void)
{
    // 使能接收中断
    CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);  // FIFO0 消息挂起中断
    
    // 使能发送中断
    CAN_ITConfig(CAN1, CAN_IT_TME, ENABLE);   // 发送邮箱空中断
    
    // 使能错误中断
    CAN_ITConfig(CAN1, CAN_IT_EWG, ENABLE);   // 错误警告中断
    CAN_ITConfig(CAN1, CAN_IT_EPV, ENABLE);   // 错误被动中断
    CAN_ITConfig(CAN1, CAN_IT_BOF, ENABLE);   // 离线中断
    CAN_ITConfig(CAN1, CAN_IT_LEC, ENABLE);   // 上次错误码中断
    CAN_ITConfig(CAN1, CAN_IT_ERR, ENABLE);   // 错误中断
    
    printf("CAN Interrupts Enabled\r\n");
}

// 禁止中断
void CAN_DisableInterrupts(void)
{
    CAN_ITConfig(CAN1, CAN_IT_FMP0, DISABLE);
    CAN_ITConfig(CAN1, CAN_IT_TME, DISABLE);
    CAN_ITConfig(CAN1, CAN_IT_EWG, DISABLE);
    CAN_ITConfig(CAN1, CAN_IT_EPV, DISABLE);
    CAN_ITConfig(CAN1, CAN_IT_BOF, DISABLE);
    CAN_ITConfig(CAN1, CAN_IT_LEC, DISABLE);
    CAN_ITConfig(CAN1, CAN_IT_ERR, DISABLE);
    
    printf("CAN Interrupts Disabled\r\n");
}

2.3 CAN 发送函数(中断方式)

// CAN 发送消息
uint8_t CAN_SendMessage(CAN_Message *msg)
{
    CanTxMsg TxMessage;
    uint8_t transmit_mailbox = 0;
    
    if (msg == NULL || msg->len > 8) {
        can_error = CAN_ERROR_TX;
        if (error_callback) error_callback(CAN_ERROR_TX);
        return 1;
    }
    
    // 配置发送消息
    if (msg->format == 0) {
        TxMessage.StdId = msg->id;      // 标准 ID
        TxMessage.IDE = CAN_ID_STD;     // 标准帧
    } else {
        TxMessage.ExtId = msg->id;      // 扩展 ID
        TxMessage.IDE = CAN_ID_EXT;     // 扩展帧
    }
    
    TxMessage.RTR = (msg->type == 1) ? CAN_RTR_REMOTE : CAN_RTR_DATA;  // 远程帧/数据帧
    TxMessage.DLC = msg->len;           // 数据长度
    
    // 复制数据
    for (int i = 0; i < msg->len; i++) {
        TxMessage.Data[i] = msg->data[i];
    }
    
    // 发送消息
    transmit_mailbox = CAN_Transmit(CAN1, &TxMessage);
    
    if (transmit_mailbox == CAN_TxStatus_NoMailBox) {
        // 邮箱已满
        tx_mailbox_full = 1;
        can_error = CAN_ERROR_TX;
        if (error_callback) error_callback(CAN_ERROR_TX);
        return 2;
    }
    
    return 0;  // 成功
}

// 发送完成回调函数
static void CAN_TxCompleteCallback(uint8_t mailbox)
{
    if (tx_callback) {
        tx_callback(0);  // 发送成功
    }
    
    // 检查是否还有消息要发送
    if (tx_mailbox_full) {
        tx_mailbox_full = 0;
        // 这里可以重新发送之前失败的消息
    }
}

2.4 CAN 接收函数(中断方式)

// 从接收缓冲区读取消息
uint8_t CAN_ReceiveMessage(CAN_Message *msg)
{
    if (rx_count == 0 || msg == NULL) {
        return 1;  // 缓冲区空
    }
    
    // 从缓冲区读取
    *msg = rx_buffer[rx_tail];
    
    // 更新缓冲区指针
    rx_tail = (rx_tail + 1) % 10;
    rx_count--;
    
    return 0;  // 成功
}

// 接收中断处理函数
static void CAN_RxInterruptHandler(void)
{
    CanRxMsg RxMessage;
    
    // 从 FIFO0 读取消息
    CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
    
    // 检查接收缓冲区是否有空间
    if (rx_count >= 10) {
        // 缓冲区满,丢弃最旧的消息
        rx_tail = (rx_tail + 1) % 10;
        rx_count--;
    }
    
    // 保存到接收缓冲区
    CAN_Message msg;
    
    if (RxMessage.IDE == CAN_ID_STD) {
        msg.id = RxMessage.StdId;
        msg.format = 0;  // 标准帧
    } else {
        msg.id = RxMessage.ExtId;
        msg.format = 1;  // 扩展帧
    }
    
    msg.type = (RxMessage.RTR == CAN_RTR_REMOTE) ? 1 : 0;  // 远程帧/数据帧
    msg.len = RxMessage.DLC;
    
    for (int i = 0; i < msg.len; i++) {
        msg.data[i] = RxMessage.Data[i];
    }
    
    rx_buffer[rx_head] = msg;
    rx_head = (rx_head + 1) % 10;
    rx_count++;
    
    // 设置消息可用标志
    rx_message_available = 1;
    
    // 调用回调函数
    if (rx_callback) {
        rx_callback(&msg);
    }
    
    // 清除挂起标志
    CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0);
}

2.5 完整 CAN 中断服务函数

// CAN 中断服务函数
void CAN_IRQHandler(void)
{
    uint32_t it_status = 0;
    
    // 1. 检查接收中断
    it_status = CAN_GetITStatus(CAN1, CAN_IT_FMP0);
    if (it_status != RESET) {
        CAN_RxInterruptHandler();
    }
    
    // 2. 检查发送中断
    it_status = CAN_GetITStatus(CAN1, CAN_IT_TME);
    if (it_status != RESET) {
        uint8_t mailbox = 0;
        
        // 检查哪个邮箱发送完成
        if (CAN_TransmitStatus(CAN1, CAN_TxMailbox_0) == CANTXOK) {
            mailbox = 0;
        } else if (CAN_TransmitStatus(CAN1, CAN_TxMailbox_1) == CANTXOK) {
            mailbox = 1;
        } else if (CAN_TransmitStatus(CAN1, CAN_TxMailbox_2) == CANTXOK) {
            mailbox = 2;
        }
        
        CAN_TxCompleteCallback(mailbox);
        CAN_ClearITPendingBit(CAN1, CAN_IT_TME);
    }
    
    // 3. 检查错误中断
    it_status = CAN_GetITStatus(CAN1, CAN_IT_EWG);
    if (it_status != RESET) {
        can_error = CAN_GetLastErrorCode(CAN1);
        if (error_callback) error_callback(can_error);
        CAN_ClearITPendingBit(CAN1, CAN_IT_EWG);
    }
    
    it_status = CAN_GetITStatus(CAN1, CAN_IT_EPV);
    if (it_status != RESET) {
        can_error = CAN_GetLastErrorCode(CAN1);
        if (error_callback) error_callback(can_error);
        CAN_ClearITPendingBit(CAN1, CAN_IT_EPV);
    }
    
    it_status = CAN_GetITStatus(CAN1, CAN_IT_BOF);
    if (it_status != RESET) {
        can_error = CAN_ERROR_BUSOFF;
        if (error_callback) error_callback(CAN_ERROR_BUSOFF);
        
        // 尝试恢复总线
        CAN_Reset();
        CAN_ClearITPendingBit(CAN1, CAN_IT_BOF);
    }
    
    it_status = CAN_GetITStatus(CAN1, CAN_IT_LEC);
    if (it_status != RESET) {
        can_error = CAN_GetLastErrorCode(CAN1);
        if (error_callback) error_callback(can_error);
        CAN_ClearITPendingBit(CAN1, CAN_IT_LEC);
    }
    
    it_status = CAN_GetITStatus(CAN1, CAN_IT_ERR);
    if (it_status != RESET) {
        can_error = CAN_GetLastErrorCode(CAN1);
        if (error_callback) error_callback(can_error);
        CAN_ClearITPendingBit(CAN1, CAN_IT_ERR);
    }
}

// 总线恢复函数
void CAN_Reset(void)
{
    CAN_DeInit();
    
    // 重新初始化
    CAN_InitTypeDef CAN_InitStructure;
    
    CAN_InitStructure.CAN_TTCM = DISABLE;
    CAN_InitStructure.CAN_ABOM = ENABLE;
    CAN_InitStructure.CAN_AWUM = ENABLE;
    CAN_InitStructure.CAN_NART = DISABLE;
    CAN_InitStructure.CAN_RFLM = DISABLE;
    CAN_InitStructure.CAN_TXFP = DISABLE;
    CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
    CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
    CAN_InitStructure.CAN_BS1 = CAN_BS1_9tq;
    CAN_InitStructure.CAN_BS2 = CAN_BS2_4tq;
    CAN_InitStructure.CAN_Prescaler = 3;  // 500kbps
    
    CAN_Init(CAN1, &CAN_InitStructure);
    
    printf("CAN Bus Reset Complete\r\n");
}

2.6 回调函数设置

// 设置接收回调函数
void CAN_SetRxCallback(CAN_RxCallback_t callback)
{
    rx_callback = callback;
}

// 设置发送回调函数
void CAN_SetTxCallback(CAN_TxCallback_t callback)
{
    tx_callback = callback;
}

// 设置错误回调函数
void CAN_SetErrorCallback(CAN_ErrorCallback_t callback)
{
    error_callback = callback;
}

// 获取 CAN 状态
uint8_t CAN_GetStatus(void)
{
    return can_error;
}

三、应用示例

3.1 主程序示例

// main.c
#include "stm32f10x.h"
#include "can.h"
#include "usart.h"
#include "led.h"
#include <stdio.h>

// 定义 LED
#define LED1_ON()    GPIO_ResetBits(GPIOC, GPIO_Pin_13)
#define LED1_OFF()   GPIO_SetBits(GPIOC, GPIO_Pin_13)
#define LED1_TOGGLE() GPIO_WriteBit(GPIOC, GPIO_Pin_13, 
                          (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_13)))

// 接收回调函数
void MyCAN_RxCallback(CAN_Message *msg)
{
    static uint32_t rx_count = 0;
    
    rx_count++;
    
    printf("Received CAN Message [%lu]:\r\n", rx_count);
    printf("  ID: 0x%03X, Format: %s, Type: %s\r\n", 
           msg->id,
           msg->format ? "Extended" : "Standard",
           msg->type ? "Remote" : "Data");
    printf("  Length: %d, Data: ", msg->len);
    
    for (int i = 0; i < msg->len; i++) {
        printf("%02X ", msg->data[i]);
    }
    printf("\r\n");
    
    // 收到消息闪烁 LED
    LED1_TOGGLE();
}

// 发送回调函数
void MyCAN_TxCallback(uint8_t status)
{
    if (status == 0) {
        printf("Message sent successfully\r\n");
    } else {
        printf("Message send failed, error: %d\r\n", status);
    }
}

// 错误回调函数
void MyCAN_ErrorCallback(uint8_t error)
{
    printf("CAN Error: ");
    switch(error) {
        case CAN_ERROR_NONE:
            printf("No Error\r\n");
            break;
        case CAN_ERROR_INIT:
            printf("Initialization Error\r\n");
            break;
        case CAN_ERROR_TX:
            printf("Transmit Error\r\n");
            break;
        case CAN_ERROR_RX:
            printf("Receive Error\r\n");
            break;
        case CAN_ERROR_BUSOFF:
            printf("Bus Off Error\r\n");
            break;
        default:
            printf("Unknown Error (%d)\r\n", error);
    }
}

// 初始化 LED
void LED_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    
    LED1_OFF();
}

int main(void)
{
    // 系统初始化
    SystemInit();
    
    // 初始化外设
    LED_Init();
    UART_Init(115200);
    
    printf("STM32F107 CAN Interrupt Example\r\n");
    printf("System Clock: %d Hz\r\n", SystemCoreClock);
    
    // 初始化 CAN
    CAN_Init(CAN_BAUDRATE_500K, CAN_MODE_NORMAL);
    
    // 设置回调函数
    CAN_SetRxCallback(MyCAN_RxCallback);
    CAN_SetTxCallback(MyCAN_TxCallback);
    CAN_SetErrorCallback(MyCAN_ErrorCallback);
    
    // 使能中断
    CAN_EnableInterrupts();
    
    printf("CAN Ready. Press any key to send test message...\r\n");
    
    uint32_t send_counter = 0;
    
    while (1) {
        // 检查串口输入
        if (UART_Available()) {
            uint8_t ch = UART_Receive();
            
            if (ch == 's' || ch == 'S') {
                // 发送测试消息
                CAN_Message test_msg;
                
                test_msg.id = 0x123;  // 标准 ID
                test_msg.format = 0;  // 标准帧
                test_msg.type = 0;    // 数据帧
                test_msg.len = 8;     // 8字节数据
                
                // 填充数据
                for (int i = 0; i < 8; i++) {
                    test_msg.data[i] = (send_counter + i) & 0xFF;
                }
                
                // 发送消息
                uint8_t result = CAN_SendMessage(&test_msg);
                
                if (result == 0) {
                    printf("Sending message %lu...\r\n", ++send_counter);
                } else {
                    printf("Send failed with code: %d\r\n", result);
                }
            }
        }
        
        // 主循环任务
        Delay_ms(10);
    }
}

3.2 中断向量表配置

// stm32f10x_it.c
#include "stm32f10x_it.h"

// 声明外部函数
extern void CAN_IRQHandler(void);

// USB 高优先级 CAN 发送中断
void USB_HP_CAN1_TX_IRQHandler(void)
{
    // 调用 CAN 中断处理函数
    CAN_IRQHandler();
}

// USB 低优先级 CAN 接收中断
void USB_LP_CAN1_RX0_IRQHandler(void)
{
    // 调用 CAN 中断处理函数
    CAN_IRQHandler();
}

// CAN1 错误中断
void CAN1_SCE_IRQHandler(void)
{
    // 调用 CAN 中断处理函数
    CAN_IRQHandler();
}

四、调试和故障排除

4.1 调试函数

// can_debug.c
#include "can.h"
#include "usart.h"

// 打印 CAN 状态
void CAN_PrintStatus(void)
{
    uint8_t status = CAN_GetStatus();
    
    printf("CAN Status: ");
    switch(status) {
        case CAN_ERROR_NONE:
            printf("OK\r\n");
            break;
        case CAN_ERROR_INIT:
            printf("Initialization Error\r\n");
            break;
        case CAN_ERROR_TX:
            printf("Transmit Error\r\n");
            break;
        case CAN_ERROR_RX:
            printf("Receive Error\r\n");
            break;
        case CAN_ERROR_BUSOFF:
            printf("Bus Off\r\n");
            break;
        default:
            printf("Unknown (%d)\r\n", status);
    }
    
    // 读取 CAN 错误计数器
    uint8_t rec = CAN_GetReceiveErrorCounter(CAN1);
    uint8_t tec = CAN_GetLSBTransmitErrorCounter(CAN1);
    
    printf("Receive Error Counter: %d\r\n", rec);
    printf("Transmit Error Counter: %d\r\n", tec);
    
    // 读取 CAN 错误状态
    uint32_t esr = CAN1->ESR;
    
    printf("Error Status Register: 0x%08lX\r\n", esr);
    printf("  REC: %lu\r\n", (esr >> 24) & 0xFF);
    printf("  TEC: %lu\r\n", (esr >> 16) & 0xFF);
    printf("  LEC: %lu\r\n", (esr >> 4) & 0x7);
    
    if (esr & CAN_ESR_BOFF) {
        printf("  Bus Off State\r\n");
    }
    if (esr & CAN_ESR_EPVF) {
        printf("  Error Passive\r\n");
    }
    if (esr & CAN_ESR_EWGF) {
        printf("  Error Warning\r\n");
    }
}

// 打印 CAN 寄存器
void CAN_PrintRegisters(void)
{
    printf("CAN Registers:\r\n");
    printf("  MCR: 0x%08lX\r\n", CAN1->MCR);
    printf("  MSR: 0x%08lX\r\n", CAN1->MSR);
    printf("  TSR: 0x%08lX\r\n", CAN1->TSR);
    printf("  RF0R: 0x%08lX\r\n", CAN1->RF0R);
    printf("  RF1R: 0x%08lX\r\n", CAN1->RF1R);
    printf("  IER: 0x%08lX\r\n", CAN1->IER);
    printf("  ESR: 0x%08lX\r\n", CAN1->ESR);
    printf("  BTR: 0x%08lX\r\n", CAN1->BTR);
}

4.2 测试程序

// test_can.c
#include "can.h"
#include "usart.h"
#include "delay.h"

void Test_CAN_SendReceive(void)
{
    CAN_Message tx_msg, rx_msg;
    uint8_t result;
    
    printf("Starting CAN Test...\r\n");
    
    // 发送消息
    tx_msg.id = 0x100;
    tx_msg.format = 0;
    tx_msg.type = 0;
    tx_msg.len = 8;
    
    for (int i = 0; i < 8; i++) {
        tx_msg.data[i] = i;
    }
    
    printf("Sending test message...\r\n");
    result = CAN_SendMessage(&tx_msg);
    
    if (result == 0) {
        printf("Message sent, waiting for response...\r\n");
        
        // 等待接收
        for (int i = 0; i < 100; i++) {
            if (CAN_ReceiveMessage(&rx_msg) == 0) {
                printf("Received response!\r\n");
                return;
            }
            Delay_ms(10);
        }
        
        printf("No response received\r\n");
    } else {
        printf("Send failed: %d\r\n", result);
    }
}

参考代码 STM32F107的CAN接收发送中断函数 www.youwenfan.com/contentcnv/103206.html

五、常见问题解决

问题 可能原因 解决方案
无中断触发 中断未使能/NVIC 未配置 检查 CAN_EnableInterrupts()NVIC_Config()
接收不到数据 过滤器配置错误 检查过滤器 ID 和掩码设置
发送失败 邮箱已满/总线错误 检查错误计数器,降低发送频率
总线关闭 错误过多 调用 CAN_Reset() 恢复
数据错误 波特率不匹配 检查两端 CAN 波特率设置

六、性能优化建议

6.1 使用 DMA 接收

// 配置 CAN DMA
void CAN_DMA_Config(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    
    // 使能 DMA 时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
    // 配置 DMA
    DMA_DeInit(DMA1_Channel5);  // CAN1_RX 使用 DMA1_Channel5
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&CAN1->sFIFOMailBox[CAN_FIFO0];
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)rx_buffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = 10;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel5, &DMA_InitStructure);
    
    // 使能 DMA
    DMA_Cmd(DMA1_Channel5, ENABLE);
    
    // 配置 CAN 使用 DMA
    CAN_DMACmd(CAN1, CAN_DMAReq_FMP0, ENABLE);
}

6.2 中断优先级优化

// 优化中断优先级
void CAN_OptimizeIRQPriority(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    
    // 接收中断优先级最高
    NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    // 发送中断次高
    NVIC_InitStructure.NVIC_IRQChannel = USB_HP_CAN1_TX_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_Init(&NVIC_InitStructure);
    
    // 错误中断最低
    NVIC_InitStructure.NVIC_IRQChannel = CAN1_SCE_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_Init(&NVIC_InitStructure);
}
posted @ 2026-06-04 09:39  lingxingqi  阅读(10)  评论(0)    收藏  举报