環(huán)境搭建
環(huán)境變量配置
為了提高一些編譯的速度,選擇了在 Linux 系統(tǒng)下進行開發(fā),在 Linux 上開發(fā) N947 需要先安裝 env 工具 https://github.com/RT-Thread/env ,按照說明文檔進行安裝即可,然后配置一些環(huán)境變量如下
其中 /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 文件的這行代碼
代碼高亮
這里使用 VSCode 中的 Clang 插件,代碼高亮和補全可以通過使用編譯時候生成的 compile_commands.json 文件來實現(xiàn),而 RT-Thread 的工程是采用的 scons 工具,所以可以使用 scons_compiledb 這個 python 包來生成 compile_commands.json 實現(xiàn)代碼高亮,修改過的 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)
最終搭建完成的效果如下所示,代碼高亮十分方便查看代碼
LVGL 適配
屏幕拓展板
FRDM-MCXN947 這個開發(fā)板預留了一個 FlexIO 接口可以適配 8080 的并口屏,于是做了一個屏幕拓展板,把手里閑置的屏幕用起來
實物如下,觸摸排線座子有點偏下,不過不影響功能
屏幕手冊說明分辨率是 240*320 驅(qū)動芯片是 ST7789V、觸摸芯片是 FT6336G,而官方的 SDK 中是有 ST7796 和 FT5406 的驅(qū)動代碼的,后續(xù)還需要稍作修改
驅(qū)動適配
在官方的 SDK 中有 ST7796 和 FT5406 的驅(qū)動程序,直接移植過來即可,同時也把 EDMA 和 SMARTDMA 的驅(qū)動復制了過來,修改一下屏幕的初始化序列即可驅(qū)動屏幕
LVGL 適配
將SDK中的 lvgl_support 復制到工程中,修改屏幕的寬高為 240*320
然后在 board 中新建一個 lv_conf.h 文件,填入關于 LVGL 的一些配置,因為許多配置在 menuconfig 中有所設置,所以這里的配置項并不多
復制過來的 lvgl_support 中有對 FreeRTOS 的支持,這里將 FreeRTOS 的 API 修改為 RTT 的 API,例如如下這段代碼
并且 N947 的驅(qū)動程序有 EDMA + FlexIO 和 SMARTDMA + FlexIO 兩種驅(qū)動方式,具體區(qū)別不太了解,不過可以運行 LVGL 的 Benchmark 測試來看下結(jié)果,左邊是 SMARTDMA 運行的結(jié)果,右邊是 EDMA 的結(jié)果,可以看到前者的 FPS 更高。后續(xù)也就繼續(xù)采用 SMARTDMA + FlexIO 的驅(qū)動方式
界面設計
然后使用 GUI Guider 設計一個界面,生成繪制界面的代碼,然后添加到工程中
還需要修改工程文件夾中的 rtconfig.py,增加一個 LV_LVGL_H_INCLUDE_SIMPLE 的預定義,因為生成的代碼默認包含 lvgl.h 是 #include "lvgl/lvgl.h"
最終適配完成的 LVGL 代碼和 GUI Guider 的代碼目錄如下,LVGL 的 UI 繪制代碼段如圖右邊所示,具體代碼可見開源地址
USB 通訊
適配 CDC
完成了下位機的界面的初始化繪制,后續(xù)的任務當然就是怎么把數(shù)據(jù)采集并發(fā)送給下位機來更新界面的數(shù)據(jù)了,下面先完成 USB 的通訊,這里使用的是 RTT 官方推薦的 CherryUSB 這個開源 USB 協(xié)議棧
將如下鏈接中的適配代碼復制到工程中
https://github.com/CherryUSB/cherryusb_mcx
因為傳輸?shù)臄?shù)據(jù)比較單一,這里使用串口屏的思路,直接用 CDC_ACM 的通訊方式,也就是在上位機顯示為一個 USB 轉(zhuǎn)串口設備,直接使用串口 API 完成通訊
將 RTT 根目錄中 rt-thread/components/drivers/usb/cherryusb/demo 文件夾中的 CDC_ACM 例程復制到工程中,并且把根目錄中的這兩行代碼屏蔽
修改工程中的 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 程序中端點收發(fā)的程序做如下修改,增加對于輸入信息的回顯
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");}
驗證
然后插上開發(fā)板的 USB HS 那個 USB 接口,用串口工具發(fā)個數(shù)據(jù)包
可以看到 已經(jīng)識別成了 USB 串行設備,PID 和 VID 也是我自己設置的 0xE6E9 和 0x1122,后續(xù)上位機與開發(fā)板建立通訊鎖定 COM 號就是依靠 PID VID 來查詢實現(xiàn),使用串口工具給開發(fā)板發(fā)送的數(shù)據(jù)也可以正常接收到
上位機
時間原因上位機做的比較簡單,實現(xiàn)了如下幾個功能:
讀取電腦的 CPU、GPU 的占用率和溫度信息、獲取當前時間
根據(jù) VID、PID 查詢 COM來與開發(fā)板通訊,下發(fā)采集數(shù)據(jù)與時間
增加幀頭后發(fā)送到下位機,固定長度 32+2 個字節(jié)
下位機數(shù)據(jù)更新
在開發(fā)板端增加一個 thread 來負責把 USB 接收到的數(shù)據(jù)更新到屏幕上面,使用 LVGL 的 API 直接修改數(shù)據(jù)即可,代碼如下
數(shù)據(jù)結(jié)構體
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 端點輸出的回調(diào)函數(shù)中增加消息隊列發(fā)送函數(shù)
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 函數(shù)中的接收消息隊列
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 的占用率和溫度信息,其他的信息還在完善當中
-
usb
+關注
關注
60文章
7936瀏覽量
264454 -
監(jiān)控
+關注
關注
6文章
2204瀏覽量
55167 -
代碼
+關注
關注
30文章
4779瀏覽量
68519
發(fā)布評論請先 登錄
相關推薦
評論