SylixOS SylixOS CAN总线驱动之三

  1. SylixOS CAN报文传送流程

  2. CAN报文传送流程框图

    在SylixOS中CAN报文的传输框图如图 11所示。

    图 11 SylixOS CAN报文传输框图

    (注:此文档承接之前的文档编写,之前文档中详细介绍过的报文,传输结构体在此文档中不做详细介绍。)

  3. SylixOS CAN报文缓存机制

    在SylixOS中CAN报文的传输不是底层和上层应用的直接传输。而是在底层和应用层中间加了一层系统缓存队列。所有收发的CAN报文都要先经过一个系统缓存机制传输到真正调用到它的地方。

  4. 系统CAN发送报文缓存

    SylixOS中CAN报文是以消息队列的方式进行缓存的,程序清单 21是向消息队列中写入一帧CAN报文的具体实现。

    程序清单 21从缓存中读取一帧CAN报文

    /*********************************************************************************************************
    ** 函数名称: __canITx
    ** 功能描述: 从发送缓冲区中读出一个数据
    ** 输 入  :
    **           pcanDev           CAN 设备
    **           pcanframe         指向待读出的数据
    ** 输 出  : ERROR_NONE or PX_ERROR
    ** 全局变量:
    ** 调用模块:
    *********************************************************************************************************/
    static INT  __canITx (__CAN_DEV  *pcanDev, PCAN_FRAME  pcanframe)
    {
        INTREG  iregInterLevel;
        INT     iTemp = 0;
    
        if (!pcanDev || !pcanframe) {
            return (PX_ERROR);
        }
    
        iTemp = __canReadQueue(pcanDev,
                               pcanDev->CAN_pcanqSendQueue,
                               pcanframe, 1);                               /*  从发送队列中读取一帧数据    */
    
        LW_SPIN_LOCK_QUICK(&pcanDev->CAN_slLock, &iregInterLevel);
        if (iTemp <=  0) {
            pcanDev->CAN_canstatWriteState.CANSTAT_bBufEmpty = LW_TRUE;     /*  发送队列空                  */
        }
        LW_SPIN_UNLOCK_QUICK(&pcanDev->CAN_slLock, iregInterLevel);
    
        API_SemaphoreBPost(pcanDev->CAN_ulSendSemB);                        /*  释放信号量                  */
        SEL_WAKE_UP_ALL(&pcanDev->CAN_selwulList, SELWRITE);                /*  释放所有等待写的线程        */
    
        return ((iTemp) ? (ERROR_NONE) : (PX_ERROR));
    }
  5. 系统CAN接收报文缓存

    上层应用向底层传输一帧CAN报文的时候也是通过系统缓存,向系统缓存中写入一帧CAN报文的具体实现如程序清单 22所示。

    程序清单 22向缓存中写入一帧CAN报文

    /*********************************************************************************************************
    ** 函数名称: __canIRx
    ** 功能描述: 向接收缓冲区中写入一个数据
    ** 输 入  :
    **           pcanDev            CAN 设备
    **           pcanframe          指向待写入的数据
    ** 输 出  : ERROR_NONE or PX_ERROR
    ** 全局变量:
    ** 调用模块:
    *********************************************************************************************************/
    static INT  __canIRx (__CAN_DEV  *pcanDev, PCAN_FRAME   pcanframe)
    {
        INT     iTemp = 0;
    
        if (!pcanDev || !pcanframe) {
            return (PX_ERROR);
        }
    
        iTemp = __canWriteQueue(pcanDev,
                                pcanDev->CAN_pcanqRecvQueue,
                                pcanframe, 1);                              /*  往接收队列中写入一帧数据    */
    
        API_SemaphoreBPost(pcanDev->CAN_ulRcvSemB);                         /*  释放信号量                  */
        SEL_WAKE_UP_ALL(&pcanDev->CAN_selwulList, SELREAD);                 /*  select() 激活               */
    
        return ((iTemp) ? (ERROR_NONE) : (PX_ERROR));
    }
  6. 具体调用实现

  7. 应用层传输到驱动层具体实现

    第一步:如程序清单 31所示,在应用层创建一个线程,打开一个CAN设备。

    程序清单 31打开CAN设备

        iFd = open(devname, O_RDWR, 0666);
        if (iFd < 0) {
            printf("failed to open %s!\n", devname);
            return  (LW_NULL);
        }

    第二步:如程序清单 32所示,填充一个CAN报文结构体。

    程序清单 32填充CAN报文

     CAN_FRAME canframe;
    canframe.CAN_bExtId = LW_FALSE;
    canframe.CAN_bRtr = LW_FALSE;
    canframe.CAN_ucLen = CAN_MAX_DATA;
    lib_memcpy((CHAR *)canframe.CAN_ucData, "01234567", CAN_MAX_DATA);
    canframe.CAN_uiId = 0;

    第三步:如程序清单 33所示,调用write函数向系统TX缓存队列中写入一帧CAN报文,再调用ioctl函数实现底层传输。

    程序清单 33填充发送缓存

    stLen = write(iFd, &canframe, sizeof(CAN_FRAME));
    ioctl(iFd, CAN_DEV_STARTUP, 0);
    case CAN_DEV_STARTUP:
         __flexcanStartup(pCanchan);
         break;

    第四步:如程序清单 34所示,最终调用到底层传输函数。从系统队列中读取一帧CAN报文后对设备寄存器进行相关操作将消息传输到总线上。

    程序清单 34底层starup函数

    /*********************************************************************************************************
    ** 函数名称: __flexcanStartup
    ** 功能描述: 启动数据发送
    ** 输  入  : pCanchan    通道对象
    ** 输  出  : NONE
    ** 全局变量:
    ** 调用模块:
    *********************************************************************************************************/
    static INT __flexcanStartup (CAN_CHAN  *pCanchan)
    {
        FLEXCAN_CHAN   *pChannel = container_of(pCanchan, FLEXCAN_CHAN, CANCH_canchan);
        CAN_FRAME       canFrame;
        INT             iCount;
    
        if (!pChannel->CANCH_pcbGetTx) {
            return  (PX_ERROR);
        }
    
        while (pChannel->CANCH_pcbGetTx(pChannel->CANCH_pvGetTxArg,         /* 从发送缓冲区中读取数据发送   */
                                       &canFrame) == ERROR_NONE) {
            __flexcanSend(pChannel, &canFrame);
    
        }
    
        return  (ERROR_NONE);
    }

    注:如果想要发送多帧CAN报文,在写入操作结束后需要加等待,以确保所有的CAN报文都成功写入系统缓存队列中。

  8. 驱动层传输到应用层

    第一步:底层如果接收到CAN报文以后,会触发一次中断,在中断服务函数中所做的事就是判断状态标志位置,如果是接收中断,就把接收到的CAN报文通过回调函数写入,系统缓存队列中,具体实现如程序清单 35所示。

    程序清单 35 CAN底层中断服务函数

    /*********************************************************************************************************
    ** 函数名称: __flexcanIrq
    ** 功能描述: can 中断服务程序
    ** 输  入  : pChannel         通道对象
    **           ulVector         中断向量号
    ** 输  出  : ERROR CODE
    ** 全局变量:
    ** 调用模块:
    *********************************************************************************************************/
    static irqreturn_t __flexcanIrq (PVOID pvArg, ULONG ulVector)
    {
        FLEXCAN_CHAN  *pChannel;
        UINT32         uiIflag1, uiEsr, uiValue;
        CAN_FRAME      canframe;
    
        can_debug("[CAN]irq\r\n");
    
        pChannel = (FLEXCAN_CHAN *)pvArg;
        uiIflag1 = CAN_READ(FLEXCAN_IFLAG1);
        uiEsr    = CAN_READ(FLEXCAN_ESR1);
    
        uiValue = CAN_READ(FLEXCAN_CTRL1);
        uiValue &= ~(FLEXCAN_CTRL1_ERR_ALL);
        CAN_WRITE(FLEXCAN_CTRL1, uiValue);
        CAN_WRITE(FLEXCAN_IMASK1, 0);
    
        if (uiEsr & FLEXCAN_ESR1_ERR_ALL) {
            can_debug("There is something wrong!\n");
            CAN_READ(FLEXCAN_ESR1);
            CAN_WRITE(FLEXCAN_ESR1, FLEXCAN_ESR1_ERR_ALL);
        }
    
        if (uiEsr & FLEXCAN_ESR1_RX) {
            if (uiIflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) {
                CAN_WRITE(FLEXCAN_IMASK1, FLEXCAN_IFLAG_DEFAULT & ~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE);
                memset(&canframe, 0, sizeof(CAN_FRAME));
                __flexcanRecv(pChannel, &canframe);
                if (pChannel->CANCH_pcbPutRcv(pChannel->CANCH_pvPutRcvArg, &canframe) != ERROR_NONE) {
                    pChannel->CANCH_pcbSetBusState(pChannel->CANCH_pvSetBusStateArg,
                                                   CAN_DEV_BUS_RXBUFF_OVERRUN);
                }
            }
        }
    
        if (uiIflag1 & FLEXCAN_IFLAG_RX_FIFO_OVERFLOW) {
            can_debug("FIFO overflow\n");
            CAN_WRITE(FLEXCAN_IFLAG1, FLEXCAN_IFLAG_RX_FIFO_OVERFLOW);
        }
    
        CAN_WRITE(FLEXCAN_IMASK1, FLEXCAN_IFLAG_DEFAULT);
        return  (LW_IRQ_HANDLED);
    }

    第二步:在应用程序中创建一个线程,在线程中所做的事情就是不间断得读取系统缓存的消息队列。如果缓存不为空,就读取里面的CAN报文,并打印,具体操作如程序清单 36所示。

    程序清单 36 CAN应用层读取缓存

        while (1) {
            stLen = read(iFd, &canframe, sizeof(STR_CANMSG_T));
            stFrameNum = stLen / sizeof(STR_CANMSG_T);
    
            if (stFrameNum != 1) {
                printf("failed to recv can frame, abort recving!\n");
                break;
            } else {
                sprintf(cFramInfo, "id=%d, len=%d, data=%02x %02x %02x %02x %02x %02x %02x %02x\n",
                        canframe.Id, (INT)canframe.DLC,
                        canframe.Data[0],
                        canframe.Data[1],
                        canframe.Data[2],
                        canframe.Data[3],
                        canframe.Data[4],
                        canframe.Data[5],
                        canframe.Data[6],
                        canframe.Data[7]);
                printf(cFramInfo);
            }
        }

    CAN报文传输流程,到此结束。

  9. 免责声明

    内部交流文档,若发现相关错误或者建议,请及时联系文档创建者进行修订和更新。

