自学内容网 自学内容网

STM32F4分别驱动SN65HVD230和TJA1050进行CAN通信

一、CAN、SN65HVD230DR

二、TJA1050

1、TJA1050 特性

  • TJA1050 是一款 CAN 总线收发器芯片,输入电平与 3.3 V 设备兼容。
  • TJA1050 专为数据速率高达 1Mbps 的应用而设计。
    线路的信号传输速率是指每秒钟的电压转换次数,单位为 bps(每秒比特数)。
  • TJA1050 控制器局域网(CAN)收发器完全兼容 ISO 11898 标准。
  • TJA1050 至少允许 110 个节点连接到总线,未上电的节点不会干扰到总线。
  • TJA1050 集成了过热保护和过流保护功能。
    • 如果结温超过约 165 ℃,过热保护电路通过关闭发射器来保护芯片免受损坏。当引脚 TXD 变为高电平时,发射器关闭状态会重置。当总线短路时,热保护电路尤其需要。
    • 限流电路可保护芯片发射器免受意外短路至正或负电源造成的损坏,在这种故障情况下过流保护电路会限制流过的最大电流直到短路情况解除。

2、TJA1050 引脚说明

引脚名称引脚说明
TXDCAN 收发器的发送输入端,通常连接到主控 MCU 的 CAN TX 引脚
RXDCAN 收发器的接收输出端,通常连接到主控 MCU 的 CAN RX 引脚
V r e f V_{ref} VrefVCC/2 参考输出引脚
STJA1050 的模式选择引脚
CANLCAN_Low 信号线,与 CAN_High 共同构成一组差分信号线
CANHCAN_High 信号线,与 CAN_Low 共同构成一组差分信号线
VCC电源正极,芯片需要 5V 电源进行供电
GND电源负极,芯片需要 5V 电源进行供电
  • TXD 引脚是用于发送数据到 CAN 总线。
    • 当 MCU 在 CAN_TX 引脚输出逻辑信号时,逻辑信号会传递到 TXD 引脚,然后 TJA1050 将逻辑信号转化为 CAN 总线的差分信号(CANH 和 CANL),最后通过 CAN 总线发送出去。
  • RXD 引脚用于接收来自 CAN 总线的数据。
    • CAN 总线的差分信号(CANH 和 CANL)被 TJA1050 接收后,差分信号(CANH 和 CANL)被转换为单端逻辑信号,输出到 RXD 引脚,供 MCU 的 CAN_RX 引脚读取。
  • TJA1050 通过 S 引脚可以设置芯片处于两种不同的工作模式:高速模式和静音模式。
    • 正常模式:该工作模式下 CAN 总线驱动器和接收器正常工作,CAN 总线双向通信。
    • 静音模式:该工作模式下 CAN 驱动器关闭,CAN 接收器保持有效,RXD 可以正常输出接收到的总线数据。
  • V r e f V_{ref} Vref 引脚输出 VCC/2 的参考电压,可连接到终端网络中的共模点,以帮助进一步稳定总线的共模电压。
    • 如果不使用 V r e f V_{ref} Vref 引脚,可将其悬空。

三、硬件设计

1、接线说明

在这里插入图片描述

2、TJA1050 模块

  • 模块的 S 引脚接地,模块处于高速模式。
  • 模块的 V r e f V_{ref} Vref 引脚悬空。
  • TJA1050 模块是一个 CAN 收发器模块,不包含内置的 CAN 控制器,因此无法独立实现 CAN 数据的接收和发送。
    模块上的 RX 和 TX 引脚并非串口的 TX 和 RX 引脚,因此不能用于实现串口 TTL 转 CAN 的功能。

在这里插入图片描述

3、SN65HVD230 模块

  • 模块的 R S R_{S} RS 引脚与地之间串联一个 1K 阻值的电阻,模块处于斜率控制模式。
  • 模块的 V r e f V_{ref} Vref 引脚悬空。
  • SN65HVD230 模块是一个 CAN 收发器模块,不包含内置的 CAN 控制器,因此无法独立实现 CAN 数据的接收和发送。
    模块上的 RX 和 TX 引脚并非串口的 TX 和 RX 引脚,因此不能用于实现串口 TTL 转 CAN 的功能。

在这里插入图片描述

四、程序设计

