0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

USB 一线通监控副屏

RT-Thread官方账号 2024-12-07 01:10 次阅读

环境搭建

环境变量配置

为了提高一些编译的速度,选择了在 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 根目录的路径

source ~/.env/env.sh export RTT_CC=gcc export RTT_ROOT=/home/book/rt-thread export RTT_DIR=/home/book/rt-thread export RTT_EXEC_PATH=/opt/arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi/bin export PATH=$PATH:$RTT_EXEC_PATH

如果需要将 N947 的例程从 rt-thread 的根文件夹中独立出来的话,需要删除工程中 Kconfig 文件的这行代码

0163bda2-b3f5-11ef-8084-92fbcf53809c.png

代码高亮

这里使用 VSCode 中的 Clang 插件,代码高亮和补全可以通过使用编译时候生成的 compile_commands.json 文件来实现,而 RT-Thread 的工程是采用的 scons 工具,所以可以使用 scons_compiledb 这个 python 包来生成 compile_commands.json 实现代码高亮,修改过的 SConstruct 文件如下

import osimport sysimport rtconfigimport scons_compiledbif os.getenv('RTT_ROOT'): RTT_ROOT = os.getenv('RTT_ROOT')else: RTT_ROOT = os.path.normpath(os.getcwd() + '/../../../../..')sys.path = sys.path + [os.path.join

(RTT_ROOT, 'tools')]try: from building import *except: print('Cannot found RT-Thread root directory, please check RTT_ROOT') print(RTT_ROOT) exit(-1)TARGET = 'rtthread.' + rtconfig.TARGET_EXTif rtconfig.PLATFORM == 'armcc': env = Environment(tools = ['mingw'], AS = rtconfig.AS, ASFLAGS = rtconfig.AFLAGS, CC = rtconfig.CC, CFLAGS = rtconfig.CFLAGS,

CXX =

rtconfig.CXX, CXXFLAGS = rtconfig.CXXFLAGS, AR = rtconfig.AR, ARFLAGS = '-rc', LINK = rtconfig.LINK, LINKFLAGS = rtconfig.LFLAGS, # overwrite cflags, because cflags has '--C99' CXXCOM = '$CXX -o $TARGET --cpp -c $CXXFLAGS $_CCCOMCOM $SOURCES')else: env = Environment(tools = ['mingw'], AS = rtconfig.AS, ASFLAGS = rtconfig.AFLAGS, CC = rtconfig.CC, CFLAGS = rtconfig.CFLAGS, CXX = rtconfig.CXX, CXXFLAGS = rtconfig.CXXFLAGS, AR = rtconfig.AR, ARFLAGS = '-rc', LINK = rtconfig.LINK, LINKFLAGS = rtconfig.LFLAGS, CXXCOM = '$CXX -o $TARGET -c $CXXFLAGS

$_CCCOMCOM

$SOURCES')env.PrependENVPath('PATH', rtconfig.EXEC_PATH)scons_compiledb.enable(env)env.CompileDb()if rtconfig.PLATFORM in ['iccarm']: env.Replace(CCCOM =

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

env["LINKCOM"] + ' --map rtthread.map')Export('RTT_ROOT')Export('rtconfig')SDK_ROOT

= os.path.abspath('./')if os.path.exists(SDK_ROOT + '/Libraries'): libraries_path_prefix = SDK_ROOT + '/Libraries'else: libraries_path_prefix

= os.path.dirname(SDK_ROOT) + '/Libraries'SDK_LIB = libraries_path_prefixExport('SDK_LIB')# prepare building environmentobjs =

PrepareBuilding(env, RTT_ROOT,

has_libcpu=False)objs.extend(SConscript(os.path.join(libraries_path_prefix, 'drivers', 'SConscript')))#

include cmsisobjs.extend(SConscript(os.path.join(libraries_path_prefix, rtconfig.BSP_LIBRARY_TYPE, 'SConscript')))# make a buildingDoBuilding(TARGET, objs)

最终搭建完成的效果如下所示,代码高亮十分方便查看代码

01755454-b3f5-11ef-8084-92fbcf53809c.png

LVGL 适配

屏幕拓展板

FRDM-MCXN947 这个开发板预留了一个 FlexIO 接口可以适配 8080 的并口屏,于是做了一个屏幕拓展板,把手里闲置的屏幕用起来

018c1bee-b3f5-11ef-8084-92fbcf53809c.png

实物如下,触摸排线座子有点偏下,不过不影响功能

019f0d6c-b3f5-11ef-8084-92fbcf53809c.png

屏幕手册说明分辨率是 240*320 驱动芯片是 ST7789V、触摸芯片是 FT6336G,而官方的 SDK 中是有 ST7796 和 FT5406 的驱动代码的,后续还需要稍作修改

