CAN总线特点:
- 多主机多从机
串行异步通讯、没时钟线
2.只有CAN_HIGH和CAN_LOW两条差分信号
3.连接方式多个节点挂在总线上,比较类似I2C总线,可以再SCL和SDA上挂载多个从
4.每个设备都是一个节点Node,节点直接可以相互通讯,相较于I2C总线,CAN线设置了终端电阻,常见的一种闭环连接模式,也有开环的连接模式
CAN的拓扑结构
(1)高速CAN
(2)低速CAN,通讯的最大带宽从1Mbps降到125Kbps,并且不再在总线的起点和重点使用两个终端电阻,而是将电阻分布在每个节点
CAN节点
CAN节点分为三个部分
MCU/CPU
CAN控制器
CAN收发器
STM32单片机内有集成的CAN控制器外设
CAN总线通过差分信号进行数据传输
CAN收发器将差分信号转换为TTL电平信号,或者将TTL电平信号转换为差分信号
CAN控制器将TTL电平信号接收传输给MCU
CAN总线协议
物理层:
前面的硬件拓扑结果,包括高速CAN和低俗CAN,CAN的收发器属于物理层
传输层则是CAN控制器所需要做的事情,包括CAN时序,同步,消息仲裁,确认,错误检验
这一层需要做的工作:
---故障约束
---错误检测
---消息验证
---信息确认
---仲裁
---信息帧
---传输速率和时间
---路由信息
对象层:
MCU属于对象层,需要对CAN消息做消息的过滤设置,CAN消息的处理
应用层:
基于对象层进一步封装,不同的CAN标准,CANopen和UDS
寻址:
CAN总线上每个节点不需要设置节点地址,而是通用消息标识符来区别信息,CAN总线的消息是广播的,就是大家都可以收到消息,比如总线上节点A、节点B、节点C,节点A发消息,节点B和节点C都会收到消息
注意:发送消息的时候,总线处于空闲状态
标识符越小,消息获取总线的优先级越高
帧类型:
数据帧、远程帧、错误帧、过载帧
数据帧:包含用于传输的节点数据的帧
远程帧:由任何检测到错误的节点发送的帧
过载帧:在数据帧或远程帧之间插入延迟的帧
代码:
CAN_HandleTypeDef hCAN;
void MX_CAN_Init(void)
{
CAN_FilterTypeDef sFilterConfig;
/*CAN单元初始化*/
hCAN.Instance = CAN1; /* CAN外设 */
/* BTR-BRP 波特率分频器 定义了时间单元的时间长度42/(1+6+7)/6=500Kbps */
hCAN.Init.Prescaler = 6;
hCAN.Init.Mode = CAN_MODE_NORMAL; /* 正常工作模式 */
hCAN.Init.SyncJumpWidth = CAN_SJW_1TQ; /* BTR-SJW 重新同步跳跃宽度 1个时间单元 */
hCAN.Init.TimeSeg1 = CAN_BS1_6TQ; /* BTR-TS1 时间段1 占用了6个时间单元 */
hCAN.Init.TimeSeg2 = CAN_BS2_7TQ; /* BTR-TS1 时间段2 占用了7个时间单元 */
hCAN.Init.TimeTriggeredMode = DISABLE; /* MCR-TTCM 关闭时间触发通信模式使能 */
hCAN.Init.AutoBusOff = ENABLE; /* MCR-ABOM 自动离线管理 */
hCAN.Init.AutoWakeUp = ENABLE; /* MCR-AWUM 使用自动唤醒模式 */
hCAN.Init.AutoRetransmission = DISABLE; /* MCR-NART 禁止报文自动重传 DISABLE-自动重传 */
/* MCR-RFLM 接收FIFO 锁定模式 DISABLE-溢出时新报文会覆盖原有报文 */
hCAN.Init.ReceiveFifoLocked = DISABLE;
/* MCR-TXFP 发送FIFO优先级 DISABLE-优先级取决于报文标示符 */
hCAN.Init.TransmitFifoPriority = DISABLE;
if (HAL_CAN_Init(&hCAN) != HAL_OK)
{
//Error_Handler();
}
// 初始化发送器
hCAN1_TxMessage.IDE = CAN_ID_STD;
hCAN1_TxMessage.RTR = CAN_RTR_DATA;
hCAN1_TxMessage.TransmitGlobalTime = ENABLE;
// 初始化滤波器 设置为0 则不对消息进行过滤
hCAN1_Filter.FilterIdHigh = 0; /* 要过滤的ID高位 */
hCAN1_Filter.FilterIdLow = 0; /* 要过滤的ID低位 */
hCAN1_Filter.FilterMaskIdHigh = 0; /* 过滤器高16位每位必须匹配 */
hCAN1_Filter.FilterMaskIdLow = 0; /* 过滤器低16位每位必须匹配 */
hCAN1_Filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;/* 过滤器被关联到FIFO 0 */
hCAN1_Filter.FilterBank = 0;
hCAN1_Filter.FilterMode = CAN_FILTERMODE_IDMASK; /* 工作在标识符屏蔽位模式 */
hCAN1_Filter.FilterScale = CAN_FILTERSCALE_32BIT; /* 过滤器位宽为单个32位。*/
hCAN1_Filter.FilterActivation = ENABLE;
hCAN1_Filter.SlaveStartFilterBank = 0;
HAL_CAN_ConfigFilter(&hCAN, &hCAN1_Filter);
while(HAL_CAN_Start(&hCAN) != HAL_OK )
{
printf("\nCAN_Start Failed!!");
HAL_Delay(100);
}
HAL_CAN_ActivateNotification(&hCAN, CAN_IT_RX_FIFO0_MSG_PENDING);
}
下面是CAN发送的函数,我们需要自己构建相应的消息帧格式,通常需要设置消息帧的ID格式,消息长度,具体如下;
void CAN_TxMessage(CAN_HandleTypeDef *hcan,uint16_t ID ,uint8_t aData[], uint8_t DLC)
{
uint32_t Tx_MailBox;
/*-1- 配置数据段长度 ----------------------------------------*/
hCAN1_TxMessage.IDE = CAN_ID_STD;
hCAN1_TxMessage.RTR = CAN_RTR_DATA;
hCAN1_TxMessage.StdId = ID;
hCAN1_TxMessage.DLC = DLC;
hCAN1_TxMessage.TransmitGlobalTime = ENABLE;
/*-2- 发送aData ---------------------------------------------*/
while(HAL_CAN_AddTxMessage(hcan, &hCAN1_TxMessage, aData, &Tx_MailBox) != HAL_OK)
{
HAL_Delay(5);
}
}
CAN_ID_STD
设置为标准ID;CAN_RTR_DATA
设置消息为数据帧;StdId
为当前消息的ID;DLC
为当前消息的长度;