rt-thread的CAN驱动框架应该是参考了STM32的CAN模块硬件实现,其中邮箱,标识符列表模式,标识符屏蔽位模式等都与STM32的CAN模块的相似。在适配其它芯片厂商的驱动时,这部分可能会有一定困扰,但是不妨碍对整个驱动框架的学习。
1.结构体说明
rt_can_device
如上图所示,CAN驱动框架的顶层结构体是 struct rt_can_device
,主要成员变量如下:
hdr
:指向一个硬件过滤器数组,数组元素类型是 struct rt_can_hdr
can_rx
:指向CAN的rx_fifo
can_tx
:指向CAN的tx_fifo
rt_can_rx_fifo
CAN接收帧的管理结构体
buffer
:指向接收msg的数组,成员变量是struct rt_can_msg_list
类型,RTT默认配置是16个接收msg_list,即最大可接收16个msg
freenumber
:16个msg_list中,有几个没有存储CAN msg。初始化时,freenumber为16,每接收一个msg,freenumber减1
freelist
:freelist头指针。初始化时,16个msg_list都挂载在freelist链表上,如上图中的non msg。每接收一个msg,从freelist上移除一个msg_list结点,然后再加入到uselist链表中
uselist
:uselist头指针。每接收一个msg,就会将该msg所对应的msg_list挂载到uselist链表上,如上图中的msg0/1/2。每次调用rt_device_read
时,会将读出的msg所对应的msg_list移出uselist链表,再加入到freelist链表。
rt_can_msg
hdr_index
:接收该msg的硬件过滤器编号
rt_can_msg_list
list
:list指针。通过该变量,将msg_list挂载到freelist或uselist
hdrlist
:hdrlist指针。如果当前接收的msg的hdr值所对应的硬件过滤器已使能(可参考过滤器配置),则该msg不仅会挂载到uselist链表,同时也会挂载到所对应硬件过滤器的list链表,如上图中的msg0/1,不仅挂载在uselist链表,同时也挂载在hdr0的list链表。如果当前接收的msg的hdr值所对应的硬件过滤器未使能,则该msg只会加入到uselist链表,而不会加入到硬件过滤器的链表,如上图中的msg2。
rt_can_filter_item
硬件过滤器配置项描述结构体
id
:该过滤器需要过滤的id
ide
:该过滤器是过滤标准帧还是扩展帧
rtr
:该过滤器是过滤数据帧还是远程帧
hdr_bank
:该过滤器编号
rt_can_filter_config
硬件过滤器配置描述结构体
count
:该config包含几个硬件过滤器
actived
:是否激活硬件过滤器
items
:指向硬件过滤器数组的指针,数组中可能包含多个硬件过滤器配置,每个数组元素对应一个硬件过滤器的配置
rt_can_hdr
connected
:指示该硬件过滤器是否连接。若硬件过滤器配置项rt_can_filter_item中的hdr_bank为-1,则在设置时不会将contected置1
msgs
:该硬件过滤器接收了多少个msg
filter
:硬件过滤器配置项。在设置硬件过滤器时,会从rt_can_filter_config中复制到该filter
list
:用于挂载该硬件过滤器接收到的msg
2.open流程
static rt_err_t rt_can_open(struct rt_device *dev, rt_uint16_t oflag)
{
if (can->can_rx == RT_NULL)
{
if (oflag & RT_DEVICE_FLAG_INT_RX)
{
int i = 0;
struct rt_can_rx_fifo *rx_fifo;
//为 rx_fifo 分配空间,RTT默认分配16个 rt_can_msg_list,即可接收16个CAN消息。
//16个CAN消息的存储空间在 struct rt_can_rx_fifo 结构体后面
rx_fifo = (struct rt_can_rx_fifo *) rt_malloc(sizeof(struct rt_can_rx_fifo) +
can->config.msgboxsz * sizeof(struct rt_can_msg_list));
RT_ASSERT(rx_fifo != RT_NULL);
//获取第一个struct rt_can_msg_list的地址,并将16个struct rt_can_msg_list清0
rx_fifo->buffer = (struct rt_can_msg_list *)(rx_fifo + 1);
rt_memset(rx_fifo->buffer, 0, can->config.msgboxsz * sizeof(struct rt_can_msg_list));
//初始化freelist和uselist
rt_list_init(&rx_fifo->freelist);
rt_list_init(&rx_fifo->uselist);
//设置freenumbers初始值,RTT默认配置为16
rx_fifo->freenumbers = can->config.msgboxsz;
for (i = 0; i < can->config.msgboxsz; i++)
{
//将struct rt_can_msg_list加入到freelist链表
rt_list_insert_before(&rx_fifo->freelist, &rx_fifo->buffer[i].list);
#ifdef RT_CAN_USING_HDR
//初始化struct rt_can_msg_list的hdrlist
rt_list_init(&rx_fifo->buffer[i].hdrlist);
rx_fifo->buffer[i].owner = RT_NULL;
#endif
}
//将rx_fifo赋值给can_rx
can->can_rx = rx_fifo;
}
}
if (can->can_tx == RT_NULL)
{
if (oflag & RT_DEVICE_FLAG_INT_TX)
{
int i = 0;
struct rt_can_tx_fifo *tx_fifo;
//为tx_fifo分配空间
tx_fifo = (struct rt_can_tx_fifo *) rt_malloc(sizeof(struct rt_can_tx_fifo) +
can->config.sndboxnumber * sizeof(struct rt_can_sndbxinx_list));
RT_ASSERT(tx_fifo != RT_NULL);
tx_fifo->buffer = (struct rt_can_sndbxinx_list *)(tx_fifo + 1);
rt_memset(tx_fifo->buffer, 0,
can->config.sndboxnumber * sizeof(struct rt_can_sndbxinx_list));
rt_list_init(&tx_fifo->freelist);
for (i = 0; i < can->config.sndboxnumber; i++)
{
rt_list_insert_before(&tx_fifo->freelist, &tx_fifo->buffer[i].list);
rt_completion_init(&(tx_fifo->buffer[i].completion));
tx_fifo->buffer[i].result = RT_CAN_SND_RESULT_OK;
}
rt_sprintf(tmpname, "%stl", dev->parent.name);
//初始化信号量sem,RTT默认配置can->config.sndboxnumber为1,即sem初始值为1
rt_sem_init(&(tx_fifo->sem), tmpname, can->config.sndboxnumber, RT_IPC_FLAG_FIFO);
can->can_tx = tx_fifo;
}
}
//初始化硬件过滤器
if (can->hdr == RT_NULL)
{
int i = 0;
struct rt_can_hdr *phdr;
//为硬件过滤器分配空间
phdr = (struct rt_can_hdr *) rt_malloc(can->config.maxhdr * sizeof(struct rt_can_hdr));
RT_ASSERT(phdr != RT_NULL);
rt_memset(phdr, 0, can->config.maxhdr * sizeof(struct rt_can_hdr));
for (i = 0; i < can->config.maxhdr; i++)
{
//初始化硬件过滤器的list链表
rt_list_init(&phdr[i].list);
}
can->hdr = phdr;
}
}
3.接收流程
3.1.过滤器配置
CAN框架通过struct rt_can_filter_item
配置每个硬件过滤器的具体配置:需要过滤的ID,是否是扩展帧/远程帧,以及配置硬件过滤器的hdr_bank(即硬件过滤器号)。框架支持配置多个硬件过滤器,多个硬件过滤器统一通过struct rt_can_filter_config
进行管理。设置硬件过滤器通过rt_device_control
接口配置。配置过程如下:
case RT_CAN_CMD_SET_FILTER:
//调用芯片硬件底层驱动,将过滤器配置设置到寄存器
res = can->ops->control(can, cmd, args);
if (pfilter->actived)
{
//通过while循环逐个设置硬件过滤器
while (count)
{
//如果hdr == -1或hdr >= can->config.maxhdr,则直接开始下一次循环
if (pitem->hdr_bank >= can->config.maxhdr || pitem->hdr_bank < 0)
{
count--;
pitem++;
continue;
}
level = rt_hw_interrupt_disable();
//contected为0,则进行配置
if (!can->hdr[pitem->hdr_bank].connected)
{
rt_hw_interrupt_enable(level);
//将硬件过滤器的配置复制到can->hdr[].filter
rt_memcpy(&can->hdr[pitem->hdr_bank].filter, pitem,
sizeof(struct rt_can_filter_item));
level = rt_hw_interrupt_disable();
//将contected赋值为1
can->hdr[pitem->hdr_bank].connected = 1;
can->hdr[pitem->hdr_bank].msgs = 0;
rt_list_init(&can->hdr[pitem->hdr_bank].list);
}
rt_hw_interrupt_enable(level);
count--;
pitem++;
}
}
由上述配置流程可知,CAN模块可以配置多个硬件过滤器(最大可配置硬件过滤器数量不超过can->config.maxhdr)。只有在rt_can_filter_item配置项中hdr_bank不等于-1,且小于can->config.maxhdr时,才会初始化硬件过滤器的list链表,在接收到msg时,才会把该msg也加入到硬件过滤器的list链表。否则,接收的msg只会加入到uselist链表。
3.2.接收isr
rt_hw_can_isr
{
case RT_CAN_EVENT_RX_IND:
/* 调用底层驱动,接收CAN消息 */
can->ops->recvmsg(can, &tmpmsg, no);
/* 接收消息数自增 */
can->status.rcvpkg++;
can->status.rcvchange = 1;
if (!rt_list_isempty(&rx_fifo->freelist)) /* freelist 链表不为空,即有空闲的 struct rt_can_msg_list */
{
//从 freelist 链表上分配一个 struct rt_can_msg_list 结点,并将该结点从 freelist 链表删除,freenumbers 减1
}
else if (!rt_list_isempty(&rx_fifo->uselist))
{
// freelist 链表为空,并且 uselist 不为空,则从 uselist 上分配一个结点,
//因为 uselist 链表上的结点都是已接收到的 msg,所以分配一个结点,等于是丢弃一个已接收到的消息。
//所以,需要将 dropedrcvpkg 加1
can->status.dropedrcvpkg++;
//将分配到的结点从 uselist 上删除。
}
//将 tmpmsg 复制到 listmsg->data, 并将该结点插入到 rx_fifo->uselist 链表中。
//如果定义了 RT_CAN_USING_HDR, 并且硬件过滤器的contected为1。
//则会将该结点通过 listmsg->hdrlist 插入到过滤器的 can->hdr[hdr].list 链表上。
if (can->hdr != RT_NULL && can->hdr[hdr].connected && can->hdr[hdr].filter.ind)
{
//如果 can->hdr 不为空, 且 can->hdr[hdr].connected 为1, 并且过滤器的回调函数已定义
//则调用过滤器的回调函数。
}
else
{
//CAN设备的回调函数已定义,计算 rxfifo 的 uselist 链表的长度(即有多少个 msg 在 uselist 链表中还未取出)。
//调用 回调函数,参数为还未从 uselist 链表中取出的字节数。
//所以, rt_device_read 一次可以读取多个 msg
}
}
4.发送流程
rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *data, int msgs)
{
while (msgs)
{
//先获取信号量,只有在一个CAN msg写入底层驱动后,才会释放信号量
rt_sem_take(&(tx_fifo->sem), RT_WAITING_FOREVER);
level = rt_hw_interrupt_disable();
tx_tosnd = rt_list_entry(tx_fifo->freelist.next, struct rt_can_sndbxinx_list, list);
RT_ASSERT(tx_tosnd != RT_NULL);
rt_list_remove(&tx_tosnd->list);
rt_hw_interrupt_enable(level);
no = ((rt_uint32_t)tx_tosnd - (rt_uint32_t)tx_fifo->buffer) / sizeof(struct rt_can_sndbxinx_list);
tx_tosnd->result = RT_CAN_SND_RESULT_WAIT;
//调用底层驱动,写入发送msg
if (can->ops->sendmsg(can, data, no) != RT_EOK)
{
}
can->status.sndchange = 1;
//等待发送完成,完成量是通过rt_hw_can_isr置位的
rt_completion_wait(&(tx_tosnd->completion), RT_WAITING_FOREVER);
//释放信号量,表示已调用底层驱动完成发送msg的写入
rt_sem_release(&(tx_fifo->sem));
}