☰
STM32F103 I2C驱动DRV2605触觉马达:从地址确认到波形触发
2026/10/28 8:15:08 网站建设 项目流程

简介:本资源面向嵌入式开发初学者与有一定基础的工程师,提供STM32F103C8T6通过I2C总线驱动DRV2605振动马达驱动器的完整工程实践。内容涵盖硬件连接、I2C外设初始化、HAL/LL库通信代码编写、DRV2605寄存器配置、振动强度与波形模式调节,以及通信错误处理与示波器调试思路,帮助读者掌握微控制器外设编程与马达驱动控制的软硬件交互方法。压缩包共247个文件,约6.95MB,以c源码、h头文件、o目标文件、d依赖文件及crf编译中间文件为主,另含bin固件、hex烧录文件、uvprojx工程文件与少量htm说明文档,构成可直接编译调试的Keil工程目录。目前已有525人学习下载,适合希望快速上手I2C通信与振动反馈控制、对照工程结构查漏补缺的开发者参考。

1. STM32F103 用 I2C 驱动 DRV2605:从最小系统到马达起振

手头有一块 STM32F103 最小系统板,一个 DRV2605 触觉驱动模块,一颗偏心转子马达或者线性谐振马达,接线只有四根:VCC、GND、SCL、SDA。很多人第一次上电就卡住——I2C 扫不到地址,或者扫到了但马达纹丝不动。这个组合看起来简单,实际踩的坑集中在三处:I2C 上拉电阻、DRV2605 的使能时序、以及马达类型和驱动模式的匹配。这篇笔记就围绕 STM32F103 的硬件 I2C 外设驱动 DRV2605 这条链路,把地址确认、寄存器配置、波形触发、效果库调用和排错方法一次讲透。适合已经能点亮 LED、会用 Keil MDK 或 STM32CubeMX 建工程,但还没把触觉反馈跑起来的嵌入式开发者。读完你能自己写出一套可复用的 DRV2605 驱动,知道每个参数为什么这么设,也能判断手里的马达到底该用 RTP 模式还是 ROM 效果库。

2. DRV2605 的寄存器模型与 I2C 地址确认

2.1 为什么先确认地址而不是先写代码

DRV2605 的 7 位 I2C 地址由 ADDR 引脚决定,常见模块默认拉低,地址是 0x5A。但市面上有些模块把 ADDR 焊到 VDD,地址变成 0x5B。如果你直接照抄别人的 0x5A 去写,HAL_I2C_Master_Transmit 会返回 HAL_ERROR 或者 HAL_TIMEOUT,这时候很多人会去怀疑时序、怀疑上拉,其实只是地址错了。我一般拿到新模块的第一件事,是用扫描程序把 0x00 到 0x7F 全跑一遍,确认应答地址再往下走。

STM32F103 的硬件 I2C 在标准模式 100kHz 下最稳,快速模式 400kHz 也能跑,但 DRV2605 手册标称支持 400kHz,实际在杜邦线连接、上拉偏弱的情况下 400kHz 容易出 NACK。建议先用 100kHz 把功能跑通,再考虑提速。

2.2 寄存器地图:从状态位到波形触发

DRV2605 的寄存器不多,但有几个是关键路径。0x00 是 Status 寄存器,读它能看到设备是否就绪、有没有过流。0x01 是 Mode 寄存器,写 0x00 进入内部触发模式,写 0x05 进入 RTP 实时播放模式。0x02 是 RTP 数据寄存器,实时模式下往这里写幅度值。0x04 是 Library 选择,0x05 是 Waveform Sequence 1 到 8,0x0C 是 GO 位,写 1 触发序列播放。0x16 到 0x1A 是 Overdrive、Sustain、Brake 时间参数,0x17 到 0x1A 配合 0x16 决定自动谐振马达的起振和刹车曲线。

初始化顺序不能乱:先写 0x01 退出待机,再写 0x1D 做自动校准,然后写 0x04 选库,最后写 0x0C 触发。如果跳过校准直接触发,马达可能只抖一下或者完全没反应,因为 DRV2605 不知道你的马达反电动势特性。

2.3 最小扫描代码与地址判定

