查看: 1763|回复: 2

[分享] 【基于MCX N947】USB 一线通监控副屏

[复制链接]
  • TA的每日心情
    开心
    2025-7-11 08:53
  • 签到天数: 301 天

    连续签到: 2 天

    [LV.8]以坛为家I

    3930

    主题

    7550

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    40090
    最后登录
    2025-8-29
    发表于 2024-10-25 13:29:27 | 显示全部楼层 |阅读模式
    【基于MCX N947】USB 一线通监控副屏


    环境搭建
    环境变量配置
    为了提高一些编译的速度,选择了在 Linux 系统下进行开发,在 Linux 上开发 N947 需要先安装 env 工具 https://github.com/RT-Thread/env ,按照说明文档进行安装即可,然后配置一些环境变量如下


    其中 /opt/arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi/bin 是自己的编译工具链的路径,/home/book/rt-thread 是 rt-thread 根目录的路径
    1. source ~/.env/env.sh

    2. export RTT_CC=gcc

    3. export RTT_ROOT=/home/book/rt-thread

    4. export RTT_DIR=/home/book/rt-thread

    5. export RTT_EXEC_PATH=/opt/arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi/bin

    6. export PATH=$PATH:$RTT_EXEC_PATH
    复制代码
    如果需要将 N947 的例程从 rt-thread 的根文件夹中独立出来的话,需要删除工程中 Kconfig 文件的这行代码
    11.png
    代码高亮
    这里使用 VSCode 中的 Clang 插件,代码高亮和补全可以通过使用编译时候生成的 compile_commands.json 文件来实现,而 RT-Thread 的工程是采用的 scons 工具,所以可以使用 scons_compiledb 这个 python 包来生成 compile_commands.json 实现代码高亮,修改过的 SConstruct 文件如下
    1. import os

    2. import sys

    3. import rtconfig

    4. import scons_compiledb

    5. if os.getenv('RTT_ROOT'):

    6.     RTT_ROOT = os.getenv('RTT_ROOT')

    7. else:

    8.     RTT_ROOT = os.path.normpath(os.getcwd() + '/../../../../..')

    9. sys.path = sys.path + [os.path.join(RTT_ROOT, 'tools')]

    10. try:

    11.     from building import *

    12. except:

    13.     print('Cannot found RT-Thread root directory, please check RTT_ROOT')

    14.     print(RTT_ROOT)

    15.     exit(-1)

    16. TARGET = 'rtthread.' + rtconfig.TARGET_EXT

    17. if rtconfig.PLATFORM == 'armcc':

    18.     env = Environment(tools = ['mingw'],

    19.         AS = rtconfig.AS, ASFLAGS = rtconfig.AFLAGS,

    20.         CC = rtconfig.CC, CFLAGS = rtconfig.CFLAGS,

    21.         CXX = rtconfig.CXX, CXXFLAGS = rtconfig.CXXFLAGS,

    22.         AR = rtconfig.AR, ARFLAGS = '-rc',

    23.         LINK = rtconfig.LINK, LINKFLAGS = rtconfig.LFLAGS,

    24.         # overwrite cflags, because cflags has '--C99'

    25.         CXXCOM = '$CXX -o $TARGET --cpp -c $CXXFLAGS $_CCCOMCOM $SOURCES')

    26. else:

    27.     env = Environment(tools = ['mingw'],

    28.         AS = rtconfig.AS, ASFLAGS = rtconfig.AFLAGS,

    29.         CC = rtconfig.CC, CFLAGS = rtconfig.CFLAGS,

    30.         CXX = rtconfig.CXX, CXXFLAGS = rtconfig.CXXFLAGS,

    31.         AR = rtconfig.AR, ARFLAGS = '-rc',

    32.         LINK = rtconfig.LINK, LINKFLAGS = rtconfig.LFLAGS,

    33.         CXXCOM = '$CXX -o $TARGET -c $CXXFLAGS $_CCCOMCOM $SOURCES')

    34. env.PrependENVPath('PATH', rtconfig.EXEC_PATH)

    35. scons_compiledb.enable(env)

    36. env.CompileDb()

    37. if rtconfig.PLATFORM in ['iccarm']:

    38.     env.Replace(CCCOM = ['$CC $CFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -o $TARGET $SOURCES'])

    39.     env.Replace(ARFLAGS = [''])

    40.     env.Replace(LINKCOM = env["LINKCOM"] + ' --map rtthread.map')

    41. Export('RTT_ROOT')

    42. Export('rtconfig')

    43. SDK_ROOT = os.path.abspath('./')

    44. if os.path.exists(SDK_ROOT + '/Libraries'):

    45.     libraries_path_prefix = SDK_ROOT + '/Libraries'

    46. else:

    47.     libraries_path_prefix = os.path.dirname(SDK_ROOT) + '/Libraries'

    48. SDK_LIB = libraries_path_prefix

    49. Export('SDK_LIB')

    50. # prepare building environment

    51. objs = PrepareBuilding(env, RTT_ROOT, has_libcpu=False)

    52. objs.extend(SConscript(os.path.join(libraries_path_prefix, 'drivers', 'SConscript')))

    53. # include cmsis

    54. objs.extend(SConscript(os.path.join(libraries_path_prefix, rtconfig.BSP_LIBRARY_TYPE, 'SConscript')))

    55. # make a building

    56. DoBuilding(TARGET, objs)
    复制代码
    最终搭建完成的效果如下所示,代码高亮十分方便查看代码
    12.png
    LVGL 适配
    屏幕拓展板
    FRDM-MCXN947 这个开发板预留了一个 FlexIO 接口可以适配 8080 的并口屏,于是做了一个屏幕拓展板,把手里闲置的屏幕用起来
    13.png
    实物如下,触摸排线座子有点偏下,不过不影响功能
    14.png
    屏幕手册说明分辨率是 240*320 驱动芯片是 ST7789V、触摸芯片是 FT6336G,而官方的 SDK 中是有 ST7796 和 FT5406 的驱动代码的,后续还需要稍作修改
    15.png
    驱动适配
    在官方的 SDK 中有 ST7796 和 FT5406 的驱动程序,直接移植过来即可,同时也把 EDMA 和 SMARTDMA 的驱动复制了过来,修改一下屏幕的初始化序列即可驱动屏幕
    16.png
    LVGL 适配
    将SDK中的 lvgl_support 复制到工程中,修改屏幕的宽高为 240*320
    17.png
    然后在 board 中新建一个 lv_conf.h 文件,填入关于 LVGL 的一些配置,因为许多配置在 menuconfig 中有所设置,所以这里的配置项并不多
    1. #ifndef LV_CONF_H

    2. #define LV_CONF_H

    3. #include <rtconfig.h>

    4. #define LV_USE_SYSMON           1

    5. #define LV_USE_PERF_MONITOR     0

    6. #define LV_COLOR_DEPTH          16

    7. #define LV_HOR_RES_MAX          240

    8. #define LV_VER_RES_MAX          320

    9. #define LV_COLOR_16_SWAP        0

    10. #define BSP_USING_LVGL_BENCHMARK_DEMO

    11. #define BSP_USING_LVGL_WIDGETS_DEMO

    12. #ifdef BSP_USING_LVGL_DAVE2D

    13.     #define LV_USE_DRAW_DAVE2D      1

    14. #endif

    15. #ifdef BSP_USING_LVGL_WIDGETS_DEMO

    16.     #define LV_USE_DEMO_WIDGETS 1

    17.     #define LV_DEMO_WIDGETS_SLIDESHOW   0

    18. #endif  /* BSP_USING_LVGL_WIDGETS_DEMO */

    19. /*Benchmark your system*/

    20. #ifdef BSP_USING_LVGL_BENCHMARK_DEMO

    21.     #define LV_USE_DEMO_BENCHMARK 1

    22.     /*Use RGB565A8 images with 16 bit color depth instead of ARGB8565*/

    23.     #define LV_DEMO_BENCHMARK_RGB565A8  1

    24.     #define LV_FONT_MONTSERRAT_14       1

    25.     #define LV_FONT_MONTSERRAT_24       1

    26. #endif  /* BSP_USING_LVGL_BENCHMARK_DEMO */

    27. /*Stress test for LVGL*/

    28. #ifdef BSP_USING_LVGL_STRESS_DEMO

    29.     #define LV_USE_DEMO_STRESS 1

    30. #endif  /* BSP_USING_LVGL_STRESS_DEMO */

    31. /*Render test for LVGL*/

    32. #ifdef BSP_USING_LVGL_RENDER_DEMO

    33.     #define LV_USE_DEMO_RENDER 1

    34. #endif  /* BSP_USING_LVGL_RENDER_DEMO */

    35. /*Music player demo*/

    36. #ifdef BSP_USING_LVGL_MUSIC_DEMO

    37.     #define LV_USE_DEMO_MUSIC 1

    38.     #define LV_DEMO_MUSIC_SQUARE    1

    39.     #define LV_DEMO_MUSIC_LANDSCAPE 0

    40.     #define LV_DEMO_MUSIC_ROUND     0

    41.     #define LV_DEMO_MUSIC_LARGE     0

    42.     #define LV_DEMO_MUSIC_AUTO_PLAY 0

    43.     #define LV_FONT_MONTSERRAT_12   1

    44.     #define LV_FONT_MONTSERRAT_16   1

    45. #endif  /* BSP_USING_LVGL_MUSIC_DEMO */

    46. #endif
    复制代码
    复制过来的 lvgl_support 中有对 FreeRTOS 的支持,这里将 FreeRTOS 的 API 修改为 RTT 的 API,例如如下这段代码
    18.png
    并且 N947 的驱动程序有 EDMA + FlexIO 和 SMARTDMA + FlexIO 两种驱动方式,具体区别不太了解,不过可以运行 LVGL 的 Benchmark 测试来看下结果,左边是 SMARTDMA 运行的结果,右边是 EDMA 的结果,可以看到前者的 FPS 更高。后续也就继续采用 SMARTDMA + FlexIO 的驱动方式
    19.png
    界面设计
    然后使用 GUI Guider 设计一个界面,生成绘制界面的代码,然后添加到工程中
    20.png
    还需要修改工程文件夹中的 rtconfig.py,增加一个 LV_LVGL_H_INCLUDE_SIMPLE 的预定义,因为生成的代码默认包含 lvgl.h 是 #include "lvgl/lvgl.h"


    CFLAGS = DEVICE + ' -Wall -D__FPU_PRESENT -DLV_LVGL_H_INCLUDE_SIMPLE'
    最终适配完成的 LVGL 代码和 GUI Guider 的代码目录如下,LVGL 的 UI 绘制代码段如图右边所示,具体代码可见开源地址
    21.png
    USB 通讯
    适配 CDC
    完成了下位机的界面的初始化绘制,后续的任务当然就是怎么把数据采集并发送给下位机来更新界面的数据了,下面先完成 USB 的通讯,这里使用的是 RTT 官方推荐的 CherryUSB 这个开源 USB 协议栈
    22.png
    将如下链接中的适配代码复制到工程中


    https://github.com/CherryUSB/cherryusb_mcx


    因为传输的数据比较单一,这里使用串口屏的思路,直接用 CDC_ACM 的通讯方式,也就是在上位机显示为一个 USB 转串口设备,直接使用串口 API 完成通讯


    将 RTT 根目录中 rt-thread/components/drivers/usb/cherryusb/demo 文件夹中的 CDC_ACM 例程复制到工程中,并且把根目录中的这两行代码屏蔽
    23.png
    修改工程中的 cherryusb_port.c 文件,添加对 CDC_ACM 的支持

    1. /*

    2. * Copyright (c) 2006-2024, RT-Thread Development Team

    3. *

    4. * SPDX-License-Identifier: Apache-2.0

    5. *

    6. * Change Logs:

    7. * Date           Author       Notes

    8. * 2024/04/23     sakumisu    first version

    9. */

    10. #include <rtthread.h>

    11. #include <board.h>

    12. /* low level init here, this has implemented in cherryusb */
    13. /* low level deinit here, this has implemented in cherryusb */
    14. #ifdef RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM
    15. int cherryusb_devinit(void)
    16. {
    17.     // extern void cherryusb_devinit(uint8_t busid, uintptr_t reg_base);
    18.     extern void cdc_acm_init(uint8_t busid, uintptr_t reg_base);
    19.     cdc_acm_init(0, USBHS1__USBC_BASE);
    20.     return 0;
    21. }
    22. INIT_COMPONENT_EXPORT(cherryusb_devinit);
    23. #endif
    24. #ifdef RT_CHERRYUSB_DEVICE_TEMPLATE_MSC
    25. int cherryusb_devinit(void)

    26. {
    27.     extern void msc_ram_init(uint8_t busid, uintptr_t reg_base);
    28.     msc_ram_init(0, USBHS1__USBC_BASE);
    29.     return 0;
    30. }
    31. INIT_COMPONENT_EXPORT(cherryusb_devinit);
    32. #endif
    33. #ifdef RT_CHERRYUSB_HOST
    34. #include "usbh_core.h"
    35. int cherryusb_hostinit(void)
    36. {
    37.     usbh_initialize(0, USBHS1__USBC_BASE);
    38.     return 0;
    39. }
    40. INIT_COMPONENT_EXPORT(cherryusb_hostinit);
    41. #endif
    复制代码

    将刚才复制到工程中的 CDC_ACM 的 demo 程序中端点收发的程序做如下修改,增加对于输入信息的回显
    1. void usbd_cdc_acm_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)

    2. {

    3.     USB_LOG_RAW("actual out len:%d\r\n", nbytes);

    4.     /* setup next out ep read transfer */

    5.     usbd_ep_start_read(busid, CDC_OUT_EP, read_buffer, 2048);

    6.     for (int i = 0; i < nbytes; i++) {

    7.         printf("%02x ", read_buffer[i]);

    8.     }

    9.     printf("\r\n");

    10. }
    复制代码

    验证
    然后插上开发板的 USB HS 那个 USB 接口,用串口工具发个数据包
    24.png
    可以看到 已经识别成了 USB 串行设备,PID 和 VID 也是我自己设置的 0xE6E9 和 0x1122,后续上位机与开发板建立通讯锁定 COM 号就是依靠 PID VID 来查询实现,使用串口工具给开发板发送的数据也可以正常接收到


    上位机
    时间原因上位机做的比较简单,实现了如下几个功能:


    读取电脑的 CPU、GPU 的占用率和温度信息、获取当前时间
    根据 VID、PID 查询 COM来与开发板通讯,下发采集数据与时间
    增加帧头后发送到下位机,固定长度 32+2 个字节
    25.png
    下位机数据更新
    在开发板端增加一个 thread 来负责把 USB 接收到的数据更新到屏幕上面,使用 LVGL 的 API 直接修改数据即可,代码如下


    数据结构体
    1. typedef struct

    2. {

    3.     uint16_t cpu_usage;

    4.     uint16_t mem_usage;

    5.     uint16_t gpu_usage;

    6.     uint16_t cpu_freq;

    7.     uint16_t cpu_temperature;

    8.     uint16_t gpu_temperature;

    9.     uint16_t board_temperature;

    10. } monitor_info_u16_t;

    11. typedef struct {

    12.     uint16_t wYear;

    13.     uint16_t wMonth;

    14.     uint16_t wDayOfWeek;

    15.     uint16_t wDay;

    16.     uint16_t wHour;

    17.     uint16_t wMinute;

    18.     uint16_t wSecond;

    19.     uint16_t wMilliseconds;

    20. } mytime_t;
    复制代码

    在 USB 端点输出的回调函数中增加消息队列发送函数
    1. void usbd_cdc_acm_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)

    2. {

    3.     USB_LOG_RAW("actual out len:%d\r\n", nbytes);

    4.     /* setup next out ep read transfer */

    5.     usbd_ep_start_read(busid, CDC_OUT_EP, read_buffer, 2048);

    6.     for (int i = 0; i < nbytes; i++) {

    7.         printf("%02x ", read_buffer[i]);

    8.     }

    9.     printf("\r\n");

    10.     if (34 == nbytes)

    11.     {

    12.         rt_mq_send(&usb_mq, read_buffer, 34);

    13.     }

    14. }
    复制代码

    main 函数中的接收消息队列
    1. uint8_t read_buffer[128];

    2. while (1)

    3. {

    4.     /* 从消息队列中接收消息 */

    5.     if (rt_mq_recv(&usb_mq, read_buffer, 34, RT_WAITING_FOREVER) > 0)

    6.     {

    7.         mytime_t* p_time_u16 = (mytime_t*)(read_buffer + 2);

    8.         monitor_info_u16_t* p_info_u16 = (monitor_info_u16_t *)(read_buffer + 2 + sizeof(mytime_t));

    9.         rt_kprintf("wYear         %u\r\n", p_time_u16->wYear);

    10.         rt_kprintf("wMonth        %u\r\n", p_time_u16->wMonth);

    11.         rt_kprintf("wDayOfWeek    %u\r\n", p_time_u16->wDayOfWeek);

    12.         rt_kprintf("wDay          %u\r\n", p_time_u16->wDay);

    13.         rt_kprintf("wHour         %u\r\n", p_time_u16->wHour);

    14.         rt_kprintf("wMinute       %u\r\n", p_time_u16->wMinute);

    15.         rt_kprintf("wSecond       %u\r\n", p_time_u16->wSecond);

    16.         rt_kprintf("wMilliseconds %u\r\n", p_time_u16->wMilliseconds);

    17.         rt_kprintf("cpu_usage         %u\r\n", p_info_u16->cpu_usage);

    18.         rt_kprintf("mem_usage         %u\r\n", p_info_u16->mem_usage);

    19.         rt_kprintf("gpu_usage         %u\r\n", p_info_u16->gpu_usage);

    20.         rt_kprintf("cpu_freq          %u\r\n", p_info_u16->cpu_freq);

    21.         rt_kprintf("cpu_temperature   %u\r\n", p_info_u16->cpu_temperature);

    22.         rt_kprintf("gpu_temperature   %u\r\n", p_info_u16->gpu_temperature);

    23.         rt_kprintf("board_temperature %u\r\n", p_info_u16->board_temperature);

    24.         lv_label_set_text_fmt(guider_ui.screen_label_cpu_temp, "%2d", p_info_u16->cpu_temperature);

    25.         lv_label_set_text_fmt(guider_ui.screen_label_gpu_temp, "%2d", p_info_u16->gpu_temperature);

    26.         lv_label_set_text_fmt(guider_ui.screen_label_cpu_load, "%2d", p_info_u16->cpu_usage);

    27.         lv_label_set_text_fmt(guider_ui.screen_label_gpu_load, "%2d", p_info_u16->gpu_usage);

    28.         lv_arc_set_value(guider_ui.screen_arc_gpu_load, p_info_u16->gpu_usage);

    29.         lv_arc_set_value(guider_ui.screen_arc_gpu_temp, p_info_u16->gpu_temperature);

    30.         lv_label_set_text_fmt(guider_ui.screen_time, "%02d:%02d", p_time_u16->wHour, p_time_u16->wMinute);

    31.         lv_label_set_text_fmt(guider_ui.screen_date, "%02d.%02d.%02d", p_time_u16->wYear, p_time_u16->wMonth, p_time_u16->wDay);

    32.     }

    33. }
    复制代码

    成品效果
    目前支持了对于时间、日期、CPU、GPU 的占用率和温度信息,其他的信息还在完善当中
    26.png

    下位机代码可见GitHub:https://github.com/POMIN-163/FRDM-MCXN947-USB-Monitor
    ————————————————
    文章出处:本文为RT-Thread论坛用户「pomin」的原创文章
    原文链接:https://club.rt-thread.org/ask/article/fd0a9bdab79b7c65.html

    qiandao qiandao
    回复

    使用道具 举报

  • TA的每日心情
    开心
    昨天 10:11
  • 签到天数: 367 天

    连续签到: 6 天

    [LV.9]以坛为家II

    12

    主题

    1184

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    2696
    最后登录
    2025-8-30
    发表于 2024-10-25 18:37:23 | 显示全部楼层
    为大佬点赞
    哎...今天够累的,签到来了~
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2025-8-8 16:43
  • 签到天数: 1504 天

    连续签到: 1 天

    [LV.Master]伴坛终老

    97

    主题

    4692

    帖子

    12

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    10093
    最后登录
    2025-8-8
    发表于 2024-10-27 14:32:12 | 显示全部楼层
    像这种在linux系统编译,真的可以加快编译进度吗?
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /3 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2025-8-31 12:13 , Processed in 0.096420 second(s), 23 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表