ESM8000工控主板搭载了NXP i.MX8M Mini Quad 64位异构处理器,包含了一颗主频1.6GHz的四核ARM Cortex-A53和一颗主频400MHz 的ARM Cortex-M4。Linux系统在Cortex-A53核心上运行,对于一些实时性要求极高的应用,Linux系统可能无法满足对中断事件的及时响应,而且频繁的中断响应也会大大的降低操作系统性能。对这类应用场合就可充分利用i.MX8MM异构多核结构,由高性能的Cortex-A53(Linux系统)完成人机交互、数据处理、通讯管理等复杂运算,而对于实时的数据采集、高速的中断事件响应等实时任务交由Cotex-M4完成。
控制器局域网 (Controller Area Network,简称CAN或者CAN bus) 是一种功能丰富的总线标准。最开始作为车载总线而被设计出来,随着CAN总线标准的不断发展和完善,在工业领域中越来越多的行业会使用到CAN总线。在实际的应用中,为了保证CAN总线的实时性,一般要求CAN总线的负载不能过高。因此在连接多个设备的时候,就需要使用到多路CAN总线来进行连接,保证每一路CAN总线的负载满足要求。
英创公司针对多CAN总线应用的需求设计了ETA706模块,该模块通过SPI总线与主板连接,利用ESM8000的SPI1,再通过ESM8000的两位GPIO译码出4个片选,扩展了4片MCP2518FD CAN控制芯片,支持CAN2.0 / CAN FD协议,加上主板上自带的两路CAN,实现了6路CAN总线方案。CAN扩展原理框图与硬件测试环境,如图1、图2所示:
图1:6x CAN扩展驱动原理框图
图2: 6x CAN硬件测试环境
当CAN总线上数据量较大时,SPI总线需要频繁的中断和读写操作,如果由Linux系统来处理中断和SPI操作,就会占用相当多的CPU资源。此时就可充分利用i.MX8MM异构CPU结构,由Crotex-M4来处理CAN中断和SPI通讯,Linux系统只处理Crotex-M4传递过来的CAN帖数据,从而大大节省ESM8000上Cortex-A53的开销,留出更多CPU资源。
Coretx-M4应用程序基于FreeRTOS,完成了对MCP2518FD参数配置、中断响应、数据收发等工作。由于扩展的4路MCP2518FD使用ESM8000的SPI1,通过2位GPIO译码出4个片选,所以对SPI1的操作使用Mutex信号量进行互斥保护。对多个MCP2815FD进行片选的实现代码如下:
#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进行优化 */ 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内再次被分为高低两组,每组对应16位IO,共享一个中断源。以扩展的CAN2和CAN3为例,它们的中断输出分别连接到ESM8000的GPIO2、GPIO3,分别对应到i.MX8MM的GPIO_PORT5_Pin26和GPIO_PORT5_Pin27,它们使用同一个GPIO中断源GPIO5_Combined_16_31_IRQn。当GPIO中断产生后,需要通过读取gpio_isr中断状态寄存器来判断具体是那位GPIO引起的中断。对应的中断处理代码如下所示:
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系统中,可以通过RPMsg和Crotex-M4进行通讯来获取相关的信息,关于RPMsg的介绍可以参考《ESM7000异构CPU实时应用之二基于rpmsg的通讯机制》。英创公司已经提供了相应的驱动文件,加载后会生成标准的CAN设备。在驱动中会通过RPMsg和Crotex-M4进行通信以及数据的处理,但对于用户来说是不需要关心的,直接使用标准的socketcan操作驱动生成的CAN设备就行了,用户程序对CAN设备的操作,驱动都会将对应的操作通过RPMsg发送给Crotex-M4,然后由Crotex-M4实际对硬件执行对应的操作,如下图所示:
图3:软件原理框图
驱动文件在系统启动完成后会自动加载,如下图:
通过命令ifconfig –a可以查看新生成的can0-can5这6个CAN设备,使用标准的socketcan就能够对这6个CAN设备进行操作。详细的程序可以参考英创公司提供的例程test_socketcan。下面主要介绍针对这6路CAN设备进行的测试情况。
首先测试单路CAN总线(can0)的性能,测试采用250Kbps波特率,在该波特率下,CAN总线负载最高大约为每秒2000帧,分别测试了收发不同数据量情况下主板的负载表现,为了更直观的体现出M4对负载的分担,我们在同样条件下测试了直接使用Linux系统(即Cortex-A53)控制时的负载:
测试数据与扩展方式 | 接收 1000帧/秒 | 接收 2000帧/秒 | 发送 1000帧/秒 | 接收和发送 各1000帧/秒 |
异构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系统的Cortex-A53为4核心,所以A53总负载为400%。通常系统会自动均衡负载,比如20%的负载,理想状态下会自动为4个核心各配分5%的负载。
下面同时对比测试两路CAN总线(can0和can1)通讯的情况:
测试数据与扩展方式 | 接收 1000帧/秒 | 接收 2000帧/秒 | 发送 1000帧/秒 | 接收和发送 各1000帧/秒 |
异构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系统的负载,留出更多的CPU资源给用户使用。
下面我们采用比较极限的情况来进行测试这套扩展方案的性能,将6路CAN总线同时运行起来,测试每一路CAN总线在250Kbps波特率下每秒接收1000帧和2000帧数据时的系统负载,测试数据如下表:
测试数据与扩展方式 | 接收 1000帧/秒 | 接收 2000帧/秒 |
异构CPU控制 | A53:37.5% M4:48% | A53:48% M4:99% |
同时我们验证了每一路CAN总线接收数据的准确性,均没有出现丢帧的情况。在250Kbps波特率下,每秒2000帧数据已经达到CAN总线的满载。而6路CAN总线均满载的情况下Cortex-M4也已经达到满载,说明这套方案的极限性能大约为6路CAN总线每一路达到每秒2000帧的数据量。
如果对这套方案感兴趣的客户,可以和英创的工程师联系,获取详细的测试代码和资料。
成都英创信息技术有限公司 028-8618 0660