<strike id="kiyse"></strike>
  • <tr id="kiyse"></tr>
  • <strike id="kiyse"></strike><samp id="kiyse"><tbody id="kiyse"></tbody></samp>
    <strike id="kiyse"><s id="kiyse"></s></strike>
    <tr id="kiyse"></tr>
    <noframes id="kiyse"><code id="kiyse"></code></noframes>
    <th id="kiyse"></th>
    <samp id="kiyse"></samp>
  • <th id="kiyse"><s id="kiyse"></s></th>
  • 低成本嵌入式Linux CAN應(yīng)用方案

     2010-8-27             

            CAN(Controller Area Network)即控制器局域網(wǎng),由于具有高性能、高可靠性以及簡單的網(wǎng)絡(luò)結(jié)構(gòu),在工業(yè)系統(tǒng)中越來越受到人們的重視,并迅速成為了目前國際上應(yīng)用最廣泛的現(xiàn)場總線之一。

     

            英利嵌入式Linux工控主板EM9260是一款面向工業(yè)自動化領(lǐng)域的高性價比工控板,板上帶有標(biāo)準(zhǔn)CAN通訊接口。與板上其他標(biāo)準(zhǔn)通訊接口一樣,EM9260的CAN接口實(shí)現(xiàn)了相應(yīng)的嵌入式Linux驅(qū)動程序,應(yīng)用程序可以通過打開文件的進(jìn)行讀寫的標(biāo)準(zhǔn)方式實(shí)現(xiàn)對CAN總線接口的數(shù)據(jù)通訊。本文側(cè)重于介紹CAN通訊方案。

     

    硬件組成

     

    EM9260嵌入式Linux工控板CAN總線示意圖

     

            EM9260嵌入式Linux工控板的CAN均采用了PHILIPS半導(dǎo)體公司的SJA1000T CAN總線控制器,SJA1000是一款獨(dú)立的控制器,主要用于汽車和一般工業(yè)環(huán)境中的控制器局域網(wǎng)絡(luò)(CAN)芯片,它是PHILIPS半導(dǎo)體PCA82C200 CAN控制器(BasicCAN)的替代產(chǎn)品,而且它增加了一種新的工作模式(PeliCAN),這種模式支持具有很多新特性的CAN 2.0B協(xié)議。

     

            EM9260的CAN通訊接口可提供高達(dá)1Mbps的數(shù)據(jù)傳輸速率,當(dāng)采用5Kbps的的數(shù)據(jù)傳輸速率時其通訊距離最高可達(dá)到10KM。硬件的錯誤檢定特性也增強(qiáng)了CAN的抗電磁干擾能力,這給數(shù)據(jù)的遠(yuǎn)程可靠傳輸提供了有利保證。

     

            EM9260的CAN通訊接口根據(jù)用戶的需要分為兩種:一種帶光電隔離,一種不帶光電隔離。帶光電隔離CAN總線通訊模塊的CAN收發(fā)器端的所有信號和電源與其它部分完全隔離,可承受至少1Kv(有效值)的電壓沖擊。光電隔離的功能可在EM9260的應(yīng)用底板上來實(shí)現(xiàn),英利公司在EM9260評估底板上提供了相應(yīng)的參考電路。

     

    CAN驅(qū)動接口函數(shù)

     

            1、CAN報(bào)文的幀格式簡介
            在CAN2.0B中存在兩種不同的幀格式,其主要的區(qū)別在于標(biāo)識符的長度,具有11位標(biāo)識符的幀稱為標(biāo)準(zhǔn)幀,而包括有29位標(biāo)識符的幀稱為擴(kuò)展幀。下面分別介紹數(shù)據(jù)幀的格式。

     

            1、CAN2.0B標(biāo)準(zhǔn)幀
            CAN標(biāo)準(zhǔn)幀信息為11個字節(jié),包括兩部分:信息和數(shù)據(jù)部分。前3個字節(jié)為信息部分,如圖所示:

     

    CAN2.0B標(biāo)準(zhǔn)幀

            注:1、字節(jié)1為幀信息。D7位表示幀格式,在標(biāo)準(zhǔn)幀中,F(xiàn)F=0;D6位表示幀的類型,RTR=0表示為數(shù)據(jù)幀,RTR=1表示為
                          遠(yuǎn)程幀,在一般的數(shù)據(jù)通訊中,只使用數(shù)據(jù)幀;DLC表示數(shù)據(jù)幀實(shí)際的數(shù)據(jù)長度
                    2、字節(jié)2、字節(jié)3為報(bào)文識別碼,11位有效
                    3、字節(jié)4~字節(jié)11為數(shù)據(jù)幀的實(shí)際數(shù)據(jù),遠(yuǎn)程幀時無效

     

            2、CAN2.0B擴(kuò)展幀
            CAN標(biāo)準(zhǔn)幀信息為13個字節(jié),包括兩部分:信息和數(shù)據(jù)部分。前5個字節(jié)為信息部分,如圖所示:

     

    CAN2.0B擴(kuò)展幀

            注:1、字節(jié)1為幀信息。D7位表示幀格式,在擴(kuò)展幀中,F(xiàn)F=1;D6位表示幀的類型,RTR=0表示為數(shù)據(jù)幀,RTR=1表示為
                          遠(yuǎn)程幀;DLC表示數(shù)據(jù)幀實(shí)際的數(shù)據(jù)長度
                    2、字節(jié)2~字節(jié)5為報(bào)文識別碼,29位有效
                    3、字節(jié)6~字節(jié)13為數(shù)據(jù)幀的實(shí)際數(shù)據(jù),遠(yuǎn)程幀時無效

     

            2、CAN應(yīng)用數(shù)據(jù)結(jié)構(gòu)
            英利公司提供的基于嵌入式Linux下的CAN操作API函數(shù),為了方便用戶的使用,結(jié)合目前常用的一些方法,對于CAN接口接收的數(shù)據(jù)報(bào)文采用了以下結(jié)構(gòu)。

     

            struct can_frame
            {
                    canid_t can_id; /* 用于定義CAN報(bào)文ID以及 EFF/RTR/ERR等標(biāo)志 */
                    __u8 can_dlc; /* 用于定義can報(bào)文數(shù)據(jù)包長度0-8 */
                    __u8 data[8]; /* 用于定義can報(bào)文數(shù)據(jù) */
            };

     

            其中的can報(bào)文ID為一個32 bit大小的結(jié)構(gòu),其中各個bit位定義如下:
            typedef __u32 canid_t;
                    bit 0-28: CAN 報(bào)文的id(標(biāo)準(zhǔn)幀11bit/擴(kuò)展幀為29bit).
                    bit 29 : CAN報(bào)文錯誤幀標(biāo)志(0 = data frame, 1 = error frame)
                    bit 30 : CAN報(bào)文遠(yuǎn)程幀標(biāo)志( 1 = rtr frame )
                    bit 31 : CAN報(bào)文幀格式標(biāo)志 (0 = 標(biāo)準(zhǔn)幀, 1 = 擴(kuò)展幀 )

     

            在進(jìn)行CAN通訊時需要設(shè)置相關(guān)的參數(shù),包括波特率、選取的數(shù)據(jù)濾波方式等,其中對于濾波器的設(shè)置,在濾波器的作用下,只有當(dāng)接收報(bào)文中的標(biāo)識位和驗(yàn)收濾波器預(yù)定義的位值相等時,CAN控制器才允許將收到的報(bào)文存入RXFIFO中。為了方便使用,在英利公司的API函數(shù)中采用了一個struct accept_filter用來設(shè)置相關(guān)驗(yàn)收濾波器的相關(guān)定義。

     

            struct accept_filter
            {
                    unsigned int accept_code; /* 用于定義CAN報(bào)文驗(yàn)收代碼位 32bit*/
                    unsigned int accept_mask; /* 用于定義CAN報(bào)文驗(yàn)收屏蔽位 32bit*/
                    unsigned char filter_mode; /* 用于定義CAN報(bào)文濾波模式 */
            };

     

            3、CAN通訊接口API函數(shù)
            EM9260的系統(tǒng)內(nèi)核中實(shí)現(xiàn)了CAN接口的驅(qū)動,實(shí)現(xiàn)CAN接口 open( ) / close() 、read( ) / write( )等函數(shù)操作。和在Linux下操作設(shè)備的方式和操作文件的方式一樣,調(diào)用open( )打開設(shè)備文件,再調(diào)用read( )、write( )對CAN接口進(jìn)行數(shù)據(jù)讀寫操作。另外在此驅(qū)動程序的基礎(chǔ)上,封裝了一套簡單實(shí)用的API函數(shù),以滿足對于CAN接口一些特殊參數(shù)設(shè)置的需要。各個函數(shù)的定義在can_api.h文件下,在該頭文件中對于各個API函數(shù)均有相應(yīng)的中文說明。

     

            具體在進(jìn)行應(yīng)用程序開發(fā)時,首先調(diào)用CAN接口的open( )函數(shù)打開CAN接口:
            sprintf( portname, '/dev/em9x60_can%d', CanNo );
            m_fd = open(portname, O_RDWR |O_NONBLOCK );

     

            得到有效的文件描述符m_fd后,然后可調(diào)用can_api.h文件中定義的API函數(shù)對CAN接口進(jìn)行相應(yīng)的通訊參數(shù)設(shè)置:
            CAN_StartChip( m_fd );
            CAN_SetBaudRate( m_fd, baudrate );
            CAN_SetGlobalAcceptanceFilter( m_fd, AcceptanceFilter );

     

            再調(diào)用read( ) / write( ) 實(shí)現(xiàn)CAN數(shù)據(jù)的收發(fā)操作。

            4、CAN通訊接口的數(shù)據(jù)收發(fā)應(yīng)用示例
            在英利公司提供的CAN方案中,CAN通訊的數(shù)據(jù)收發(fā)均采用的中斷方式,驅(qū)動程序中已自動完成了數(shù)據(jù)的收發(fā),以及內(nèi)部定義的CAN接收緩沖區(qū)和發(fā)送緩沖區(qū)的管理。對于用戶開發(fā)應(yīng)用程序來說,只需要調(diào)用英創(chuàng)公司提供的CAN通訊API函數(shù)中的收發(fā)函數(shù)即可。本小節(jié)主要介紹一個CAN通訊的綜合應(yīng)用示例程序。

     

            app_cantest是一個支持CAN數(shù)據(jù)通訊的示例,該例程采用了面向?qū)ο蟮腃++編程,把CAN數(shù)據(jù)通訊作為一個對象進(jìn)行封裝,用戶調(diào)用該對象提供的接口函數(shù)即可方便地完成CAN數(shù)據(jù)通訊的操作。

     

            // 定義CAN通訊類
            class EM9X60_CAN
            {
            private:
                    // 通訊線程標(biāo)識符ID
                    pthread_t m_thread;
                    // CAN接收線程
                    static int ReceiveThreadFunc( void* lparam );
            public:
                    EM9X60_CAN();
                    virtual ~EM9X60_CAN();
                    // 已打開的CAN文件描述符
                    int m_fd;
                    unsigned int m_canid;

                    can_frame rxmsg;

                    // 退出數(shù)據(jù)接收線程標(biāo)志
                    int m_ExitThreadFlag;

                    // 按照指定的參數(shù)打開CAN接口,并創(chuàng)建CAN接口接收線程
                    int OpenCAN( int CanNo, CAN_BAUDRATE baudrate, accept_filter *AcceptanceFilter );
                    // 關(guān)閉接口并釋放相關(guān)資源
                    int CloseCAN( );

                    // 初始化設(shè)置CAN數(shù)據(jù)包id信息
                    int InitCanIDInfo( struct CanIDInfo* pcanid );
                    // CAN接口寫數(shù)據(jù)
                    int WriteCAN( char* Buf, int len );
                    // CAN接收數(shù)據(jù)處理函數(shù)
                    virtual int PackagePro( char* Buf, int len );
            };

     

            OpenCAN 函數(shù)用于根據(jù)輸入?yún)?shù)打開CAN設(shè)備,并創(chuàng)建CAN數(shù)據(jù)接收線程。

            res = pthread_create( &m_thread, &attr, (void*)&ReceiveThreadFunc, (void*)this );

     

            ReceiveThreadFunc函數(shù)是CAN數(shù)據(jù)接收和處理的主要核心代碼,在該函數(shù)中調(diào)用select( ),等待串口數(shù)據(jù)的到來。對于接收到的數(shù)據(jù)處理也是在該函數(shù)中實(shí)現(xiàn),在本例程中處理為簡單的數(shù)據(jù)回發(fā),用戶可結(jié)合實(shí)際的應(yīng)用修改此處代碼,修改PackagePro( )函數(shù)即可。流程如下:

     

    CAN接口API函數(shù)流程圖

     

            int EM9X60_CAN::ReceiveThreadFunc(void* lparam)
            {
                    EM9X60_CAN *pCAN = (EM9X60_CAN*)lparam;
                    int len;

                    // 定義讀事件集合
                    fd_set fdRead;
                    int ret;
                    struct timeval aTime;

                    while( 1 )
                    {
                            // 收到退出事件,結(jié)束線程
                            if( pCAN->m_ExitThreadFlag )
                            { 
                                    break;
                            }

                            FD_ZERO(&fdRead);
                            FD_SET(pCAN->m_fd,&fdRead);

                            aTime.tv_sec = 0; 
                            aTime.tv_usec = 30000;

                            ret = select( pCAN->m_fd+1,&fdRead,NULL,NULL,&aTime );

                            if (ret < 0 )
                            {
                                    pCAN->CloseCAN( );
                                    break;
                            }

                            if (ret >= 0)
                            {
                                    // 判斷是否讀事件
                                    if (FD_ISSET(pCAN->m_fd,&fdRead))
                                    {
                                            len = read( pCAN->m_fd, (char*)&pCAN->rxmsg, sizeof(can_frame) );
                                            while( len > 0 ) 
                                            {
                                                    // 對接收的數(shù)據(jù)進(jìn)行處理,這里為簡單的數(shù)據(jù)回發(fā) 
                                                    pCAN->PackagePro( (char*)&pCAN->rxmsg, len );
                                                    // 處理完畢
                                                    len = read( pCAN->m_fd, (char*)&pCAN->rxmsg, sizeof(can_frame) );
                                            }
                                    }
                            } 
                    }

                    printf( 'ReceiveThreadFunc finished\n' );
                    pthread_exit( NULL );
                    return 0;
            }

     

            需要注意的是,select( )函數(shù)中的時間參數(shù)在Linux下,每次都需要重新賦值,否則會自動歸0。

    99精品国产自在现线观看| 精品乱码一卡2卡三卡4卡网| 国产精品天天在线| 午夜精品在线观看| 国产精品高清在线| 青青青在线观看国产精品| 女人香蕉久久**毛片精品| 国产一区二区精品久久岳√ | 国产精品免费无遮挡无码永久视频| 国产精品亚洲专区无码WEB| 久久99精品视频| 久久www免费人成精品香蕉| 午夜精品一区二区三区在线视| 99re最新地址精品视频| 国产精品九九久久免费视频 | 日本精品一区二区三区四区 | 国产精品特级露脸AV毛片| 久久99热只有频精品8| 国产午夜福利精品久久| 精品久久洲久久久久护士| 亚洲精品免费在线视频| 国产精品女同一区二区| 国产精品一区二区三区99 | 成人区人妻精品一区二区不卡| 亚洲欧洲精品国产区| 老司机69精品成免费视频| 国产精品久操视频| 又紧又大又爽精品一区二区| 亚洲AV永久无码精品网站在线观看| 亚洲精品免费在线视频| 无码精品A∨在线观看| 国产精品无码无片在线观看| 国产精品乱码一区二区三区| 午夜精品久久久久久久99热| 日韩精品无码人妻免费视频| 一本久久精品一区二区| 国产精品久久久久久福利69堂| 99国产精品自在自在久久| 久久精品国产99久久| 日本人精品video黑人| 精品国产品香蕉在线观看75|