一、简介
它是一种串行异步半双工的通信方式,无主从机之分,采用差分信号传输具有较好的抗干扰能力以及纠错能力,常被用在各种环境恶劣,干扰强的各种工业现场。
二、物理层
1.差分信号
Can总线总共有两条线,分别为can_high以及can_low,采用差分信号的方式传输数据。
显性电平:逻辑0
隐性电平:逻辑1
2.CAN的通讯节点
(1)单片的CAN外设并引出了两个引脚:CAN_TX、CAN_RX。和485串口一样,单片机的CAN外设并不能直接CAN网络所需要的差分信号,而是通过外接的CAN收发器,将CAN_TX的TTL信号转换为差分信号;将CAN网络的差分信号转换成TTL信号给CAN_RX
(2)CAN网络上的节点没有主从机之说,原始的CAN节点之间是不做区分的,只有报文之间才做区分,节点之间没有地址或者片选线来寻址,CAN只负责向网络上发送消息,需要的节点自然会从网络上接收需要的消息;同理CAN网络上发送完消息后,需要的CAN节点自然会去接收对应的消息;
3.CAN的总线类型
根Can的物理形式主要分为开环总线和闭环总线两种。
(1)闭环总线
闭环总线适用于短距高速(相对与开环总线而言而言)的传输场合,其最大传输距离为40m,最大传输速率为1Mbps。总线两端要求有两个120欧的终端电阻。
开环总线适用于长距低速场合,最大传输速率为125Kbps,最大传输距离为1km。每根总线上要求串联一个2.2k欧的电阻。
三、协议层
1.同步
Can属于异步通讯方式,在通讯之前节点之间要约定好波特率,且CAN还会使用“位同步”的方式来对抗干扰、吸收误差
(1) 硬同步
在总线刚刚从空闲状态中走出来的时候,在帧头的位置都会进行一次同步。此时所有的节点位时间重新开始计算,就像所有的运动员都再一次回到了起跑线上。这种同步方式被称作硬同步。
(2) 再同步(位同步)
CAN的每个位都被分为SS段、PTS段、PSB1段、PBS2段。最小时间单位是Tq,一个完整的位由8~25Tq组成。
SS段: 同步段,长度固定为1Tq,当跳变沿落在ss段时认为总线时序是同步的。
PTS段: 传播时间段,用来弥补信号的物理延迟。
PBS1段:相位缓冲段,用来补偿边沿误差,在再同步的时候可以加长
PBS2段:相位缓冲段,用来补偿边沿误差,在再同步的时候可以缩短
采样点位置在PBS1和PSB2交界的地方。
2.波特率
波特率:总tq数分之一。
3.报文格式
(1) 报文类型
上述报文中数据帧最常用需要着重记忆
(2) 报文格式
1.数据帧
- SOF:帧起始信号
- RTR:远程帧标志 0 表示数据帧
- IDE:扩展帧标志位 0 表示标准帧
- DLC:数据段长度
- DATA:数据段
(1)关于canid
- CAN-ID不用来标识某个节点,而是用来标识某种类型的报文
- 标准帧有CAN-ID占11位 ,扩展帧CAN-ID占29位
2.其他帧
四、STM32中的CAN总线
1.功能框图
(1)stm32can总线外设简介(STM32F103)
-
Stm32can总线外设最高通信速率为1Mbps;
-
can外设可以自动收发can报文,且支持标准ID和扩展ID;
-
can外设有三个发送邮箱,可以软件控制发送优先级,还可以记录发送时间;
-
can有两个深度为3的FIFO,同时有报文过滤功能;
-
可以配置自动重发;
-
不支持DMA接收数据;
(2)功能框图重要部分介绍
这张框图你只需要了解以下四点
- CAN有三个发送邮箱
- 有两个深度为3的接收FIFO
- 有28个筛选器,0~13是给CAN1使用的, 14~27是给CAN2使用的
- 每个筛选器都可以连接到对应的配置给某个FIFO,一个FIFO可以配置多个筛选器
(3)工作模式
CAN总共有四种工作模式:
-
普通模式:可以接收,也可以发送。
-
静默模式:可以接收,只能往总线上发送1(隐性位),发送的数据也会被自己接收。
-
回环模式:不能接收总线上的数据,发送的数据会被自己接收,总线上的数据也可以被检测到。
-
静默回环模式:不能接收总线上的数据,只能往总线上发送1 发送的数据会被自己接收。
(4)过滤器
1. 过滤器工作模式
stm32f103有28组过滤器,每组过滤器有2个32位的寄存器。在不同的工作模式下这两个寄存器有不同的作用,过滤器有两种工作模式:
-
列表工作模式:
列表模式类似于白名单,两个寄存器全部用来设置canid,只有和设置的canid完全匹配才能通过过滤器。STM32的CAN有两个FIFO。
-
掩码工作模式:
对于掩码工作模式,过滤器的一个寄存器用来设置掩码,另一个用来设置canid。掩码和设置的canid是与的关系,只有对应位和设置的canid的未屏蔽位匹配的CAN报文才能通过过滤器。
2.过滤器大小
过滤器有16位和32位两种大小,具体如下:
(5)重要中断介绍
bxCAN占用4个专用的中断向量。通过设置CAN中断允许寄存器(CAN_IER),每个中断源都可以
单独允许和禁用。
-
发送中断可由下列事件产生:
- 发送邮箱0变为空,CAN_TSR寄存器的RQCP0位被置’1’。
- 发送邮箱1变为空,CAN_TSR寄存器的RQCP1位被置’1’。
- 发送邮箱2变为空,CAN_TSR寄存器的RQCP2位被置’1’。
-
FIFO0中断可由下列事件产生:
- FIFO0接收到一个新报文,CAN_RF0R寄存器的FMP0位不再是’00’。
- FIFO0变为满的情况,CAN_RF0R寄存器的FULL0位被置’1’。
- FIFO0发生溢出的情况,CAN_RF0R寄存器的FOVR0位被置’1’。
-
FIFO1中断可由下列事件产生:
- FIFO1接收到一个新报文,CAN_RF1R寄存器的FMP1位不再是’00’。
- FIFO1变为满的情况,CAN_RF1R寄存器的FULL1位被置’1’。
- FIFO1发生溢出的情况,CAN_RF1R寄存器的FOVR1位被置’1’。
-
错误和状态变化中断可由下列事件产生:
-
出错情况,关于出错情况的详细信息请参考CAN错误状态寄存器(CAN_ESR)。
-
唤醒情况,在CAN接收引脚上监视到帧起始位(SOF)。
-
CAN进入睡眠模式。
上述中断对应的中断服务函数名称分别为:
void CAN1_RX1_IRQHandler (void); void USB_HP_CAN1_TX_IRQHandler (void); void USB_LP_CAN1_RX0_IRQHandler (void); void CAN1_SCE_IRQHandler (void);
-
2.CAN配置和使用
(1)标准库
1.重要结构体
初始化结构体
typedef struct
{
uint16_t CAN_Prescaler; //can外设时钟预分频器
uint8_t CAN_Mode; //CAN的操作模式:初始化模式、普通模式(工作时使用这个模式)、睡眠模式
uint8_t CAN_SJW; //再同步时,相位缓冲段调整的长度
uint8_t CAN_BS1; //相位缓冲段1
uint8_t CAN_BS2; //相位缓冲段2
FunctionalState CAN_TTCM; //时间除服模式,bool值
FunctionalState CAN_ABOM; //自动离线模式,bool值
FunctionalState CAN_AWUM; //自动唤醒模式,bool值
FunctionalState CAN_NART; //是否关闭自动重传
FunctionalState CAN_RFLM; //接收FIFO锁定模式,FIFO满时锁定
FunctionalState CAN_TXFP; //发送邮箱是否按优先级顺序发送报文
} CAN_InitTypeDef;
过滤器结构体
typedef struct
{
uint16_t CAN_FilterIdHigh; //过滤器高16位
uint16_t CAN_FilterIdLow; //过滤器低16位
uint16_t CAN_FilterMaskIdHigh; //过滤器掩码高16位
uint16_t CAN_FilterMaskIdLow; //过滤器掩码低16位
uint16_t CAN_FilterFIFOAssignment; //FIFO号
uint8_t CAN_FilterNumber; //过滤器号
uint8_t CAN_FilterMode; //过滤器工作模式:列表模式、掩码模式
uint8_t CAN_FilterScale; //过滤器宽度:16位或32位
FunctionalState CAN_FilterActivation; //是否激活这个过滤器
} CAN_FilterInitTypeDef;
消息头结构体
typedef struct
{
uint32_t StdId; //标准CANID
uint32_t ExtId; //扩展CANID
uint8_t IDE; //是否使用扩展帧
uint8_t RTR; //是否是远程帧
uint8_t DLC; //数据段长度
uint8_t Data[8]; //数据段数据
} CanTxMsg;
2.初始化代码
static void Can_Init(void)
{
CAN_InitTypeDef CAN_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1,ENABLE);
CAN_InitStruct.CAN_ABOM = ENABLE;
CAN_InitStruct.CAN_AWUM = ENABLE;
CAN_InitStruct.CAN_NART = DISABLE;
CAN_InitStruct.CAN_RFLM = DISABLE;
CAN_InitStruct.CAN_TXFP = DISABLE;
CAN_InitStruct.CAN_TTCM = DISABLE;
CAN_InitStruct.CAN_Mode = CAN_Mode_Normal;
/* 波特率:36MHz/9/(2+1+1)=1mbps */
CAN_InitStruct.CAN_Prescaler = 9;
CAN_InitStruct.CAN_SJW = CAN_SJW_1tq;
CAN_InitStruct.CAN_BS1 = CAN_BS1_2tq;
CAN_InitStruct.CAN_BS2 = CAN_BS2_1tq;
CAN_Init(Canfestival_Canx,&CAN_InitStruct);
/*开启FIFO0有数据中断*/
CAN_ITConfig(Canfestival_Canx,CAN_IT_FMP0,ENABLE);
}
3.常用的API
初始化
/**********************************************************************************
* @brief 初始化CAN外设
* @param CANx: CAN外设
* @param CAN_InitStruct: 初始化结构体
* @retval CAN_InitStatus_Failed 或者 CAN_InitStatus_Success.
********************************************************************************/
uint8_t CAN_Init(CAN_TypeDef* CANx, CAN_InitTypeDef* CAN_InitStruct)
开中断
/*****************************************************************************************
* @brief 配置CAN中断
* @param can外设
* @param CAN_IT: 中断类型
* - CAN_IT_TME, //发送邮箱空
* - CAN_IT_FMP0, //FIFO0中有数据
* - CAN_IT_FF0, //FIFO0满
* - CAN_IT_FOV0, //FIFO0溢出
* - CAN_IT_FMP1, //FIFO1中有数据
* - CAN_IT_FF1, //FIFO1满
* - CAN_IT_FOV1, //FIFO1溢出
* - CAN_IT_EWG,
* - CAN_IT_EPV,
* - CAN_IT_LEC,
* - CAN_IT_ERR, //错误
* - CAN_IT_WKU or //唤醒
* - CAN_IT_SLK. //睡眠确认
* @param NewState: new state of the CAN interrupts.
* This parameter can be: ENABLE or DISABLE.
* @retval None.
*************************************************************************************/
void CAN_ITConfig(CAN_TypeDef* CANx, uint32_t CAN_IT, FunctionalState NewState);
过滤器
/*********************************************************************************
* @brief 初始化过滤器
* @param 配置结构体
* @retval None.
*********************************************************************************/
void CAN_FilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct)
接收数据
/******************************************************************************
* @brief Receives a message.
* @param CANx: where x can be 1 or 2 to to select the CAN peripheral.
* @param FIFONumber: Receive FIFO number, CAN_FIFO0 or CAN_FIFO1.
* @param RxMessage: pointer to a structure receive message which contains
* CAN Id, CAN DLC, CAN datas and FMI number.
* @retval None.
******************************************************************************/
void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage)
发送数据
/*********************************************************************************
* @brief Initiates the transmission of a message.
* @param CANx: where x can be 1 or 2 to to select the CAN peripheral.
* @param TxMessage: pointer to a structure which contains CAN Id, CAN
* DLC and CAN data.
* @retval The number of the mailbox that is used for transmission
* or CAN_TxStatus_NoMailBox if there is no empty mailbox.
**********************************************************************************/
uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage)
4.中断服务函数
void CAN1_RX1_IRQHandler (void);
void USB_HP_CAN1_TX_IRQHandler (void);
void USB_LP_CAN1_RX0_IRQHandler (void);
void CAN1_SCE_IRQHandler (void);
(2)hal库
1. cubmax配置
参数配置
- Prescaler (for Time Quantum) : 分频系数,决定了Tq的大小
- Time Quanta in Bit Segment 1 :相位缓冲段1 同步时加长
- Time Quanta in Bit Segment 2 :相位缓冲段2 同步时缩短
- ReSynchronization Jump Width: 再同步时相位缓冲段增加和减少的长度单位是Tq
- Time Triggered Communication Mode :时间触发模式
- Automatic Bus-Off Management: 自动下线功能,当检测128次11位连续的隐性位下线
- Automatic Wake-Up Mode: 自动唤醒功能,通过检测CAN报文 由硬件唤醒CAN
- Automatic Retransmission:自动重发功能,会一直重传到发送成功
- Receive FIfo Locked Mode:当接收FIFO满时,FIFO被锁定,新来的报文被丢弃;否则会覆盖原有的报文
- Transmit Fifo Priority: 使能时CAN报文发送的顺序由发送的顺序决定;不使能时,由报文优先级决定。
2. 重要结构体
CAN对象
typedef struct
{
CAN_TypeDef *Instance; /*寄存器接口 */
CAN_InitTypeDef Init; /*初始化参数 */
... /*其他成员不常用,暂时不列出*/
} CAN_HandleTypeDef;
初始化结构体
//和上述CubMax配置相同,不过多介绍
typedef struct
{
uint32_t Prescaler;
uint32_t Mode;
uint32_t SyncJumpWidth;
uint32_t TimeSeg1;
uint32_t TimeSeg2;
FunctionalState TimeTriggeredMode;
FunctionalState AutoBusOff;
FunctionalState AutoWakeUp;
FunctionalState AutoRetransmission;
FunctionalState ReceiveFifoLocked;
FunctionalState TransmitFifoPriority;
} CAN_InitTypeDef;
过滤器结构体
typedef struct
{
uint32_t FilterIdHigh; //过滤器高位
uint32_t FilterIdLow; //过滤器低位
uint32_t FilterMaskIdHigh; //过滤器掩码高位
uint32_t FilterMaskIdLow; //过滤器掩码低位
uint32_t FilterFIFOAssignment; //FIFO号
uint32_t FilterBank; //过滤器号
uint32_t FilterMode; //过滤器模式:掩码模式、列表模式
uint32_t FilterScale; //过滤器大小
uint32_t FilterActivation; //是否使能该过滤器
uint32_t SlaveStartFilterBank;
} CAN_FilterTypeDef;
消息结构体
typedef struct
{
uint32_t StdId;
uint32_t ExtId;
uint32_t IDE;
uint32_t RTR;
uint32_t DLC;
FunctionalState TransmitGlobalTime;
} CAN_TxHeaderTypeDef;
3.cubmax生成的初始化代码以及分析
4. 常用的API
/**************************************************************************************************
* @brief 发送CAN_Message
* @param [in] hcan can对象/句柄
* @param [in] pHeader 报文头
* @param [in] aData 数据
* @param [out] pTxMailbox该消息通过哪个邮箱发送这个消息
* @retval HAL_OK 成功
HAL_ERROR 错误
***************************************************************************************************/
HAL_StatusTypeDef HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan, const CAN_TxHeaderTypeDef *pHeader,
const uint8_t aData[], uint32_t *pTxMailbox);
/**************************************************************************************************
* @brief 接收CAN_Message
* @param [in] hcan can对象/句柄
* @param [in] RxFifo FIFO号
* @param [out] pHeader 报文头
* @param [in] aData 数据
* @retval HAL_OK 成功
HAL_ERROR 错误
***************************************************************************************************/
HAL_StatusTypeDef HAL_CAN_GetRxMessage(CAN_HandleTypeDef *hcan, uint32_t RxFifo,
CAN_RxHeaderTypeDef *pHeader, uint8_t aData[]);
/**************************************************************************************************
* @brief 配置过滤器
* @param [in] hcan can对象/句柄
* @param [in] sFilterConfig 过滤器配置
* @retval HAL_OK 成功
HAL_ERROR 错误
***************************************************************************************************/
HAL_StatusTypeDef HAL_CAN_ConfigFilter(CAN_HandleTypeDef *hcan, const CAN_FilterTypeDef *sFilterConfig);
/**************************************************************************************************
* @brief 激活中断
* @param [in] hcan can对象/句柄
* @param [in] ActiveITs 中断类型
* @retval HAL_OK 成功
HAL_ERROR 错误
***************************************************************************************************/
HAL_StatusTypeDef HAL_CAN_ActivateNotification(CAN_HandleTypeDef *hcan, uint32_t ActiveITs);
/**************************************************************************************************
* @brief 失活中断
* @param [in] hcan can对象/句柄
* @param [in] ActiveITs 中断类型
* @retval HAL_OK 成功
HAL_ERROR 错误
***************************************************************************************************/
HAL_StatusTypeDef HAL_CAN_DeactivateNotification(CAN_HandleTypeDef *hcan, uint32_t InactiveITs);
/*************************************************************************************************
* @brief 判断某个mailbox是否发送完成
* @param hcan CAN 句柄
* @param TxMailboxes mailbox号
* @retval Status
* - 0 : No pending transmission request on any selected Tx Mailboxes.
* - 1 : Pending transmission request on at least one of the selected
* Tx Mailbox.
**************************************************************************************************/
uint32_t HAL_CAN_IsTxMessagePending(const CAN_HandleTypeDef *hcan, uint32_t TxMailboxes)
5. 中断回调函数
/*发送邮箱发送完成回调函数*/
void HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_TxMailbox1CompleteCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_TxMailbox2CompleteCallback(CAN_HandleTypeDef *hcan);
/*fifo收到消息和fifo满回调函数*/
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_RxFifo0FullCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan);
void HAL_CAN_RxFifo1FullCallback(CAN_HandleTypeDef *hcan);
/*睡眠回调函数*/
void HAL_CAN_SleepCallback(CAN_HandleTypeDef *hcan);
/*被唤醒回调函数*/
void HAL_CAN_WakeUpFromRxMsgCallback(CAN_HandleTypeDef *hcan);
/*错误回调函数*/
void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan);
注意:上述回调函数被调用后,对应中断有可能会被关闭,需要在对应回调函数中调用HAL_CAN_ActivateNotification来重行激活
6.重要配置
滤波器配置代码
-
32位滤波器,掩码模式(29位CAN-ID)
//敬请期待[坏笑]
-
32位滤波器,列表模式(29位CAN-ID)
//敬请期待[坏笑]
-
16位滤波器,掩码模式(11位CAN-ID)
//敬请期待[坏笑]
-
16位滤波器,列表模式(11位CAN-ID)
//敬请期待[坏笑]
发送消息配置代码
//敬请期待[坏笑]