#include "stm32f1xx_hal.h" extern I2C_HandleTypeDef hi2c1; void DRV2605_Scan(void) { uint8_t addr; HAL_StatusTypeDef ret; for (addr = 0x01; addr < 0x7F; addr++) { ret = HAL_I2C_IsDeviceReady(&hi2c1, addr << 1, 2, 10); if (ret == HAL_OK) { printf("Found device at 0x%02X\r\n", addr); } } }

这段代码用 HAL_I2C_IsDeviceReady 逐个地址探测,超时 10ms,重试 2 次。注意传入的地址要左移一位,因为 HAL 库要求 8 位格式。如果扫到 0x5A 或 0x5B,说明硬件连接和上拉基本正常。扫不到任何设备,先查 SCL/SDA 是否接反、上拉电阻是否焊了、VCC 是否到 3.3V。DRV2605 的 VDD 范围是 2.5V 到 5.5V,但 I2C 电平跟随 VDD,如果模块 VDD 接 5V,而 STM32F103 的 IO 是 3.3V,长期工作会损伤 IO,建议模块 VDD 也接 3.3V,或者加电平转换。

提示:STM32F103 的 I2C1 默认引脚是 PB6/SCL 和 PB7/SDA,用 CubeMX 配置时记得把 GPIO 模式设为开漏复用,并且外部上拉 4.7k 到 3.3V。内部上拉太弱,不足以支撑 100kHz 以上的边沿。

3. 初始化序列与自动校准的实操参数

3.1 上电后的寄存器写入顺序

DRV2605 上电后处于待机,必须先把 Mode 寄存器写 0x00 唤醒。紧接着做自动校准,这一步是很多教程跳过的,但对手里的马达能不能起振至关重要。校准过程是:写 0x01 = 0x00,延时 1ms;写 0x1D = 0x00 触发校准;轮询 0x00 的 bit0,直到读到 1 表示校准完成。校准完成后,0x16 到 0x1A 会被硬件填入适合当前马达的反馈参数。如果你换马达,必须重新校准,否则效果库里的波形会偏弱或者过冲。

校准之后写 0x04 选择效果库。DRV2605 内置 6 个库,库 1 到 5 是 ERM 马达的波形,库 6 是 LRA 马达的波形。选错库的典型现象是:LRA 马达用库 1 播放,声音大但振感散;ERM 马达用库 6,几乎感觉不到振动。我一般先用库 1 测试 ERM,用库 6 测试 LRA,确认马达类型后再微调。

3.2 自动校准的代码实现与超时处理

#define DRV2605_ADDR (0x5A << 1) uint8_t DRV2605_Init(void) { uint8_t status; uint8_t mode = 0x00; uint8_t reg; // 退出待机 HAL_I2C_Mem_Write(&hi2c1, DRV2605_ADDR, 0x01, 1, &mode, 1, 100); HAL_Delay(1); // 触发自动校准 reg = 0x00; HAL_I2C_Mem_Write(&hi2c1, DRV2605_ADDR, 0x1D, 1, &reg, 1, 100); // 等待校准完成,超时 200ms uint32_t tick = HAL_GetTick(); do { HAL_I2C_Mem_Read(&hi2c1, DRV2605_ADDR, 0x00, 1, &status, 1, 100); if (HAL_GetTick() - tick > 200) { return 1; // 校准超时 } } while ((status & 0x01) == 0); // 选择库 1(ERM)或库 6(LRA) reg = 0x01; HAL_I2C_Mem_Write(&hi2c1, DRV2605_ADDR, 0x04, 1, &reg, 1, 100); return 0; }

HAL_I2C_Mem_Write 的第二个参数是 8 位地址,第三个是寄存器偏移,第四个是偏移长度,这里都是 1 字节。校准超时设 200ms 是保守值,实际马达校准通常在 50ms 内完成。如果超时,先检查马达是否接在 OUT+ 和 OUT- 之间,以及马达内阻是否在 DRV2605 允许范围内(ERM 通常 8 到 100 欧,LRA 更低)。校准失败时 Status 寄存器的 bit0 不会置位,读回来一直是 0。

3.3 效果库与 RTP 模式的选型对比

