一、CAN、SN65HVD230DR
二、TJA1050
1、TJA1050 特性
- TJA1050 是一款 CAN 总线收发器芯片,输入电平与 3.3 V 设备兼容。
- TJA1050 专为数据速率高达 1Mbps 的应用而设计。
线路的信号传输速率是指每秒钟的电压转换次数,单位为 bps(每秒比特数)。 - TJA1050 控制器局域网(CAN)收发器完全兼容 ISO 11898 标准。
- TJA1050 至少允许 110 个节点连接到总线,未上电的节点不会干扰到总线。
- TJA1050 集成了过热保护和过流保护功能。
- 如果结温超过约 165 ℃,过热保护电路通过关闭发射器来保护芯片免受损坏。当引脚 TXD 变为高电平时,发射器关闭状态会重置。当总线短路时,热保护电路尤其需要。
- 限流电路可保护芯片发射器免受意外短路至正或负电源造成的损坏,在这种故障情况下过流保护电路会限制流过的最大电流直到短路情况解除。
2、TJA1050 引脚说明
引脚名称 | 引脚说明 |
---|
TXD | CAN 收发器的发送输入端,通常连接到主控 MCU 的 CAN TX 引脚 |
RXD | CAN 收发器的接收输出端,通常连接到主控 MCU 的 CAN RX 引脚 |
V
r
e
f
V_{ref}
Vref | VCC/2 参考输出引脚 |
S | TJA1050 的模式选择引脚 |
CANL | CAN_Low 信号线,与 CAN_High 共同构成一组差分信号线 |
CANH | CAN_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_handle_struct.Instance = can;
can_handle_struct.Init.Prescaler = pres;
can_handle_struct.Init.Mode = mode;
can_handle_struct.Init.SyncJumpWidth = sjw;
can_handle_struct.Init.TimeSeg1 = ts1;
can_handle_struct.Init.TimeSeg2 = ts2;
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;
can_handle_struct.Init.TransmitFifoPriority = DISABLE;
if(HAL_CAN_Init(&can_handle_struct) != HAL_OK)
{
printf("HAL_CAN_Init ERROR\r\n");
return 1;
}
else
{
printf("HAL_CAN_Init OK\r\n");
}
CAN_FilterTypeDef can_filter_struct;
can_filter_struct.FilterIdHigh = 0x0000;
can_filter_struct.FilterIdLow = 0x0000;
can_filter_struct.FilterMaskIdHigh = 0x0000;
can_filter_struct.FilterMaskIdLow = 0x0000;
can_filter_struct.FilterFIFOAssignment = CAN_FILTER_FIFO0;
can_filter_struct.FilterBank = 0;
can_filter_struct.FilterMode = CAN_FILTERMODE_IDMASK;
can_filter_struct.FilterScale = CAN_FILTERSCALE_32BIT;
can_filter_struct.FilterActivation = CAN_FILTER_ENABLE;
can_filter_struct.SlaveStartFilterBank = 14;
if(HAL_CAN_ConfigFilter(&can_handle_struct, &can_filter_struct) != HAL_OK)
{
printf("HAL_CAN_ConfigFilter ERROR\r\n");
return 2;
}
else
{
printf("HAL_CAN_ConfigFilter OK\r\n");
}
if(HAL_CAN_Start(&can_handle_struct) != HAL_OK)
{
printf("HAL_CAN_Start ERROR\r\n");
return 3;
}
else
{
printf("HAL_CAN_Start OK\r\n");
}
return 0;
}
void HAL_CAN_MspInit(CAN_HandleTypeDef *hcan)
{
if(CAN1 == hcan->Instance)
{
CAN1_RX_GPIO_CLK_ENABLE();
CAN1_TX_GPIO_CLK_ENABLE();
CAN1_CLK_ENABLE();
GPIO_InitTypeDef gpio_init_struct;
gpio_init_struct.Pin = CAN1_TX_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_AF_OD;
gpio_init_struct.Pull = GPIO_NOPULL;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
gpio_init_struct.Alternate = GPIO_AF9_CAN1;
HAL_GPIO_Init(GPIOA, &gpio_init_struct);
gpio_init_struct.Pin = CAN1_RX_GPIO_PIN;
HAL_GPIO_Init(GPIOA, &gpio_init_struct);
}
}
2、CAN_Send_Msg、CAN_Receive_Msg
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;
can_txhandle_struct.ExtId = Id;
can_txhandle_struct.IDE = IDE;
can_txhandle_struct.RTR = RTR;
can_txhandle_struct.DLC = DLC;
if(HAL_CAN_AddTxMessage(&can_handle_struct, &can_txhandle_struct, msg, &TxMailBox) != HAL_OK)
{
return 1;
}
while(HAL_CAN_GetTxMailboxesFreeLevel(&can_handle_struct) != 3)
{
t++;
if(t > 0xFFF)
{
HAL_CAN_AbortTxRequest(&can_handle_struct, TxMailBox);
printf("HAL_CAN_AbortTxRequest Time out\r\n");
return 1;
}
}
return 0;
}
uint8_t CAN_Receive_Msg(uint32_t Id, uint32_t IDE, uint32_t RTR, uint8_t *buf)
{
if(HAL_CAN_GetRxFifoFillLevel(&can_handle_struct, CAN_RX_FIFO0) == 0)
{
return 0;
}
if (HAL_CAN_GetRxMessage(&can_handle_struct, CAN_RX_FIFO0, &can_rxhandle_struct, buf) != HAL_OK)
{
return 0;
}
if (can_rxhandle_struct.StdId!= Id || can_rxhandle_struct.IDE != IDE || can_rxhandle_struct.RTR != RTR)
{
return 0;
}
return can_rxhandle_struct.DLC;
}
五、功能展示
1、接线图

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

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

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