时间: 2024-10-05 04:44:55

SylixOS SylixOS CAN总线驱动之三的相关文章

SylixOS iMX6平台I2C总线驱动

原理概述 I2C总线驱动概述 I2C总线驱动是I2C适配器的软件实现,提供I2C适配器与从设备间完成数据通信的能力,比如起始,停止,应答信号和MasterXfer的实现函数.驱动程序包含初始化I2C总线控制器__i2cHwInit函数,操作函数集(总线传输__i2cTransfer函数,总线控制__i2cMasterCtl函数). Imx6ul控制器的硬件描述 imx6ul处理器内部集成了一个I2C控制器,通过五个寄存器来进行控制. I2Cx_IADR I2C地址寄存器 I2Cx_IFDR I2

SylixOS中RTC设备驱动

1.概述 本文档基于SylixOS-EVB-i.MX6Q验证平台,介绍SylixOS中RTC设备驱动实现过程,可作为在SylixOS集成开发环境下进行字符设备驱动开发的参考. 2.RTC设备驱动 2.1硬件原理 实时时钟(RTC)的主要功能是在系统掉电的情况下,利用备用电源使时钟继续运行,保证不会丢失时间信息. i.MX6Q验证平台上使用的是外置实时时钟集成电路ISL1208.硬件接线如图 2.1所示. 图 2.1 RTC硬件接线 图中,X1和X2为内部反向放大器的输入和输出引脚,要求外置一个3

