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. 免責聲明

    內部交流文檔,若發現相關錯誤或者建議,請及時聯繫文檔創建者進行修訂和更新。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章