2026 M-Design设计竞赛 - 基于U5G9DK2的智能家居大屏音响姿态感知
该项目使用了U5G9DK2和LSM6DSO,实现了智能家居大屏音响姿态感知的设计,它的主要功能为:读取陀螺仪的姿态,可显示在LCD屏幕上。
标签
嵌入式系统
MEMS
LSM6DSO
U5G9DK2
zhjlmt
更新2026-06-10
35

一、项目介绍和创意介绍

本项目针对智能家居大屏音响的使用场景,基于 STM32U5G9DK2 开发板和 LSM6DSO 6 轴惯性测量单元,开发了一套姿态感知自适应 GUI 系统。系统能够实时检测大屏音响的空间姿态(横屏、竖屏、倾斜角度),并根据不同姿态自动切换对应的 GUI 界面,通过 UART 串口输出姿态数据和调试日志,实现了 "姿态即交互" 的智能化体验,大幅提升了大屏音响在不同使用场景下的操作便捷性。

二、使用到的硬件介绍

  1. 主控单元:STM32U5G9DK2 开发板(核心芯片 STM32U5G9),作为系统控制核心,负责传感器数据采集、姿态解算、GUI 界面渲染和交互逻辑处理;
  2. 姿态传感器:LSM6DSO 6 轴惯性测量单元(集成 3 轴加速度计 + 3 轴陀螺仪),通过 I2C/SPI 接口与 MCU 通信,实时采集加速度和角速度数据;
  3. 显示单元:STM32U5G9DK2 开发板自带 800×480 分辨率 LCD 触摸屏,用于显示自适应 GUI 界面和接收触控输入;
  4. 输入单元:开发板自带 USER 物理按键,用于系统复位、模式切换等基础操作;
  5. 调试单元:UART1 串口,波特率 115200,输出实时姿态数据、传感器状态和系统日志;
  6. 电源单元:开发板 USB 供电或外部 5V 电源供电,支持锂电池供电拓展,适配智能家居设备的移动使用需求。

三、方案框图

┌───────────────┐     ┌───────────────┐     ┌───────────────┐
│ 输入模块 │ │ 主控模块 │ │ 输出模块 │
│ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │
│ │ LSM6DSO传感器│─────┼─┤ STM32U5G9J │─────┼─┤ LCD触摸屏 │ │
│ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │
│ ┌───────────┐ │ │ │ │ └───────────────┘
│ │ USER按键 │ │ │ │ │
│ └───────────┘ │ │ ▼ │
└───────────────┘ │ ┌───────────┐ │ ┌───────────────┐
│ │ UART1调试 │ ├─────┤ 日志输出 │
│ └───────────┘ │ └───────────────┘
└───────────────┘

四、软件程图和关键代码介绍

1. 软件流程图

┌─────────────────────────┐
│ 系统初始化(HAL/时钟) │
└───────────┬─────────────┘

┌─────────────────────────┐
│ 外设初始化 │
(LSM6DSO/LCD/触摸/UART)
└───────────┬─────────────┘

┌─────────────────────────┐
│ 传感器校准 │
- 加速度计零偏校准 │
- 陀螺仪零偏校准 │
└───────────┬─────────────┘

┌─────────────────────────┐
GUI初始化 │
- 加载默认界面(平放) │
- 初始化界面元素 │
└───────────┬─────────────┘

┌─────────────────────────┐
│ 主循环:Main_Process │
├───────────┬─────────────┤
│ 传感器数据读取 │
├───────────┼─────────────┤
│ 姿态解算(互补滤波) │
├───────────┼─────────────┤
│ 姿态判断与稳定检测 │
├───────────┼─────────────┤
│ 姿态变化?→ 是:切换GUI
│ → 否:继续 │
├───────────┼─────────────┤
│ 触摸事件处理 │
├───────────┼─────────────┤
│ 界面刷新 │
UART输出调试日志 │
├───────────┼─────────────┤
│ 低功耗检测 │
└───────────┬─────────────┘

└─────────────────────────┘

2. 关键代码介绍

使用ST官方的SEQ调度器来控制LCD屏幕显示音乐的图标