STM32的FSMC总线驱动ili9341,掉电重启无法正常显示的问题

问题描述 通过STM32的FSMC总线驱动ili9341,程序调试和刚下载的时候,显示完全正常.可是就在我掉电关机,重新启动的时候就完全跑飞了.这令我非常疑惑.以下是我的FSMC总线配置程序, static void LCD_FSMC_Config(void) { FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure; FSMC_NORSRAMTimingInitTypeDef p; p.FSMC_AddressSetupTime = 0x02; p

驱动程序分层分离概念_总线驱动设备模型_P

分层概念: 驱动程序向上注册的原理: 比如:输入子程序一个input.c作为一层,下层为Dev.c和Dir.c,分别编写Dev.c和Dir.c向上Input.c注册:如图所示 分离概念: 分离概念主要是讲,设备驱动程序分成两个部分,也将引进另一个新概念bus_dri_dev模型 总线-驱动-设备模式,是讲吧一个驱动分成两个部分,分别挂载到一条总线上的链表中,总线上有.match函数还对两个链表相同名字相匹配,匹配成功跳到driver驱动程序的probe函数来实现驱动的操作. 一下例子主要编写总线

9.平台总线驱动设计

平台总线驱动设计 平台总线(Platform bus)是linux2.6内核加入的一种虚拟总线,其优势在于采用了总线的模型对设备与驱动进行了管理,这样提高了程序的可移植性.虚拟总线和实际的总线优势相当.我们只要把驱动和设备挂载到虚拟总线就可以了. 平台总线驱动与设备匹配机制 平台总线的结构:platform_bus_type: 该结构中,最重要的是我们的匹配函数platform_match: 在匹配函数里,有我们熟悉的代码,最后一行: Strcmp(pdev->name,drv->name)这

LinuxI2C核心、总线驱动与设备驱动

I2C体系结构分为三个部分:I2C核心.总线驱动.设备驱动 I2C核心: I2C核心提供了一组不依赖硬件的接口函数,I2C总线驱动和设备驱动之间依赖于I2C核心作为纽带 (1)增加/删除i2c_adapter int i2c_add_adapter(struct i2c_adapter *adap); int i2c_del_adapter(struct i2c_adapter *adap); (2)增加/删除i2c_driver int i2c_register_driver(struct m

浅谈平台总线驱动设计

平台总线是linux2.6内核加入的一种虚拟总线,使用流程: 1.定义设备 2.注册设备 3.定义驱动 4.注册驱动 总线上的设备和驱动相互匹配由总线来完成. 一.定义设备 平台设备描述结构:struct platform_device struct platform_device { const char * name; int id; struct device dev; u32 num_resources; struct resource * resource; const struct

基于MCP2515的Linux CAN总线驱动程序设计

MCP2515简介 MCP2515是一种独立的CAN总线通信控制器,是Microchip公司首批独立CAN解决方案的升级器件,其传输能力较Microchip公司原有CAN控制器(MCP2510)高两倍,最高通信速率可达到1Mbps.MCP2515能够接收和发送标准数据帧和扩展数据帧以及远程帧,通过两个接收屏蔽寄存器和六个接收过滤寄存器滤除无关报文,从而减轻CPU负担. MCP2515主要功能参数及电气特性如下: (1)支持CAN技术规范2.0A/B, 最高传输速率达到1Mbps: (2)支持标准

Exynos4412 IIC总线驱动开发(二)—— IIC 驱动开发

前面在Exynos4412 IIC总线驱动开发(一)-- IIC 基础概念及驱动架构分析 中学习了IIC驱动的架构,下面进入我们的驱动开发过程 首先看一张代码层次图,有助于我们的理解 上面这些代码的展示是告诉我们:linux内核和芯片提供商为我们的的驱动程序提供了 i2c驱动的框架,以及框架底层与硬件相关的代码的实现.  剩下的就是针对挂载在i2c两线上的i2c设备了device,而编写的即具体设备驱动了,这里的设备就是硬件接口外挂载的设备,而非硬件接口本身(soc硬件接口本身的驱动可以理解为总