基于STM32F105系列使用CAN总线实现双机通信代码

基于STM32F105系列使用CAN总线实现双机通信代码,包含初始化、发送和接收功能。这里使用标准外设库,并假设使用CAN1。

1. CAN总线初始化

#include "stm32f10x.h"
#include "stm32f10x_can.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "misc.h"

// CAN初始化结构体
CAN_InitTypeDef        CAN_InitStructure;
CAN_FilterInitTypeDef  CAN_FilterInitStructure;
CanTxMsg TxMessage;
CanRxMsg RxMessage;

void CAN_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    // 使能GPIO和CAN时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
    
    // 配置CAN引脚: PA11->RX, PA12->TX
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;  // TX
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 推挽复用输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;  // RX
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;  // 上拉输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void CAN_NVIC_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    
    // 配置CAN接收中断
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    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);
}

void CAN_Mode_Config(void)
{
    CAN_DeInit(CAN1);
    CAN_StructInit(&CAN_InitStructure);
    
    // CAN工作模式配置
    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;  // 禁止FIFO锁定
    CAN_InitStructure.CAN_TXFP = DISABLE;  // 禁止发送FIFO优先级
    
    // 配置波特率
    // CAN波特率 = APB1时钟 / ((CAN_BS1 + CAN_BS2 + 1) * CAN_Prescaler)
    // 假设APB1时钟为36MHz,波特率设为500kbps
    CAN_InitStructure.CAN_BS1 = CAN_BS1_9tq;  // 时间段1
    CAN_InitStructure.CAN_BS2 = CAN_BS2_4tq;  // 时间段2
    CAN_InitStructure.CAN_Prescaler = 6;      // 分频系数
    // 总时间片 = 1 + 9 + 4 = 14
    // 波特率 = 36MHz / (14 * 6) = 428.57kHz (接近500k)
    
    CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;  // 正常工作模式
    
    CAN_Init(CAN1, &CAN_InitStructure);
    
    // 配置CAN过滤器
    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高位
    CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;   // 要过滤的ID低位
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;  // 掩码高位
    CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;   // 掩码低位
    CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0;  // 关联到FIFO0
    CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;  // 启用过滤器
    CAN_FilterInit(&CAN_FilterInitStructure);
    
    // 使能CAN接收中断
    CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
}

2. CAN数据发送函数

uint8_t CAN_Send_Msg(uint8_t* msg, uint8_t len, uint32_t id)
{
    uint8_t i = 0;
    uint8_t transmit_mailbox = 0;
    
    TxMessage.StdId = id;               // 标准ID
    TxMessage.ExtId = 0x00;             // 扩展ID
    TxMessage.IDE = CAN_Id_Standard;    // 标准帧
    TxMessage.RTR = CAN_RTR_Data;       // 数据帧
    TxMessage.DLC = len;                // 数据长度(最大8字节)
    
    // 填充数据
    for(i = 0; i < len; i++)
    {
        TxMessage.Data[i] = msg[i];
    }
    
    // 发送数据
    transmit_mailbox = CAN_Transmit(CAN1, &TxMessage);
    
    // 等待发送完成
    i = 0;
    while((CAN_TransmitStatus(CAN1, transmit_mailbox) != CANTXOK) && (i < 0xFF))
    {
        i++;
    }
    
    if(i >= 0xFF)
    {
        return 1;  // 发送失败
    }
    
    return 0;  // 发送成功
}

3. CAN数据接收中断服务函数

void USB_LP_CAN1_RX0_IRQHandler(void)
{
    static uint8_t receive_buffer[8];
    
    if(CAN_GetITStatus(CAN1, CAN_IT_FMP0) != RESET)
    {
        // 读取接收到的数据
        CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
        
        // 清除中断标志
        CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0);
        
        // 处理接收到的数据
        if(RxMessage.IDE == CAN_Id_Standard)
        {
            uint8_t i;
            
            // 复制接收到的数据
            for(i = 0; i < RxMessage.DLC; i++)
            {
                receive_buffer[i] = RxMessage.Data[i];
            }
            
            // 在这里处理接收到的数据
            // 例如:将数据发送到串口或进行其他处理
            // Process_Received_Data(receive_buffer, RxMessage.DLC, RxMessage.StdId);
        }
    }
}

4. 主函数示例

int main(void)
{
    uint8_t send_data[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
    uint32_t message_id = 0x123;  // CAN消息ID
    
    // 初始化系统时钟
    SystemInit();
    
    // 配置GPIO
    CAN_GPIO_Config();
    
    // 配置NVIC中断
    CAN_NVIC_Config();
    
    // 配置CAN模式
    CAN_Mode_Config();
    
    // 主循环
    while(1)
    {
        // 每隔1秒发送一次数据
        Delay(1000);  // 自定义延时函数
        
        // 发送CAN消息
        CAN_Send_Msg(send_data, 8, message_id);
        
        // 可以在这里添加其他处理逻辑
    }
}

参考代码 stm32f105系列使用can总线实现双机通信源代码 www.youwenfan.com/contentcsv/70917.html

5. 双机通信配置说明

设备A配置(发送设备):

// 设置设备A的ID为0x100
uint32_t device_a_id = 0x100;

设备B配置(接收设备):

// 配置过滤器只接收来自设备A的消息
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0100 << 5;  // ID高16位
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;       // ID低16位
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0xFFFF;   // 掩码高位
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;    // 掩码低位

6. 完整的工程配置步骤

  1. 硬件连接

    • CAN_H 连接 CAN_H
    • CAN_L 连接 CAN_L
    • 两个设备的GND相连
    • 在总线的两端接120Ω终端电阻
  2. 软件配置

    // 波特率计算
    // 假设系统时钟为72MHz,APB1时钟为36MHz
    // 500kbps配置:BS1=9, BS2=4, Prescaler=6
    // 1Mbps配置:BS1=9, BS2=4, Prescaler=3
    
  3. 编译和下载

    • 使用Keil MDK或STM32CubeIDE
    • 确保包含正确的头文件
    • 配置正确的系统时钟

注意事项

  1. 终端电阻:必须在CAN总线的两端各接一个120Ω的终端电阻
  2. 波特率:两个设备的波特率必须相同
  3. 中断优先级:根据实际应用需求合理配置中断优先级
  4. 错误处理:在实际应用中应添加错误检测和重发机制
  5. ID冲突:确保每个设备的CAN ID唯一,避免冲突

专注于matlab/simulink,电子电路,编程