1、CAN_Init:CAN 外设初始化函数

  • 函数中,sjw、pres、ts1、ts2 这四个参数共同决定 CAN 的波特率。
    • Fpclk:CAN 控制器的输入时钟频率,在 STM32 上通常为 APB1 时钟频率。
    • pres:波特率分频器,决定时间量化单元(Time Quantum,简称 tq)的长度;tq 越长,波特率越低。
    • ts1:时间段 1(Time Segment 1),由若干时间量化单元组成;包括传播时间段(Propagation Segment)和相位缓冲段 1(Phase Segment 1),决定信号从发送到接收节点传播的时间。
    • ts2:时间段 2(Time Segment 2),由若干时间量化单元组成;包括相位缓冲段 2(Phase Segment 2),用于对信号采样点后的波动进行调整。
    • +1:同步段的时间量化单元,始终固定为 1 个 tq,用于同步接收节点与发送节点的时钟。
    • tq 是 CAN 定时的基本单位,其大小由 Fpclk 和 pres 决定:tq = pres * tpclk;(tpclk = 1 / Fpclk)。
  • CAN 波特率公式:波特率 = Fpclk / ((ts1 + ts2 + 1) * pres)
    • 设置 Fpclk = 42Mhz、pres = 6、ts1 = 6、ts2 = 7,则CAN波特率为:42M / ((6 + 7 + 1) * 6) = 500Kbps。
uint8_t CAN_Init(CAN_TypeDef *can, uint32_t mode, uint32_t sjw, uint32_t pres, uint32_t ts1, uint32_t ts2)
{
    /* 设置 CAN 驱动的基本参数 */
    can_handle_struct.Instance = can;                           /* 指定要使用的 CAN 外设 */
    can_handle_struct.Init.Prescaler = pres;                    /* 设置预分频器,控制 CAN 通信速率 */
    can_handle_struct.Init.Mode = mode;                         /* 设置 CAN 模式:标准模式/回环模式 */
    can_handle_struct.Init.SyncJumpWidth = sjw;                 /* 设置同步跳距,提升数据同步性能 */
    can_handle_struct.Init.TimeSeg1 = ts1;                      /* 设置时间段 1,调节通信准确性 */
    can_handle_struct.Init.TimeSeg2 = ts2;                      /* 设置时间段 2,调节通信稳定性 */
    can_handle_struct.Init.TimeTriggeredMode = DISABLE;         /* 禁用时间触发模式,提高灵活性 */
    can_handle_struct.Init.AutoBusOff = DISABLE;                /* 禁用自动总线关闭功能 */
    can_handle_struct.Init.AutoWakeUp = DISABLE;                /* 禁用自动唤醒功能 */
    can_handle_struct.Init.AutoRetransmission = DISABLE;        /* 禁用自动重发功能 */
    can_handle_struct.Init.ReceiveFifoLocked = DISABLE;         /* 禁用接收 FIFO 锁定,允许溢出覆盖 */
    can_handle_struct.Init.TransmitFifoPriority = DISABLE;      /* 禁用发送 FIFO 优先级,按先进先出顺序发送 */
    
    /* 初始化 CAN 外设 */
    if(HAL_CAN_Init(&can_handle_struct) != HAL_OK)              /* HAL函数,实现CAN驱动初始化 */
    {
        printf("HAL_CAN_Init ERROR\r\n");                       /* 如果初始化失败,打印错误信息 */
        return 1;                                               /* 返回错误代码 1 */
    }
    else
    {
        printf("HAL_CAN_Init OK\r\n");                          /* 初始化成功,打印成功信息 */
    }
    
    CAN_FilterTypeDef can_filter_struct;                            /* 定义 CAN 过滤器结构体,用于配置接收消息的过滤规则 */
    
    can_filter_struct.FilterIdHigh = 0x0000;                        /* 过滤器高位ID */
    can_filter_struct.FilterIdLow = 0x0000;                         /* 过滤器低位ID */
    can_filter_struct.FilterMaskIdHigh = 0x0000;                    /* 设置过滤器掩码的高位 */
    can_filter_struct.FilterMaskIdLow = 0x0000;                     /* 设置过滤器掩码的低位 */
    
    can_filter_struct.FilterFIFOAssignment = CAN_FILTER_FIFO0;      /* 指定过滤后的数据存入 FIFO0 */
    can_filter_struct.FilterBank = 0;                               /* 指定过滤器的过滤组序号 */
    can_filter_struct.FilterMode = CAN_FILTERMODE_IDMASK;           /* 使用 ID 掩码模式,通配 ID 匹配规则 */
    can_filter_struct.FilterScale = CAN_FILTERSCALE_32BIT;          /* 设置过滤器为 32 位模式 */
    can_filter_struct.FilterActivation = CAN_FILTER_ENABLE;         /* 启用过滤器 */
    can_filter_struct.SlaveStartFilterBank = 14;                    /* 配置从机过滤器的起始序号 */
    
    /* 配置 CAN 接收过滤器,过滤器用于筛选接收到的 CAN 消息,避免处理不需要的消息。 */
    if(HAL_CAN_ConfigFilter(&can_handle_struct, &can_filter_struct) != HAL_OK)  /* 调用 HAL 库函数配置过滤器,判断 CAN 接收过滤器是否成功配置 */
    {
        printf("HAL_CAN_ConfigFilter ERROR\r\n");                               /* 如果配置失败,打印错误信息 */
        return 2;                                                               /* 返回错误代码 2 */
    }
    else
    {
        printf("HAL_CAN_ConfigFilter OK\r\n");                                  /* 配置成功,打印成功信息 */
    }
    
    /* 启动 CAN 外设 */
    if(HAL_CAN_Start(&can_handle_struct) != HAL_OK)                             /* 调用 HAL 库函数启动 CAN 外设,判断 CAN 外设是否成功启动 */
    {
        printf("HAL_CAN_Start ERROR\r\n");                                      /* 如果启动失败,打印错误信息 */
        return 3;                                                               /* 返回错误代码 3 */
    }
    else
    {
        printf("HAL_CAN_Start OK\r\n");                                         /* 启动成功,打印成功信息 */
    }
    
    return 0;                                                                   /* 返回 0,表示初始化成功 */
}

