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

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

基于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/contentali/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唯一,避免冲突
相关文章
|
4月前
|
数据采集 人工智能 运维
从报警风暴到主动免疫:吉利汽车智能运维落地实践
分享我们和阿里云 STAROps 一起,共建高质量智能运维的三步路径。
437 26
|
4月前
|
设计模式 算法 物联网
【免费开源】STM32矩阵键盘驱动程序:从零搭建4x4键盘扫描与消抖完整实战项目分享
【免费开源】STM32 4×4矩阵键盘驱动,基于HAL库,支持行列/反转双扫描、软件消抖、短按/长按/连按识别及回调机制,兼容F1/F4系列,仅需8个IO,附完整原理、流程图与可移植代码。
|
4月前
|
消息中间件 人工智能 运维
Agentic AICon【智能体基础设施与 AgentOps 专场】精彩回顾 & PPT 下载
Agentic AICon【智能体基础设施与 AgentOps 专场】精彩回顾 & PPT 下载。
|
4月前
|
人工智能 运维 Linux
AI Agent进阶:从OpenClaw迁移至Hermes Agent全教程:安装、配置与实战及避坑指南
在AI智能体快速迭代的2026年,各类开源Agent框架逐步从基础工具调用向自进化、长效运行方向升级。OpenClaw作为早期热门AI智能体,凭借稳定的网关架构、丰富的插件生态收获大量用户,而同生态的**Hermes Agent**凭借独有的自进化学习闭环、分层记忆系统、更高的运行透明度,成为新一代优选框架。不少原有OpenClaw用户希望平滑迁移至Hermes Agent,同时保留历史配置、记忆内容与自定义技能。本文结合官方文档与实测经验,全面讲解两款框架的核心架构差异、前置调研要点、完整安装步骤、一键迁移流程、配置调试、多场景实战以及常见问题排查,全程兼顾零基础使用者与专业开发者,帮助用户
478 0
|
Ubuntu Java Python
ROS2通讯中间件安装与使用
本文记录主要ROS2的安装、如何JAVA和PYTHON对ROS2消息的订阅和发布。
|
运维 监控
浅析SPI与CAN通信
SPI是一种常用的MCU与外设的通信方式,英文全称Serial Peripheral Interface。与之前介绍过的UART不同,SPI是串行,全双工,同步通信方式。SPI通常有4根物理连接线,分别是CS片选,SCK时钟,MOSI主机输出从机输入和MISO主机输入从机输出。CS片选是从机选择信号线,低电平有效。当CS为低电平时认为主机目前选中的本从机。SCK是串行时钟线,同步通信需要主从机时钟同步,主机利用SCK线与从机实现时钟同步。时钟由主机产生,决定了通讯的速率。
1268 0
|
4月前
|
人工智能 安全 搜索推荐
AI 热点驱动的钓鱼攻击形态、危害与全域防御体系研究
本文剖析AI品牌诱饵与AI赋能鱼叉式钓鱼两大新型攻击,揭示其多层跳转、代码签名伪装、生成式内容等手法,并构建“技术防护+流程管控+人员培训+监测响应”五位一体防御体系,辅以Python自动化检测工具验证落地效果。(239字)
173 2
|
4月前
|
运维 API
获取网页重定向地址-网页风险识别API接口介绍
本文介绍网页重定向地址获取API,可完整解析多级跳转链路,精准识别钓鱼、黑产等恶意中转节点,助力反诈监管与金融风控;同时支持研发测试快速排查301/302异常、死循环等问题,接口简洁,易于集成。
415 1
|
4月前
|
存储 人工智能 负载均衡
一人公司技术基建实战:从 0 到 1 用阿里云搭建你的创业装备库
本文以独立开发者视角,详解一人公司从MVP验证、正式发布到增长期的全周期技术基建方案:轻量服务器快速部署AI助手、大模型知识库沉淀经验、创业网盘多端协同、OSS低成本存储、域名+云服务器一体化配置,以及CDN加速与负载均衡平滑扩容。阿里云OPC打包方案助创业者省去选型配置之苦,专注产品本身。
|
4月前
|
消息中间件 缓存 小程序
同城外卖系统开发包含哪些功能模块?一文看懂整体架构
同城外卖系统是集用户下单、商家接单、骑手配送、订单管理、支付结算与运营分析于一体的综合平台。涵盖用户端(浏览/下单/支付)、商家端(商品/订单管理)、骑手端(抢单/导航/收入)、运营后台及核心订单中心五大模块,需结合Redis、消息队列等技术保障高并发稳定运行。

热门文章

最新文章