ESM8000工控主板搭載了NXP i.MX8M Mini Quad 64位異構(gòu)處理器,包含了一顆主頻1.6GHz的四核ARM Cortex-A53和一顆主頻400MHz 的ARM Cortex-M4。Linux系統(tǒng)在Cortex-A53核心上運行,對于一些實時性要求極高的應(yīng)用,Linux系統(tǒng)可能無法滿足對中斷事件的及時響應(yīng),而且頻繁的中斷響應(yīng)也會大大的降低操作系統(tǒng)性能。對這類應(yīng)用場合就可充分利用i.MX8MM異構(gòu)多核結(jié)構(gòu),由高性能的Cortex-A53(Linux系統(tǒng))完成人機交互、數(shù)據(jù)處理、通訊管理等復(fù)雜運算,而對于實時的數(shù)據(jù)采集、高速的中斷事件響應(yīng)等實時任務(wù)交由Cotex-M4完成。
控制器局域網(wǎng) (Controller Area Network,簡稱CAN或者CAN bus) 是一種功能豐富的總線標(biāo)準(zhǔn)。最開始作為車載總線而被設(shè)計出來,隨著CAN總線標(biāo)準(zhǔn)的不斷發(fā)展和完善,在工業(yè)領(lǐng)域中越來越多的行業(yè)會使用到CAN總線。在實際的應(yīng)用中,為了保證CAN總線的實時性,一般要求CAN總線的負(fù)載不能過高。因此在連接多個設(shè)備的時候,就需要使用到多路CAN總線來進行連接,保證每一路CAN總線的負(fù)載滿足要求。
英創(chuàng)公司針對多CAN總線應(yīng)用的需求設(shè)計了ETA706模塊,該模塊通過SPI總線與主板連接,利用ESM8000的SPI1,再通過ESM8000的兩位GPIO譯碼出4個片選,擴展了4片MCP2518FD CAN控制芯片,支持CAN2.0 / CAN FD協(xié)議,加上主板上自帶的兩路CAN,實現(xiàn)了6路CAN總線方案。CAN擴展原理框圖與硬件測試環(huán)境,如圖1、圖2所示:
圖1:6x CAN擴展驅(qū)動原理框圖
圖2: 6x CAN硬件測試環(huán)境
當(dāng)CAN總線上數(shù)據(jù)量較大時,SPI總線需要頻繁的中斷和讀寫操作,如果由Linux系統(tǒng)來處理中斷和SPI操作,就會占用相當(dāng)多的CPU資源。此時就可充分利用i.MX8MM異構(gòu)CPU結(jié)構(gòu),由Crotex-M4來處理CAN中斷和SPI通訊,Linux系統(tǒng)只處理Crotex-M4傳遞過來的CAN帖數(shù)據(jù),從而大大節(jié)省ESM8000上Cortex-A53的開銷,留出更多CPU資源。
Coretx-M4應(yīng)用程序基于FreeRTOS,完成了對MCP2518FD參數(shù)配置、中斷響應(yīng)、數(shù)據(jù)收發(fā)等工作。由于擴展的4路MCP2518FD使用ESM8000的SPI1,通過2位GPIO譯碼出4個片選,所以對SPI1的操作使用Mutex信號量進行互斥保護。對多個MCP2815FD進行片選的實現(xiàn)代碼如下:
#define CH_SEL0 ESM_GPIO26 #define CH_SEL1 ESM_GPIO27 ecspi_rtos_handle_t *DRV_SPI_ChipSelectAssert(uint8_t spiSlaveDeviceIndex, bool assert) { ecspi_rtos_handle_t *handle = NULL; static uint8_t last_spiIndex = 0; if (assert) { if(spiSlaveDeviceIndex < 2) handle = &spi[1 + spiSlaveDeviceIndex]; else { /* Lock resource mutex */ xSemaphoreTake(spi1_mutex, portMAX_DELAY); handle = &spi[0]; spiSlaveDeviceIndex -= 2; /* i.MX8MM 操作一次GPIO的需要300ns左右,所以這里對操作GPIO進行優(yōu)化 */ if(spiSlaveDeviceIndex != last_spiIndex) { switch (spiSlaveDeviceIndex) { case 0: GPIO_PortOutClear(CH_SEL0, (0x1 << CH_SEL0->pin) | (0x1 << CH_SEL1->pin)); break; case 1: if((last_spiIndex & 0x01) == 0) GPIO_OutSet(CH_SEL0, 1); if((last_spiIndex & 0x02) != 0) GPIO_OutSet(CH_SEL1, 0); break; case 2: if((last_spiIndex & 0x01) != 0) GPIO_OutSet(CH_SEL0, 0); if((last_spiIndex & 0x02) == 0) GPIO_OutSet(CH_SEL1, 1); break; case 3: GPIO_PortOutSet(CH_SEL0, (0x1 << CH_SEL0->pin) | (0x1 << CH_SEL1->pin)); break; default: handle = NULL; last_spiIndex = 0; GPIO_PortOutClear(CH_SEL0, (0x1 << CH_SEL0->pin) | (0x1 << CH_SEL1->pin)); } last_spiIndex = spiSlaveDeviceIndex; } } } else { if (spiSlaveDeviceIndex > 1) { /* Unlock resource mutex */ (void)xSemaphoreGive(spi1_mutex); } } return handle; }
4路MCP2815FD的中斷輸出直接連接到ESM8000的4位GPIO, GPIO在i.MX8MM處理器上是按組(PORT)操作的。每組32位IO,而IO中斷又在PORT內(nèi)再次被分為高低兩組,每組對應(yīng)16位IO,共享一個中斷源。以擴展的CAN2和CAN3為例,它們的中斷輸出分別連接到ESM8000的GPIO2、GPIO3,分別對應(yīng)到i.MX8MM的GPIO_PORT5_Pin26和GPIO_PORT5_Pin27,它們使用同一個GPIO中斷源GPIO5_Combined_16_31_IRQn。當(dāng)GPIO中斷產(chǎn)生后,需要通過讀取gpio_isr中斷狀態(tài)寄存器來判斷具體是那位GPIO引起的中斷。對應(yīng)的中斷處理代碼如下所示:
static inline void GPIO_CommonIRQHandler(uint8_t index, uint32_t gpio_isr, uint32_t gpio_imr, BaseType_t *reschedule) { if (((gpio_imr >> mcp251xfd[index].int_gpio->pin) & 1) != 0) { if (((gpio_isr >> mcp251xfd[index].int_gpio->pin) & 1) != 0) { /* Disable GPIO pin interrupt */ GPIO_PinIntEnalbe_FromISR(mcp251xfd[index].int_gpio, false); /* clear the interrupt status */ GPIO_ClearStatus(mcp251xfd[index].int_gpio); /* Unlock the task to process the event. */ xSemaphoreGiveFromISR(mcp251xfd[index].xMcp251xINT_event, reschedule); } } } void GPIO5_Combined_16_31_IRQHandler(void) { uint32_t gpio_isr, gpio_imr; BaseType_t reschedule = false; gpio_isr = GPIO_PortGetStatus(GPIO5); gpio_imr = GPIO_PortGetIMR(GPIO5); GPIO_CommonIRQHandler(2, gpio_isr, gpio_imr, &reschedule); GPIO_CommonIRQHandler(3, gpio_isr, gpio_imr, &reschedule); /* Perform a context switch to wake the higher priority task. */ portYIELD_FROM_ISR(reschedule); }
在ESM8000的Linux系統(tǒng)中,可以通過RPMsg和Crotex-M4進行通訊來獲取相關(guān)的信息,關(guān)于RPMsg的介紹可以參考《ESM7000異構(gòu)CPU實時應(yīng)用之二基于rpmsg的通訊機制》。英創(chuàng)公司已經(jīng)提供了相應(yīng)的驅(qū)動文件,加載后會生成標(biāo)準(zhǔn)的CAN設(shè)備。在驅(qū)動中會通過RPMsg和Crotex-M4進行通信以及數(shù)據(jù)的處理,但對于用戶來說是不需要關(guān)心的,直接使用標(biāo)準(zhǔn)的socketcan操作驅(qū)動生成的CAN設(shè)備就行了,用戶程序?qū)AN設(shè)備的操作,驅(qū)動都會將對應(yīng)的操作通過RPMsg發(fā)送給Crotex-M4,然后由Crotex-M4實際對硬件執(zhí)行對應(yīng)的操作,如下圖所示:
圖3:軟件原理框圖
驅(qū)動文件在系統(tǒng)啟動完成后會自動加載,如下圖:
通過命令ifconfig –a可以查看新生成的can0-can5這6個CAN設(shè)備,使用標(biāo)準(zhǔn)的socketcan就能夠?qū)@6個CAN設(shè)備進行操作。詳細的程序可以參考英創(chuàng)公司提供的例程test_socketcan。下面主要介紹針對這6路CAN設(shè)備進行的測試情況。
首先測試單路CAN總線(can0)的性能,測試采用250Kbps波特率,在該波特率下,CAN總線負(fù)載最高大約為每秒2000幀,分別測試了收發(fā)不同數(shù)據(jù)量情況下主板的負(fù)載表現(xiàn),為了更直觀的體現(xiàn)出M4對負(fù)載的分擔(dān),我們在同樣條件下測試了直接使用Linux系統(tǒng)(即Cortex-A53)控制時的負(fù)載:
測試數(shù)據(jù)與擴展方式 | 接收 1000幀/秒 | 接收 2000幀/秒 | 發(fā)送 1000幀/秒 | 接收和發(fā)送 各1000幀/秒 |
異構(gòu)CPU控制 | A53:10% M4:8.5% | A53:18% M4:15.8% | A53:10% M4:5.3% | A53:18% M4:14.8% |
Linux直接控制 | A53:10% | A53:22% | A53:24% | A53:38% |
因為運行Linux系統(tǒng)的Cortex-A53為4核心,所以A53總負(fù)載為400%。通常系統(tǒng)會自動均衡負(fù)載,比如20%的負(fù)載,理想狀態(tài)下會自動為4個核心各配分5%的負(fù)載。
下面同時對比測試兩路CAN總線(can0和can1)通訊的情況:
測試數(shù)據(jù)與擴展方式 | 接收 1000幀/秒 | 接收 2000幀/秒 | 發(fā)送 1000幀/秒 | 接收和發(fā)送 各1000幀/秒 |
異構(gòu)CPU控制 | A53:13% M4:24.3% | A53:25% M4:39% | A53:18% M4:10.1% | A53:32% M4:31.8% |
Linux直接控制 | A53:40% | A53:80% | A53:46% | A53:88% |
通過上面表格的對比,可以看出來通過Cortex-M4擴展的方案可以有效的降低Linux系統(tǒng)的負(fù)載,留出更多的CPU資源給用戶使用。
下面我們采用比較極限的情況來進行測試這套擴展方案的性能,將6路CAN總線同時運行起來,測試每一路CAN總線在250Kbps波特率下每秒接收1000幀和2000幀數(shù)據(jù)時的系統(tǒng)負(fù)載,測試數(shù)據(jù)如下表:
測試數(shù)據(jù)與擴展方式 | 接收 1000幀/秒 | 接收 2000幀/秒 |
異構(gòu)CPU控制 | A53:37.5% M4:48% | A53:48% M4:99% |
同時我們驗證了每一路CAN總線接收數(shù)據(jù)的準(zhǔn)確性,均沒有出現(xiàn)丟幀的情況。在250Kbps波特率下,每秒2000幀數(shù)據(jù)已經(jīng)達到CAN總線的滿載。而6路CAN總線均滿載的情況下Cortex-M4也已經(jīng)達到滿載,說明這套方案的極限性能大約為6路CAN總線每一路達到每秒2000幀的數(shù)據(jù)量。
如果對這套方案感興趣的客戶,可以和英創(chuàng)的工程師聯(lián)系,獲取詳細的測試代碼和資料。
成都英創(chuàng)信息技術(shù)有限公司 028-8618 0660