/* ======================================================================== */
/* DRAW: Music player main screen */
/* ======================================================================== */
static void Draw_Player(void)
{
UTIL_LCD_SetBackColor(UTIL_LCD_COLOR_BLACK);
UTIL_LCD_SetTextColor(UTIL_LCD_COLOR_WHITE);
UTIL_LCD_FillRect(0, 0, 800, 50, UTIL_LCD_COLOR_BLACK);
UTIL_LCD_SetFont(&Font24);
UTIL_LCD_DisplayStringAt(0, 12, (uint8_t *)" Music Player", CENTER_MODE);

/* Album art placeholder */
UTIL_LCD_SetTextColor(UTIL_LCD_COLOR_ST_BLUE);
UTIL_LCD_FillRect(300, 70, 200, 130, UTIL_LCD_COLOR_ST_BLUE);
UTIL_LCD_SetTextColor(UTIL_LCD_COLOR_WHITE);
UTIL_LCD_SetFont(&Font24);
UTIL_LCD_DisplayStringAt(0, 115, (uint8_t *)"ALBUM", CENTER_MODE);

/* Song name */
UTIL_LCD_SetFont(&Font20);
UTIL_LCD_SetTextColor(UTIL_LCD_COLOR_YELLOW);
snprintf(lcd_buf, sizeof(lcd_buf), "%s - %s", song_names[cur_song], song_artists[cur_song]);
UTIL_LCD_DisplayStringAt(0, 215, (uint8_t *)lcd_buf, CENTER_MODE);

/* Progress bar background */
UTIL_LCD_FillRect(80, 260, 640, 20, 0xFF333333);
/* Progress bar fill */
int pw = (640 * play_progress) / 100;
UTIL_LCD_FillRect(80, 260, pw, 20, UTIL_LCD_COLOR_ST_BLUE_LIGHT);
UTIL_LCD_DrawRect(80, 260, 640, 20, UTIL_LCD_COLOR_WHITE);
/* Percentage */
UTIL_LCD_SetFont(&Font16);
UTIL_LCD_SetTextColor(UTIL_LCD_COLOR_WHITE);
snprintf(lcd_buf, sizeof(lcd_buf), "%d%%", play_progress);
UTIL_LCD_DisplayStringAt(0, 264, (uint8_t *)lcd_buf, CENTER_MODE);

/* Play controls */
UTIL_LCD_SetFont(&Font24);
UTIL_LCD_SetTextColor(UTIL_LCD_COLOR_WHITE);
UTIL_LCD_DisplayStringAt(180, 300, (uint8_t *)"<<", LEFT_MODE);
UTIL_LCD_DisplayStringAt(380, 300, (uint8_t *)(is_playing ? "||" : ">"), CENTER_MODE);
UTIL_LCD_DisplayStringAt(580, 300, (uint8_t *)">>", LEFT_MODE);

/* Volume indicator */
UTIL_LCD_SetFont(&Font16);
UTIL_LCD_SetTextColor(UTIL_LCD_COLOR_GRAY);
snprintf(lcd_buf, sizeof(lcd_buf), "Vol: %d%%", volume);
UTIL_LCD_DisplayStringAt(20, 305, (uint8_t *)lcd_buf, LEFT_MODE);

/* Bottom nav bar */
UTIL_LCD_FillRect(0, 400, 800, 80, 0xFF1A1A2E);
UTIL_LCD_SetFont(&Font16);
UTIL_LCD_SetTextColor(UTIL_LCD_COLOR_WHITE);
UTIL_LCD_DisplayStringAt(40, 425, (uint8_t *)"Play", LEFT_MODE);
UTIL_LCD_DisplayStringAt(230, 425, (uint8_t *)"Lyrics", LEFT_MODE);
UTIL_LCD_DisplayStringAt(430, 425, (uint8_t *)"Vol", LEFT_MODE);
UTIL_LCD_DisplayStringAt(630, 425, (uint8_t *)"List", LEFT_MODE);

/* Pose hint */
UTIL_LCD_SetFont(&Font12);
UTIL_LCD_SetTextColor(0xFF666666);
UTIL_LCD_DisplayStringAt(0, 370, (uint8_t *)"Tilt left=Vol Tilt right=List Tilt fwd/bk=Prev/Next", CENTER_MODE);
}

注册I2C总线读取LSM6DS0传感器