/**
 * @brief       CAN底层驱动函数:引脚配置,时钟配置
                此函数会被HAL_CAN_Init()调用
 * @param       hcan:CAN句柄
 * @retval      无
 */
void HAL_CAN_MspInit(CAN_HandleTypeDef *hcan)
{
    if(CAN1 == hcan->Instance)                              /* 判断是否为 CAN1 外设实例 */
    {
        CAN1_RX_GPIO_CLK_ENABLE();                          /* 启用 CAN1 接收(RX)引脚的时钟 */
        CAN1_TX_GPIO_CLK_ENABLE();                          /* 启用 CAN1 发送(TX)引脚的时钟 */
        CAN1_CLK_ENABLE();                                  /* 启用 CAN1 外设的时钟 */
        
        GPIO_InitTypeDef gpio_init_struct;                  /* 定义 GPIO 初始化结构体,用于配置 CAN 引脚 */
        
        gpio_init_struct.Pin = CAN1_TX_GPIO_PIN;            /* 设置要配置的 GPIO 引脚为 CAN1 TX 引脚 */
        gpio_init_struct.Mode = GPIO_MODE_AF_OD;            /* 配置为复用功能,开漏模式;开漏输出便于支持总线型通信 */
        gpio_init_struct.Pull = GPIO_NOPULL;                /* 无上下拉电阻;CAN 通信本身对上下拉没有硬性要求,由硬件电路决定是否需要。 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;      /* 高速模式;保证高频信号传输的质量 */
        gpio_init_struct.Alternate = GPIO_AF9_CAN1;         /* 设置引脚的复用功能为 CAN1 */
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);            /* 初始化 CAN1 TX 引脚 */
        
        gpio_init_struct.Pin = CAN1_RX_GPIO_PIN;            /* 设置要配置的 GPIO 引脚为 CAN1 RX 引脚 */
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);            /* 初始化 CAN1 RX 引脚 */
    }
}

2、CAN_Send_Msg、CAN_Receive_Msg

/**
 * @brief       CAN 发送一组数据
 * @param       Id          : ID(帧标识符)
 * @param       IDE         : 标识符类型 (标准帧/扩展帧)
 * @param       RTR         : 帧类型 (数据帧/远程帧)
 * @param       DLC         : 数据长度
 * @param       msg         : 数据指针,指向要发送的数据
 * @param       TxMailBox   : 发送邮箱
 * @retval      发送状态
 *  @arg        0, 发送成功; 
 *  @arg        1, 发送失败。
 */
uint8_t CAN_Send_Msg(uint32_t Id, uint32_t IDE, uint32_t RTR, uint32_t DLC, uint8_t *msg, uint32_t TxMailBox)
{
    uint16_t t = 0;     /* 定义超时计数器,用于检测发送是否超时 */
    
    can_txhandle_struct.StdId = Id;     /* 设置标准标识符 (11 位 ID),仅在标准帧模式下有效 */
    can_txhandle_struct.ExtId = Id;     /* 设置扩展标识符 (29 位 ID),仅在扩展帧模式下有效 */
    can_txhandle_struct.IDE = IDE;      /* 设置标识符类型:标准帧 (0) 或扩展帧 (1) */
    can_txhandle_struct.RTR = RTR;      /* 设置帧类型:数据帧 (0) 或远程帧 (1) */
    can_txhandle_struct.DLC = DLC;      /* 设置帧数据长度,有效值为 0-8 */
    
    /* 将待发送的数据和帧信息添加到 CAN 控制器的发送邮箱 */
    if(HAL_CAN_AddTxMessage(&can_handle_struct, &can_txhandle_struct, msg, &TxMailBox) != HAL_OK)
    {
        return 1;       /* 如果添加到发送邮箱失败,返回 1 表示发送失败 */
    }
    
    /* 等待发送完成,循环检测发送邮箱是否已空闲。
       CAN 控制器的邮箱有 3 个,每个邮箱的空闲状态由寄存器标志位控制。 */
    while(HAL_CAN_GetTxMailboxesFreeLevel(&can_handle_struct) != 3)
    {
        t++;    /* 每次循环递增超时计数器 */
        
        if(t > 0xFFF)   /* 如果超时计数器超过预设阈值 (0xFFF),则认为发送超时 */
        {
            HAL_CAN_AbortTxRequest(&can_handle_struct, TxMailBox);      /* 超时,直接中止邮箱的发送请求,释放邮箱 */
            printf("HAL_CAN_AbortTxRequest Time out\r\n");              /* 打印提示信息,通知超时 */
            return 1;                                                   /* 返回 1,表示发送失败 */
        }
    }
    return 0;   /* 返回 0,表示发送成功 */
}