01a54f42-b3f5-11ef-8084-92fbcf53809c.png

驱动适配

在官方的 SDK 中有 ST7796 和 FT5406 的驱动程序,直接移植过来即可,同时也把 EDMA 和 SMARTDMA 的驱动复制了过来,修改一下屏幕的初始化序列即可驱动屏幕

01b9d34a-b3f5-11ef-8084-92fbcf53809c.png

LVGL 适配

将SDK中的 lvgl_support 复制到工程中,修改屏幕的宽高为 240*320

01cb7cee-b3f5-11ef-8084-92fbcf53809c.png

然后在 board 中新建一个 lv_conf.h 文件,填入关于 LVGL 的一些配置,因为许多配置在 menuconfig 中有所设置,所以这里的配置项并不多

复制过来的 lvgl_support 中有对 FreeRTOS 的支持,这里将 FreeRTOS 的 API 修改为 RTT 的 API,例如如下这段代码

01f0e010-b3f5-11ef-8084-92fbcf53809c.png

并且 N947 的驱动程序有 EDMA + FlexIO 和 SMARTDMA + FlexIO 两种驱动方式,具体区别不太了解,不过可以运行 LVGL 的 Benchmark 测试来看下结果,左边是 SMARTDMA 运行的结果,右边是 EDMA 的结果,可以看到前者的 FPS 更高。后续也就继续采用 SMARTDMA + FlexIO 的驱动方式

0205046e-b3f5-11ef-8084-92fbcf53809c.png

界面设计

然后使用 GUI Guider 设计一个界面,生成绘制界面的代码,然后添加到工程中

0234912a-b3f5-11ef-8084-92fbcf53809c.png

还需要修改工程文件夹中的 rtconfig.py,增加一个 LV_LVGL_H_INCLUDE_SIMPLE 的预定义,因为生成的代码默认包含 lvgl.h 是 #include "lvgl/lvgl.h"

最终适配完成的 LVGL 代码和 GUI Guider 的代码目录如下,LVGL 的 UI 绘制代码段如图右边所示,具体代码可见开源地址

02451fcc-b3f5-11ef-8084-92fbcf53809c.png

USB 通讯

适配 CDC

完成了下位机的界面的初始化绘制,后续的任务当然就是怎么把数据采集并发送给下位机来更新界面的数据了,下面先完成 USB 的通讯,这里使用的是 RTT 官方推荐的 CherryUSB 这个开源 USB 协议栈

026a57ba-b3f5-11ef-8084-92fbcf53809c.png

将如下链接中的适配代码复制到工程中

https://github.com/CherryUSB/cherryusb_mcx

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

将 RTT 根目录中 rt-thread/components/drivers/usb/cherryusb/demo 文件夹中的 CDC_ACM 例程复制到工程中,并且把根目录中的这两行代码屏蔽

0274391a-b3f5-11ef-8084-92fbcf53809c.png

修改工程中的 cherryusb_port.c 文件,添加对 CDC_ACM 的支持

/* * Copyright (c) 2006-2024, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2024/04/23 sakumisu first version */#include #include /* low level init here, this has implemented in cherryusb *//* low level deinit here, this has implemented in cherryusb */#ifdef RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_ACMint

cherryusb_devinit(void){ // extern void cherryusb_devinit(uint8_t busid, uintptr_t reg_base); extern void cdc_acm_init(uint8_t busid, uintptr_t reg_base);

cdc_acm_init(0, USBHS1__USBC_BASE); return 0;}INIT_COMPONENT_EXPORT(cherryusb_devinit);#endif#ifdef

RT_CHERRYUSB_DEVICE_TEMPLATE_MSCint cherryusb_devinit(void){ extern void msc_ram_init(uint8_t busid, uintptr_t reg_base);

msc_ram_init(0, USBHS1__USBC_BASE); return 0;}INIT_COMPONENT_EXPORT(cherryusb_devinit);#endif#ifdef RT_CHERRYUSB_HOST#include

"usbh_core.h"int cherryusb_hostinit(void){

usbh_initialize(0, USBHS1__USBC_BASE); return 0;}

INIT_COMPONENT_EXPORT(cherryusb_hostinit);#endif

将刚才复制到工程中的 CDC_ACM 的 demo 程序中端点收发的程序做如下修改,增加对于输入信息的回显

void usbd_cdc_acm_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes){ USB_LOG_RAW("actual out len:%d\r\n", nbytes); /* setup next out ep read transfer */ usbd_ep_start_read(busid, CDC_OUT_EP, read_buffer, 2048); for (int i = 0; i < nbytes; i++) { printf("%02x ", read_buffer[i]); } printf("\r\n");}