/* ======================================================================== */
/* I2C1 init — PG13/PG14 AF4 (verified working) */
/* ======================================================================== */
static void I2C1_UserInit(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWREx_EnableVddIO2();

PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_I2C1;
PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
Error_Handler();

__HAL_RCC_GPIOG_CLK_ENABLE();
__HAL_RCC_I2C1_CLK_ENABLE();

GPIO_InitStruct.Pin = GPIO_PIN_13 | GPIO_PIN_14;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x70426363;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
Error_Handler();
HAL_Delay(10);
}

static int32_t I2C_ReadReg(uint16_t a, uint16_t r, uint8_t *d, uint16_t l)
{ return (int32_t)HAL_I2C_Mem_Read(&hi2c1, a, r, I2C_MEMADD_SIZE_8BIT, d, l, 1000); }

static int32_t I2C_WriteReg(uint16_t a, uint16_t r, uint8_t *d, uint16_t l)
{ return (int32_t)HAL_I2C_Mem_Write(&hi2c1, a, r, I2C_MEMADD_SIZE_8BIT, d, l, 1000); }

static int32_t Sensor_Init(void) { I2C1_UserInit(); return 0; }
static void Sensor_Delay(uint32_t ms) { HAL_Delay(ms); }
static uint32_t Sensor_GetTick(void) { return HAL_GetTick(); }

五、功能展示图及说明

核心的用户代码结构

image.png

主函数,也是初始化等功能代码集中的地方

image.png

GUI显示的代码

image.png

主函数,50ms调度一次

image.png

不同角度的显示


IMG_20260602_002359.jpgIMG_20260602_003238.jpg

IMG_20260602_003306.jpg

六、设计中遇到的难题和解决方法

难点1:LSM6DSO传感器无法获取数据,因为我这块U5G9DK2出现过I2C总线故障问题,疑似是芯片老化导致的,一开始读取不到传感器的数值还以为是硬件问题。后来才发现I2C的引脚使用错误导致的问题。看来没有硬件的支持,嵌入式是没办法运行的。

难点2:传感器数据没有消抖,稍微拿在手上,读取的传感器数据有变动就切换屏幕,还会出现屏幕叠加,最后消抖解决的。

心得体会软硬件协同设计的重要性:本项目涉及硬件连接、传感器驱动、算法实现和 GUI 设计多个方面,只有软硬件协同优化,才能实现系统的最佳性能;

附件下载
U5G9_LSM6DSO_LCD.zip
U5G9_LSM6DSO_LCD.hex
团队介绍
独立完成
评论
0 / 100
查看更多
猜你喜欢
制作FPGA电子琴1. 存储一段音乐,并可以进行音乐播放, 2. 可以自己通过板上的按键进行弹奏,支持两个按键同时按下(和弦)并且声音不能失真,板上的按键只有13个,可以通过有上方的“上“、”下”两个按键对音程进行扩展
john
2658
2026 M-Design设计竞赛-便携式高精度姿态传感测量系统该项目使用了STM32,MPU6050,实现了便携式高精度姿态传感测量系统的设计,它的主要功能为:实现了一款基于STM32F103C8T6微控制器与MPU6050六轴惯性传感器的便携式高精度姿态传感测量系统。系统实时采集物体在三维空间中的俯仰角(Pitch)、翻滚角(Roll)和偏航角(Yaw),并通过卡尔曼滤波算法对姿态数据进行平滑处理,最终在2.4寸TFT-LCD屏幕上清晰显示。设备支持绝对角度测量与相对角度测量双模式,可通过四个多功能按键实现显示模式切换、角度/弧度单位转换、显示冻结、零点重置等操作。内置1200mAh锂电池,配合TP4056充电管理芯片和DW01保护芯片,实现安全充放电,并具备实时电量监测和低电量蜂鸣器报警功能。整机结构紧凑、响应迅速(单轴刷新100ms,三轴300ms),适用于建筑安装、机械调平、机器人姿态调试等现场工程场景。。
叶佳敏
104
2026 M-Design设计竞赛 - 基于U5G9DK2的PWM调试端该项目使用了U5G9DK2,实现了PWM调试设备的设计,它的主要功能为:可通过屏幕来联动,输出不同类型的PWM波形。
zhjlmt
87
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号