tag 标签: nxp

相关博文
  • 2025-6-16 17:21
    32 次阅读|
    0 个评论
    概要 尽管大多数嵌入式软件应用针对单一微处理器或微控制器开发,但在某些特定场景下需要采用分布式架构。以汽车行业为例,不同子系统可能作为独立应用程序部署在分离的处理器上,例如负责发动机控制系统的嵌入式软件与防抱死制动系统应用就分别运行在不同的微控制器上。 医疗器械行业是嵌入式软件分布于多个微处理器的另一典型案例。例如,用户界面由一个微处理器实现,而负责患者交互的控制系统则位于另一个微控制器上。 可以看出,职责是根据功能划分的,而处理器则根据职责进行选择。例如,用户界面通常在基于 Linux 的系统上实现,这类系统专为类似 ARM Cortex-A 内核的处理器设计;而关键的嵌入式软件应用则采用实时操作系统(RTOS),这种系统最适合类似 ARM Cortex-R 或 Cortex-M 内核的处理器。 然而,若采用多处理器架构,处理器间的通信便至关重要。例如汽车行业就通过 CAN 总线实现这一功能。在医疗设备领域,各处理器可采用 UART、SPI 或 I2C 等通信协议进行交互,如下图所示: 然而,多个处理器分布在不同的芯片上会增加底层硬件、软件和固件的复杂性。这进而可能导致额外的开发和调试工作,从而影响产品的时间线和成本。 为了应对这种额外的复杂性,我们可以将多个处理器集成到同一块芯片上,如下图所示: 在上图中,ARM Cortex-A 和 ARM Cortex-M 内核被集成在同一块芯片上,称为系统级芯片(SoC)。SoC 将 CPU 内核与其他必要的控制器相结合,形成一个功能全面且强大的处理器。如图中显示,嵌入式软件的功能安全(FuSa)部分通过 Zephyr Project 实时操作系统在 ARM Cortex-M 内核上实现,而图形用户界面(GUI)则在 ARM Cortex-A 内核的 Linux 系统中运行。 该设计大幅缩短了硬件开发与调试时间。无需再管理多个独立处理器及电气和机械硬件互联,问题简化为围绕单个处理器的硬件设计。挑战也就主要转移到固件和软件层面,因为每个内核的嵌入式软件需要与其他内核的嵌入式软件建立通信机制。 在这篇博客文章中,我们将学习如何从运行于 ARM Cortex-A 核心的 Linux 内核加载运行于 ARM Cortex-M 核心上的固件。我们将以 Toradex Verdin iMX95 模块为例演示具体实现方法。 OpenAMP项目 Open Asymmetric Multi-Processing(OpenAMP)项目的设立旨在实现同一片上系统内不同核心之间的高效通信。该项目基于德州仪器早期将 Remoteproc 和 RPMsg 纳入 Linux 内核的工作成果发展而来:Remoteproc 使 Linux 内核能够管理远端处理器(remote processor)上的嵌入式软件生命周期,而 RPMsg 则实现了 Linux 内核与远端处理器固件间的通信。OpenAMP 项目将德州仪器的专有实现方法扩展为适用于各类系统的通用框架,标准化了 Linux 内核与基于实时操作系统(RTOS)的嵌入式应用间通信协议,并为希望利用其功能特性(如 RPMsg)的 RTOS 及裸机嵌入式应用提供了参考实现方案。 如前所述,Remoteproc 和 RPMsg 是 OpenAMP 项目的两大核心功能,它们使我们能够管理运行在同一 SoC 不同内核上的嵌入式软件。Remoteproc 负责将固件加载到远程处理器上,并启动和停止远程处理器的执行。在 Linux 系统中,Remoteproc 通常用于管理运行在 Cortex-M 等类似内核上的嵌入式软件生命周期。而 RPMsg 则负责支持运行在 Cortex-M 内核的嵌入式软件与运行 Cortex-A 内核的 Linux 系统之间的消息交换。 Toradex Verdin iMX95 Toradex Verdin iMX95 是一款基于 NXP iMX95 SoC 的高性能模块(SoM)。如下图所示,iMX95 SoC 包含三种不同类型的 ARM 核心架构: ARM Cortex-A55 运行 Linux 操作系统,ARM Cortex-M7 负责执行实时固件,而 ARM Cortex-M33 则管理 Cortex-A55 和 Cortex-M7 的运行。 Zephyr Project RTOS 尽管“实时操作系统”是 Zephyr 项目 RTOS 的官方名称,但 “Zephyr” 远不止是一个 RTOS。它是一个完整的生态系统。虽然它确实包含了 RTOS 的典型功能和数据结构,如任务、信号量、互斥锁和消息队列,但它还拥有负责嵌入式系统其他重要功能的软件栈。例如,它包括: 用于控制和操作厂商特定硬件的驱动程序 实现完整通信协议栈的软件,例如蓝牙低功耗(BLE)和WiFi 实现整个子系统的模块,例如文件系统和引导加载程序 Zephyr 还支持来自众多厂商的 750 多款开发板,包括 Toradex Verdin iMX95。如果我们下载 Zephyr 代码库,可以看到在 boards/nxp/imx95_evk 目录下提供了对 iMX95 EVK 的支持,如下图所示: Zephyr 借鉴了 Linux 内核的 Kconfig 和 Devicetree 功能,我们可以在上图中看到这一点。上图所示的 iMX95 EVK “device driver” 包含: “board.c”, 负责初始板卡启动的C源代码 “imx95_evk_mimx9596_m7.dts”, 用于告知 Zephyr 该板卡上存在哪些外设的 Devicetree 文件 “Kconfig.imx95_evk”, 需要启用支持该板卡关键功能的 Kconfig 的选项集合 例如,如果我们打开“Kconfig.imx95_evk”文件(如下图所示),可以看到它启用了 iMX95 SoC 的 Kconfig 选项。该 Kconfg 选项随后会启用 SoC 所需的其他功能: 例如,Verdin iMX95的“device driver”位于 soc/nxp/imx/imx9/imx95 目录下,如下图所示: 同样的,“soc.c” 是负责初始 SoC 启动的 C 源代码,通过分析设备树中的相应节点,我们可以发现该 SoC 的 Kconfig 文件定义了 Flash 大小。 整合所有步骤 让我们演示如何为 iMX95 编译 Zephyr 应用程序,并从同一开发板上运行的 Cortex-A 内核加载 Zephyr 应用程序到 Cortex-M 内核。首先,我们可以按照 Zephyr入门指南( https://docs.zephyrproject.org/latest/develop/getting_started/index.html )中的步骤确保环境配置正确。 然后执行以下命令使用 West 工具获取 Zephyr 源代码: $ west init -m https://github.com/mabembedded/zephyr.git $ west update 之后,我们可以执行以下命令为 Verdin iMX95 编译一个简单的“Hello World”应用程序: $ west build -p -b imx95_evk/mimx9596/m7 samples/hello_world 然后,我们可以通过执行以下命令(确保将下方列出的 IP 地址替换为我们开发板的实际地址),将编译好的 ELF 格式二进制文件传输至运行在 iMX95 Cortex-A 内核上的 Linux 系统: $ scp build/zephyr/zephyr.elf root@10.10.2.22:~/ 随后,如果我们登录到 iMX95 设备,可以运行以下命令来指示 Linux 将 Zephyr ELF 二进制文件加载至 Verdin iMX95 的 Cortex-M7 核心上: root@imx95-19x19-verdin:~# cd /sys/devices/platform/imx95-cm7/remoteproc/remoteproc1/ root@imx95-19x19-verdin:~# echo ~/zephyr.elf firmware 最后,我们可以在 iMX95 Linux 控制台中输入以下命令来启动 Cortex-M7 核心: root@imx95-19x19-verdin:~# echo start state 我们可以在 iMX95 的 Cortex-M7 控制台上看到以下内容: 上述示例展示了 Linux 内核中 OpenAMP 项目的 Remoteproc 功能。 我们还可以通过如下所示的 openamp_rsc_table 示例应用程序,了解 Zephyr 中的 RPMsg 功能: 如上图所示,“ imx95_evk_mimx9596_m7.conf ” 包含了该应用启用的 Kconfig 选项。下图可见,此文件已启用相关的 OpenAmp 和 Mbox Kconfig 配置项: 该目录还包含一个 Devicetree “overlay”,用于为此特定应用定制开发板的 Devicetree。如下图所示,此覆盖层定义了 RPMsg 应用所需的功能,并引用了特定的硬件组件: 这些特性包括: 各个核心间用于交换数据的共享内存 Linux内核通过资源表来识别远程处理器及固件支持的功能 双核通信使用的邮箱机制 接下来演示如何编译并运行 RPMsg Zephyr 应用程序。首先执行以下命令进行编译: $ west build -p -b imx95_evk/mimx9596/m7 samples/subsys/ipc/openamp_rsc_table 随后,我们可以按照之前概述的步骤将生成的 ELF 二进制文件从 Cortex-A55 上的 Linux 系统传输并加载到 Cortex-M7 中。在从 Linux 端启动 Cortex-M7 后,我们将在 Cortex-M7 的控制台上看到以下内容,这展示了从 Linux 端接收到的消息: 同样,如果观察 Cortex-A55 端的 Linux 内核日志,我们也能看到它通过 RPMsg 接收到了来自 Cortex-M7 上 Zephyr 应用程序的消息,如下所示: 总结 在本篇博客中,我们了解了利用 OpenAMP 项目特性简化嵌入式系统硬件设计的优势。探讨了 Toradex Verdin iMX95 开发板如何通过 SoC 上不同的 ARM 内核实现创新应用场景。实践演示了使用 Zephyr 实时操作系统快速构建 "Hello World" 示例程序,并从运行于 Cortex-A55 的 Linux 系统加载至 Verdin iMX95 的Cortex-M7 核心的全过程。最后,我们还完成了基于 RPMsg 协议的 Zephyr 应用程序编译、从 Linux 环境加载以及验证 Zephyr 与 Linux 之间消息交互的实验。
  • 2025-6-13 16:28
    0 个评论
    在嵌入式系统领域,嵌入式实时操作系统(RTOS) 的应用正日益广泛,采用RTOS能够更合理、更高效地利用CPU资源,FreeRTOS作为一款轻量级且成熟的实时操作系统内核,其核心功能完备,包括任务管理、时间管理(如延时、定时器)、同步机制(信号量、互斥锁)、进程间通信(消息队列)等等。这些特性使其能够很好地满足资源相对有限的中小型嵌入式系统的需求。 i.MX 9352作为NXP 推出的新一代轻量级边缘AI处理器,集成2个Cortex-A55核和1个Cortex-M33实时核,其架构设计充分体现了对实时性与复杂任务处理能力的兼顾。为了帮助开发者充分利用i.MX 9352 M33核的实时能力,其配套的M核SDK包提供的FreeRTOS例程分为两类,一类介绍FreeRTOS系统组件特性,如信号量、互斥量、队列等,另一类是介绍外设接口如何在FreeRTOS使用,我们分别挑选这两类下的例程进行演示。 演示平台:飞凌嵌入式OK-MX9352-C开发板 1、FreeRTOS-generic 飞凌嵌入式OK-MX9352-C开发板支持FreeRTOS功能特性示例代码如下: freertos_event :任务事件演示例程 freertos_queue :队列消息实现任务间通信的演示例程 freertos_mutex :互斥锁使用例程 freertos_sem :信号量使用例程 freertos_swtimer :软件计数器及其回调的用法。 freertos_tickless :使用 LPTMR 延时唤醒或者硬件中断唤醒例程 freertos_generic :task、queue、swtimer、tick hook 、semaphore 组合利用演示例程。 因FreeRTOS_generic例程使用的FreeRTOS特性较多,我们重点分析此例程。 (1)软件实现 示例程序内容包括:任务创建、队列、软定时器、系统节拍时钟、信号量、异常处理。具体如下: 任务创建: 主函数创建了队列发送、接收,信号量三个任务。 // 创建队列接收任务if ( xTaskCreate ( prvQueueReceiveTask, "Rx" ,configMINIMAL_STACK_SIZE+ 166 , NULL ,mainQUEUE_RECEIVE_TASK_PRIORITY, NULL ) ! = pdPASS ) // 创建队列发送任务if ( xTaskCreate ( prvQueueSendTask, "TX" ,configMINIMAL_STACK_SIZE+ 166 , NULL , mainQUEUE_SEND_TASK_PRIORITY, NULL ) ! = pdPASS ) // 创建信号量任务if ( xTaskCreate ( prvEventSemaphoreTask, "Sem" ,configMINIMAL_STACK_SIZE+ 166 , NULL ,mainEVENT_SEMAPHORE_TASK_PRIORITY, NULL ) ! = pdPASS ) 队列: 队列发送任务,阻塞200ms后向队列发送数据;队列接收任务,任务阻塞读取队列,数据读取正确,则打印此时的队列接收数量。 // 队列发送任务,阻塞200ms后 向队列发送数据 static void prvQueueSendTask ( void *pvParameters) { TickType_t xNextWakeTime; const uint32_t ulValueToSend = 100UL ; xNextWakeTime = xTaskGetTickCount (); for (;;) { // 任务阻塞,直至200ms延时结束 vTaskDelayUntil (xNextWakeTime, mainQUEUE_SEND_PERIOD_MS); // 向队列发送数据,阻塞时间为0表示当队列满的时候就立即返回 xQueueSend (xQueue, ulValueToSend, 0 ); } } // 队列接收任务,任务阻塞读取队列,数据读取正确,则打印此时的队列接收数量。 static void prvQueueReceiveTask ( void *pvParameters) { uint32_t ulReceivedValue; for (;;) { // 任务一直阻塞,知道队列内读取到数据 xQueueReceive (xQueue, ulReceivedValue, portMAX_DELAY); // 队列数据和发送一致,队列接收数量+1 输出此时的队列接收数量 if (ulReceivedValue == 100UL ) { ulCountOfItemsReceivedOnQueue++; PRINTF ( "Receive message counter: %d.\r\n" , ulCountOfItemsReceivedOnQueue); } } } 软定时器: 设置软定时器周期1s,时间到后,调用回调函数,记录次数并串口打印。 // 创建软件定时器任务 时间为1s,周期循环 xExampleSoftwareTimer = xTimerCreate ( "LEDTimer" , mainSOFTWARE_TIMER_PERIOD_MS, pdTRUE, ( void *) 0 , vExampleTimerCallback); // 启动软件定时器 xTimerStart (xExampleSoftwareTimer, 0 ); // 回调函数 static void vExampleTimerCallback ( TimerHandle_t xTimer ) { // 每1s进入一次回调函数,计数增加 ulCountOfTimerCallbackExecutions++; PRINTF ( "Soft timer: %d s.\r\n" , ulCountOfTimerCallbackExecutions); } 系统节拍时钟: 通过设置文件 FreeRTOSConfig.h 中 configTICK_RATE_HZ 设置任务节拍中断频率, 在启动任务调度器时,系统会根据另一个变量CPU的频率configCPU_CLOCK_HZ计算对应写入节拍计数器的值,启动定时器中断。 // 设置系统时钟节拍为 1000/200=5ms # define configTICK_RATE_HZ ((TickType_t)200) 信号量: 每个系统节拍时钟中断中,调用函数vApplicationTickHook,累积500次即500*5ms=2.5s后,发送信号量。信号量任务获取信号后,计数并打印累积次数。 // 系统节拍为5ms,每个500*5ms=2.5s 释放事件信号量 void vApplicationTickHook ( void ) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; static uint32_t ulCount = 0 ; ulCount++; if (ulCount = 500UL) { // 在中断中释放事件信号量 xSemaphoreGiveFromISR(xEventSemaphore, xHigherPriorityTaskWoken); ulCount = 0UL; } } // 任务阻塞等待信号量,收到后,接收次数增加,并通过串口打印 static void prvEventSemaphoreTask ( void *pvParameters) { for (;;) { // 任务阻塞,直到能获取信号量 if (xSemaphoreTake(xEventSemaphore, portMAX_DELAY) != pdTRUE) { PRINTF( "Failed to take semaphore.\r\n" ); } // 接收到信号量的次数累加 ulCountOfReceivedSemaphores++; PRINTF( "Event task is running. Get semaphore :%d \r\n" ,ulCountOfReceivedSemaphores); } } 异常处理: 当内存分配失败、堆栈发生错误或任务空闲时,进入相应的函数,用户可添加相应的处理函数。 // 内存分配失败函数,当内存分配失败时,进入此函数 void vApplicationMallocFailedHook ( void ) { for (;;) ; } // 堆栈错误检查函数,当堆栈发生溢出时,进入此函数 void vApplicationStackOverflowHook (TaskHandle_t xTask, char *pcTaskName) { ( void )pcTaskName; ( void )xTask; for (;;) ; } // 空闲任务,优先级最低,没有实际意义,只是让CPU有事情做,用户可以自己添加自己的函数 void vApplicationIdleHook ( void ) { volatile size_t xFreeStackSpace; xFreeStackSpace = xPortGetFreeHeapSize (); if (xFreeStackSpace 100 ) { } } (2)实验现象 ① 编译程序:在uboot手动加载M核程序。 ② 队列:每隔200ms,队列发送任务发送数据,队列接收任务获取数据,从阻塞态到运行态,打印计数。 ③ 软定时器:每隔1s,时间到达,调用回调函数,打印计数。 ④ 信号量:每隔5ms,系统时钟节拍中断调用函数,超过500次后,释放信号量。信号量任务获的信号量,从阻塞态到运行态,打印计数。 2、FreeRTOS-外设 飞凌嵌入式OK-MX9352-C开发板支持外设使用FreeRTOS完成相应功能,示例代码如下: freertos_uart:freertos串口演示例程 freertos_lpi2c_b2b:freertos I2C演示例程 freertos_lpspi_b2b:freertos SPI演示例程 因freertos_uart例程使用的FreeRTOS特性比较典型,我们重点分析此例程。 (1)软件实现 示例程序内容包括:串口初始化任务、串口发送任务、串口接收任务。具体如下: 串口初始化任务: 主要包含串口外设初始化,发送、接收互斥量,发送和接收事件组。串口外设初始化在裸跑串口例程中已展现,此处不再详述。 // 创建串口发送互斥量 handle - txSemaphore = xSemaphoreCreateMutex (); // 创建串口接收互斥量 handle - rxSemaphore = xSemaphoreCreateMutex (); // 创建发送事件标志组 handle - txEvent = xEventGroupCreate (); // 创建接收事件标志组 handle - rxEvent = xEventGroupCreate (); 串口发送: 发送前获取信号量,启动发送流程,在中断中置位发送完成事件标志。发送任务获取到事件后,释放发送信号量。 // 1 获取发送信号量 if (pdFALSE == xSemaphoreTake (handle - txSemaphore, 0 )) { return kStatus_Fail; } handle - txTransfer.data = (uint8_t *)buffer; handle - txTransfer.dataSize = (uint32_t)length; // 2 阻塞式发送 status = UART_TransferSendNonBlocking (handle - base, handle - t_state, handle - txTransfer); if (status != kStatus_Success) { (void) xSemaphoreGive (handle - txSemaphore); return kStatus_Fail; } // 3 等待发送完成的事件 ev = xEventGroupWaitBits (handle - txEvent, RTOS_UART_COMPLETE, pdTRUE, pdFALSE, portMAX_DELAY); // 等待并判断多个事件位 if ((ev RTOS_UART_COMPLETE) == 0 U) { retval = kStatus_Fail; } // 4 发送完成,释放发送信号量 if (pdFALSE == xSemaphoreGive (handle - txSemaphore)) // 释放信号量 { retval = kStatus_Fail; } 串口接收: 接收前获取信号量,调用串口接收函数,在中断中置位获取事件标志。接收任务获取到事件后,释放接收信号量。 // 1获取接收信号量 if (pdFALSE == xSemaphoreTake (handle - rxSemaphore, portMAX_DELAY)) { return kStatus_Fail; } handle - rxTransfer.data = buffer; handle - rxTransfer.dataSize = (uint32_t)length; // 2 串口接收函数 status = UART_TransferReceiveNonBlocking (handle - base, handle - t_state, handle - rxTransfer, n); if (status != kStatus_Success) { (void) xSemaphoreGive (handle - rxSemaphore); return kStatus_Fail; } // 3 获取接收事件 ev = xEventGroupWaitBits (handle - rxEvent,RTOS_UART_COMPLETE | RTOS_UART_RING_BUFFER_OVERRUN | RTOS_UART_HARDWARE_BUFFER_OVERRUN, pdTRUE, pdFALSE, portMAX_DELAY); // 等待并判断接收完成事件位 // 3.1 硬件接收错误 if ((ev RTOS_UART_HARDWARE_BUFFER_OVERRUN) != 0 U) { UART_TransferAbortReceive (handle - base, handle - t_state); (void) xEventGroupClearBits (handle - rxEvent, RTOS_UART_COMPLETE); // 将接收完成的事件位清零, retval = kStatus_UART_RxHardwareOverrun; local_received = 0 ; } // 3.2 接收缓冲区过载错误 else if ((ev RTOS_UART_RING_BUFFER_OVERRUN) != 0 U) { UART_TransferAbortReceive (handle - base, handle - t_state); (void) xEventGroupClearBits (handle - rxEvent, RTOS_UART_COMPLETE); // 将接收完成的事件位清零, retval = kStatus_UART_RxRingBufferOverrun; local_received = 0 ; } // 3.3 接收完成 else if ((ev RTOS_UART_COMPLETE) != 0 U) { retval = kStatus_Success; local_received = length; } else { retval = kStatus_UART_Error; local_received = 0 ; } // 4 释放接收信号量 if (pdFALSE == xSemaphoreGive (handle - rxSemaphore)) { retval = kStatus_Fail; } (2)实验现象 ① 编译程序,在uboot手动加载M核程序。 ② 装置上电后,串口打印程序信息,此时通过键盘输入4个字符,M核调试串口将回显,重复输入和回显字符,证明程序运行成功。 以上就是在飞凌嵌入式i.MX 9352开发板M核上软件设计FreeRTOS的例程演示,希望能够对各位工程师朋友有所帮助。
  • 2025-6-9 14:59
    100 次阅读|
    0 个评论
    B y Toradex 秦海 1). 简介 在前述文章中,我们介绍了如何基于 Weston Composito 实现多屏幕分别显示不同的应用,而进一步延申出的一个应用场景,就是多屏幕之一的 HDMI 屏幕需要进行热插拔,而在热插拔的同时其对应的显示应用也同时启动或者停止,以便不影响其他屏幕的显示。本文就基于前述文章同样的 NXP i.MX8MP 平台来测试如何实现这个功能场景。 本文所演示的平台来自于 Toradex Verdin i.MX8MP 嵌入式平台 。 2. 准备 a). Verdin i.MX8MP ARM 核心版配合 Dahlia 载板, 并连接调试串口用于测试 。 b). Dahlia 载板分别由 DSI-HDMI 转接卡和 native HDMI 两个接口连接两台 HDMI 显示器以便于进行多屏显示测试。 3). 部署流程 a). 为了实现对于 native HDMI 接口连接的 HDMI-2 显示器热插拔动作的响应,需要通过 udev rule 来捕获相应触发模块并进行动作。 ./ 首先通过执行如下命令,然后操作硬件热插拔,获取被触发的 udev “ KERNEL ” 组件是 “ card1 ” , ” SUBSYSTEM ” 是 “ drm ” , ” Action ” 是 “ change ” --------------------------------------- root@verdin-imx8mp-06849028:~# udevadm monitor monitor will print the received events for: UDEV - the event which udev sends out after rule processing KERNEL - the kernel uevent KERNEL change /devices/platform/display-subsystem/drm/card1 (drm) UDEV change /devices/platform/display-subsystem/drm/card1 (drm) KERNEL change /devices/platform/display-subsystem/drm/card1 (drm) UDEV change /devices/platform/display-subsystem/drm/card1 (drm) --------------------------------------- ./ 基于上述信息生成如下 udev rules 文件 - /etc/udev/rules.d/99-hdmi-hotplug.rules ,这样无论当 HDMI 显示器连接还是断开的时候都会触发这个规则,并执行 hdmi- hotplug .sh 脚本代码。 --------------------------------------- # When HDMI is attached or unattached ACTION=="change", KERNEL=="card1", SUBSYSTEM=="drm", RUN+="/home/root/hdmi- hotplug .sh" --------------------------------------- b). hdmi- hotplug .sh 脚本代码实现 ./ native HDMI 对应系统 “/sys/class/drm/card1-HDMI-A-2” 设备,设备节点中有 “ status ” 项目对应当前 HDMI hotplug 状态 --------------------------------------- ### HDMI attached ### root@verdin-imx8mp-06849028:~# cat /sys/class/drm/card1-HDMI-A-2/status c onnected ### HDMI unattached ### root@verdin-imx8mp-06849028:~# cat /sys/class/drm/card1-HDMI-A-2/status disconnected --------------------------------------- ./ 基于上述判断生成 hdmi-hotplug.sh 脚本来启动或者停止 smarthome Qt 应用(可以见章节 1 提到的前述双屏显示文章)。在这里需要通过 systemd service 来启动应用,不能直接执行 smarthome 应用,否则在 hdmi-hotplug.sh 退出后应用也会同时退出。 --------------------------------------- #!/bin/bash tty=/dev/ttymxc2 hdmistatus=$(cat /sys/class/drm/card1-HDMI-A-2/status) if ; then echo "HDMI connected..." $tty systemctl start smarthome-app-launch else echo "HDMI disconnected..." $tty systemctl stop smarthome-app-launch fi --------------------------------------- ./ 赋予 hdmi-hotplug.sh 脚本可执行属性 --------------------------------------- root@verdin-imx8mp-06849028:~# chmod +x hdmi-hotplug.sh --------------------------------------- c). 部署 smarthome-app-launch service 文件 ./ smarthome-app-launch service 文件 - /lib/systemd/system/smarthome-app-launch.service --------------------------------------- Description=Start a wayland application After=weston.service Requires=weston.service Type=simple User=root PAMName=login Environment=WAYLAND_DISPLAY=/run/wayland-0 Environment=QT_QPA_PLATFORM=wayland-egl WorkingDirectory=/usr/share/qtsmarthome-1.0/ ExecStart=/usr/share/qtsmarthome-1.0/smarthome Restart=on-failure RestartSec=1 WantedBy=graphical.target --------------------------------------- ./ 更新 systemd service 文件 --------------------------------------- root@verdin-imx8mp-06849028:~# systemctl daemon-reload --------------------------------------- d). 参考章节 1 前述双屏显示文章同样内容,修改 /etc/xdg/weston/weston.ini 文件。 --------------------------------------- --- a/etc/xdg/weston/weston.ini +++ b/etc/xdg/weston/weston.ini @@ -4,6 +4,7 @@ idle-time=0 xwayland=true #enable-overlay-view=1 +shell=kiosk-shell.so @@ -12,13 +13,16 @@ touchscreen_calibrator=true calibration_helper=/usr/bin/toradex-save-touchscreen-calibration -# -#name=HDMI-A-1 -#mode=1920x1080@60 + +name=HDMI-A-1 +app-ids=Qt5_CinematicExperience +mode=1920x1080@60 #transform=rotate-90 -# -#name=HDMI-A-2 + +name=HDMI-A-2 +app-ids=smarthome +mode=1920x1080 #mode=off # WIDTHxHEIGHT Resolution size width and height in pixels # off Disables the output --------------------------------------- e). 另外, wayland-app-launch.service 是 Toradex Yocto Multimedia BSP 默认使能用于 Qt5_CinematicExperience 应用启动的 systemd service 文件,如果之前关闭了,可以通过下面命令使能。 --------------------------------------- root@verdin-imx8mp-06849028:~# systemctl enable wayland-app-launch --------------------------------------- f). 最后所有上述修改完成后重新启动。 4 ). 测试 a). 重新启动后,当两个 HDMI 显示器都连接,可以通过屏幕观察以及如下进程查询确认两个 Qt 应用都分别显示在相应的显示器上面。 ------------------------------- root@verdin-imx8mp-06849028:~# ps -aux |grep Qt5_CinematicExperience root 522 38.3 3.8 1085948 153664 ? Ssl 06:20 0:41 /usr/share/cinematicexperienc e-1.0/Qt5_CinematicExperience --fullscreen root 569 0.0 0.0 2944 1280 ttymxc2 S+ 06:22 0:00 grep Qt5_CinematicExperience root@verdin-imx8mp-06849028:~# ps -aux |grep smarthome root 520 31.8 1.9 1007248 77160 ? Ssl 06:20 0:37 /usr/share/qtsmarthome-1.0/sm arthome root 571 0.0 0.0 2944 1152 ttymxc2 S+ 06:22 0:00 grep smarthome ------------------------------- b ). 当将 native HDMI 连接的 HDMI-2 屏幕断开后,对应的 smarthome 应用也同时退出 ------------------------------- root@verdin-imx8mp-06849028:~# HDMI disconnected... root@verdin-imx8mp-06849028:~# ps -aux |grep smarthome root 645 0.0 0.0 2944 1152 ttymxc2 S+ 06:28 0:00 grep smarthome ------------------------------- c ). 当 native HDMI 连接的 HDMI-2 屏幕重新连接后,对应的 smarthome 应用也同时启动 ------------------------------- root@verdin-imx8mp-06849028:~# HDMI connected... root@verdin-imx8mp-06849028:~# ps -aux |grep smarthome root 650 72.3 1.8 1007248 74052 ? Ssl 06:28 0:04 /usr/share/qtsmarthome-1.0/sm arthome root 662 0.0 0.0 2944 1280 ttymxc2 S+ 06:28 0:00 grep smarthome ------------------------------- 5 ). 总结 本文 基于 NXP i.MX8MP 处理器平台测试了 Yocto Linux 下 HDMI 显示器热插拔情况下对应显示应用同步启动和停止。 参考文档 https://blog.csdn.net/qq_17292655/article/details/134670878
  • 2025-5-30 11:04
    183 次阅读|
    0 个评论
    米尔电子基于与NXP长期合作的嵌入式处理器开发经验,在i.MX 6和i.MX 8系列核心板领域已形成完整产品矩阵,米尔累计推出5个平台共计二十余款NXP核心板,涵盖工业物联网、新能源、医疗等领域。此次推出的米尔基于‌ NXP i.MX 91核心板及开发板 ‌(MYC-LMX91),延续了米尔在嵌入式模组领域的技术积累,赋能新一代入门级嵌入式Linux应用。提供1GB LPDDR4 8GB eMMC 的核心板和开发板,核心板采用218PIN引脚的LGA封装设计,工作温度为-40℃-85℃,适应工业级的严苛环境使用。 MYC-LMX91 核心板及开发板 ‌基于 NXP i.MX 91作为NXP新款入门级处理器,具有低成本、低功耗的特点。i.MX 91配备单核 Cortex-A55@1.4 GHz,可与i.MX 93处理器实现引脚兼容。此外,这款处理器支持多种外设接口资源,2个千兆以太网接口、2个USB2.0接口、2个CAN-FD接口、8个UART接口,8个I2C,8个SPI,2个I3C等,适用于充电桩、HMI、工业网关等场景。 配套开发板 MYD-LMX91开发板,采用 12V/2A 直流供电,搭载了2路千兆以太网接口、1路M.2 B型插座的5G/4G 模块接口、板载1路 WIFI模块、1路 RGB 显示接口、1路音频输入输出接口、2路USB HOST Type A、1路USB OTG Type-C接口、1路Micro SD接口、1路CAN由凤凰端子引出、1路RS485由凤凰端子引出、1路RS232 凤凰端子引出,1路JTAG调试接口,1路ADC接口。 米尔 i.MX 91 核心板配置型号 表MYC- LMX91 核心板选型表 米尔 i.MX 91 开发板配置型号 表MYD-LMX91开发板选型表 NXP i.MX 9、i.MX 93、i.MX 9 1、 i.MX 9核心板、i.MX 9开发板
  • 2025-5-28 12:00
    0 个评论
    B y Toradex 胡珊逢 简介 在 上一篇文章 中我们已经介绍如何使用 meta-toradex-security layer 创建一个单独的分区。接下来我们将说明,如何在该分区上使用加密功能,读写性能测试,如果你还感兴趣,最后部分内容将阐述分区加密背后的原理。技术实现细节隐藏在 meta-toradex-security 的 recipes 里面,用户能够仅使用几行简单的配置即可开启分区加密。 Yocot Project 配置 和之前的文章一样,首先需要搭建 Yocto Project 编译环境。这里我们将以 Verdin iMX8MP 为例进行介绍。在 local.conf 的结尾添加下面两行配置即可。 INHERIT += "tdx-tezi-data-partition tdx-encrypted" TDX_ENC_STORAGE_LOCATION = "/dev/mmcblk2p3" tdx-encrypted 被添加后,所编译的镜像中就能够使用分区加密。'TDX_ENC_STORAGE_LOCATION' 用于配置需要加密的分区。在 Verdin iMX8MP 上 eMMC 会被挂载到 /dev/mmcblk2 下面,mmcblk2p3 是需要新创建的第三个分区。'TDX_ENC_STORAGE_LOCATION' 也可以设置为 /dev/sda1 或者 /dev/mmcblk0p1,分别对应外部的 U 盘、SD 卡等。 为了提供加密的安全性和效率,meta-toradex-security 可以利用加密运算单元,例如 iMX8M Plus SoC 上的 CAAM(Cryptographic Accelerator and Assurance Module)模块。当然也支持外部的 TPM,例如在 Verdin AM62 上由于没有 SoC 内置的加密运算单元,meta-toradex-security 则能够使用底板上的 TPM,如 Mallow 底板。对于使用 TPM 的模块,local.conf 中还需要添加 TDX_ENC_KEY_BACKEND = "tpm"。 然后使用 bitbake 命令编译镜像即可。 bitbake tdx-reference-minimal- image 当安装完系统并重启后,可以看到 /dev/mapper/encdata,这就是 /dev/mmcblk2p3 分区 DATA 对应的加密设备,它被挂载到 /run/encdata 目录下。在 local.conf 文件中 TDX_ENC_STORAGE_MOUNTPOINT 参数可以用来更改挂载路径。 ~# mount -l/dev/mapper/encdata on /run/encdata type ext4 (rw,relatime) Linux 的文件系统 RFS 使用非加密分区,我们将分别往加密的 /run/encdata 和非加密的 /home/root 目录下写入和读取 1GB 的文件,对比加密操作对读写文件的影响。 #write fio --name= test --filename=testfile.tmp --size=1G --bs=256k --iodepth=64 \ --readwrite =write --direct= 1 --ioengine=libaio --gtod_reduce= 1 # read fio --name= test --filename=testfile.tmp --size=1G --bs=256k --iodepth=64 \ --readwrite =read --direct= 1 --ioengine=libaio --gtod_reduce= 1 测试结果如下: /run/encdata /home/root 写入 1GB 文件 32.8MB/s 61.9MB/s 读取 1GB 文件 32.6MB/s 314MB/s 实现原理 如前面介绍,借助 meta-toradex-security 开启和使用加密分区是非常简单的,对于应用程序来讲,底层的加密是透明的。如果你还对实现原理感兴趣,请继续阅读下面的内容。 i.MX8 SoC 上有一个 CAAM 模块可以用于密钥生成、加密和解密运算。通常用户不会直接调用 CAAM API 进行相关操作。为了更好地保护密钥,我们使用 Linux 的一个内核功能 Trusted Keys 。Trusted Keys 能够在内核空间中生成和维护一个密钥,而在用户空间中则是该密钥的加密文件(encrypted blobs),用户空间中无法直接访问到密钥。密钥的加密和解密是在 CAAM 上完成,而 CAAM 就是 Trusted Keys 的 Trust Source。对于没有 CAAM 作为 Trust Source 的平台,例如 Verdin AM62,我们还可以使用外部的 TPM,甚至是 TEE (Trusted Execution Environment)。这些在 meta-toradex-security 中均得到支持。 另外一个内核功能 dm-crypt 实现了透明的分区加密。dm-crypt 会使用一个密钥用于加密需要写入到 /run/encdata 目录下文件。该密钥可以存储在文件系统分区 RFS 的一个目录下,默认配为 / var /local/ private /.keys/tdx-enc-key.blob 。如果 RFS 是只读文件系统,tdx-enc-key.blob 也可以直接存放在 eMMC 上。但 tdx-enc- key .blob 并是直接用于加密和解密的密钥。如前面提到的,密钥的维护是由 Trusted Keys 实现。 在 CAAM 中首先生成一个密钥 encryption key,该密钥不会离开 CAAM。该密钥通过同样位于 CAAM 的 Test key 或者 OTPMK Key 加密后,交给位于 kernel space 的 Trust Keys 维护。Trust Keys 为 dm-crypt 映射一个密钥,并存放在 / var /local/ private /.keys/tdx-enc-key.blob 。当位于 user space 的应用程序需要读写 /run/encdata 的文件时,dm-crypt 将 tdx-enc- key .blob 提交给 Trusted Keys。Trusted Keys 在 kernel space 中找到对应的密钥,然后将该密钥交给 CAAM。在 CAAM 中可以使用 Test key 或者 OTPMK Key 解密出 encryption key。并在 CAAM 中使用 encryption key 完成加密或者解密数据。这个过程中 Test key/OTPMK Key 和 encryption key 始终不会离开 CAAM,并且加密和解密操作也均在 CAAM 中进行。由于 / var /local/ private /.keys/tdx-enc-key.blob 并不是直接用于加密和解密的密钥。因此,即使该密钥泄漏,也不会导致原来位于 /run/encdata 的数据被破解。 对于使用 CAAM 的模块,例如 Verdin iMX8MM、Verdin iMX8MP、Apalis iMX8QM 等,如果没有开启 Secure Boot 功能,如前面提到,CAAM 会使用预制的 Test key 来加密密钥,这是不安全的,也不推荐在生产环境中使用。开启 Secure Boot 功能后,CAAM 则使用 OTPMK Key 来加密密钥。这是一个 256bit 随机生产的密钥,在 fuse 设备的时候烧录到 CAAM 中。因此该密钥必须妥善保管。 总结 借助 meta-toradex-security,用户能够轻松而安全地使用加密分区,保护数据和应用。更多来自 meta-toradex-security 实用功能,我们将在后续文章介绍,敬请关注。
相关资源