news 2026/9/3 21:07:13

MT6835 TMR磁编码器SPI读取实战:从角度数据到位置闭环

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MT6835 TMR磁编码器SPI读取实战:从角度数据到位置闭环

简介:面向电机位置检测与编码器应用开发者,MT6835编码器角度读取示例代码提供了一套完整的固件工程,可帮助用户快速完成角度数据采集、寄存器配置与通信调试。资源共1042个文件,以C源文件与H头文件为主体,同时包含工程配置、链接脚本、IAR与Keil相关库文件以及可烧录的Hex输出等,压缩包仅7.35MB,便于在嵌入式环境下直接参考或移植。工程内已集成官方DSP数学库及角度插值与滤波相关算法,便于提高角度读取的精度与稳定性;代码分层清晰,目录结构完整,适合具有一定STM32基础、需要快速上手MT6835传感器的开发者。目前已有1446人学习浏览,借助这套示例代码可大幅缩短编码器调试周期,是电机位置感知类项目的有力参考。 做电机控制这些年,编码器这块我从光电换到磁编码器,踩了不少坑。前阵子项目里需要换一颗角度传感器,原来的AS5047P供货紧张,评估了一圈,选了MT6835这颗TMR磁编码器。它的数据手册和示例代码不算多,网上讨论也零散,我把自己从“第一次读角度”到“跑通位置闭环”整个过程整理出来,重点放在SPI方式读取角度数据,代码可以直接抄,坑也一并排好。

1. 先搞懂MT6835这颗芯片,再谈读角度

1.1 TMR磁编码器到底强在哪

MT6835是麦歌恩(MagnTek)推出的一款基于TMR(隧道磁阻)效应的磁编码器芯片。传统霍尔编码器检测的是磁场强度,TMR检测的是磁场方向,这决定了它天生更适合做角度传感。做个不严谨但好懂的类比:霍尔方案像用温度计判断你在南半球还是北半球,TMR方案像直接看指南针指向哪里,精度和稳定性完全不在一个量级。

这颗芯片内部集成了TMR传感器阵列、信号调理电路、ADC和多种输出接口,外部只需要一个径向充磁的磁铁,放在芯片正上方旋转,就能得到0到360度的绝对角度。分辨率14位,也就是一圈分16384份,每份大约0.022度,做机器人关节、云台、伺服电机的位置反馈都够用。

还有个容易被忽略的点:TMR对磁场强度的适应范围比较宽,磁铁和芯片之间的距离留一点余量,角度输出依然稳定。我试过把磁铁抬高0.3毫米,读数偏差很小。换作霍尔方案,这个安装公差已经会让输出明显漂移了。

1.2 输出接口有好几种,为什么推荐SPI

MT6835同时支持SPI、ABZ增量输出、UVW换向输出和PWM输出。不同输出对应的使用场景完全不同,选错会非常难受。

ABZ输出本质是增量信号,上电后你不知道转子当前在哪个角度,必须配合Z信号或者外部初始化流程做一次“找零”动作。UVW是给无刷电机换相用的,只有6个扇区位置,精度对位置环来说不够细。PWM输出好一些,是绝对角度,但角度信息调制在占空比里,MCU测脉宽需要额外占用定时器,而且更新率通常比SPI低不少。

SPI是这几条路里最直接的:CSB拉低,SCK给时钟,MISO上就吐出来一个14位绝对角度。MCU在几微秒内拿到完整位置,延迟低、精度不打折,非常适合做电流环、速度环、位置环这种实时性要求高的控制。这篇文章后面所有内容都围绕SPI方式展开。

2. 硬件准备:引脚连接和最容易忽略的时序前提

2.1 供电、磁铁安装和CSB上拉的几个细节

MT6835常见的供电范围是3.3V到5V,这给接入不同系统留了很大方便。不过有一点必须注意,芯片电源引脚旁边一定要放一个0.1uF的去耦电容,并且尽量贴近芯片引脚。我在第一版测试板上偷懒,电容放得远了5毫米,结果角度数据偶发跳变,排查了半天才找到是电源纹波干扰。这种问题在原理图阶段就规避,不要指望后面软件滤波能救回来。