模式寄存器操作适用场景参数重点
内部触发(ROM 库)0x01=0x00,0x04 选库,0x05 写效果号,0x0C=0x01按键反馈、通知提醒效果号 1 到 123,库 6 专供 LRA
RTP 实时播放0x01=0x05,0x02 写幅度游戏振动、音频跟随幅度 0 到 127,需持续刷新
PWM 输入0x01=0x03,外部 PWM 进 IN/TRIG低成本方案,省 I2C 带宽频率和占空比决定振感

内部触发最省事,适合大多数 UI 反馈。RTP 模式灵活但占 I2C 带宽,STM32F103 在 100kHz 下每写一次幅度大约 0.2ms,刷新率能到 1kHz 以上,做音频跟随够用。PWM 模式需要额外接线,但完全不占 I2C,适合马达数量多的场景。

4. 触发波形与实时振动的代码落地

4.1 用效果库播放一段振动

void DRV2605_PlayEffect(uint8_t effect) { uint8_t reg; // 确保处于内部触发模式 reg = 0x00; HAL_I2C_Mem_Write(&hi2c1, DRV2605_ADDR, 0x01, 1, &reg, 1, 100); // 写效果号到序列 1 HAL_I2C_Mem_Write(&hi2c1, DRV2605_ADDR, 0x05, 1, &effect, 1, 100); // 序列 2 到 8 写 0,表示序列结束 reg = 0x00; for (uint8_t i = 0x06; i <= 0x0C; i++) { HAL_I2C_Mem_Write(&hi2c1, DRV2605_ADDR, i, 1, &reg, 1, 100); } // 触发 GO reg = 0x01; HAL_I2C_Mem_Write(&hi2c1, DRV2605_ADDR, 0x0C, 1, &reg, 1, 100); }

效果号 1 是强点击,47 是软弹,库 6 的 1 到 5 是 LRA 专用。写序列时,0x05 到 0x0C 对应序列 1 到 8,遇到 0 就停止。所以只播一个效果时,把效果号写 0x05,0x06 写 0 即可。GO 位写 1 后硬件自动播放,播放期间再写 GO 会被忽略,需要等 Status 的 bit3 置位表示播放完成。

4.2 RTP 模式做振动跟随

void DRV2605_RTP_Start(void) { uint8_t mode = 0x05; HAL_I2C_Mem_Write(&hi2c1, DRV2605_ADDR, 0x01, 1, &mode, 1, 100); } void DRV2605_RTP_Write(uint8_t amplitude) { if (amplitude > 127) amplitude = 127; HAL_I2C_Mem_Write(&hi2c1, DRV2605_ADDR, 0x02, 1, &amplitude, 1, 100); } void DRV2605_RTP_Stop(void) { uint8_t mode = 0x00; uint8_t zero = 0x00; HAL_I2C_Mem_Write(&hi2c1, DRV2605_ADDR, 0x02, 1, &zero, 1, 100); HAL_I2C_Mem_Write(&hi2c1, DRV2605_ADDR, 0x01, 1, &mode, 1, 100); }

RTP 模式下,0x02 的值直接映射到马达驱动幅度,0 停,127 最大。注意 RTP 模式不会自动刹车,停止时先把幅度写 0,再切回内部触发模式,否则马达会余振。如果做音频跟随,每 1ms 到 5ms 更新一次幅度,更新太快 I2C 总线会饱和,太慢振感会滞后。我一般用 2ms 定时器中断里调 DRV2605_RTP_Write,STM32F103 的 TIM3 配 2ms 中断很稳。

4.3 参数微调:Overdrive 和 Brake 怎么改

校准完成后,0x16 到 0x1A 已经有值,但如果你觉得起振太慢或者刹车拖尾,可以手动改。0x16 是 Overdrive 时间偏移,0x17 是 Sustain 时间正偏移,0x18 是 Sustain 时间负偏移,0x19 是 Brake 时间偏移,0x1A 是 Brake 时间正偏移。ERM 马达想更跟手,把 0x16 减小、0x19 增大;LRA 马达对刹车不敏感,重点调 0x16 让起振更快。每次改完要重新听感验证,不要一次改太多。

注意:手动改这些寄存器会覆盖校准结果,换马达后必须重新校准,否则参数不匹配。

5. 避坑与排查:I2C 扫不到、马达不振、振感异常

5.1 扫不到地址,HAL 返回 HAL_ERROR

现象:扫描程序跑完没有任何输出,或者只扫到 0x00。原因通常是上拉电阻缺失或阻值太大。STM32F103 内部上拉约 40k,杜邦线电容又大,边沿爬升太慢,I2C 从机采样不到。解决:在 SCL 和 SDA 上各焊一个 4.7k 到 3.3V 的上拉,线长控制在 20cm 以内。如果模块自带 10k 上拉,可以并联一个 10k 变成 5k,但不要低于 2.2k,否则灌电流太大。

5.2 地址扫到了,但初始化校准超时

现象:DRV2605_Init 返回 1,Status 的 bit0 一直不置位。原因可能是马达没接,或者马达内阻超出范围。DRV2605 校准需要检测反电动势,开路时校准无法完成。解决:确认马达接在 OUT+ 和 OUT-,用万用表量内阻,ERM 在 8 到 100 欧之间,LRA 在 4 到 50 欧之间。如果内阻正常还是超时,把校准超时放宽到 500ms,有些小马达起振慢。

5.3 效果库播放时马达只轻微抖动

现象:调用 DRV2605_PlayEffect(1) 后马达有反应但很弱。原因通常是库选错了。ERM 马达选了库 6,或者 LRA 马达选了库 1。解决:ERM 用库 1 到 5,LRA 用库 6。如果不确定马达类型,两个库都试一遍,振感强的那个就是对的。另外检查 VDD 是否够 3.3V,电压低时驱动幅度也会下降。

5.4 RTP 模式停止后马达余振

现象:DRV2605_RTP_Stop 调用后马达还转一小会儿。原因是 RTP 模式没有自动刹车,幅度写 0 后马达靠惯性停。解决:停止时先把 0x02 写 0,延时 10ms 等马达停,再切回 0x01=0x00。如果余振明显,可以在 0x02 写 0 后短暂写一个反向小幅度,但 DRV2605 不支持负值,所以只能靠延时。

5.5 播放效果时 I2C 总线卡死

现象:连续快速触发效果,程序卡在 HAL_I2C_Mem_Write 里出不来。原因是 DRV2605 在播放期间不响应 I2C,如果此时主机强行写,从机拉低 SCL 导致总线死锁。解决:触发 GO 后不要立刻写下一个效果,轮询 Status 的 bit3,等播放完成再操作。或者在 I2C 初始化时打开时钟拉伸超时,HAL 库默认超时 100ms,够用。

6. 进阶:用序列组合做复杂触觉反馈与功耗控制

单效果播放只能做简单提示,真正让触觉反馈有层次的是序列组合。DRV2605 的 0x05 到 0x0C 可以放 8 个效果号,硬件会按顺序播放,每个效果之间可以插入延时。比如先播效果 1(强点击),再播效果 47(软弹),最后播效果 0(停止),就能做出“哒-嘟”的两段反馈。序列里写 0 表示结束,写 1 到 123 是效果号,写 0x80 以上的值是等待时间,单位 10ms。比如 0x85 表示等待 50ms。这个技巧在按键确认加错误提示的组合里很好用。

功耗方面,DRV2605 在待机时电流约 0.5uA,播放时峰值能到 100mA 以上。如果做电池供电的触觉手环,建议在不需要振动时把 0x01 写 0x40 进入待机,需要时再唤醒。唤醒到就绪大约 1ms,不影响体验。另外 RTP 模式比 ROM 模式耗电,因为 I2C 一直在传数据,能用 ROM 库就别用 RTP。

验证方法上,我习惯用示波器看 OUT+ 和 OUT- 的差分波形。正常播放效果时应该看到包络先上升后下降,如果波形削顶,说明 Overdrive 太大,把 0x16 调小。如果波形起振慢,把 0x16 再调小一点。没有示波器的话,用手机慢动作录像拍马达,看偏心轮从静止到最大转速的时间,也能粗略判断。

最后说个血泪教训:我最早做这个方案时,把 DRV2605 的 VDD 接了 5V,STM32 的 IO 是 3.3V,跑了一周后 I2C 引脚间歇性失灵,换了芯片才好。后来所有模块我都统一 3.3V 供电,再没出过这个问题。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询