CAN是Controller Area Network的缩写,是ISO国际标准化的串行通信协议。是德国博世公司开发面向汽车的CAN通信协议。
CAN控制器根据两根线上的电位差来判断总线电平。总线电平分为显性电平和隐形电平,二者必居其一。
CAN协议具有以下特点:
1.多主控制,在总线空闲时多个单元都可以发送消息,而两个单元以上同时开始发送消息时,根据标识符(identifier 简称ID)决定优先级。ID表示访问总线的消息的优先级。优先级高的继续发,优先级低的就停止发送了。
2.系统的柔软性。增加单元时不用改硬件,直接接上即可(速度需要与总线速度一致)。
3.通信速度快,通信距离远。最高1Mbps(小于40M),最远可达10KM(低于5Kbps)
4.具有错误检测、错误通知和错误恢复功能。
5.故障封闭功能。
6.连接节点多。
CAN协议经过ISO标准化有两个标准:ISO11898(125Kbps~1Mbps高速通信标准)和ISO11519-2(125Kbps以下低速通信标准)。
标识符过滤是一种过滤标识符的功能,它可以将想接收的标识符的过滤接收,也可以将想拒绝的标识符过滤掉。它的存在减少了CPU处理CAN通信的开销。
STM32F429共有标识符过滤器28组,每一组由两个32位的寄存器,CAN_FxR1和CAN_FxR2组成。而这个32位寄存器可以根据配置为1个32位过滤器,也可以为两个16位过滤器。
为了过滤一组标识符,应该设置过滤器组工作在屏蔽位模式。
为了过滤一个标识符,应该设置过滤器组工作在标识符列表模式。
掩码就是对用ID的哪一位需要相匹配。比如掩码CAN_FxR2[7:0]:0111 1110则表示标识符CAN_FxR1[7:0]的第一位和第八位不用匹配,其余位需要匹配。
通过这个过滤器就可以让想接收的标识符的数据到FIFO中(我们可以直接从FIFO中读接收到的数据,这就是为什么可以节省MCU的开销),过滤掉不想要的标识符的数据。
STM32F429提供了两种测试模式,环回模式和静默模式。如果只有一个板子的话可以用环回模式测CAN是否好用。环回模式CAN把发送的报文同时保存在接收邮箱中(通过过滤器),也就是自收发。
正常模式,也就是两个板子相连,一个发送一个接收。但我资金有限,只有一个板子。也没有什么USB转CAN的东西。所以我们在实验的时候需要有一个从正常模式切换至环回模式的过程。用环回模式去测试CAN是否好用。
CAN的一包只能发送最大8个字节,接收最大也是8个字节。
CAN我看了一遍,比较复杂的说,所以前面也只是简单写一下值得注意的地方,具体用到还是建议看一遍相关资料,并且再看一遍原子的实验说明。
接下来就是直接配置cubemx实现CAN通信。我们发送固定的字符串,看是否能接收到,显示在屏幕上。
如果想要500Kbps的速度,这样配置:
已知APB1 peripheral = 45MHZ,而CAN波特率= 45000/((BS1+BS2+1)*prescaler) = 45000/(10*6) = 500Kpbs
如何知道是500Kpbs呢?我们看到time for one bit经过分频和规划得到1999ns,那么1/1999ns = 500Kpbs。
生成代码后,cubemx会生成CAN的初始化,但是距离发送数据和接收数据我们还欠缺一些配置。比如指定发送结构体指针和接收结构体指针。配置好后我们可以像串口一样,在发送前设置好发送结构体指针。在接收后读取接收结构体指针。
还有过滤器的配置。如果不配置的话,消息是无法到FIFO中的,我们也就读不到了。
CanTxMsgTypeDef TxMessage; //发送消息
CanRxMsgTypeDef RxMessage; //接收消息
//mode; 正常模式 CAN_MODE_NORMAL
// 回环模式 CAN_MODE_LOOPBACK
void CAN1_Init(uint32_t u32Mode)
{
CAN_FilterConfTypeDef CAN1_FilerConf;
hcan1.pTxMsg = &TxMessage; //cubemx并没有指定发送结构体指针,需要自己指定
hcan1.pRxMsg = &RxMessage; //cubemx并没有指定接收结构体指针,需要自己指定
hcan1.Init.Mode = u32Mode; //模式可以在此更换
if (HAL_CAN_Init(&hcan1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
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 掩码设置为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(&hcan1,&CAN1_FilerConf)!=HAL_OK) //滤波器初始化
{
_Error_Handler(__FILE__, __LINE__);
}
}
发送函数:
//can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)
//len:数据长度(最大为8)
//msg:数据指针,最大为8个字节.
//返回值:1,成功;
// 0,失败;
uint8_t CAN1_Send_Msg(uint8_t* pu8Buff, uint8_t u8Len)
{
uint16_t i=0;
if(u8Len>8) //不能超过8个字节
return 0;
hcan1.pTxMsg->StdId = 0X12; //标准标识符
hcan1.pTxMsg->ExtId = 0x12; //扩展标识符(29位)
hcan1.pTxMsg->IDE = CAN_ID_STD; //使用标准帧
hcan1.pTxMsg->RTR = CAN_RTR_DATA; //数据帧
hcan1.pTxMsg->DLC = u8Len;
for(i=0; iData[i] = pu8Buff[i];
if(HAL_CAN_Transmit(&hcan1,10)!=HAL_OK) return 0; //发送
return 1;
}
接收函数:
//can口接收数据查询
//buf:数据缓存区;
//返回值:0,无数据被收到;
// 其他,接收的数据长度;
uint8_t CAN1_Receive_Msg(uint8_t *pu8Buff)
{
uint32_t i;
if(HAL_CAN_Receive(&hcan1,CAN_FIFO0,0)!=HAL_OK) return 0; //接收数据,超时时间设置为0。若没有接收到数据则直接跳出,返回0
for(i=0; iDLC; i++) //若接收到数据,则向pu8Buff复制 接收到长度的数据
pu8Buff[i] = hcan1.pRxMsg->Data[i];
return hcan1.pRxMsg->DLC; //返回接收到了多少数据
}
在main()中实现:
1.按下KEY_UP切换模式(正常模式-回环模式),在正常模式时LED1灭,在回环模式下LED1亮。
2.在回环模式下按下KEY1,CAN输出字符串TEXT_Buffer
3.按下KEY0更换TEXT_Buffer的内容(hello w"或"hello ")。
4.LCD显示接收到的字符串以及发送成功的字符串。
CAN总线很“智能”,应用于多节点的汽车行业真的舒服,多主的特点让优先级高的单元拥有更高的“话语权”,检测错误的功能也使总线更加安全。需要注意的是一个总线上所有的单元速度应该一致。
虽然他的优点很多,但是缺点是一包数据只能携带8个字节,在两点间数据量大的情况下就不太试用了,。实时上,汽车上并没有数据量很大的交流,一般的发动机转速、故障信息等都可以简单地用几个字节表示。所以很适用于汽车这样单元多,数据量少的环境。