磁铁安装是另一个大坑。MT6835要求磁铁的旋转轴心和芯片封装中心对齐,磁铁端面尽量平行于芯片表面。稍微偏一点,角度误差就会出现两个周期的正弦波动,也就是转子转一圈,误差波动两次。怎么快速判断?不要急着看精度,先手动缓慢转一圈,把读出来的角度值连成曲线,如果看到明显的正弦形状,基本就是磁铁装偏了。对心安装座在这里不是可有可无的,是保证精度的刚需。

CSB片选引脚建议用一个10k电阻上拉到高电平。芯片上电初期,如果CSB处于不确定电平,有可能误进入配置模式,导致后续SPI通信错乱。很多示例代码不会写这一条,但我在批量测试里确实遇到过几次初始化失败,加上上拉之后问题再也没出现过。

2.2 SPI模式选择:Mode 1还是Mode 3

MT6835的数据手册里对SPI时钟极性和相位的描述,不同批次或烧录版本可能有差异,最稳妥的做法是拿到手册先查“SPI Mode”那一节。我这边测试下来常用的是Mode 3,即CPOL=1、CPHA=1,SCK空闲时为高电平,在第二个边沿采样。但也有工程师反映某些型号按Mode 1也能读,其实是因为部分配置下时序窗口比较宽。

这里给出一个实测建议:先用1MHz的低SPI时钟跑通通信,确认能稳定读出角度,再慢慢把时钟频率往上提。直接上10MHz往往不是芯片不行,而是杜邦线太长、接触不良或者地线回路太大,导致信号质量变差。低速跑通了,至少能证明SPI配置和寄存器地址是对的,后面再优化速度。

另外注意,读角度数据是一个“最少两个字节”的传输过程。CSB拉低后,MCU发送时钟的同时接收数据,发出去的数据内容是0x00即可。这个特征和很多普通SPI外设不一样,容易让人困惑,下一节详细展开。

3. 角度数据藏在哪个寄存器,SPI读时序怎么组织

3.1 寄存器地图:角度位置和状态字

MT6835内部寄存器不多,但读角度之前最好花两分钟把地址和位宽理清楚。最核心的是一个14位的角度寄存器,通常手册里标注为Angle Data Register,地址类似0x3FFF这样的只读区域,14位数据铺在寄存器的高14位或低14位,具体布局看手册的“Register Map”表。我的习惯是先把数据手册寄存器章节截图存下来,写代码时对照着看,避免记混。

除了角度值,芯片还提供磁场状态信息,比如磁场过弱、过强报警。角度数据可信的前提是磁场强度正常,所以正式项目里不要只读角度,建议顺带把状态位也读出来,一旦磁场异常,可以马上切到安全逻辑。自己玩可以先忽略,但项目越复杂,这个状态字越值钱。

角度值换算成角度很简单:raw范围是0到16383,实际角度 = raw * 360 / 16384。写代码时浮点运算要留意效率,如果是在MCU里做控制,可以预先算好刻度因子,或者直接用raw做差值和闭环运算,最后输出角度时再转成标准单位。

3.2 一次完整的SPI读取:命令字节、MISO返回和角度合并

MT6835的SPI读操作,CSB拉低之后,SCK开始工作,芯片在MISO线上按高位在前的方式把数据推出来。所谓“命令字节”,其实在纯读角度这个场景里可以不纠结,因为要的不是写寄存器,角度数据是芯片主动输出的。但为了兼容性和扩展性,发送两个0x00字节是通用做法。第一次收到的高字节和第二次收到的低字节拼接起来,就得到一个16位数据。

这里要特别说清楚为什么会写“MOSI发0就行”:SPI是全双工协议,MCU要接收数据就必须同时发送数据,发的内容对这个读操作本身没有意义,所以写0是最自然的。有人在这里卡住,觉得“我发错了命令,芯片不理我”,其实读到的一直稳定的变化值就是角度数据本身。

合并代码我用的是:

uint16_t raw = ((uint16_t)rx[0] << 8) | rx[1]; uint16_t angle_raw = raw >> 2; // 14位有效数据

