rt-thread CAN驱动框架

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));
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
作者:annysky 概述智能家居是目前最火热的应用方向,基于对物联网和智能家居的热衷和喜爱,以自己小屋为实际应用模板,将智能家居的设想变为现实状态。本智能家居应用平台基于STM32H7和STM32F4为主芯片进行设计,分为主控平台和网关平台。主控平台以ART-PI开发板为核心,主要任务是查询网关平台的数据信息、查询和设置参数、与云平台进行交互等;网关平台以STM32F407为核心,主要任务是采集数据和分析处理数据、并将主控平台下发的指令进行分析处理后来控制终端,该平台目前采集4路温湿度、1路电量、1路甲醛、1路PM2.5、2路烟雾报警、1路水浸报警、2路门锁状态的数据,输出控制1路加热和散热,通过CAN通讯与主控平台进行联络,并且使用屏进行显示各个状态,还可以使用WLAN与Onenet进行数据交互。后续根据需要在CAN网络上增加设备或模块(智能窗帘、CAN温湿度传感器、智能继电器、门禁系统、摄像头等),能够监测和控制终端产品。 开发环境硬件: (1)主控:ART-PI(STM32H750XB),ART-PI-DOCK扩展板(含屏) (2)网关:WT-19S42(STM32F407VGT6) (3)RT-Thread版本:RT-Thread V 4.0.3 (4)开发工具及版本:MDK 5.27,CANTest,XCOM V2.0,VGUS2020,DGUS_V759-t5l RT-Thread使用情况概述(1)内核部分:调度器,信号量,消息队列,libcpu/BSP。 调度器:创建多个线程来实现不同的工作。 信号量:用来同步线程。 消息队列:用来实现线程之间传递的数据。 libcpu/BSP:外设CAN驱动、UART驱动。 (2)组件部分:CAN框架,UART框架, CAN框架:使用FDCAN2框架来与网关平台进行数据交互,上层代码可以提高代码的可重用性。 UART框:使用UART框架来与ART-PI-DOCK扩展板进行数据交互。 (3)软件包部分: cJSON:C语言实现的极简的解析 JSON 格式的软件包。 WebNet软件包:由RT-Thread 自主研发的,基于 HTTP 协议的 Web 服务器实现,它不仅提供设备与 HTTP Client 通讯的基本功能,而且支持多种模块功能扩展,且资源占用少、可裁剪性强,充分满足开发者对嵌入式设备服务器的功能需求。 Onenet: RT-Thread 针对 OneNET 平台连接做的的适配,通过这个软件包,可以让设备在 RT-Thread 上非常方便的连接 OneNet 平台,完成数据的发送、接收、设备的注册和控制等功能。 硬件框架总方案原理图 智能家居应用平台方案原理图如图1所示,主要由两部分组成(目前状态,后续根据实际应用情况进行扩展),分别是主控平台和网关平台。 图1总方案原理图 主控平台: 主控平台的硬件分为两部分。一是ART-PI开发板,是由RT-Thread 设计的开源硬件,核心芯片是STM32H750XB,主频最高达480M,支持超低功耗。开发板板载功能众多,标配 TYPE-C 接口,板载 WIFI, 蓝牙配网,还有 LCD(RGB888),SDRAM, TF-Card, USB-OTG等应有尽有。 另外一部分是ART-PI-DOCK扩展板:根据ART-PI开发板的扩展接口自主设计的ART-PI-DOCK扩展板。主要的功能是通讯和人机交互,不参与对外设传感器或者模块的控制和信号采集(除光敏和温湿度)。板子主要包含以下几种功能: (1)2路CAN通讯 (2)1路485通讯; (3)1路232通讯; (4)1路TTL通讯; (5)1路WLAN网络通讯; (6)CAN匹配电阻设置; (7)主机ID设置; (8)1路温湿度传感器; (9)1路光敏传感器; (10)1路锂电池充电管理模块接口; (11)1路外设5V输入接口; (12)1路串口屏接口; (13)1路TFT液晶屏接口。 扩展板涵盖的功能非常全面,包含了目前应该最广泛的几种通讯方式和人机交互方式(可选),可适应不同应用场景。 图2 ART-PI-DOCK原理框图 网关平台 网关核心芯片是STM32F407VGT6,1024k Flash,192KB SRAM,工作频率为168 MHz。基于STM32F407强大的功能,在设计网关平台时,使用了符合IEEE 1588 v2标准要求的以太网MAC10/100,并使用了高达6个USART设备。另外使用了1路CAN,4路串行设备,12路开关量输入,8路开关量输出,各个接口均进行了标识。 图3网关平台原理框图 软件框架说明图4 主控平台软件流程图 图5 网关平台软件流程图 本项目采用的是主控与网关两个平台,主控平台完成对网关平台数据的处理显示,并对网关平台的参数

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值