Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
CDC
LittlevGL_LVGL
USB
【24嵌入式设计大赛】USB 一线通监控副屏
发布于 2024-09-17 23:03:27 浏览:1538
订阅该版
[tocm] ## 环境搭建 ### 环境变量配置 为了提高一些编译的速度,选择了在 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 根目录的路径 ```sh 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 文件的这行代码 ![N947一线通监控屏_figure_1.png](https://oss-club.rt-thread.org/uploads/20240917/59e101c733ba55fceee2774978b88ac2.png) ### 代码高亮 这里使用 VSCode 中的 Clang 插件,代码高亮和补全可以通过使用编译时候生成的 compile_commands.json 文件来实现,而 RT-Thread 的工程是采用的 scons 工具,所以可以使用 scons_compiledb 这个 python 包来生成 compile_commands.json 实现代码高亮,修改过的 SConstruct 文件如下 ```python import os import sys import rtconfig import scons_compiledb if 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_EXT if 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_prefix Export('SDK_LIB') # prepare building environment objs = PrepareBuilding(env, RTT_ROOT, has_libcpu=False) objs.extend(SConscript(os.path.join(libraries_path_prefix, 'drivers', 'SConscript'))) # include cmsis objs.extend(SConscript(os.path.join(libraries_path_prefix, rtconfig.BSP_LIBRARY_TYPE, 'SConscript'))) # make a building DoBuilding(TARGET, objs) ``` 最终搭建完成的效果如下所示,代码高亮十分方便查看代码 ![N947一线通监控屏_figure_2.png](https://oss-club.rt-thread.org/uploads/20240917/5d158880d3963ed942c8e75c6e3c7150.png.webp) ## LVGL 适配 ### 屏幕拓展板 FRDM-MCXN947 这个开发板预留了一个 FlexIO 接口可以适配 8080 的并口屏,于是做了一个屏幕拓展板,把手里闲置的屏幕用起来 ![N947一线通监控屏_figure_3.png](https://oss-club.rt-thread.org/uploads/20240917/34d60fad617e14fa92cd9cf49b28cb15.png.webp) 实物如下,触摸排线座子有点偏下,不过不影响功能 ![N947一线通监控屏_figure_4.png](https://oss-club.rt-thread.org/uploads/20240917/30a7ccdbfc1117d90a863625ecdd611a.png.webp) 屏幕手册说明分辨率是 240\*320 驱动芯片是 ST7789V、触摸芯片是 FT6336G,而官方的 SDK 中是有 ST7796 和 FT5406 的驱动代码的,后续还需要稍作修改 ![N947一线通监控屏_figure_5.png](https://oss-club.rt-thread.org/uploads/20240917/2e3289390711e17c652bd25143c9a074.png.webp) ### 驱动适配 在官方的 SDK 中有 ST7796 和 FT5406 的驱动程序,直接移植过来即可,同时也把 EDMA 和 SMARTDMA 的驱动复制了过来,修改一下屏幕的初始化序列即可驱动屏幕 ![N947一线通监控屏_figure_6.png](https://oss-club.rt-thread.org/uploads/20240917/4a4f57269022250cc96adb15f35b36e8.png.webp) ### LVGL 适配 将SDK中的 lvgl_support 复制到工程中,修改屏幕的宽高为 240\*320 ![N947一线通监控屏_figure_7.png](https://oss-club.rt-thread.org/uploads/20240917/510b0858668dcf07b330e3ef31592aa3.png.webp) 然后在 board 中新建一个 lv_conf.h 文件,填入关于 LVGL 的一些配置,因为许多配置在 menuconfig 中有所设置,所以这里的配置项并不多 ```c #ifndef LV_CONF_H #define LV_CONF_H #include
#define LV_USE_SYSMON 1 #define LV_USE_PERF_MONITOR 0 #define LV_COLOR_DEPTH 16 #define LV_HOR_RES_MAX 240 #define LV_VER_RES_MAX 320 #define LV_COLOR_16_SWAP 0 #define BSP_USING_LVGL_BENCHMARK_DEMO #define BSP_USING_LVGL_WIDGETS_DEMO #ifdef BSP_USING_LVGL_DAVE2D #define LV_USE_DRAW_DAVE2D 1 #endif #ifdef BSP_USING_LVGL_WIDGETS_DEMO #define LV_USE_DEMO_WIDGETS 1 #define LV_DEMO_WIDGETS_SLIDESHOW 0 #endif /* BSP_USING_LVGL_WIDGETS_DEMO */ /*Benchmark your system*/ #ifdef BSP_USING_LVGL_BENCHMARK_DEMO #define LV_USE_DEMO_BENCHMARK 1 /*Use RGB565A8 images with 16 bit color depth instead of ARGB8565*/ #define LV_DEMO_BENCHMARK_RGB565A8 1 #define LV_FONT_MONTSERRAT_14 1 #define LV_FONT_MONTSERRAT_24 1 #endif /* BSP_USING_LVGL_BENCHMARK_DEMO */ /*Stress test for LVGL*/ #ifdef BSP_USING_LVGL_STRESS_DEMO #define LV_USE_DEMO_STRESS 1 #endif /* BSP_USING_LVGL_STRESS_DEMO */ /*Render test for LVGL*/ #ifdef BSP_USING_LVGL_RENDER_DEMO #define LV_USE_DEMO_RENDER 1 #endif /* BSP_USING_LVGL_RENDER_DEMO */ /*Music player demo*/ #ifdef BSP_USING_LVGL_MUSIC_DEMO #define LV_USE_DEMO_MUSIC 1 #define LV_DEMO_MUSIC_SQUARE 1 #define LV_DEMO_MUSIC_LANDSCAPE 0 #define LV_DEMO_MUSIC_ROUND 0 #define LV_DEMO_MUSIC_LARGE 0 #define LV_DEMO_MUSIC_AUTO_PLAY 0 #define LV_FONT_MONTSERRAT_12 1 #define LV_FONT_MONTSERRAT_16 1 #endif /* BSP_USING_LVGL_MUSIC_DEMO */ #endif ``` 复制过来的 lvgl_support 中有对 FreeRTOS 的支持,这里将 FreeRTOS 的 API 修改为 RTT 的 API,例如如下这段代码 ![N947一线通监控屏_figure_8.png](https://oss-club.rt-thread.org/uploads/20240917/3e564ce4d528b7eb14e86cd34cab9ef4.png.webp) 并且 N947 的驱动程序有 EDMA + FlexIO 和 SMARTDMA + FlexIO 两种驱动方式,具体区别不太了解,不过可以运行 LVGL 的 Benchmark 测试来看下结果,左边是 SMARTDMA 运行的结果,右边是 EDMA 的结果,可以看到前者的 FPS 更高。后续也就继续采用 SMARTDMA + FlexIO 的驱动方式 ![N947一线通监控屏_figure_9.png](https://oss-club.rt-thread.org/uploads/20240917/e9f151630fc981304cfa08b752c56ab1.png.webp) ### 界面设计 然后使用 GUI Guider 设计一个界面,生成绘制界面的代码,然后添加到工程中 ![N947一线通监控屏_figure_10.png](https://oss-club.rt-thread.org/uploads/20240917/9c5c99233d1da638b5105f7006b8ca22.png.webp) 还需要修改工程文件夹中的 rtconfig.py,增加一个 LV_LVGL_H_INCLUDE_SIMPLE 的预定义,因为生成的代码默认包含 lvgl.h 是 `#include "lvgl/lvgl.h"` ```c CFLAGS = DEVICE + ' -Wall -D__FPU_PRESENT -DLV_LVGL_H_INCLUDE_SIMPLE' ``` 最终适配完成的 LVGL 代码和 GUI Guider 的代码目录如下,LVGL 的 UI 绘制代码段如图右边所示,具体代码可见开源地址 ![N947一线通监控屏_figure_11.png](https://oss-club.rt-thread.org/uploads/20240917/f667b18c0d7e3dab5e1a2c5ecf971945.png.webp) ## USB 通讯 ### 适配 CDC 完成了下位机的界面的初始化绘制,后续的任务当然就是怎么把数据采集并发送给下位机来更新界面的数据了,下面先完成 USB 的通讯,这里使用的是 RTT 官方推荐的 CherryUSB 这个开源 USB 协议栈 ![N947一线通监控屏_figure_12.png](https://oss-club.rt-thread.org/uploads/20240917/18f9a90051fd70b99a121a75b117cdc8.png.webp) 将如下链接中的适配代码复制到工程中 https://github.com/CherryUSB/cherryusb_mcx 因为传输的数据比较单一,这里使用串口屏的思路,直接用 CDC_ACM 的通讯方式,也就是在上位机显示为一个 USB 转串口设备,直接使用串口 API 完成通讯 将 RTT 根目录中 `rt-thread/components/drivers/usb/cherryusb/demo` 文件夹中的 CDC_ACM 例程复制到工程中,并且把根目录中的这两行代码屏蔽 ![N947一线通监控屏_figure_13.png](https://oss-club.rt-thread.org/uploads/20240917/57934dadcaa0ffcba3f051efa10e8482.png.webp) 修改工程中的 cherryusb_port.c 文件,添加对 CDC_ACM 的支持 ```c /* * 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_ACM int 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_MSC int 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 程序中端点收发的程序做如下修改,增加对于输入信息的回显 ```c 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 接口,用串口工具发个数据包 ![N947一线通监控屏_figure_14.png](https://oss-club.rt-thread.org/uploads/20240917/7b21979017eef7e7d687fc017076287c.png.webp) 可以看到 已经识别成了 USB 串行设备,PID 和 VID 也是我自己设置的 0xE6E9 和 0x1122,后续上位机与开发板建立通讯锁定 COM 号就是依靠 PID VID 来查询实现,使用串口工具给开发板发送的数据也可以正常接收到 ### 上位机 时间原因上位机做的比较简单,实现了如下几个功能: - 读取电脑的 CPU、GPU 的占用率和温度信息、获取当前时间 - 根据 VID、PID 查询 COM来与开发板通讯,下发采集数据与时间 - 增加帧头后发送到下位机,固定长度 32+2 个字节 ![N947一线通监控屏_figure_15.png](https://oss-club.rt-thread.org/uploads/20240917/ef55a334a4e52e71a01092df5d175389.png.webp) ### 下位机数据更新 在开发板端增加一个 thread 来负责把 USB 接收到的数据更新到屏幕上面,使用 LVGL 的 API 直接修改数据即可,代码如下 数据结构体 ```c 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 端点输出的回调函数中增加消息队列发送函数 ```c 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 函数中的接收消息队列 ```c 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 的占用率和温度信息,其他的信息还在完善当中 ![N947一线通监控屏_figure_16.png](https://oss-club.rt-thread.org/uploads/20240917/e2f22b016cf51a759f2952b72d321dfb.png.webp) 下位机代码可见GitHub:https://github.com/POMIN-163/FRDM-MCXN947-USB-Monitor [上位机代码.zip](https://club.rt-thread.org/file_download/412971c88469523e) [视频.mp4](https://club.rt-thread.org/file_download/ac333cc9ed03e24b)
5
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
pomin
这家伙很懒,什么也没写!
文章
5
回答
0
被采纳
0
关注TA
发私信
相关文章
1
请教USB Host
2
STM32F4调试USB 读卡器(Slave)提示格式化
3
急求 STM32F4 USB Device MSC+SD 的相关问题
4
USB 框架问题
5
USB键盘
6
LPC17xx 如何添加USB HOST设备
7
RT-Thread目前支持USB HOST了吗?
8
USB HOST的支持问题
9
RTT 2.0.1 USB存储设备问题,枚举到USBREQ_GET_MAX_LUN后复位
10
USB库已经很久没更新了
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
WIZnet_W5500
ota在线升级
cubemx
PWM
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
SFUD
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
xusiwei1236
8
个答案
2
次被采纳
踩姑娘的小蘑菇
1
个答案
2
次被采纳
用户名由3_15位
9
个答案
1
次被采纳
bernard
4
个答案
1
次被采纳
RTT_逍遥
3
个答案
1
次被采纳
本月文章贡献
聚散无由
2
篇文章
15
次点赞
catcatbing
2
篇文章
5
次点赞
Wade
2
篇文章
4
次点赞
Ghost_Girls
1
篇文章
7
次点赞
xiaorui
1
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部