为什么要右移2位?因为14位数据在16位帧里通常占据高14位,低2位要么是0,要么是状态位或校验位。具体低2位是什么,同样以手册为准,但右移2位拿到纯角度值这个思路在多数MT系列芯片上是通用的。

4. 示例代码逐行拆解:从STM32 HAL到裸机移植

4.1 初始化部分:CSB引脚和SPI外设配置

下面这套代码我在STM32F103和GD32E230上都跑过,逻辑是通用的。CSB用普通推挽GPIO控制,这样比硬件NSS更可控。SPI初始化按Mode 3设置,时钟先给1MHz验证,后续再调速。HAL库的写法如下:

void MT6835_SPI_Init(void) { GPIO_InitTypeDef gpio = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_SPI1_CLK_ENABLE(); gpio.Pin = GPIO_PIN_4; // CSB gpio.Mode = GPIO_MODE_OUTPUT_PP; gpio.Pull = GPIO_PULLUP; gpio.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &gpio); gpio.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; // SCK, MISO, MOSI gpio.Mode = GPIO_MODE_AF_PP; gpio.Alternate = GPIO_AF0_SPI1; HAL_GPIO_Init(GPIOA, &gpio); hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_2LINES; hspi1.Init.DataSize = SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH; // CPOL=1 hspi1.Init.CLKPhase = SPI_PHASE_2EDGE; // CPHA=1 hspi1.Init.NSS = SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; HAL_SPI_Init(&hspi1); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // CSB默认高 }

这里有个值得说的细节:CSB用GPIO_PULLUP是因为前面提到的上拉需求,但如果你外部已经接了10k上拉电阻,内部上拉开不开都行。另外SPI的NSS配置成软件模式,不要让硬件NSS自动控制,不然CSB时序会乱。

4.2 读角度函数:核心就这么几行

#define MT6835_CS_LOW() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET) #define MT6835_CS_HIGH() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET) uint16_t MT6835_ReadAngleRaw(void) { uint8_t tx[2] = {0x00, 0x00}; uint8_t rx[2] = {0x00, 0x00}; MT6835_CS_LOW(); HAL_SPI_TransmitReceive(&hspi1, tx, rx, 2, 10); MT6835_CS_HIGH(); return ((uint16_t)rx[0] << 8) | rx[1]; }

重点说一下为什么用TransmitReceive而不是单独的Receive。SPI从机要输出数据,必须有主机提供的时钟才能移出来,所以即使我们不需要发送任何有意义的命令,也要写寄存器并触发SCK。发送缓冲区放0x00,接收缓冲区拿到的就是芯片返回的16位数据。HAL_SPI_TransmitReceive一句搞定发送和接收,比手动先发送再接收更稳定。

CSB的拉低和拉高时机也值得注意。必须在SCK第一个边沿之前拉低,并且在最后一个SCK边沿结束后才能拉高,否则芯片可能把这次传输当成无效帧。代码里的顺序很简单,但如果你用DMA或者中断,务必保证CSB操作和SPI传输之间的时序不能被其他任务插入。

4.3 角度合并、零位校准和简单的滤波

读取到的16位数据,低2位按前面说的方式处理。如果不想在每次读取时都做一遍右移,可以在初始化时把角度值直接对齐到16位范围,即左移2位存起来,这样后续做差值和比例运算更方便。

实际项目中还有一个绕不开的需求:把当前机械位置清零作为零位。实现上不需要改芯片配置,MCU里记住一个零位偏移就行。每上电时,让设备转到机械零位,然后调用一次采集零位的函数。后续角度 = (当前raw + 16384 - 零位raw) & 0x3FFF,这个写法能正确处理角度从16383回绕到0的情况,比简单的相减更稳妥。

滤波方面要分场景。如果你只是看角度给上位机显示,滑动平均或者一阶低通都可以。但如果在电流环里做位置反馈,滤波反而会引入相位延迟,不建议对原始数据做深度滤波。我个人的习惯是:闭环控制用原始值,显示和诊断用轻滤波之后的值。

5. 实测中的几个坑:启动时间、非正交安装和零点漂移

5.1 上电之后立刻读,会读到什么

MT6835上电之后并不是瞬间就能输出有效角度数据,芯片内部有上电自检和稳定流程。我第一次用的时候,在MCU初始化之后立刻读角度,发现输出偶尔是0xFFFF或者看起来随机的跳变值,一度以为是通信配置有问题。

后来看了手册里关于启动时间的描述,又用示波器对比了CSB和MISO波形,才确认是读取时机不对。芯片从上电到内部ADC稳定,通常需要几个毫秒到几十毫秒,具体数值受电源质量和磁铁状态影响。稳妥做法:上电后至少延时50到100毫秒再开始读角度,或者在初始化流程里先连续读几次,丢弃前几个值,直到数据稳定。

这个坑在带电机驱动的系统里更明显。电机驱动上电瞬间会产生很大的电流冲击,如果编码器和功率电路共用一个电源,即使芯片已经进入稳定状态,也有可能被拉出异常。有条件的话,编码器电源和驱动电源分开,或者至少加磁珠和电容隔离。

5.2 磁铁偏心和非正交安装的故障特征

磁铁的安装问题不会直接导致通信失败,但会让角度精度变得很差。典型特征是:你手动匀速转动磁铁,读到的角度值却不是线性增长,而是有一个周期性的非线性波动,每转一圈波动两个完整的正弦周期,这是TMR传感器检测方向夹角导致的固有特性。

我遇到过一种更隐蔽的情况:磁铁和芯片表面不平行,也就是磁铁有一侧翘起来了,造成角度误差在小角度区域尤其严重,误差曲线像被掰弯的S形。排查方法还是那个土办法,记录一整圈的raw值,在PC上画出来,和理想的直线做差,误差曲线的形状会告诉你安装问题的类型。

修正方式有两种:一是改机械结构,用带定位台阶的转轴和安装座保证同心度和垂直度,这是治本;二是在软件里做误差补偿表,通过实际标定获得误差曲线,在算法里查表修正。如果精度要求不是极端苛刻,机械修正优先,软件补偿表是最后的手段。

5.3 零位偏移和温度漂移

零位偏移是每个绝对编码器系统都会遇到的问题。MT6835的电气零点通常和磁铁角度零点不重合,你装上去之后读到的0度,未必是机械意义上的0度。解决办法就是前面说的软件零位校准,把当前角度记录为偏移量。

但温度上来之后,磁铁和芯片的相对位置会因为材料热胀冷缩发生微小变化,角度输出的零点也会跟着漂。要求高的系统,除了选用低温漂磁铁,还会在结构上让芯片和磁铁的固定方式尽量对称,减小热应力带来的位移。如果应用场景温度变化很大,可以定期做校零,或者每次上电都重新校准。比如协作机械臂每次上电做一次零点回归,就是这个原因。

6. 从“能读角度”到“能用角度”:多圈、速度和控制闭环

6.1 角度回绕和圈数计数

单圈绝对角度很好用,但实际控制系统经常需要知道累计旋转了多少圈。MT6835本身输出的是单圈绝对角度,没有内部多圈计数器,所以多圈逻辑要放在MCU里做。

核心思路是:每次采样,把当前raw和上一次raw做差,如果差值大于半圈(比如大于8192),说明发生了正转回绕,圈数加一;如果差值小于负半圈,说明发生了反转回绕,圈数减一。用代码表示:

static int16_t rotation_count = 0; static uint16_t last_raw = 0; int32_t MT6835_GetMultiTurnRaw(void) { uint16_t cur_raw = MT6835_ReadAngleRaw(); int32_t delta = (int32_t)cur_raw - (int32_t)last_raw; if (delta > 8192) rotation_count--; else if (delta < -8192) rotation_count++; last_raw = cur_raw; return (int32_t)rotation_count * 16384 + (int32_t)cur_raw; }

这个算法的前提是相邻两次采样之间转过的角度不超过半圈,也就是180度。对采样周期来说,这个要求通常很容易满足。但如果MCU被高优先级中断卡了很久,错过一次采样,圈数就可能算错。对实时性要求高的场景,建议把角度读取放在定时器中断里,保证采样的连续性。

6.2 速度计算和滤波的工程取舍

从角度信号计算速度,最直接的方法是位置差分:速度 = 角度差 / 时间差。这里建议直接对raw做差,不要先转成角度再算。raw的差值是整数,MCU跑起来快,也不会引入浮点误差,控制环直接用这个差值作为速度环的比例项输入,非常方便。

差分测速有个问题:角度量化误差会被放大。如果转速很低,相邻两次采样之间角度只变化几个LSB,差分结果就会出现阶梯状跳变,控制效果不好。我的做法是:低速时把时间窗口拉长一点,比如每隔固定时间累加一次角度增量,再做平均;高速时缩短窗口,保证响应速度。窗口大小要根据控制周期和电机极对数实测调整,没有通吃全场的固定值。

最后说一个关于滤波的经验:不要在位置环反馈路径上放一个很强的低通滤波器,哪怕它能让你在示波器上看到平滑的曲线。滤波带来的延迟会让系统的相位裕度下降,表现出来就是电机嗡嗡响或者轻微震荡。我习惯把滤波放在速度计算之后,给速度环用,位置反馈保持原始值,这样系统既稳定又不丢失分辨率。

MT6835这颗芯片的SPI读取链路并不复杂,真正的功夫都在硬件细节和实际调试上。如果你也正在把磁编码器集成进控制系统,建议先做一块最简单的转接板,低速跑通SPI读角度,再装到电机上验证,最后才敢放量。这条路我替你走了一遍,能跳过不少坑。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/3 21:02:23

计算机毕业设计之基于JavaWeb的在线文具购物平台的设计与实现

本论文借助 Java 编程语言&#xff0c;运用VUE 前端架构及SpringBoot 后端架构&#xff0c;以 MySQL 数据库为依托&#xff0c;对一套在线文具购物平台进行了分析和设计。此系统包括热销文具、优惠券等功能&#xff0c;并划分为用户和管理员二个角色&#xff0c;各角色具备不同…

作者头像 李华
网站建设 2026/9/3 21:00:05

ESP32-C5双频Wi-Fi天线切换实战:GPIO控制RF开关全解析

很多人拿到 ESP32-C5 的第一反应是&#xff1a;终于有双频 Wi-Fi 了&#xff0c;2.4GHz 拥挤的问题可以缓解了。但真正上手调试后会发现&#xff0c;双频带来的不只是速度提升&#xff0c;还有天线切换这件以前不太需要操心的小事。如果只在实验室里用官方开发板&#xff0c;感…

作者头像 李华
网站建设 2026/9/3 20:55:44

python的图论工业场景模拟第五十九篇:动态图演化与拓扑鲁棒性时间序列追踪,任务:模拟网络在50个时间步内随机增删边,追踪连通度与效率的时间序列,图建模说明:动态时序图,边集动态更新。

动态图演化与拓扑鲁棒性时间序列追踪&#xff1a;给工业网络做"心电图" "车间的工业网络像个活物——链路随时在变&#xff1a;新设备接入&#xff08;加边&#xff09;、光缆老化断开&#xff08;删边&#xff09;、无线备份临时顶上&#xff08;加边&#xff…

作者头像 李华
网站建设 2026/9/3 20:54:32

【AI 应用 -- 白话大模型(篇七)】《为 Agent 铸造时间记忆:缓存、持久存储与遗忘法则---辨析长短记忆,解析语义、情景与程序记忆,走完记忆完整生命周期,借助 Mem0 搭建智能体长效》

一.⼈⼯介⼊&#xff08;Human-in-the-Loop/HITL&#xff09; 1.什么是⼈⼯介⼊ a.HITL(⼈在回路&#xff09; Human-in-the-Loop 是指在 AI Agent 或⾃动化系统中引⼊⼈类的监督、⼲预或审批环节。当 Agent遇到不确定、⾼⻛险或需要判断的任务时&#xff0c;会主动暂停、向⼈…

作者头像 李华