LPC1768/1769之CAN控制器概述(附庫函數下載地址)


一、背景:
        使用LPC1769來做CAN的收發,在此對使用LPC1769的CAN控制器進行收發做個總結和記錄,以備下
    次開發快速上手使用。
        附:LPC1768/1769除了支持最高頻率不同以外,其它基本上一致。

二、正文:
        先貼一張LPC1769 CAN控制器的方框圖:

  .

        由上圖可見,整個CAN控制器一頭是CPU,另一頭是CAN收發器:
     CAN收發器負責CAN數據與CAN網絡的通信。CAN內核模塊解析和封裝要發送到CAN收發器以及從CAN
   收發器發過來的數據,此處CAN內核工作由硬件自行完成。
     CPU通過APB總線即可設置CAN控制器狀態,以及讀取中斷信息和中斷狀態。
     一共有3個發送緩沖器(郵箱),這樣就可以保證,最少可以發送3組並發的CAN數據;2個接收緩沖
   器(郵箱),這樣就可以在CPU處理1個郵箱的接收數據的同時,還能用另一個郵箱接收網絡上的數據。
     LPC1769 CAN的驗收濾波器比較特殊,它是一個獨立於CAN控制器的器件,也屬於一種外設,不過
   比較特殊的是,它是服務於CAN控制器的外設,這么做的意義就在於,驗收濾波這方面,不再需要軟件來
   來做任何事情,直接由硬件來實現查表算法,節省寶貴的CPU資源,由於它也算是一個獨立的外設,運用
   起來也比較復雜,而本篇篇幅有限,暫不詳述,下次再另開篇博客來說。
     至此,LPC1769 CAN的結構就
介紹完畢,接下說明,做哪些事情可讓其開始正確收發CAN數據。
        CAN1/2 使用以下寄存器進行設置:
        a)電源使能:在PCONP 寄存器中,設置PCAN1/2。
        注:復位時,CAN1/2 會被使能(PCAN1/2=0)。
        b)時鍾使能:在PCLK_SEL0 寄存器中選擇PCLK_CAN1/2 和驗收濾波器的PCLK_ACF。
        注:如果所需使用的 CAN 波特率必須高於100kbit/s(參見表16.12),那么就不能選擇IRC
     作為時鍾源。 c)喚醒:CAN 控制器能夠將微控制器從掉電模式喚醒。 d)引腳:通過PINSEL 寄存器選擇CAN1
/2 引腳,並通過PINMODE 寄存器選擇引腳模式。 e)中斷:CAN 中斷是通過CAN1/2IER 寄存器來使能的。中斷的使能是通過在NIVC 中使 用相應的中斷設置使能寄存器(Interrupt Set Enable register)來實現的。 f)CAN 控制器初始化:在CANNOD 寄存器中設置。
以上為數據手冊介紹的CAN控制器初始化過程,白話點說:
"a)"CAN是LPC1769的外設器件,要讓其工作,首先要設置PCONP,該寄存器的各個位來決定外設 的時鍾是否打開或關閉,若某個外設不被使用,則關閉它,以達到節省功耗的目的;此處要需
       要使用CAN,所以先打開CAN的外設時鍾。
"b)"其次,外設若要正常工作,則均需要一個合適的時鍾頻率,通PCLK_SEL0l來決定CAN的外設 時鍾來源,以及大小。 "c)"為了進一步減少MCU的功耗,當CAN網絡上沒有數據傳輸時,也沒有CAN中斷在處理,並且對應
       的睡眠位被置“1”,CAN外設會進入睡眠狀態,若CAN總線上出現了顯性位,則CAN外設從睡眠
       狀態被喚醒。同時,若已配置了相關位,且此時整個MCU都進入掉電或者深度睡眠模式,則CAN
       也可將MCU喚醒 
