ESM8000异构CPU实时应用——6路CAN-FD的实现

 2021-9-7     作者:刘乾坤 黄志超 朱贤武     [nemail]    
[lablebox]

  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所示:


ESM8000异构CPU实时应用——多路CAN扩展.png

图1:6x CAN扩展驱动原理框图


sbc880+esm8000+eta706.png

图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实际对硬件执行对应的操作,如下图所示:


ESM8000异构CPU实时应用——多路CAN扩展.png

图3:软件原理框图


  驱动文件在系统启动完成后会自动加载,如下图:


ESM8000异构CPU实时应用——多路CAN扩展.png


  通过命令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帧的数据量。


  如果对这套方案感兴趣的客户,可以和英创的工程师联系,获取详细的测试代码和资料。

[lablebox]