验证

然后插上开发板的 USB HS 那个 USB 接口,用串口工具发个数据包

0279df00-b3f5-11ef-8084-92fbcf53809c.png

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

上位机

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

读取电脑的 CPU、GPU 的占用率和温度信息、获取当前时间

根据 VID、PID 查询 COM来与开发板通讯,下发采集数据与时间

增加帧头后发送到下位机,固定长度 32+2 个字节

028d2204-b3f5-11ef-8084-92fbcf53809c.png

下位机数据更新

在开发板端增加一个 thread 来负责把 USB 接收到的数据更新到屏幕上面,使用 LVGL 的 API 直接修改数据即可,代码如下

数据结构体

typedef struct{ uint16_t cpu_usage; uint16_t mem_usage; uint16_t gpu_usage; uint16_t cpu_freq; uint16_t cpu_temperature; uint16_t gpu_temperature; uint16_t board_temperature;} monitor_info_u16_t;typedef struct { uint16_t wYear; uint16_t wMonth; uint16_t wDayOfWeek; uint16_t wDay; uint16_t wHour; uint16_t wMinute; uint16_t wSecond; uint16_t wMilliseconds;} mytime_t;

在 USB 端点输出的回调函数中增加消息队列发送函数

void usbd_cdc_acm_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes){ USB_LOG_RAW("actual out len:%d\r\n", nbytes); /* setup next out ep read transfer */ usbd_ep_start_read(busid, CDC_OUT_EP, read_buffer, 2048); for (int i = 0; i < nbytes; i++) { printf("%02x ", read_buffer[i]); } printf("\r\n"); if (34 == nbytes) { rt_mq_send(&usb_mq, read_buffer, 34); }}

main 函数中的接收消息队列

uint8_t read_buffer[128];while (1){ /* 从消息队列中接收消息 */ if (rt_mq_recv(&usb_mq, read_buffer, 34, RT_WAITING_FOREVER) > 0) { mytime_t* p_time_u16 = (mytime_t*)(read_buffer + 2); monitor_info_u16_t* p_info_u16 = (monitor_info_u16_t *)(read_buffer + 2 + sizeof(mytime_t)); rt_kprintf("wYear %u\r\n", p_time_u16->wYear);

rt_kprintf("wMonth %u\r\n", p_time_u16->wMonth); rt_kprintf("wDayOfWeek %u\r\n", p_time_u16->wDayOfWeek); rt_kprintf("wDay %u\r\n", p_time_u16->wDay); rt_kprintf("wHour %u\r\n", p_time_u16->wHour); rt_kprintf("wMinute %u\r\n", p_time_u16->wMinute);

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

rt_kprintf("cpu_usage %u\r\n", p_info_u16->cpu_usage); rt_kprintf("mem_usage %u\r\n", p_info_u16->mem_usage); rt_kprintf("gpu_usage %u\r\n", p_info_u16->gpu_usage); rt_kprintf("cpu_freq %u\r\n", p_info_u16->cpu_freq); rt_kprintf("cpu_temperature %u\r\n", p_info_u16->cpu_temperature); rt_kprintf("gpu_temperature %u\r\n", p_info_u16->gpu_temperature); rt_kprintf("board_temperature %u\r\n",

p_info_u16->board_temperature); lv_label_set_text_fmt(guider_ui.screen_label_cpu_temp,

"%2d", p_info_u16->cpu_temperature);

lv_label_set_text_fmt(guider_ui.screen_label_gpu_temp,

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

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

p_info_u16->gpu_usage); lv_arc_set_value(guider_ui.screen_arc_gpu_temp,

p_info_u16->gpu_temperature); lv_label_set_text_fmt(guider_ui.screen_time, "%02d:%02d", p_time_u16->wHour, p_time_u16->wMinute); lv_label_set_text_fmt(guider_ui.screen_date, "%02d.%02d.%02d", p_time_u16->wYear, p_time_u16->wMonth, p_time_u16->wDay); }}

成品效果

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

029ebb04-b3f5-11ef-8084-92fbcf53809c.jpg

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • usb
    usb
    +关注

    关注

    60

    文章

    7927

    浏览量

    264333
  • 监控
    +关注

    关注

    6

    文章

    2200

    浏览量

    55140
  • 代码
    +关注

    关注

    30

    文章

    4768

    浏览量

    68469
