2026/5/17 10:30:58
网站建设
项目流程
景德镇市建设局建设信用网站,跨境平台有哪些,阿里指数在线查询,wordpress获取qq昵称 头像本项目为RT-Thread嵌入式大赛获奖作品#xff0c;基于RT-Thread和兆易创新GD32F527I-EVAL的健康监测站。目录项目概述系统硬件框架结构基础驱动程序实现整体驱动实现工程效果演示视频及代码演示视频链接#xff1a;https://www.bilibili.com/video/BV1WgUoBXE2n/?pop_share1…本项目为RT-Thread嵌入式大赛获奖作品基于RT-Thread和兆易创新GD32F527I-EVAL的健康监测站。目录项目概述系统硬件框架结构基础驱动程序实现整体驱动实现工程效果演示视频及代码演示视频链接https://www.bilibili.com/video/BV1WgUoBXE2n/?pop_share1vd_sourcee1bd226340c8b87027d5dcfc6b0c33441 项目概述1.1 项目背景血氧、心率监测是人们最常关心的特别是一些特殊的群体比如老人患有心血管系统、呼吸系统疾病的人。能方便及时的监测到心率与血氧。本项目主要利用了开发板的大内存、大屏幕移植LVGL能够让老年人也看得清楚。1.2 系统功能介绍基于GD32F527I-EVAL开发板实现如下功能使用管方的RTTherad库管理整个系统资源。移植LCD驱动给LVGL提供显示基础移植触摸驱动给LVGL提供用户输入基础移植USART5实现与传感器的交互接口。移植LVGL设计GUI界面实现传感器驱动实现血氧、心率的监测。1.3 系统使用的技术要点整个系统由国产开源操作系统RT-Thead实现驱动的模块化设计以及系统调度。硬件GD32F527I-EVAL开发板语言C、GuiGuider2 系统硬件框架结构2.1 TFT—LCD接口开发板板载了LCD屏其原理图如下2.2 SDRAM接口由于开发板上外扩了RAM给LVGL驱动提供了大内存空间原理图如下2.3 USART5接口由于开发板的外设非常多找到摄像头接口的PC6、PC7作为USART5的接口3 基础驱动程序实现3.1 基础工程3.1.1 下载RT-Thread源码https://gitee.com/rtthread/rt-thread下载源码到本地。3.1.2 同步并打包下载好源码后进入rt-thread/tree/master/bsp/gd32/arm/gd32527I-eval目录下面执行pkgs —upgrade-force同步然后进行pkgs —update执行scons —targetmdk5然后进行scons —dist打包单独的工程。复制打包好的工程到单独的目录下。现在单独的工程就创建好了。3.2 移植LVGL创建好工程后首先就需要生成用户交互界面。我在论坛有单独的作品https://club.rt-thread.org/ask/article/a2dba0eaa063757a.html3.3 移植触摸驱动LVGL需要驱动触摸屏我也有单独的作品https://club.rt-thread.org/ask/article/104ea5e6b33e788e.html3.4 移植mks传感器串口接口详见单独作品https://club.rt-thread.org/ask/article/1074357ba9757cdb.html4 整体驱动实现通过上的基工程的实现接下来就是整合驱动实整个工程。4.1 界面设计4.1.1 GuiGuide设计我使用开源的GuiGuider设了用户交互界面控件1btn_start实现开始、停止测量的复用功能控件2label_heart 用于显示测量到的心率值控件3label_spo2 用于显示测量到的心率值控件4bar 用于显示测量的进度条其余控件为固定标签。4.1.2 事件添加为btn_start 添加clicked事件最后生成C语言的工程。4.2 LVGL工程移植4.2.1 复制guiguider工程在生成工程的目录中我复制generated文件夹到基础工程下的LVGL目录下面替换掉原来的generated文件夹。4.2.2 添加文件到工程在mdk中将generated下面的所有.c/h添加到mdk工程中4.2.3 添加bnt事件代码根据传感器的手册我们开始测量时是向串口发送数据0x8A停止是向串口发送0x88。添加按键的的驱动代码如下extern lv_ui guider_ui;externintmks_cmd(int argc, char *argv[]);// 全局/静态变量控制进度条和定时器确保回调中可访问staticlv_timer_t *progress_timer NULL; // 进度条定时器staticint current_progress 0; // 当前进度值0-100// 进度条定时器回调函数每隔 200ms 触发一次staticvoidprogress_timer_cb(lv_timer_t *timer){ current_progress; // 每次进度 1%200ms × 100 20 秒 // 更新进度条值关闭动画避免和定时器冲突 lv_bar_set_value(guider_ui.screen_bar, current_progress, LV_ANIM_OFF); // 进度达到 100%停止定时器并重置状态 if (current_progress 100) { lv_timer_del(progress_timer); // 删除定时器释放资源 progress_timer NULL; // 重置定时器指针 current_progress 0; // 重置进度值 char *argv[] {mks_cmd, stop, NULL}; int argc 2; mks_cmd(argc, argv); // 可选进度完成后自动将按钮切回「开始」 lv_label_set_text(guider_ui.screen_btn_start_label, Start); lv_bar_set_value(guider_ui.screen_bar, 0, LV_ANIM_OFF); }}staticvoidscreen_btn_start_event_handler(lv_event_t *e){ lv_event_code_t code lv_event_get_code(e); switch (code) { case LV_EVENT_CLICKED: { constchar *btn_text lv_label_get_text(guider_ui.screen_btn_start_label);; if (btn_text ! NULL strcmp(btn_text, Start) 0) { rt_kprintf(start test\n); // 停止已存在的定时器避免重复启动防止进度加速 if (progress_timer ! NULL) { lv_timer_del(progress_timer); progress_timer NULL; } // 初始化进度状态 current_progress 0; lv_bar_set_range(guider_ui.screen_bar, 0, 100); // 设置进度条范围0-100百分比 lv_bar_set_value(guider_ui.screen_bar, 0, LV_ANIM_OFF); // 进度条归零 // 创建并启动定时器周期 200ms触发回调函数 progress_timer lv_timer_create(progress_timer_cb, 200, NULL); lv_timer_resume(progress_timer); // 启动定时器LVGL 定时器默认创建后启动保险起见手动调用 // 构造参数并调用 char *argv[] {mks_cmd, start, NULL}; int argc 2; mks_cmd(argc, argv); lv_label_set_text(guider_ui.screen_btn_start_label, Stop); } elseif (btn_text ! NULL strcmp(btn_text, Stop) 0) { // 停止并删除定时器 if (progress_timer ! NULL) { lv_timer_del(progress_timer); progress_timer NULL; } // 进度条归零状态重置 current_progress 0; lv_bar_set_value(guider_ui.screen_bar, 0, LV_ANIM_OFF); char *argv[] {mks_cmd, stop, NULL}; int argc 2; mks_cmd(argc, argv); lv_label_set_text(guider_ui.screen_btn_start_label, Start); lv_obj_center(guider_ui.screen_btn_start_label); } break; } default: break; }}4.2.4 添加初始化gui代码在main.c中添加用于lvgl 心跳包的任务与初始化lvgl的代码#define LED1_PIN GET_PIN(E, 3)#define LVGL_TASK_PERIOD 10 // ms#define LVGL_TICK_PERIOD 5 // mslv_ui guider_ui; staticrt_thread_t lvgl_task_thread RT_NULL;staticrt_thread_t lvgl_tick_thread RT_NULL;staticvoidlvgl_tick_thread_entry(void *parameter){ while (1) { lv_tick_inc(5); rt_thread_delay(5); }}staticvoidlvgl_task_thread_entry(void *parameter){ lv_init(); lcd_init(); lv_port_disp_init(); lv_port_indev_init(); setup_ui(guider_ui); setup_ui(guider_ui); events_init(guider_ui); while (1) { lv_task_handler(); rt_thread_delay(10); }}intmain(void){ /* set LED1 pin mode to output */ rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT); exmc_synchronous_dynamic_ram_init(EXMC_SDRAM_DEVICE0); //在这里启用两个任务 lvgl_task_thread rt_thread_create(lvgl_task, lvgl_task_thread_entry, RT_NULL, 4096, 25, 10); if (lvgl_task_thread ! RT_NULL) rt_thread_startup(lvgl_task_thread); lvgl_tick_thread rt_thread_create(lvgl_tick, lvgl_tick_thread_entry, RT_NULL, 512, 25, 10); if (lvgl_tick_thread ! RT_NULL) rt_thread_startup(lvgl_tick_thread); while (1) { rt_thread_mdelay(1000); // 主线程可以做其他事情 } return RT_EOK;}将程序下载到开发板后按下按键能在USART5上面看到的0x8A、0x88输出说明程序运转正常。4.3 mks传感器驱动4.3.1 mks传感器通信协议根据通信协议进行串口的代码实现。4.3.2 mks串口添加在menuconfig中打开usart5保存工程并重新生成工程。4.3.3 mks串口驱动实现在驱动中按照rtthead srial的标准驱动。由于mkd传感器的波特率为38400因此需要在初始化后重新配置serial的驱动结构体并进行配置。定义他为uart5 实现中断接收功能详见驱动代码如下接收传感器并实现解码并实时将接收到的数据通过LVGL显示到界面中。#includeapp.h#includestring.h#includestdio.h#includertthread.h#includertdevice.h#includelvgl.h#includegui_guider.h#define MAX_BUFFSIZE 128#define RECEIVE_LENGTH 88 #define PACKET_HEADER 0xFF#define SAMPLE_UART_NAME uart5/* 用于接收消息的信号量 */staticstructrt_semaphorerx_sem;staticrt_device_t serial;// --- 全局变量定义 ---int8_t mks_waveform_data[MKS_WAVEFORM_SAMPLES];uint8_t mks_heart_rate 0;uint8_t mks_spo2 0;volatileuint8_t mks_new_data_flag 0; // Use volatile as it might be checked in timer/ISR context later// --- 内部接收缓冲区 ---staticuint8_t mks_rx_buffer[MKS_PACKET_SIZE];staticuint8_t mks_bytes_received 0;// lvglextern lv_ui guider_ui; staticchar buf[4];/* 接收数据回调函数 */staticrt_err_tuart_input(rt_device_t dev, rt_size_t size){ /* 串口接收到数据后产生中断调用此回调函数然后发送接收信号量 */ rt_sem_release(rx_sem); return RT_EOK;}/** * 串口接受线程 * param parameter */staticvoidserial_thread_entry(void *parameter){ char ch; mks_bytes_received 0; // Reset buffer state mks_new_data_flag 0; // 初始无新数据 // 初始化数据为0 memset(mks_rx_buffer,0, RECEIVE_LENGTH); mks_heart_rate 0; mks_spo2 0; while (1) { /* 从串口读取一个字节的数据没有读取到则等待接收信号量 */ while (rt_device_read(serial, -1, ch, 1) ! 1) { /* 阻塞等待接收信号量等到信号量后再次读取数据 */ rt_sem_take(rx_sem, RT_WAITING_FOREVER); } if(ch PACKET_HEADER) { mks_bytes_received 1; mks_new_data_flag 0; mks_rx_buffer[0] (uint8_t)ch; } else { if(mks_bytes_received0) { //mks_bytes_received; mks_rx_buffer[mks_bytes_received] (uint8_t)ch; if(mks_bytes_received RECEIVE_LENGTH) { mks_new_data_flag 1; mks_bytes_received 0; sprintf(buf, %d, mks_rx_buffer[65]) lv_label_set_text(guider_ui.screen_label_heart, buf); //更新到LVGL sprintf(buf, %d, mks_rx_buffer[66]); lv_label_set_text(guider_ui.screen_label_spo2, buf);//更新到LVGL memset(mks_rx_buffer,0, RECEIVE_LENGTH); mks_new_data_flag 0; } } } }}staticintuart_sample(int argc, char *argv[]){ rt_err_t ret RT_EOK; struct serial_configurecfg; // 配置结构体 char uart_name[RT_NAME_MAX]; char str[] hello RT-Thread!\r\n; if (argc 2) { rt_strncpy(uart_name, argv[1], RT_NAME_MAX); } else { rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX); } /* 查找系统中的串口设备 */ serial rt_device_find(uart_name); if (!serial) { rt_kprintf(find %s failed!\n, uart_name); return RT_ERROR; } cfg.baud_rate BAUD_RATE_38400; // 目标波特率可改为 38400、19200 等 cfg.data_bits DATA_BITS_8; // 8 数据位 cfg.stop_bits STOP_BITS_1; // 1 停止位 cfg.parity PARITY_NONE; // 无校验 cfg.bit_order BIT_ORDER_LSB; // 低位优先默认 cfg.invert NRZ_NORMAL; cfg.bufsz RT_SERIAL_RB_BUFSZ; cfg.flowcontrol RT_SERIAL_FLOWCONTROL_NONE; cfg.reserved 0; rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, cfg); /* 初始化信号量 */ rt_sem_init(rx_sem, rx_sem, 0, RT_IPC_FLAG_FIFO); /* 以中断接收及轮询发送模式打开串口设备 */ rt_device_open(serial, RT_DEVICE_FLAG_INT_RX); /* 设置接收回调函数 */ rt_device_set_rx_indicate(serial, uart_input); /* 发送字符串 */ rt_device_write(serial, 0, str, (sizeof(str) - 1)); /* 创建 serial 线程 */ rt_thread_t thread rt_thread_create(serial, serial_thread_entry, RT_NULL, 1024, 25, 10); /* 创建成功则启动线程 */ if (thread ! RT_NULL) { rt_thread_startup(thread); } else { ret RT_ERROR; } return ret;}intmks_cmd(int argc, char *argv[]){ rt_err_t ret RT_EOK; uint8_t send_data 0; // 要发送的字节数据 // 第一步检查串口设备是否已初始化首次使用时查找并打开 if (serial RT_NULL) { // 查找串口设备 serial rt_device_find(SAMPLE_UART_NAME); if (serial RT_NULL) { rt_kprintf(错误未找到串口设备 %s\n, SAMPLE_UART_NAME); return RT_ERROR; } // 打开串口RT_DEVICE_OFLAG_RDWR读写模式 if (rt_device_open(serial, RT_DEVICE_OFLAG_RDWR) ! RT_EOK) { rt_kprintf(错误打开串口设备 %s 失败\n, SAMPLE_UART_NAME); serial RT_NULL; // 打开失败重置句柄 return RT_ERROR; } } // 第二步解析命令参数 if (argc 2) // 仅支持 mks start 或 mks stop2个参数 { // 比较第二个参数argv[1] if (strcmp(argv[1], start) 0) { send_data 0x8A; // start 对应发送 0x8A rt_kprintf(执行 mks start发送数据0x%02X\n, send_data); } elseif (strcmp(argv[1], stop) 0) { send_data 0x88; // stop 对应发送 0x88 rt_kprintf(执行 mks stop发送数据0x%02X\n, send_data); } else { // 无效参数提示正确用法 rt_kprintf(错误无效命令参数\n); rt_kprintf(正确用法\n); rt_kprintf( mks start - 发送 0x8A\n); rt_kprintf( mks stop - 发送 0x88\n); return -RT_EINVAL; } // 第三步通过串口发送数据发送1个字节 ret rt_device_write(serial, 0, send_data, 1); if (ret ! 1) // rt_device_write 返回实际发送的字节数成功应为 1 { rt_kprintf(错误串口发送失败返回值%d\n, ret); return -RT_ERROR; } } else { // 参数个数错误提示正确用法 rt_kprintf(错误命令格式错误\n); rt_kprintf(正确用法\n); rt_kprintf( mks start - 发送 0x8A\n); rt_kprintf( mks stop - 发送 0x88\n); return -RT_EINVAL; } return ret;}// 3. 导出命令到 FinSH 终端支持在终端直接输入 mks 命令MSH_CMD_EXPORT(mks_cmd, mks command: mks start/stop);/* 导出到 msh 命令列表中 */MSH_CMD_EXPORT(uart_sample, uart device sample);接收传感器并实现解码并实时将接收到的数据通过LVGL显示到界面中。5 工程效果6 演示视频及代码演示视频链接https://www.bilibili.com/video/BV1WgUoBXE2n/?pop_share1vd_sourcee1bd226340c8b87027d5dcfc6b0c3344源代码https://club.rt-thread.org/file_download/a6fe3781d8bcf12d非常感谢RTT论坛通过这次参赛让我进一步的体会到了RT-Thread开源操作系统的强大。RT-Thread Github 开源仓库欢迎撒个星(Star)支持更期待你的代码贡献 https://github.com/RT-Thread/rt-thread添加小师弟微信↓拉你进RT-Thread技术交流群找到组织想要在RT-Thread平台或社区投放内容或想参与相关直播活动及赛事RT-Thread已开放对接窗口请通过邮件与我们取得联系期待合作合作邮箱: tongfangyirt-thread.com点击“阅读原文”