CAN简介
CAN 是 Controller Area Network 的缩写(以下称为 CAN),是 ISO 国际标准化的串行通信协议。在当前的汽车产业中,出于对安全性、舒适性、方便性、低公害、低成本的要求,各种各样的电子控制系统被开发了出来。由于这些系统之间通信所用的数据类型及对可靠性的要求不尽相同,由多条总线构成的情况很多,线束的数量也随之增加。为适应“减少线束的数量”、“通过多个 LAN,进行大量数据的高速通信”的需要,1986 年德国电气商博世公司开发出面向汽车的 CAN 通信协议。此后,CAN 通过 ISO11898 及 ISO11519 进行了标准化,现在在欧洲已是汽车网络的标准协议。
CAN 控制器根据两根线上的电位差来判断总线电平。总线电平分为显性电平和隐性电平,二者必居其一。发送方通过使总线电平发生变化,将消息发送给接收方。
STM32 CAN系统架构
CAN协议
CAN 协议的基本概念
CAN 协议如表 所示涵盖了 ISO 规定的 OSI1 基本参照模型中的传输层、数据链路层及物理层。
数据链路层分为 MAC 子层和 LLC 子层,MAC 子层是 CAN 协议的核心部分。数据链路层的功能是将物理层
收到的信号组织成有意义的消息,并提供传送错误控制等传输控制的流程。具体地说,就是消息的帧化、仲裁、
应答、错误的检测或报告。数据链路层的功能通常在 CAN 控制器的硬件中执行。在物理层定义了信号实际的发送方式、位时序、位的编码方式及同步的步骤。但具体地说,信号电平、通信速度、采样点、驱动器和总线的电气特性、连接器的形态等均未定义1。这些必须由用户根据系统需求自行确定。
CAN协议帧
1、数据帧
用于发送单元向接收单元传送数据的帧
帧起始,这个比较简单,标准帧和扩展帧都是由 1 个位的显性电平表示帧起始。
仲裁段,表示数据优先级的段,标准帧和扩展帧格式在本段有所区别,看下图
控制段,由 6 个位构成,表示数据段的字节数。标准帧和扩展帧的控制段稍有不同,如图
数据段,该段可包含 0~8 个字节的数据。从最高位(MSB)开始输出,标准帧和扩展帧在这个段的定义都是一样的。如图
CRC 段,该段用于检查帧传输错误。由 15 个位的 CRC 顺序和 1 个位的 CRC 界定符(用于分隔的位)组成,标准帧和扩展帧在这个段的格式也是相同的。如图
ACK 段,此段用来确认是否正常接收。由 ACK 槽(ACK Slot)和 ACK 界定符 2 个位组成。1标准帧和扩展帧在这个段的格式也是相同的。如图
帧结束,这个段也比较简单,标准帧和扩展帧在这个段格式一样,由 7 个位的隐性位组成。
2、遥控帧
用于接收单元向具有相同 ID 的发送单元请求数据的帧
3、错误帧
用于当检测出错误时向其它单元通知错误的帧
4、过载帧
用于接收单元通知其尚未做好接收准备的帧
5、间隔帧
用于将数据帧及遥控帧与前面的帧分离开来的帧
CAN物理层
闭环总线
CAN 物理层的形式主要有两种, 图 39_0_1 中的 CAN 通讯网络是一种遵循 ISO11898 标准的高速、短距离“闭环网络”,它的总线最大长度为 40m,通信速度最高为 1Mbps,总线的两端各要求有一个“120 欧”的电阻。
开环总线
它的最大传输距离为 1km,最高通讯速率为 125kbps,两根总线是独立的、不形成闭环,要求每根总线上各串联有一个“2.2千欧”的电阻
差分信号逻辑表示
CAN 发送流程
CAN 发送流程为:程序选择 1 个空置的邮箱(TME=1)->设置标识符(ID),数据长度和发送数据->设置 CAN_TIxR 的 TXRQ 位为 1,请求发送->邮箱挂号(等待成为最高优先级)->预定发送(等待总线空闲)->发送->邮箱空置。整个流程如图 34.1.12 所示:
CAN 接收流程
CAN 接收到的有效报文,被存储在 3 级邮箱深度的 FIFO 中。FIFO 完全由硬件来管理,从而节省了 CPU 的处理负荷,简化了软件并保证了数据的一致性。应用程序只能通过读取 FIFO输出邮箱,来读取 FIFO 中最先收到的报文。这里的有效报文是指那些正确被接收的(直到 EOF都没有错误)且通过了标识符过滤的报文。
前面我们知道 CAN 的接收有 2 个 FIFO,我们每个滤波器组都可以设置其关联的 FIFO,通过 CAN_FFA1R 的设置,可以将滤波器组关联到FIFO0/FIFO1。
CAN 接收流程为:FIFO 空->收到有效报文->挂号_1(存入 FIFO 的一个邮箱,这个由硬件控制,我们不需要理会)->收到有效报文->挂号_2->收到有效报文->挂号_3->收到有效报文->溢出。
CAN的使用
HAL库结构体
CAN_HandleTypeDef 结构体定义
/**
* @brief CAN handle Structure definition
*/
typedef struct
{
CAN_TypeDef *Instance; /*!< Register base address */
CAN_InitTypeDef Init; /*!< CAN required parameters */
CanTxMsgTypeDef* pTxMsg; /*!< Pointer to transmit structure */
CanRxMsgTypeDef* pRxMsg; /*!< Pointer to reception structure */
__IO HAL_CAN_StateTypeDef State; /*!< CAN communication state */
HAL_LockTypeDef Lock; /*!< CAN locking object */
__IO uint32_t ErrorCode; /*!< CAN Error code */
}CAN_HandleTypeDef;
CAN_InitTypeDef 结构体定义
/**
* @brief CAN init structure definition
*/
typedef struct
{
uint32_t Prescaler; /*!< Specifies the length of a time quantum.
This parameter must be a number between Min_Data = 1 and Max_Data = 1024 */
uint32_t Mode; /*!< Specifies the CAN operating mode.
This parameter can be a value of @ref CAN_operating_mode */
uint32_t SJW; /*!< Specifies the maximum number of time quanta
the CAN hardware is allowed to lengthen or
shorten a bit to perform resynchronization.
This parameter can be a value of @ref CAN_synchronisation_jump_width */
uint32_t BS1; /*!< Specifies the number of time quanta in Bit Segment 1.
This parameter can be a value of @ref CAN_time_quantum_in_bit_segment_1 */
uint32_t BS2; /*!< Specifies the number of time quanta in Bit Segment 2.
This parameter can be a value of @ref CAN_time_quantum_in_bit_segment_2 */
uint32_t TTCM; /*!< Enable or disable the time triggered communication mode.
This parameter can be set to ENABLE or DISABLE. */
uint32_t ABOM; /*!< Enable or disable the automatic bus-off management.
This parameter can be set to ENABLE or DISABLE */
uint32_t AWUM; /*!< Enable or disable the automatic wake-up mode.
This parameter can be set to ENABLE or DISABLE */
uint32_t NART; /*!< Enable or disable the non-automatic retransmission mode.
This parameter can be set to ENABLE or DISABLE */
uint32_t RFLM; /*!< Enable or disable the receive FIFO Locked mode.
This parameter can be set to ENABLE or DISABLE */
uint32_t TXFP; /*!< Enable or disable the transmit FIFO priority.
This parameter can be set to ENABLE or DISABLE */
}CAN_InitTypeDef;
CAN_FilterConfTypeDef 过滤器结构体定义
/**
* @brief CAN filter configuration structure definition
*/
typedef struct
{
uint32_t FilterIdHigh; /*!< Specifies the filter identification number (MSBs for a 32-bit
configuration, first one for a 16-bit configuration).
This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
uint32_t FilterIdLow; /*!< Specifies the filter identification number (LSBs for a 32-bit
configuration, second one for a 16-bit configuration).
This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
uint32_t FilterMaskIdHigh; /*!< Specifies the filter mask number or identification number,
according to the mode (MSBs for a 32-bit configuration,
first one for a 16-bit configuration).
This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
uint32_t FilterMaskIdLow; /*!< Specifies the filter mask number or identification number,
according to the mode (LSBs for a 32-bit configuration,
second one for a 16-bit configuration).
This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
uint32_t FilterFIFOAssignment; /*!< Specifies the FIFO (0 or 1) which will be assigned to the filter.
This parameter can be a value of @ref CAN_filter_FIFO */
uint32_t FilterNumber; /*!< Specifies the filter which will be initialized.
This parameter must be a number between Min_Data = 0 and Max_Data = 27 */
uint32_t FilterMode; /*!< Specifies the filter mode to be initialized.
This parameter can be a value of @ref CAN_filter_mode */
uint32_t FilterScale; /*!< Specifies the filter scale.
This parameter can be a value of @ref CAN_filter_scale */
uint32_t FilterActivation; /*!< Enable or disable the filter.
This parameter can be set to ENABLE or DISABLE. */
uint32_t BankNumber; /*!< Select the start slave bank filter.
This parameter must be a number between Min_Data = 0 and Max_Data = 28 */
}CAN_FilterConfTypeDef;
CanTxMsgTypeDef 结构体
/**
* @brief CAN Tx message structure definition
*/
typedef struct
{
uint32_t StdId; /*!< Specifies the standard identifier.
This parameter must be a number between Min_Data = 0 and Max_Data = 0x7FF */
uint32_t ExtId; /*!< Specifies the extended identifier.
This parameter must be a number between Min_Data = 0 and Max_Data = 0x1FFFFFFF */
uint32_t IDE; /*!< Specifies the type of identifier for the message that will be transmitted.
This parameter can be a value of @ref CAN_Identifier_Type */
uint32_t RTR; /*!< Specifies the type of frame for the message that will be transmitted.
This parameter can be a value of @ref CAN_remote_transmission_request */
uint32_t DLC; /*!< Specifies the length of the frame that will be transmitted.
This parameter must be a number between Min_Data = 0 and Max_Data = 8 */
uint8_t Data[8]; /*!< Contains the data to be transmitted.
This parameter must be a number between Min_Data = 0 and Max_Data = 0xFF */
}CanTxMsgTypeDef;
CanRxMsgTypeDef 结构体
/**
* @brief CAN Rx message structure definition
*/
typedef struct
{
uint32_t StdId; /*!< Specifies the standard identifier.
This parameter must be a number between Min_Data = 0 and Max_Data = 0x7FF */
uint32_t ExtId; /*!< Specifies the extended identifier.
This parameter must be a number between Min_Data = 0 and Max_Data = 0x1FFFFFFF */
uint32_t IDE; /*!< Specifies the type of identifier for the message that will be received.
This parameter can be a value of @ref CAN_Identifier_Type */
uint32_t RTR; /*!< Specifies the type of frame for the received message.
This parameter can be a value of @ref CAN_remote_transmission_request */
uint32_t DLC; /*!< Specifies the length of the frame that will be received.
This parameter must be a number between Min_Data = 0 and Max_Data = 8 */
uint8_t Data[8]; /*!< Contains the data to be received.
This parameter must be a number between Min_Data = 0 and Max_Data = 0xFF */
uint32_t FMI; /*!< Specifies the index of the filter the message stored in the mailbox passes through.
This parameter must be a number between Min_Data = 0 and Max_Data = 0xFF */
uint32_t FIFONumber; /*!< Specifies the receive FIFO number.
This parameter can be CAN_FIFO0 or CAN_FIFO1 */
}CanRxMsgTypeDef;
HAL库函数
初始化类库函数
/* Initialization/de-initialization functions ***********************************/
HAL_StatusTypeDef HAL_CAN_Init(CAN_HandleTypeDef* hcan);
HAL_StatusTypeDef HAL_CAN_ConfigFilter(CAN_HandleTypeDef* hcan, CAN_FilterConfTypeDef* sFilterConfig);
HAL_StatusTypeDef HAL_CAN_DeInit(CAN_HandleTypeDef* hcan);
void HAL_CAN_MspInit(CAN_HandleTypeDef* hcan);
void HAL_CAN_MspDeInit(CAN_HandleTypeDef* hcan);
操作类库函数
/* I/O operation functions ******************************************************/
HAL_StatusTypeDef HAL_CAN_Transmit(CAN_HandleTypeDef *hcan, uint32_t Timeout);
HAL_StatusTypeDef HAL_CAN_Transmit_IT(CAN_HandleTypeDef *hcan);
HAL_StatusTypeDef HAL_CAN_Receive(CAN_HandleTypeDef *hcan, uint8_t FIFONumber, uint32_t Timeout);
HAL_StatusTypeDef HAL_CAN_Receive_IT(CAN_HandleTypeDef *hcan, uint8_t FIFONumber);
HAL_StatusTypeDef HAL_CAN_Sleep(CAN_HandleTypeDef *hcan);
HAL_StatusTypeDef HAL_CAN_WakeUp(CAN_HandleTypeDef *hcan);
void HAL_CAN_IRQHandler(CAN_HandleTypeDef* hcan);
void HAL_CAN_TxCpltCallback(CAN_HandleTypeDef* hcan);
void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* hcan);
void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan);
CAN示例
#include "can.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F429开发板
//CAN驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2015/12/29
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
CAN_HandleTypeDef CAN1_Handler; //CAN1句柄
CanTxMsgTypeDef TxMessage; //发送消息
CanRxMsgTypeDef RxMessage; //接收消息
CAN初始化
//tsjw:重新同步跳跃时间单元.范围:CAN_SJW_1TQ~CAN_SJW_4TQ
//tbs2:时间段2的时间单元. 范围:CAN_BS2_1TQ~CAN_BS2_8TQ;
//tbs1:时间段1的时间单元. 范围:CAN_BS1_1TQ~CAN_BS1_16TQ
//brp :波特率分频器.范围:1~1024; tq=(brp)*tpclk1
//波特率=Fpclk1/((tbs1+tbs2+1)*brp); 其中tbs1和tbs2我们只用关注标识符上标志的序号,例如CAN_BS2_1TQ,我们就认为tbs2=1来计算即可。
//mode:CAN_MODE_NORMAL,普通模式;CAN_MODE_LOOPBACK,回环模式;
//Fpclk1的时钟在初始化的时候设置为45M,如果设置CAN1_Mode_Init(CAN_SJW_1tq,CAN_BS2_6tq,CAN_BS1_8tq,6,CAN_MODE_LOOPBACK);
//则波特率为:45M/((6+8+1)*6)=500Kbps
//返回值:0,初始化OK;
// 其他,初始化失败;
u8 CAN1_Mode_Init(u32 tsjw,u32 tbs2,u32 tbs1,u16 brp,u32 mode)
{
CAN_FilterConfTypeDef CAN1_FilerConf;
CAN1_Handler.Instance=CAN1;
CAN1_Handler.pTxMsg=&TxMessage; //发送消息
CAN1_Handler.pRxMsg=&RxMessage; //接收消息
CAN1_Handler.Init.Prescaler=brp; //分频系数(Fdiv)为brp+1
CAN1_Handler.Init.Mode=mode; //模式设置
CAN1_Handler.Init.SJW=tsjw; //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1TQ~CAN_SJW_4TQ
CAN1_Handler.Init.BS1=tbs1; //tbs1范围CAN_BS1_1TQ~CAN_BS1_16TQ
CAN1_Handler.Init.BS2=tbs2; //tbs2范围CAN_BS2_1TQ~CAN_BS2_8TQ
CAN1_Handler.Init.TTCM=DISABLE; //非时间触发通信模式
CAN1_Handler.Init.ABOM=DISABLE; //软件自动离线管理
CAN1_Handler.Init.AWUM=DISABLE; //睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
CAN1_Handler.Init.NART=ENABLE; //禁止报文自动传送
CAN1_Handler.Init.RFLM=DISABLE; //报文不锁定,新的覆盖旧的
CAN1_Handler.Init.TXFP=DISABLE; //优先级由报文标识符决定
if(HAL_CAN_Init(&CAN1_Handler)!=HAL_OK) return 1; //初始化
CAN1_FilerConf.FilterIdHigh=0X0000; //32位ID
CAN1_FilerConf.FilterIdLow=0X0000;
CAN1_FilerConf.FilterMaskIdHigh=0X0000; //32位MASK
CAN1_FilerConf.FilterMaskIdLow=0X0000;
CAN1_FilerConf.FilterFIFOAssignment=CAN_FILTER_FIFO0;//过滤器0关联到FIFO0
CAN1_FilerConf.FilterNumber=0; //过滤器0
CAN1_FilerConf.FilterMode=CAN_FILTERMODE_IDMASK;
CAN1_FilerConf.FilterScale=CAN_FILTERSCALE_32BIT;
CAN1_FilerConf.FilterActivation=ENABLE; //激活滤波器0
CAN1_FilerConf.BankNumber=14;
if(HAL_CAN_ConfigFilter(&CAN1_Handler,&CAN1_FilerConf)!=HAL_OK) return 2;//滤波器初始化
return 0;
}
//CAN底层驱动,引脚配置,时钟配置,中断配置
//此函数会被HAL_CAN_Init()调用
//hcan:CAN句柄
void HAL_CAN_MspInit(CAN_HandleTypeDef* hcan)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_CAN1_CLK_ENABLE(); //使能CAN1时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
GPIO_Initure.Pin=GPIO_PIN_11|GPIO_PIN_12; //PA11,12
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //推挽复用
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FAST; //快速
GPIO_Initure.Alternate=GPIO_AF9_CAN1; //复用为CAN1
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化
#if CAN1_RX0_INT_ENABLE
__HAL_CAN_ENABLE_IT(&CAN1_Handler,CAN_IT_FMP0);//FIFO0消息挂起中断允许.
//CAN1->IER|=1<<1; //FIFO0消息挂起中断允许.
HAL_NVIC_SetPriority(CAN1_RX0_IRQn,1,2); //抢占优先级1,子优先级2
HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn); //使能中断
#endif
}
#if CAN1_RX0_INT_ENABLE //使能RX0中断
//CAN中断服务函数
void CAN1_RX0_IRQHandler(void)
{
HAL_CAN_IRQHandler(&CAN1_Handler);//此函数会调用CAN_Receive_IT()接收数据
}
//CAN中断处理过程
//此函数会被CAN_Receive_IT()调用
//hcan:CAN句柄
void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* hcan)
{
int i=0;
//CAN_Receive_IT()函数会关闭FIFO0消息挂号中断,因此我们需要重新打开
__HAL_CAN_ENABLE_IT(&CAN1_Handler,CAN_IT_FMP0);//重新开启FIF00消息挂号中断
printf("id:%d\r\n",CAN1_Handler.pRxMsg->StdId);
printf("ide:%d\r\n",CAN1_Handler.pRxMsg->IDE);
printf("rtr:%d\r\n",CAN1_Handler.pRxMsg->RTR);
printf("len:%d\r\n",CAN1_Handler.pRxMsg->DLC);
for(i=0;i<8;i++)
printf("rxbuf[%d]:%d\r\n",i,CAN1_Handler.pRxMsg->Data[i]);
}
#endif
//can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)
//len:数据长度(最大为8)
//msg:数据指针,最大为8个字节.
//返回值:0,成功;
// 其他,失败;
u8 CAN1_Send_Msg(u8* msg,u8 len)
{
u16 i=0;
CAN1_Handler.pTxMsg->StdId=0X12; //标准标识符
CAN1_Handler.pTxMsg->ExtId=0x12; //扩展标识符(29位)
CAN1_Handler.pTxMsg->IDE=CAN_ID_STD; //使用标准帧
CAN1_Handler.pTxMsg->RTR=CAN_RTR_DATA; //数据帧
CAN1_Handler.pTxMsg->DLC=len;
for(i=0;i<len;i++)
CAN1_Handler.pTxMsg->Data[i]=msg[i];
if(HAL_CAN_Transmit(&CAN1_Handler,10)!=HAL_OK) return 1; //发送
return 0;
}
//can口接收数据查询
//buf:数据缓存区;
//返回值:0,无数据被收到;
// 其他,接收的数据长度;
u8 CAN1_Receive_Msg(u8 *buf)
{
u32 i;
if(HAL_CAN_Receive(&CAN1_Handler,CAN_FIFO0,0)!=HAL_OK) return 0;//接收数据,超时时间设置为0
for(i=0;i<CAN1_Handler.pRxMsg->DLC;i++)
buf[i]=CAN1_Handler.pRxMsg->Data[i];
return CAN1_Handler.pRxMsg->DLC;
}