ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

STM32F103C8T6读取MPU6050六轴数据串口输出实战

2026/9/11 18:43:36 拓冰建站 浏览量
STM32F103C8T6读取MPU6050六轴数据串口输出实战 简介采用STM32F103C8T6读取MPU6050六轴数据的完整源码工程面向初学STM32与传感器应用的开发者实现三轴陀螺仪和加速度计数据实时读取并打印到串口代码对F103系列各型号具备良好兼容性。压缩包共72个文件体积仅1.26MB以C源码、头文件和Keil工程文件为主同时附有hex烧录文件与使用说明txt工程内包含main、MPU6050驱动、串口、延时及LED、KEY等基础模块结构清晰可快速导入编译验证。已有483人学习下载资源适合学习I2C通信、传感器寄存器配置和串口调试方法。基于该源码可继续扩展计步、跌倒检测、姿态判断等应用既能支撑课程设计也可作为后续项目开发的底层参考。1. 一块 C8T6、一个 MPU6050先把六轴数据从串口吐出来用 STM32F103C8T6 读取 MPU6050 六轴数据再通过串口打印到 PC是 F103 系列里被问得最多、也最容易翻车的基础工程。网上代码不少但经常是拿 RCT6、VET6 的工程直接塞进 C8T6结果串口助手要么不输出要么输出一堆乱码。这套源码工程的主控是 C8T6MPU6050 接在 PB6/PB7 上跑软件 I2CUSART1 以 115200 波特率打印三轴加速度和三轴陀螺仪原始值。它适合刚入手 C8T6 最小系统板、想确认传感器读数是否正常的开发者也适合后续做计步、跌倒判断、姿态解算时先解决数据源问题。数据通路一旦跑通剩下的都是算法问题。2. 先把工程骨架理顺F103C8T6 的启动文件与容量宏2.1 源码包里哪些文件必须看打开源码包第一眼是目录结构。.uvproj是 MDK 工程文件.uvopt是调试器与窗口布局配置.uvgui_Administrator.bak是前一次打开的 UI 备份OBJECT 目录里全是编译缓存Rebuild 后会自动刷新不需要手工维护。真正的源码集中在 USER、SYSTEM、HARDWARE 这几个目录里剩下的大多是辅助文件。文件/目录作用USER/main.c主循环负责读取 MPU6050 并串口打印SYSTEM/usart.c串口初始化与 printf 重定向SYSTEM/delay.c提供微秒/毫秒延时供 I2C 时序使用HARDWARE/MPU6050/mpu6050.c六轴数据读取与寄存器配置HARDWARE/MPU6050/mpuiic.c软件 I2C 底层时序startup_stm32f10x_hd.s启动文件HD 容量版本OBJECT/STM32F103HD.hex编译产物可直接烧录这套工程叫 STM32F103HD启动文件用的也是 HD 版本所以当你拿到 C8T6 最小系统板时这里就是第一个要动刀的地方。原说明里说“只要是 F103 系列都可以直接使用”这句话从外设角度成立MPU6050 用的是普通 GPIO 模拟 I2CUSART1 用 PA9/PA10这些外设在所有 F103 上完全一致。但严谨一点看容量类型和启动文件并不完全对应。2.2 为什么 F103 系列能兼容到这个程度F103 系列内部寄存器布局高度统一GPIO、USART、I2C 的地址和操作方式几乎相同所以 mpu6050.c、usart.c 这类代码可以在 C8T6、RCT6、VET6、ZET6 之间直接移植。但标准外设库在编译时通过STM32F10X_MD、STM32F10X_HD这类宏决定一批参数其中最典型的是 Flash 页大小。#if defined(STM32F10X_HD) || defined(STM32F10X_HD_VL) #define FLASH_PageSize ((uint16_t)0x800) /* 2 Kbytes */ #else #define FLASH_PageSize ((uint16_t)0x400) /* 1 Kbytes */ #endif这段代码是标准外设库里很常见的条件编译。F103C8T6 是 64KB Flash属于中容量 MD每页 1KBF103ZET6 是 512KB Flash属于高容量 HD每页 2KB。如果你在 C8T6 上保留 HD 宏跑读传感器、串口打印这类简单应用可能暂时不暴露问题但一旦做 IAP、写入参数或者擦除指定页就会按 2KB 去操作把相邻数据一起擦掉。所以拿到这个工程我建议先把容量定义改准确。C8T6 最小系统板原理图里一般只有 LDO、8MHz 晶振、复位电路和 BOOT 跳线板子上能找到 3.3V、GND、PB6、PB7、PA9、PA10 这几个引脚就可以接。MPU6050 模块通常自带 4.7k 上拉电阻不需要额外接上拉唯一要注意的是供电VCC 接 3.3V 最稳接 5V 虽然不少模块内部有稳压但长期运行发热明显。2.3 上电前先改这三个地方如果你用的是 C8T6建议拿到工程后先做三个修改而不是直接编译烧录。打开 Options for Target - Device选择 STM32F103C8。打开 C/C - Define把STM32F10X_HD改成STM32F10X_MD。在 Project 窗口里删除startup_stm32f10x_hd.s从标准外设库中重新添加startup_stm32f10x_md.s。改动之后重新 Build再查看生成 map 文件里的 Reset_Handler 地址确认启动文件确实换成了 MD 版本。这一条必须做因为 F103C8T6 的向量表虽然能跑 HD 启动文件但后面加任何一个依赖STM32F10X_MD的外设库函数都可能出现编译警告或者地址越界。别把“小概率能跑”当成“一定没问题”。3. MPU6050 软件 I2C 读取寄存器配置与六轴原始值拼接3.1 为什么不用硬件 I2CF103 的硬件 I2C 外设在早期生态里口碑并不好。事件标志多、中断配合复杂稍微处理不当就卡死在I2C_CheckEvent等待里特别是有中断嵌套时容易死等。MPU6050 的数据量很小100Hz 采样率下每秒也就 1200 字节左右软件 GPIO 模拟 I2C 完全够用。只要延时函数准确400kHz 以内的时序都能稳定跑。工程里 mpuiic.c 做的就是这部分工作SCL 对应 PB6SDA 对应 PB7。接线表如下MPU6050 引脚STM32F103C8T6说明VCC3.3V模块供电部分模块兼容 5VGNDGND共地IIC_SDAPB6软件 I2C 数据线IIC_SCLPB7软件 I2C 时钟线MPU_INT不接本工程未使用硬件中断MPU_AD0不接/接 GND从机地址 0x68这里要特别注意 AD0。MPU6050 的 7 位 I2C 地址由 AD0 决定悬空或接 GND 时是 0x68接 VCC 时变成 0x69。在代码里做 8 位地址运算时写地址是0x68 1读地址是(0x68 1) | 1也就是常见的 0xD0 和 0xD1。很多用户把 I2C 时序写对了但地址用错导致读回来的数据全是 0xFF浪费好几个钟头排查。3.2 六轴原始数据在寄存器里的位置MPU6050 内部的加速度计、陀螺仪分别输出 16 位有符号整数按大端方式存放在一组连续寄存器里。读取时不需要分别操作每个字节直接从起始地址连续读 6 个字节就行。常用寄存器如下寄存器地址寄存器名作用0x6BPWR_MGMT_1电源管理复位后选择时钟源0x19SMPLRT_DIV采样率分频器0x1ACONFIG数字低通滤波 DLPF 配置0x1BGYRO_CONFIG陀螺仪量程配置0x1CACCEL_CONFIG加速度计量程配置0x3B ~ 0x40ACCEL_XOUT_H ~ ACCEL_ZOUT_L三轴加速度原始值0x43 ~ 0x48GYRO_XOUT_H ~ GYRO_ZOUT_L三轴陀螺仪原始值默认量程下加速度计是 ±2g陀螺仪是 ±250°/s。16 位数据满量程对应 32768所以加速度计读出来除以 16384 得到以 g 为单位的值陀螺仪除以 131 得到每秒度数。这个工程直接打印原始整数把换算放到上位机做这样可以降低单片机端的浮点开销也方便对比原始数据质量。3.3 初始化顺序和读取代码初始化第一步是复位然后等芯片从复位状态恢复再配置时钟源、采样率和量程。顺序不能乱尤其是复位后要延时否则后续寄存器写入可能不生效。void MPU6050_Init(void) { I2C_GPIO_Config(); // 初始化 PB6/PB7 开漏模式 MPU6050_WriteReg(0x6B, 0x80); // PWR_MGMT_1 置位 DEVICE_RESET delay_ms(50); // 等待复位完成 MPU6050_WriteReg(0x6B, 0x01); // 选择 PLL X 轴陀螺为时钟源 MPU6050_WriteReg(0x19, 0x07); // 采样率 1kHz / (1 7) 125Hz MPU6050_WriteReg(0x1A, 0x03); // DLPF 带宽约 44Hz MPU6050_WriteReg(0x1B, 0x18); // 陀螺仪量程 ±2000°/s MPU6050_WriteReg(0x1C, 0x00); // 加速度计量程 ±2g }代码里0x19写 7表示采样率分频器为 7内部 1kHz 基础频率经过分频后得到 125Hz。0x1A写 3打开数字低通滤波陀螺仪信号带宽降到 44Hz 左右能滤掉一部分机械振动噪声。0x1B写 0x18 对应 2000°/s0x1C写 0x00 对应 ±2g。量程选择影响后续换算系数如果后面做计步算法±2g 灵敏度最高但剧烈运动可能削波做跌倒检测时我会把加速度计量程放到 ±4g也就是写 0x08。读取加速度计六字节的代码也很直接void MPU6050_Read_Accel(int16_t *ax, int16_t *ay, int16_t *az) { uint8_t buf[6]; MPU6050_ReadRegs(0x3B, buf, 6); // 连续读 6 字节 *ax (int16_t)((buf[0] 8) | buf[1]); // 高字节在前 *ay (int16_t)((buf[2] 8) | buf[3]); *az (int16_t)((buf[4] 8) | buf[5]); }这里使用了int16_t强转目的是把字节拼接后的无符号值变成真正的有符号数。MPU6050 的数据是大端输出buf[0]是高字节如果顺序反了静止时数据会在正负值之间来回跳看起来像剧烈振动。陀螺仪的读取方式完全一样只是起始地址换成 0x43。3.4 数据漂移和 I2C 卡死的坑刚上电的前几十秒MPU6050 内部晶振和传感器都处于温度爬升阶段陀螺仪零漂会比正常工作时大这是正常现象。如果长时间漂移明显先看供电是否干净许多最小系统板的 3.3V 是 LDO 直接输出纹波大时传感器输出会有规则毛刺。另一个常见问题是杜邦线过长SDA/SCL 线间电容变大波形上升沿变缓软件 I2C 读回的数据就可能错位。我一般把杜邦线控制在 15cm 以内模块背面再并一个 100nF 电容到 GND。软件 I2C 还有一个潜在风险如果没有超时机制主设备一直等从设备 ACK而 MPU6050 地址写错或者模块断电程序会卡死在 while 循环里。调试时如果发现 LED 不闪串口也不打印先怀疑这里用调试器暂停看 PC 指针停在哪个函数。4. 串口输出与上位机验证波特率、printf 重定向和波形观察4.1 USART1 初始化与引脚复用串口部分用的是 USART1TXD 在 PA9RXD 在 PA10。C8T6 上这两个引脚默认是 GPIO必须先开复用功能否则 TX 输出不了数据。初始化方式如下void USART1_Config(void) { GPIO_InitTypeDef gpio; USART_InitTypeDef usart; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE); gpio.GPIO_Pin GPIO_Pin_9; // TX gpio.GPIO_Mode GPIO_Mode_AF_PP; // 复用推挽 gpio.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, gpio); gpio.GPIO_Pin GPIO_Pin_10; // RX gpio.GPIO_Mode GPIO_Mode_IN_FLOATING; // 浮空输入 GPIO_Init(GPIOA, gpio); usart.USART_BaudRate 115200; usart.USART_WordLength USART_WordLength_8b; usart.USART_StopBits USART_StopBits_1; usart.USART_Parity USART_Parity_No; usart.USART_Mode USART_Mode_TX | USART_Mode_RX; USART_Init(USART1, usart); USART_Cmd(USART1, ENABLE); }参数说明115200 是串口调试助手里最常用的波特率8 位数据位、1 位停止位、无校验简称 8N1。PA10 配浮空输入是因为 RX 引脚本身由外部设备驱动浮空输入足够。如果你用的是板载 USB-TTL 芯片注意板子上的 TX/RX 要和外部模块交叉连接也就是单片机 TX 接 USB-TTL 的 RX单片机 RX 接 USB-TTL 的 TX。4.2 printf 重定向和打印帧格式工程里直接用printf打印数据这说明源码包已经重定向了 C 库的fputc。核心代码一般是int fputc(int ch, FILE *f) { while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET); USART_SendData(USART1, (uint8_t)ch); return ch; }USART_FLAG_TXE表示发送数据寄存器为空while循环是为了等一下上一次数据发送完避免连续打印时丢字节。使用 printf 到串口时还有一件事经常被忽略必须在 Keil 的 Options for Target 里勾选 MicroLIB否则 C 库的 printf 会走半主机通道程序跑到第一个打印语句就死掉而且看不出明确报错。主循环里打印格式可以这样设计printf(acc: %d %d %d gyro: %d %d %d\r\n, ax, ay, az, gx, gy, gz);注意\r\n而不是只加\n。原因是串口调试助手在 Windows 下把回车和换行当成一行的结束标记只发\n时某些工具会串行覆盖数据看起来像在刷新但没换行。4.3 用串口调试助手和 Python 快速验证数据插上 USB-TTL 后设备管理器里会多出一个 COM 口常见的驱动芯片是 CH340。打开串口调试助手波特率选 115200数据位 8停止位 1无校验。如果显示一串数字说明数据链路已经通了。常见问题排查参考下表现象可能原因检查方向串口无输出TX/RX 没交叉连接检查 PA9 和 USB-TTL 的 RXD 是否交叉输出乱码波特率不匹配或晶振不对确认两边都是 1152008MHz 晶振焊接可靠数据全是 0xFFMPU6050 地址错或模块没供电检查 AD0确认 VCC/GND 正常数据跳变剧烈供电纹波大或杜邦线太长缩短线距并联 100nF 电容如果想把数据留档做进一步分析用 Python 比串口助手更合适。下面这段代码按行读取并按空格拆分出十个整数import serial ser serial.Serial(COM5, 115200, timeout1) with open(mpu6050.csv, w) as f: f.write(ax,ay,az,gx,gy,gz\n) while True: line ser.readline().decode(utf-8, ignore).strip() if line.startswith(acc:): items line.replace(acc:, ) items items.replace(gyro:, ) vals items.split() if len(vals) 6: f.write(,.join(vals[:6]) \n) print(vals)使用前需要安装pyserial并把COM5换成自己的实际串口号。这段代码把六个原始整数存进 CSV后面用 Excel 或 Python 画波形都很方便。注意这里前提是 main.c 打印格式固定如果你改了打印内容解析逻辑也要同步改。5. 从原始数据到运动状态一个能立刻用的加速度阈值判断5.1 合加速度幅值和运动状态识别拿到原始六轴数据后先别急着做卡尔曼滤波很多应用不需要完整姿态角只需要区分运动、静止和跌倒。最实用的特征是加速度计三轴的平方和再开根号也就是合加速度幅值acc_mag。静止时acc_mag应该稳定在 1g 附近行走时每一步会形成一个高峰和低谷跌倒时身体失重阶段acc_mag会先快速下降到接近 0g接着落地冲击迅速超过 2g。这里给出一个最小实现量程按工程初始化的 ±2g除以 16384 得到以 g 为单位的值#define ACC_THRESHOLD_MOTION 0.15f // 运动判据阈值 #define ACC_THRESHOLD_FALL 0.4f // 失重判据阈值 float acc_mag; float max_val, min_val; static float acc_g_value(int16_t raw) { return raw / 16384.0f; } void Motion_Check(int16_t ax, int16_t ay, int16_t az) { float x acc_g_value(ax); float y acc_g_value(ay); float z acc_g_value(az); acc_mag sqrtf(x * x y * y z * z); }MAX和MIN可以在一个 1 秒窗口内更新。窗口内max_val - min_val超过 0.15g 时认为正在运动低于 0.05g 且持续 2 秒则认为静止。这个值不是固定的手环戴在手腕和放在口袋里阈值要差出一倍。落到工程里最稳妥的方式是把Motion_Check放进定时器中断每 20ms 采一次样并维护一个长度为 50 的环形缓冲。5.2 用静止数据验证传感器是否正常在跑任何算法之前先采集一组静止数据是最快的验证手段。把模块平放在桌面上采集 100 组加速度计数据计算合加速度的平均值和极差。正常情况下平均值在 0.98 到 1.02 之间极差不超过 0.05g陀螺仪三轴则应该在 0°/s 附近漂移数值大小取决于 DLPF 配置通常在几十个 LSB 以内。如果加速度计数据出现明显方向偏差先看模块是不是放平了如果合加速度超过 1.05g大概率是电源噪声叠加在了信号上。做了这一步后面再做计步、跌倒判断时就能排除传感器本身的问题。阈值调参时也可以直接用这个工程打印出的原始值把程序跑起来掰动模块观察串口输出的数字变化就能直观感受数据跳变范围再回头改阈值。数据通路和验证方法都在手里后续项目就只差你自己的算法了。本文还有配套的精品资源点击获取