收藏 人收藏

    评论

    相关推荐

    USB一线通监控设计方案

    在2024年全国大学生嵌入式芯片与系统设计竞赛中,各大高校学子纷纷展现出卓越的创新能力和扎实的技术功底。今天,特别为大家分享获奖作品——USB一线通监控
    的头像 发表于 11-28 09:45 569次阅读
    <b class='flag-5'>USB</b><b class='flag-5'>一线通</b><b class='flag-5'>监控</b><b class='flag-5'>副</b><b class='flag-5'>屏</b>设计方案

    4.3寸【串口案例】电脑应用

    为HardwareMonitor_V1.0。HardwareMonitor_V1.0软件主要用于UI界面的编辑与烧录。 如图所示,个制作完成的UI工程。 设置完变量地址后即可实现机箱硬件
    发表于 01-31 11:12

    BeagleBone Black USB一线通(1)

    eMMC的内容。二、启动与终端 购置的板子,附送的USB转串口线,是非常有用的。连接u***串口的GND、RXD、TXD,对应到 BBB板上J1的1脚、5脚、4脚。打开串口终端,设置速率115200
    发表于 10-22 17:01

    BeagleBone Black USB一线通(2)

    。 在主机这边,应该也安装好了跟BB-Black相关的驱动程序,其中就有个网络设备 Linux USB Ethernet/RNDIS Gadget。为了和BB-Black连接方便,可以把主机的这个网卡
    发表于 10-22 17:03

    BeagleBone Black USB一线通(3)

    20来寸的显示器上,还是有些不协调。索性我们使用图形界面远程登陆,实现BB-Black的图形显示。这样的话,仍然是使用那条连接到USB-OTG上面的多接口USB线缆,就可以实现一线通的目的。功能完备又
    发表于 10-22 17:05

    今日话题:一线城市 or 二线城市?

    `社会学家郑也夫说人生不外乎在追求三种状态:舒服、刺激、牛B。其实也是道城市选择题:皆因年轻的我们,爱冒险胜过爱安逸,爱牛B胜过爱舒服,自愿进入人生愿境最诱人、生存逻辑又最残酷的一线城市。这是
    发表于 03-09 10:10

    lcd2004一线仿真

    )图5四,编辑main.c 代码如Proteus8.9 VSM Studio GCC编译器仿真STM32F407ZGT6系列015_lcd2004一线(如下图6所示)图6五,Main.c 代码:/* Main.c file generated by New Projec
    发表于 12-13 07:20

    FPGA的JTAG接口和ARM的JTAG接口线是否可以共用同一线

    FPGA的JTAG接口和ARM的JTAG接口线是否可以共用同一线呢?是否可以用同usb转JTAG线给ARM和FPGA以及dsp调试程序呢?
    发表于 08-10 14:54

    基于TINI的一线制网络互连技术

    概述 TINI的基本概念和工作原理,分析 TINI的软件环境;简述基于一线制技术的温度型iButton DS1920和小型气象仪,给出利用 TINI实现一线制网络与 Internet连接的应用实例。
    发表于 04-13 10:15 20次下载

    CCEF新品:博视Allshow全景液晶L32A703I

    产品特性·高清晰液晶·博视全景·HDMI高清一线通·数字电视精密显示
    发表于 04-07 20:51 1691次阅读

    一线通校园智能寻址调频广播系统解决方案

    一线通校园智能寻址调频广播系统解决方案,有兴趣的同学可以下载学习
    发表于 05-04 15:48 0次下载

    LED显示厂商:全面奋战一线市场,锁定用户范围

    “当前一线市场不是变差了,而是对手变强了;同样地,一线需求不是变少了,而是用户要求更多了。..。..。”那么,面对这种持续的变化,LED显示企业如何能更好的抢夺用户并转化为产品销量呢?
    发表于 07-30 13:06 719次阅读

    Switch通过Type-C一线直连汽车中控方案

    5 月21 日消息,今日新能源造车势力官方微博剧透新车型。新配置。汽车中控,还有驾娱乐,后舱娱乐,均可以通过 Type-C 接口一线
    的头像 发表于 05-21 19:04 5093次阅读
    Switch通过Type-C<b class='flag-5'>一线</b>直连汽车中控<b class='flag-5'>屏</b>方案

    typec显示器方案推荐 分别有什么特点

    支持typec一线通的显示方案就是通过条typec线把电脑或者手机画面直接投到显示上,这需要显示
    的头像 发表于 11-04 16:11 6187次阅读

    STM32用一线式驱动SD NAND,SDIO的一线式驱动

    SDIO(Secure Digital Input/Output)是种用于在嵌入式系统中连接外部设备的标准接口。在SDIO标准中,一线式和四线式是指SDIO接口的不同工作模式。
    的头像 发表于 12-19 14:14 904次阅读
    STM32用<b class='flag-5'>一线</b>式驱动SD NAND,SDIO的<b class='flag-5'>一线</b>式驱动