笔者来聊聊can的认识以及can接收数据的驱动编写
1、STM32 Can 外设的认识
Can的特性就不多说了,主要来聊聊can的一些标识符以及收发状态。can有一套收发机制,发送和接收都有硬件缓存,叫邮箱,通过下面的图可以看出,下面这张图很经典,被各大网站和博客上面引用。
发送时,有三个报文邮箱缓存,接收时有两个fifo缓存,每个fifo有三个邮箱。
从上面图还可以看出,can数据接收到fifo,是经过滤波器过滤才可以到fifo,总共有27个过滤器(互联型,其他只有13个)每个标识符经过一个滤波器后就可以到接收fifo。
1.1、发送过程
3个发送报文缓存邮箱,发送时如果有之前的报文没发送出去,会缓存在邮箱里面,等待发送。
之所以用邮箱,是因为邮箱不仅可以有优先级发送,也可以顺序发送(fifo)
发送时的几个状态:
- 空状态(TME=1):邮箱空闲状态,此时肯定能请求成功,假如此时3个邮箱都不空闲,可能请求失败
- 挂起状态(TME=0 RQCP=0 TXOK=0):TXRQ=1,请求发送数据,此时等待邮箱成为最高优先级然后发送数据(can标识符低的优先发送),因为可能存在多个邮箱数据等待发送;此时也有情况发生邮箱已满,此时就只能等待,后面发送数据,那前面状态则不变。
- 已安排状态(TME=0 RQCP=0 TXOK=0):邮箱成为最高优先级,此时准备发送数据,准备把can数据扔到总线上面(可能此时can总线上面正在繁忙,所以要等待can总线空闲)
- 发送状态(TME=0 RQCP=0 TXOK=0):此时can数据发送到总线上面;有自动重发选项,可以一直发送(等can总线空闲之后);也有终止请求, 可以终止发送,则会存在多种情况。
- 发送成功(TME=1 RQCP=1 TXOK=1):数据发送成功
- 发送失败(TME=1 RQCP=1 TXOK=0):数据发送失败,
1.2、标识符过滤
can总线上面一般会有多个设备,那么总线上的数据,多个设备都会收到,那么就需要指明“地址”进行过滤掉其他数据。
can里面一般会用标识符规则来过滤数据,会根据此标识符确定是否接收该数据,一般不说标识符是can的节点,因为标识符可修改,或者该标识符可以过滤筛选一组can数据,
过滤通常有两种方法,标识符列表模式,标识符掩码模式
- 标识符列表模式:接收的标识符和过滤器指定标识符必须完全匹配,则会被过滤出来,送到fifo邮箱。
- 标识符掩码模式:接收的标识符和过滤器指定的标识符部分位匹配则就可以被过滤出来,送到fifo邮箱。
换句话说:筛选一组标识符,则选用掩码模式,筛选特定标识符,则选用标识符列表模式。
每个过滤器有两个寄存器可用,均是32位,可以拆分为2个16位的寄存器来使用,则相当于多了1过滤器来使用。
初步看上面这张表,笔者也看的不是很懂,后面仔细研究过后,其实这是一个过滤器的多种配置,
- 32位模式下:
- 掩码模式:标识符寄存器和掩码寄存器为一个过滤器,以标识符寄存器的值为准(笔者亲测),所以其过滤编号也是1个
- 列表模式:两个寄存器分别代表一个ID,所以是两个过滤器,标识符寄存器在前,掩码在后(过滤器编号)。
- 16位模式下:
- 掩码模式:两个寄存器的低16位和高16位各组成一个32位,总共作为两个过滤器,
- 列表模式:两个寄存器的高低16位都组成一个过滤器,总共作为四个过滤器。
然后接下来这张表就可以看懂了。多个过滤器的一种配置方式,以及编号,该编号可以再收到数据的时候打印出来,可以判断是哪个过滤器接收到的数据。
注意:
- 只要一个过滤器满足条件就可以过滤到FIFO中,不是同时满足所有的过滤器的要求。
- 如果同时满足多个筛选器,有优先级规则,
- 32 位筛选器优先于 16 位筛选器。
- 对于尺度相等的筛选器, 标识符列表模式优先于标识符掩码模式。
- 对于尺度和模式均相等的筛选器,则按筛选器编号确定优先级(编号越低,优先级越高)
typedef struct
{
uint32 t stdid;
uint32 t ExtId;
uint8 t IDE;
uint8 t RTR;
uint8 t DLC;
uint8 t Data[8];
uint8 t FMI;
}CanRxMsg;
/* CanRxMsg.FMI 就是指示了哪个过滤器的过滤出来的该数据,
1.3、接收过程
接收时上述已经提过,有两个fifo,每个fifo有3个邮箱,那么总共可以存储6个报文(硬件存储)。
共有三个状态:有报文,报文满,报文溢出。
FMP:指示了当前有多少报文
FOVR:指示是否溢出,
需要注意的是:
- 两个fifo是独立的,不会说是FIFO0满了,放到FIFO1,这是由过滤器决定的,满足哪个过滤器,就会放到该fifo,即使是溢出了。
- 所以接收报文的时候,每次缓存最多就是3个报文,再多就溢出了,如果需要更大缓存, 可以软件上面加缓存,下文介绍。
- 两个fifo,对应的是两个接收中断。
2、STM32 Can驱动函数的编写
can中断接收数据
CanRxMsg Can1RxMessage;
u8 can1_data_flag;
u8 can1_data_len;
u8 can1_broadcast_flag;
void CAN1_RX0_IROHandler(void)
{
/*receive a can messaae*/
CAN_Receive(CAN1,CAN FIFO,&Can1RxMessage);
can1_data_flag = TRUE;
can1_data_len = Can1RxMessage.DLC;
/*check if is broadcast data */
if(CAN_BROADCAST_CMD== Can1RxMessage.ExtId )
can1_broadcast_flag = TRUE;
else
can1_broadcast_flag = FALSE:
}
CAN_EIFORelease(CAN1, CAN_FIFO):
CAN_IT_Config(CAN1, CAN_IT_FMP0, DISABLE);
}
上面驱动程序只是一个简单的示例来接收can的程序,其有以下缺点:
- 接收一个8字节,然后处理一次,然后才再可以接收,效率低,没有发挥中断的优势,将中断和处理串起来。
- 没有发挥硬件fifo的作用(3个)
- 超过3个fifo的数据来临时,没有缓存能力
软件fifo接收数据。
CanRxMsg Can1RxMessage;
u8 can_fifo_index ;
u8 can_rx_fifo[1024];
u8 can1_data_flag ;
u8 can1_broadcast_flag ;
void CAN1_RX0_IRQHandler(void)
{
if(CAN_GetFlagstatus(CAN1,CAN_FLAG_FOV0))
printf("OVER CAN\r\n");
u8 fifo_count;/* 3 hardware fifo for can rx */
for(fifo_count=0; fifo_count<3; fifo_count++)
{
if(CAN_GetFlagstatus(CAN1,CAN_FLAG_FMPO))
{
/*receive a can data*/
if(can_fifo_index >= CAN_RX_FIFO_MAX_SIZE) /*1024*/
{
printf("over 1k \r\n");
CAN_ITConfig(CAN1, CAN_IT_FMPO, DISABLE);
break;
}
else
{
CAN Receive(CAN1, CAN FIFO, &Can1RxMessage);
memcpy(&can_rx_fifo[can_fifo_index], (u8 *)8Can1RxMessage.Data[0], Can1RxMessage.DLC);
can_fifo_index += Can1RxMessage.DLC;
CAN_FIFORelease(CAN1,CAN_FIFOO);
}
can1_data_flag = TRUE;
/*check if is broadcast data */
if(CAN_BROADCAST_CMD == Can1RxMessage.ExtId)
can1_broadcast_flag = TRUE;
else
can1_broadcast_flag = FALSE:
}
else
break;
}
}
以上代码也只是demo,相应的错误处理没有增加,在溢出时,只是增加了打印,比如可以增加标志位,然后将其传递出去。