"d)"配置CAN的收發引腳,無需多言,告訴CAN控制器,從哪個引腳收發CAN數據。 "e)"配置CAN的各種中斷使能條件,此處使能了發送/接收中斷,錯誤中斷;以及配置NVIC內 CAN外設中斷。 "f)"配置CAN相關的參數,譬如波特率等等。 至此,CAN控制器初始化部分完成,還需要做接收和發送函數,以及中斷函數,來實現CAN的收發 ,和錯誤管理。 當然,在CAN控制器初始化部分,波特率的參數設置還有許多要說,本篇篇幅有限,暫不詳述,下次 再開篇博客進行介紹。 CAN中斷函數: /*----------------- INTERRUPT SERVICE ROUTINES --------------------------*/ /*********************************************************************//** * @brief CAN_IRQ Handler, control receive message operation * param[in] none * @return none **********************************************************************/ void CAN_IRQHandler() { uint8_t IntStatus; uint32_t data1; /* get interrupt status * Note that: Interrupt register CANICR will be reset after read. * So function "CAN_IntGetStatus" should be call only one time */ // 以下函數獲取的是CAN1ICR/CAN2ICR的寄存器數據,該寄存器指明了中斷來源 IntStatus = CAN_IntGetStatus(LPC_CAN1);if((IntStatus>>0)&0x01) {// 接收中斷 } ... // 省略的內容為,根據寄存器的各位的中斷來源數據來解析中斷信息。
       // IntStatus = CAN_IntGetStatus(LPC_CAN2);
       // if(...) ...
} CAN接收函數: 此函數為NXP提供的庫函數,庫函數下載鏈接在本文第三部分,該函數做的內容無非就是,在中 斷內,檢查兩個接收郵箱內是否有信息,若有,則將信息提取。 /********************************************************************//** * @brief Receive message data * @param[in] CANx pointer to LPC_CAN_TypeDef, should be: * - LPC_CAN1: CAN1 peripheral * - LPC_CAN2: CAN2 peripheral * @param[in] CAN_Msg point to the CAN_MSG_Type Struct, it will contain received * message information such as: ID, DLC, RTR, ID Format * @return Status: * - SUCCESS: receive message successfully * - ERROR: receive message unsuccessfully *********************************************************************/ Status CAN_ReceiveMsg (LPC_CAN_TypeDef *CANx, CAN_MSG_Type *CAN_Msg) { uint32_t data; CHECK_PARAM(PARAM_CANx(CANx)); //check status of Receive Buffer if((CANx->SR &0x00000001)) { /* Receive message is available */ /* Read frame informations */ CAN_Msg->format = (uint8_t)(((CANx->RFS) & 0x80000000)>>31); CAN_Msg->type = (uint8_t)(((CANx->RFS) & 0x40000000)>>30); CAN_Msg->len = (uint8_t)(((CANx->RFS) & 0x000F0000)>>16); /* Read CAN message identifier */ CAN_Msg->id = CANx->RID; /* Read the data if received message was DATA FRAME */ if (CAN_Msg->type == DATA_FRAME) { /* Read first 4 data bytes */ data = CANx->RDA; *((uint8_t *) &CAN_Msg->dataA[0])= data & 0x000000FF; *((uint8_t *) &CAN_Msg->dataA[1])= (data & 0x0000FF00)>>8;; *((uint8_t *) &CAN_Msg->dataA[2])= (data & 0x00FF0000)>>16; *((uint8_t *) &CAN_Msg->dataA[3])= (data & 0xFF000000)>>24; /* Read second 4 data bytes */ data = CANx->RDB; *((uint8_t *) &CAN_Msg->dataB[0])= data & 0x000000FF; *((uint8_t *) &CAN_Msg->dataB[1])= (data & 0x0000FF00)>>8; *((uint8_t *) &CAN_Msg->dataB[2])= (data & 0x00FF0000)>>16; *((uint8_t *) &CAN_Msg->dataB[3])= (data & 0xFF000000)>>24; /*release receive buffer*/ CANx->CMR = 0x04; } else { /* Received Frame is a Remote Frame, not have data, we just receive * message information only */ CANx->CMR = 0x04; /*release receive buffer*/ return SUCCESS; } } else { // no receive message available return ERROR; } return SUCCESS; } CAN發送函數: 該函數還是庫函數,即依次查詢3個發送郵箱的狀態,若郵箱狀態為空,則將數據填充到該郵箱 並置位發送標志,然后由CAN內核模塊硬件自動發送。發送的優先級在寄存器內均可配置,不詳述。 篇幅不想過長,因此查詢郵箱2/3代碼部分省略。 /********************************************************************//** * @brief Send message data * @param[in] CANx pointer to LPC_CAN_TypeDef, should be: * - LPC_CAN1: CAN1 peripheral * - LPC_CAN2: CAN2 peripheral * @param[in] CAN_Msg point to the CAN_MSG_Type Structure, it contains message * information such as: ID, DLC, RTR, ID Format * @return Status: * - SUCCESS: send message successfully * - ERROR: send message unsuccessfully *********************************************************************/ Status CAN_SendMsg (LPC_CAN_TypeDef *CANx, CAN_MSG_Type *CAN_Msg) { uint32_t data; CHECK_PARAM(PARAM_CANx(CANx)); CHECK_PARAM(PARAM_ID_FORMAT(CAN_Msg->format)); if(CAN_Msg->format==STD_ID_FORMAT) { CHECK_PARAM(PARAM_ID_11(CAN_Msg->id)); } else { CHECK_PARAM(PARAM_ID_29(CAN_Msg->id)); } CHECK_PARAM(PARAM_DLC(CAN_Msg->len)); CHECK_PARAM(PARAM_FRAME_TYPE(CAN_Msg->type)); //Check status of Transmit Buffer 1 if (CANx->SR & (1<<2)) { /* Transmit Channel 1 is available */ /* Write frame informations and frame data into its CANxTFI1, * CANxTID1, CANxTDA1, CANxTDB1 register */ CANx->TFI1 &= ~0x000F0000; CANx->TFI1 |= (CAN_Msg->len)<<16; if(CAN_Msg->type == REMOTE_FRAME) { CANx->TFI1 |= (1<<30); //set bit RTR } else { CANx->TFI1 &= ~(1<<30); } if(CAN_Msg->format == EXT_ID_FORMAT) { CANx->TFI1 |= (0x80000000); //set bit FF } else { CANx->TFI1 &= ~(0x80000000); } /* Write CAN ID*/ CANx->TID1 = CAN_Msg->id; /*Write first 4 data bytes*/ data = (CAN_Msg->dataA[0])|(((CAN_Msg->dataA[1]))<<8)|
           ((CAN_Msg->dataA[2])<<16)|((CAN_Msg->dataA[3])<<24); CANx->TDA1 = data; /*Write second 4 data bytes*/ data = (CAN_Msg->dataB[0])|(((CAN_Msg->dataB[1]))<<8)|
           ((CAN_Msg->dataB[2])<<16)|((CAN_Msg->dataB[3])<<24); CANx->TDB1 = data; /*Write transmission request*/ // 注意該值,置位發送郵箱1,告知硬件,郵箱1的信息已經填充完畢可發送。 CANx->CMR = 0x21; return SUCCESS; } //check status of Transmit Buffer 2 else if(CANx->SR & (1<<10)) { /* Transmit Channel 2 is available */ /* Write frame informations and frame data into its CANxTFI2, * CANxTID2, CANxTDA2, CANxTDB2 register */ ... /*Write transmission request*/ // 注意該值,置位發送郵箱2,告知硬件,郵箱2的信息已經填充完畢可發送。 CANx->CMR = 0x41; return SUCCESS; } //check status of Transmit Buffer 3 else if (CANx->SR & (1<<18)) { /* Transmit Channel 3 is available */ /* Write frame informations and frame data into its CANxTFI3, * CANxTID3, CANxTDA3, CANxTDB3 register */ ... /*Write transmission request*/ // 注意該值,置位發送郵箱3,告知硬件,郵箱3的信息已經填充完畢可發送。 CANx->CMR = 0x81; return SUCCESS; } else { // 所有郵箱都處於非空閑狀態,無法發送 return ERROR; } } 至此,有了初始化部分,CAN中斷函數,CAN發送、接收函數,也就實現了CAN數據的收發。 濾波以波特率以及CAN總線錯誤處理,下次再開博客詳述。 三、參考文檔 LPC175x_6x CMSIS-Compliant Standard Peripheral Firmware Driver Library (Keil, IAR, GNU)   https://www.lpcware.com/content/nxpfile/lpc175x6x-cmsis-compliant-standard-peripheral-firmware-driver-library-keil-iar-gnu 至此,記錄完畢。 記錄時間:2016-11-28 記錄地點:深圳WZ

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM