一、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);
}