/** 
 * @brief       CAN 接收数据查询 
 * @param       Id      : ID(帧标识符) 
 * @param       IDE     :标识符类型 (标准帧/扩展帧)
 * @param       RTR     :帧类型 (数据帧/远程帧)
 * @param       buf     : 数据缓存区,用于存储接收到的数据 
 * @retval      接收结果 
 *  @arg        0   , 无数据被接收到; 
 *  @arg        其他, 接收的数据长度。
 */
uint8_t CAN_Receive_Msg(uint32_t Id, uint32_t IDE, uint32_t RTR, uint8_t *buf)
{
    /* 检查 CAN 接收 FIFO0 是否为空,即是否有数据可接收。
    HAL_CAN_GetRxFifoFillLevel 函数返回 FIFO 中的帧数,如果为 0,表示没有数据可接收。 */
    if(HAL_CAN_GetRxFifoFillLevel(&can_handle_struct, CAN_RX_FIFO0) == 0)   /* 没有接收到数据 */
    {
        return 0;    /* 如果接收FIFO为空,则直接返回0,表示无数据 */
    }
    
    /* 从 CAN 的接收 FIFO0 中读取一帧数据。
       HAL_CAN_GetRxMessage 用于接收一帧 CAN 消息。如果返回值不是 HAL_OK,表示读取失败。 */
    if (HAL_CAN_GetRxMessage(&can_handle_struct, CAN_RX_FIFO0, &can_rxhandle_struct, buf) != HAL_OK)    /* 读取数据 */
    {
        return 0;   /* 如果读取数据失败,返回0,表示接收失败 */
    }

    /* 检查接收到的帧是否符合预期的 ID、标识符类型和帧类型 */
    if (can_rxhandle_struct.StdId!= Id || can_rxhandle_struct.IDE != IDE || can_rxhandle_struct.RTR != RTR)     /* 接收到的ID不对 / 不是标准帧 / 不是数据帧 */
    {
        return 0;   /* 如果帧不符合条件,返回0,表示不符合预期的接收条件 */ 
    }
     return can_rxhandle_struct.DLC;    /* DLC 表示接收到的数据长度,返回值大于0时表示接收成功 */
}

五、功能展示

1、接线图

  • 两个开发板烧录同一个程序。

在这里插入图片描述

2、CAN 数据收发测试

  • 第一个 STM32F407ZGT6 开发板:PA11 连接 SN65HVD230 模块的 RX,PA12 连接 SN65HVD230 模块的 TX。
  • 测试步骤:
    1. CAN1 上电默认为回环模式。
    2. 通过连接 PA0 引脚的 WKUP 按键设置 CAN1 的工作模式(正常模式 / 回环模式)。
    3. 通过连接 PB9 引脚的 K0 按键控制数据的发送。

在这里插入图片描述

  • 第二个 STM32F407ZGT6 开发板:PA11 连接 TJA1050 模块的 RX,PA12 连接 TJA1050 模块的 TX。
  • 测试步骤:
    1. CAN1 上电默认为回环模式。
    2. 通过连接 PA0 引脚的按键设置 CAN1 的工作模式(正常模式 / 回环模式)。
    3. 通过连接 PB9 引脚的按键控制数据的发送。

在这里插入图片描述

六、总结

  1. 每个 CAN 节点都包含 CAN 控制器以及 CAN 收发器。
  2. SN65HVD230 模块是一个 CAN 收发器模块,不包含内置的 CAN 控制器,因此无法独立实现 CAN 数据的接收和发送。
    模块上的 RX 和 TX 引脚并非串口的 TX 和 RX 引脚,因此不能用于实现串口 TTL 转 CAN 的功能。

原文地址:https://blog.csdn.net/qq_42250136/article/details/145050349

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!