温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

SylixOS SylixOS CAN总线驱动之三

发布时间:2020-07-23 02:25:09 来源:网络 阅读:573 作者:Best_CC 栏目:网络安全


  1. SylixOS CAN报文传送流程

  2. CAN报文传送流程框图

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

    SylixOS SylixOS CAN总线驱动之三

    图 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. 免责声明

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


向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI