尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32F030驱动MT6816磁编码器:SPI时序、零点标定与双编码器实现

STM32F030驱动MT6816磁编码器:SPI时序、零点标定与双编码器实现 简介这套资源围绕STM32F030读取MT6816磁编码器输出、计算绝对角度这一嵌入式任务展开适合电机控制、机器人定位、转台测角等需要高精度角度检测的开发者参考。压缩包共2个文件1个C语言源代码文件包含底层通信、数据解析和角度换算等核心实现1份PDF芯片手册说明MT6816的工作原理、接口定义、电气参数与应用注意事项资源整体约605KB。已有3959人学习。借助代码与手册开发者可掌握STM32F030的GPIO初始化、定时器输入捕获和中断服务程序编写方法理解磁编码器A/B/Z相信号的捕获与识别逻辑并在实际项目中应用滤波、误差校验等提高测量稳定性的技巧。资料虽精简但涵盖了从芯片配置到角度输出的完整流程尤其适合有一定STM32基础、希望快速落地磁编码器驱动模块的工程师。1. MT6816 与 STM32F030 搭出磁编码器的第一块跳板用 STM32F030 驱动 MT6816 磁编码器一小时拿到可用的角度数据不是难事但多数移植项目把时间耗在 SPI 时序和零点标定上。这颗芯片把磁场矢量运算封装在内部直接输出 16 位绝对角度码程序要解决的是怎么把数字可靠搬回来而不是在 M0 核上重新实现反正切。适合的场景包括 FOC 驱动器、机械臂关节、旧增量编码器替换以及双磁编码器测电机位置这类需要高角度分辨率的场合。下面按实际项目里推进的顺序从输出语义讲到驱动实现再到标定、双片读取和验证手段代码可以直接落到工程里改。2. MT6816 的角度输出语义与 SPI 读取时序2.1 芯片内部已经完成的反正切计算MT6816 内部是正交磁阻/霍尔结构旋转磁钢让芯片平面的磁场矢量转动芯片把正交分量做反正切运算直接输出 16 位绝对角度码。外部程序不需要在 STM32F030 里再写 atan2Cortex-M0 核没有 FPU浮点反正切会拖慢控制周期而且完全没有必要芯片已经把这一步做死在内部。程序编写的大部分工作因此落在时序、标定和角度语义转换上。很多第一次接触磁编码器的人把它当成旋变解码芯片或光栅细分电路来用这是惯性误区。MT6816 的职责是把「磁场角度到数字」这步封装好单片机要做的是把这个数字无损搬走。多极电机里还有一个换算关系要提前想清楚编码器磁钢装在轴端时芯片读到的是轴的真实机械角FOC 电流环需要的电角度等于机械角乘以极对数这个乘法放在控制器层做但前提是先拿到符号一致、原点确定的机械角。2.2 16 位绝对角度码与零点偏移语义MT6816 输出范围是 0 到 65535均匀覆盖 360°每个 LSB 对应 360 除以 65536约 0.0055°。这个分辨率对电机位置环完全够用但要注意它输出的是绝对角度不是增量脉冲。磁钢静止时不管上电多少次读回来的都是同一个码这个特性在替换增量编码器时最有用系统启动瞬间就能拿到位置不需要找零点。绝对角度码的原点是芯片出厂时定的和机械零位没有任何关系。替换旧设备时最常见的问题也出在这里光电增量编码器有 Z 相脉冲做机械基准程序依赖回零动作MT6816 没有这个机制必须自己在机械零位处采一次读数保存为 offset。这个标定流程在整个方案里的地位不亚于驱动本身第 4 章会给出具体实现。还有一个语义必须提前理解0 和 65535 是相邻值。轴承正转经过零位时读数从 65535 跳到 0不是芯片故障。如果程序里直接做无符号减法跨零瞬间会算出一个接近 65535 的伪速度我在 3.3 节给出用 int16 差值处理回绕的办法。2.3 SPI 帧格式与 CPOL/CPHA 的确认方法MT6816 的 SPI 读时序属于典型的片选低电平期间发指令、回数据帧结构。常见做法是拉低 CS主机发出读命令沿 SCK 从 MISO 拿回 16 位数据最后拉高 CS。SCK 空闲电平和采样沿在不同批次手册里画法不完全一样移植现成驱动时不能只改引脚就上板必须先核对时序。我一般会在初始化 SPI 之前先用逻辑分析仪抓一组真实波形确认三件事SCK 空闲电平是高还是低、MISO 数据变化沿和采样沿哪个先出现、CS 低电平期间一共有多少个 SCK 沿。MT6816 与另一颗常用的 AS5047P 在编程上有几个容易踩的差异点列在下面。对比项MT6816AS5047P标称分辨率16 bit14 bit返回数据有效位16 bit 角度码高 14 位为角度码角度数据流水线上电首帧通常无效需连续读两帧取后帧存在延迟帧手册同样要求处理首帧寄存器体系专用角度寄存器为主含诊断、设置寄存器组移植时特别要注意分辨率差异AS5047P 的 14 位角度数据放到 16 位变量后最低 2 位固定为 0 或无效直接按 16 位换算会整体偏小需要先把数据对齐到统一坐标系。MT6816 是完整 16 位输出少这一步但返回帧里的字节序仍然要在代码里做成可调项。这类磁角度芯片普遍有一个隐藏要求命令发出后对应数据通常要到下一帧才完整出现在 MISO 上所以连续读两帧、取后一帧是更可靠的姿势或者在上电初始化后主动丢弃第一个读取结果。这个习惯保留在驱动里能省掉很多排查时间。3. STM32F030 上的 MT6816 角度程序编写3.1 F030 的资源边界与接线STM32F030 主频 48MHzCortex-M0 核没有硬件除法器。对磁编码器程序而言这个资源不算紧张因为驱动层只是把角度码从 SPI 搬回来真正的运算在后续的 FOC 或位置环里。我建议把读角度做成很薄的驱动层对外输出原始 uint16不在驱动层做浮点角度换算避免把 M0 的软浮点开销带进控制环。接线按 SPI1 默认引脚映射就能用PA5 做 SCKPA6 做 MISOPA7 做 MOSICS 用一颗普通 GPIO。不要启用芯片的硬件 NSS手动拉 CS 有两个实际好处一是 F030 的硬件 NSS 在 master 模式下行为复杂容易在字节之间自动翻转把 MT6816 的一帧切碎二是手动控制片选可以精确管理帧边界后面挂双磁编码器时轮询不同 CS 的思路是一致的。信号STM32F030MT6816SCLKPA5SCLKMISOPA6SOMOSIPA7SICSPA4按需调整CSN地GNDGNDMOSI 这路只读角度时也可以不接但保留它更稳妥因为初始化阶段芯片需要接收命令字节。供电上注意 MT6816 的工作电压范围如果板子上有 5V 器件电平必须在进芯片前做转换不能直接共线。3.2 裸机读取程序的最小实现下面这段代码是可运行的最小驱动HAL 库只做寄存器封装CS 全程由 GPIO 控制。#include main.h #include stdint.h #define MT6816_CS_PORT GPIOA #define MT6816_CS_PIN GPIO_PIN_4 /* 读帧长度首字节作为命令/哑字节后两字节为角度数据。 * 如果确认手册是固定 16 SCK 的单帧格式把长度改成 2 * 同时调整 raw 的取数位置。 */ #define MT6816_FRAME_LEN 3u static SPI_HandleTypeDef hspi1; void MT6816_Init(void) { /* CS 默认拉高保证芯片不在片选状态 */ HAL_GPIO_WritePin(MT6816_CS_PORT, MT6816_CS_PIN, GPIO_PIN_SET); hspi1.Instance SPI1; hspi1.Init.Mode SPI_MODE_MASTER; hspi1.Init.Direction SPI_DIRECTION_2LINES; hspi1.Init.DataSize SPI_DATASIZE_8BIT; /* CPOL/CPHA 以手册时序图为准逻辑分析仪抓完再定 */ hspi1.Init.CLKPolarity SPI_POLARITY_LOW; hspi1.Init.CLKPhase SPI_PHASE_1EDGE; hspi1.Init.NSS SPI_NSS_SOFT; /* PCLK 48MHz / 16 3MHz调试阶段先用低速 */ hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_16; hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; HAL_SPI_Init(hspi1); } uint16_t MT6816_ReadRaw(void) { uint8_t tx[MT6816_FRAME_LEN] {0x00, 0x00, 0x00}; uint8_t rx[MT6816_FRAME_LEN] {0}; uint16_t raw; HAL_GPIO_WritePin(MT6816_CS_PORT, MT6816_CS_PIN, GPIO_PIN_RESET); /* 一次收发完整帧不要把一帧拆成多次传输 */ HAL_SPI_TransmitReceive(hspi1, tx, rx, MT6816_FRAME_LEN, HAL_MAX_DELAY); HAL_GPIO_WritePin(MT6816_CS_PORT, MT6816_CS_PIN, GPIO_PIN_SET); /* 返回帧第 1、2 字节为角度数据字节序按手册核对 */ raw ((uint16_t)rx[1] 8) | rx[2]; return raw; } float MT6816_AngleDeg(uint16_t raw) { return 360.0f * (float)raw / 65536.0f; }逻辑说明CS 拉低之前芯片处于非选中态拉低后必须由主机持续提供 SCK从机的 MISO 才会把数据移出来。这里用 TransmitReceive 同时发命令和收数据而不是单独调 Receive因为从机没有外部时钟就不会主动输出数据。帧长度宏设成 3 字节首字节是命令或哑字节后两字节承载角度。波特率预分频 16 对应 3MHz这是调试阶段的保守值确认时序无误后再往上提。还有一点调试打印角度时用浮点没问题但量产代码里尽量避免在控制周期内调用浮点转换M0 的软浮点库开销不小。把 raw 直接传给控制环换算放到上位机或只在日志里做。3.3 用 int16 差值处理零位回绕在 1ms 定时器里轮询 MT6816 时最常用的是相邻帧差值求速度。直接写 now - prev在 C 语言里是 int 运算跨零时 0 减 65535 得到负六万五明显异常。把差值强制成 int16 即可自动修正uint16_t prev 0; void Timer_1ms_Callback(void) { uint16_t now MT6816_ReadRaw(); int16_t delta (int16_t)(now - prev); prev now; /* delta 0 正转delta 0 反转单位是 1 LSB */ }当 now 等于 0、prev 等于 65535 时两者相减的低 16 位是 1int16 解释为 1正好对应正向跨过一个 LSB反向跨零同理得到 -1。速度计算因此不需要任何 if 判断这是磁编码器驱动里最值得保留的一行代码。轮询间隔我通常用 1msF030 的定时器资源足够。如果 CPU 紧张改到 5ms 也可以MT6816 的数据寄存器实时刷新不存在保持时间不够的问题。反过来要注意的是 CS 拉低到拉高的时间窗尽量短避免在磁场快速变化时读到的高字节和低字节来自两次不同位置的采样造成角度跳变。4. MT6816 零点标定与双磁编码器位置测量4.1 标定零点的环形均值写法零点标定的目的一致把机械零位对应的芯片读数固化到 Flash运行后用当前读数减去 offset得到相对机械零位的角度。增量编码器靠 Z 脉冲找零MT6816 没有这个脉冲offset 就是它的 Z 脉冲只是由软件维护。标定流程通常分三步把电机或转轴锁在机械零位进入标定模式连续采样写 Flash。采样时要小心芯片读数本身在零位附近抖动直接取平均会在 65535 和 0 之间来回跳普通求和平均会得到 32768 这样完全错误的结果。用环形均值可以避开回绕uint16_t MT6816_CalibrateZero(void) { uint16_t first MT6816_ReadRaw(); /* 基准帧 */ int32_t sum 0; for (uint8_t i 0; i 99; i) { /* 与基准做有符号差值跨零时差值为 1 / -1 */ sum (int16_t)(MT6816_ReadRaw() - first); } /* 均值加回基准再截断回 uint16 空间 */ return (uint16_t)(first sum / 99); }环形均值的核心是先把每个采样点转换成相对基准的有符号偏移偏移量做完平均后再加回基准。即使采样期间读数在 65535 和 0 之间摆动每个偏移量也只是正负几个 LSB不会被平均算法放大。offset 写入 Flash 时按 STM32F030 的页大小整页擦写建议单独占一页避免频繁擦写影响寿命。4.2 偏心引入的一次谐波与 12 点标定表磁编码器安装总会有偏心磁铁旋转中心和芯片感光中心不重合角度误差会呈现近似正弦形状一圈一个波峰称为一次谐波误差。这个误差在替换旧编码器后最容易暴露位置环低速运行时你会看到周期性的速度波动频率和转速一致幅值可能到 0.5° 甚至 2°。提示一次谐波误差用软件标定可以压到 0.1° 以内但前提是磁铁本身充磁均匀、贴装端面平整。误差曲线出现明显二次谐波时优先查机械装配不要指望软件全补。常见的补偿做法是 12 点标定每 30° 一个点用转台或光栅基准记录芯片读数和真实角度把差值存成表运行时线性插值。表结构如下。字段类型含义ref_rawuint16_t标定台给出的基准角度编码mt6816_rawuint16_t芯片实际读数err_q8int16_t误差定点化单位约 0.005°运行时先找到当前 raw 落在哪两个标定点之间按比例插值出误差值再从读数里减去。标定表用 const 数组放 Flash不占 RAM。这里要注意的是插值和零点 offset 的顺序先减 offset 得到相对角度再查偏心误差表做补偿两者不要混在一个变量里算。4.3 双磁编码器测电机位置时的 SPI 片选轮询双磁编码器测电机位置的典型场景是电机端和负载端各装一个 MT6816电机端读转子角度负载端读减速器输出角度两个角度相减得到扭转量可以实时估计减速器刚度或输出侧位置滞后。两块芯片可以共用同一条 SPI 总线SCK、MOSI、MISO 并联CS 分别接两个 GPIO这样只需要一份 SPI 外设资源。/* 片选参数化两个编码器共用一条 SPI 总线 */ #define CS_MOTOR_PORT GPIOA #define CS_MOTOR_PIN GPIO_PIN_3 #define CS_LOAD_PORT GPIOA #define CS_LOAD_PIN GPIO_PIN_2 uint16_t MT6816_ReadWithCS(GPIO_TypeDef* port, uint16_t pin) { uint8_t tx[3] {0x00, 0x00, 0x00}; uint8_t rx[3] {0}; uint16_t raw; HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET); HAL_SPI_TransmitReceive(hspi1, tx, rx, 3, HAL_MAX_DELAY); HAL_GPIO_WritePin(port, pin, GPIO_PIN_SET); raw ((uint16_t)rx[1] 8) | rx[2]; return raw; } void DualEncoder_Read(void) { uint16_t motor_angle MT6816_ReadWithCS(CS_MOTOR_PORT, CS_MOTOR_PIN); uint16_t load_angle MT6816_ReadWithCS(CS_LOAD_PORT, CS_LOAD_PIN); int16_t twist (int16_t)(load_angle - motor_angle); /* twist 就是电机端到负载端的角度滞后 */ }参数说明片选引脚被传进读取函数而不是写死成全局宏这是挂双编码器时最值得做的一个结构调整。read 函数内部先拉低目标片选完成一帧传输后立刻拉高保证同一时刻只有一个芯片的 MISO 在驱动总线。两块芯片的 MISO 是推挽输出如果同时拉低 CS两路输出会打架轻则读数错乱重则损伤引脚。切换片选时有一个细节上一片 CS 拉高后MISO 需要一点时间释放下一片才能安全驱动总线所以在两个读取之间加一个 1us 左右的短延时。48MHz 主频下大约 48 个 NOP也可以用定时器计一个微秒。延时太短会偶发读到上一个芯片的残留位延时太长会挤压控制周期这个只要在逻辑分析仪上看一眼 MISO 切换波形就能定准。5. 不信任读数时验证 MT6816 数据的几个手段5.1 匀速旋转读回绕波形把手柄或电机轴匀速转一圈串口按无符号格式打印 raw。正常波形应该是一条从 0 到 65535 的单增长斜坡跨越零位时直接跳回 0中间没有任何阶梯。如果看到 0、128、256 这类楼梯状递增说明字节序取反了只有高字节在变化把 rx[1] 和 rx[2] 对调即可。如果读数在 65535 和 0 之间来回反复检查方向判断逻辑不要动驱动。5.2 静止抖动判断信号质量把轴锁死连续读 100 次看原始数据抖动。3 个 LSB 以内属于正常对应约 0.016° 的电气噪声抖动超过 10 个 LSB先查电源纹波、地线回路和 CS 线走线再考虑滤波。很多人在这个阶段直接上低通滤波把抖动抹平但根源不解决换一台设备或温度一变化问题会换个形式冒出来。5.3 用 PWM 输出做交叉验证MT6816 支持通过 PWM 方式输出角度把 PWM 引脚接到 F030 的定时器输入捕获通道解出占空比后换算角度和 SPI 读数放到同一张图对比。两种方式都是芯片内部同一个角度源导出的理论上偏差不会超过 0.5°。如果 PWM 正常而 SPI 数据乱问题锁死在 SPI 帧格式或时序上如果两种方式都偏回头看磁钢安装和偏心标定。这个交叉验证方法比反复改代码更省时间也是我每次换芯片型号时第一个会做的对比实验。把 SPI 读数和 PWM 解算值同时打到串口绘图工具里偏差超过 0.5° 就去查磁铁安装而不是继续调滤波参数。本文还有配套的精品资源点击获取
返回列表