2026/10/4 1:07:33

STM32 SPI读取IC-MU磁绝对值编码器多圈位置及调试经验

STM32 SPI读取IC-MU磁绝对值编码器多圈位置及调试经验 做绝对值编码器通讯这件事最坑的往往不是协议本身而是你以为读对了、实际却在临界状态反复横跳。IC-MU这颗磁绝对值编码器前前后后我在STM32平台上调了两周从SPI时序到码盘数据拼接踩了不少坑今天把这套完整方案理清楚希望对正在搞伺服关节、云台回馈、数控分度头或者自动化设备位置闭环的朋友有参考价值。这篇文章的核心是ST32如何通过SPI读取IC-MU磁绝对值编码器的多圈位置数据并对原始码盘数据进行零点校准、方向归一化、滤波和单位换算既有硬件连线、协议帧解析也有我实际调试中被坑出来的经验。1. 磁绝对值编码器与IC-MU核心认知1.1 IC-MU为什么值得用SPI去读先明确一个概念什么叫“绝对值编码器”。增量式编码器上电之后不知道自己在哪必须回零点或者靠外部传感器标定绝对值编码器不一样每个机械位置都对应唯一一组数字输出断电再上电位置信息还在。IC-MU这类磁绝对值编码器内部通过磁阻或霍尔阵列感应旋转磁铁的磁场方向换算成绝对角度再叠加多圈计数输出的是“圈数圈内角度”的组合数据。相比光电绝对值编码器磁编码器对油污、粉尘和振动更耐受安装尺寸也紧凑中空轴结构可以直接套在电机轴或旋转轴上非常适合做关节模组和分度装置。IC-MU的通信接口一般是SPI也有SSI和BiSS等派生型号具体看批次和定制型号。我手上这颗IC-MU是带SPI引脚的版本单圈分辨率可以配置到十几位多圈范围通常支持几十到上百圈具体圈数看内部存储结构。SPI接口的好处是引脚少、速率高、时序可控性强在STM32上可以直接用硬件SPI外设驱动不需要额外的ULC脉冲计数通道。也就是说读它不需要专用编码器接口普通SPI引脚就能干活这是项目选型时非常划算的一点。1.2 应用场景和选型时的几个判断点IC-MU比较常见的使用场景是机器人关节内部做电机输出轴的位置反馈、云台俯仰和偏航轴的角度闭环、数控分度盘或者旋转工作台的角度定位还有打印设备和自动化产线上的旋转机构。共同特点是需要断电记忆位置对机械安装空间有限制同时希望省掉编码器电池或后备电源。选型时容易忽略的几个点单圈位数直接决定角度分辨率比如14位对应360/16384≈0.022度16位对应0.0055度选高了数据量变大选低了精度不够。多圈圈数是靠内部非易失存储还是靠磁传感累积会影响断电瞬间的位置保持能力如果驱动器频繁断电上电这一点特别关键。接口形式决定后续电路复杂度SPI最容易接STM32SSI需要额外时钟换向逻辑BiSS则需要更严格的主站时序。机械同轴度要求让很多项目翻车磁铁旋转中心和编码器感应中心偏离太大读数就会出现周期性跳动。我这边最终选定SPI版本是因为STM32的SPI外设足够成熟CubeMX配置快而且后续做多圈数据拼接不需要额外硬件代码里可以直接组合出32位绝对位置省事。2. SPI通讯方案设计2.1 IC-MU的SPI协议要点在动手写代码之前一定要先把数据手册里的SPI帧结构吃透。IC-MU的SPI通常工作在从机模式主机STM32发起读操作。引脚定义上一般是四根线SCK时钟、MOSI主机输出从机输入、MISO主机输入从机输出再加上片选CSB低电平有效。部分芯片还引出DIR方向控制脚、零位信号脚或者EEPROM写保护脚具体以丝印和数据手册为准。SPI模式方面不同批次IC-MU支持的时钟极性和相位会有差异常见的是Mode 1CPOL0, CPHA1或Mode 3CPOL1, CPHA1也就是数据在SCK的第二个边沿采样。不要想当然按最常用的Mode 0去配动手之前拿逻辑分析仪抓一下芯片输出的数据或者直接看手册里时序图标注的采样沿这是最稳妥的做法。时钟频率方面IC-MU这类磁编码器芯片内部通常是移位寄存器结构支持的最高SCK频率一般在几MHz到十几MHz之间但要注意实际项目里SPI时钟频率越高对PCB走线、线缆长度和接触可靠性的要求越高。我实际使用中比较保守SCK配的是2MHz左右数据稳定性和电磁兼容性都好很多。SPI读取的数据格式通常包含状态位、圈数和角度位置三部分。以常见帧格式为例主机发送一个读命令字节或若干个时钟脉冲后从机在MISO上依次移出状态字、圈数数据、位置数据。数据位宽有的是24位、有的是32位还有的带CRC校验位。一定不要只按“读到的前两个字节就是角度”来理解需要把帧对齐到片选信号的下降沿和上升沿。2.2 STM32 SPI外设的配置思路STM32端我用的是STM32F405SPI1挂在APB2总线上时钟84MHz选择2MHz左右的SCK需要分频系数42左右实际配成32分频得到2.625MHz或者配成64分频得到1.3125MHz都可以接受。配置流程直接用STM32CubeMX选择SPI1设置为Full-Duplex Master模式。配置SCK、MOSI、MISO引脚复用功能选AF5。数据帧长度根据IC-MU数据位宽选择8位或16位如果帧格式不是标准的整字节倍数建议用8位模式配合软件移位拼接。时钟极性CPOL和相位CPHA按IC-MU手册选择我的配置是CPOLHigh、CPHA2Edge即Mode 3。NSS引脚选择Disable片选用普通GPIO控制这样拉低拉高的时机完全由代码控制。这里强调一下软件片选和硬件NSS的区别。STM32的硬件NSS在主模式下可以自动管理片选但自动片选时机和从机芯片的数据准备时间未必匹配容易出现CS拉低后立刻发时钟、从机还没准备好的问题。IC-MU这类从机芯片要求片选拉低后再等待一个小的延时往往是几十纳秒到几个微秒然后才能开始正常的SCK时钟输出。用GPIO软件控制片选降沿后可以灵活加延时升沿时机也能精确控制所以我强烈建议项目里采用软件片选。初始化代码大致这样void IC_MU_SPI_Init(void) { GPIO_InitTypeDef gpio {0}; __HAL_RCC_SPI1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); // CSB --- PB6, 软件片选 gpio.Pin GPIO_PIN_6; gpio.Mode GPIO_MODE_OUTPUT_PP; gpio.Pull GPIO_PULLUP; gpio.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, gpio); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); // SCK --- PA5, MOSI --- PA7, MISO --- PA6 gpio.Pin GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; gpio.Mode GPIO_MODE_AF_PP; gpio.Pull GPIO_NOPULL; gpio.Speed GPIO_SPEED_FREQ_HIGH; gpio.Alternate GPIO_AF5_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; hspi1.Init.CLKPhase SPI_PHASE_2EDGE; hspi1.Init.NSS SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_32; hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; HAL_SPI_Init(hspi1); }这段代码走的是HAL库CubeMX生成工程后手动补充CSB的GPIO初始化即可。值得注意的坑是MISO引脚一定要配置成复用模式而不是输入模式如果MISO配置错了读回来的数据会一直停在某一电平表现就是所有数值都相同。2.3 数据帧解析与CRC校验SPI底层通信只是搬运原始字节真正容易出错的是帧解析环节。IC-MU的数据帧往往不是简单的一个角度值而是把状态位、圈数高位、圈数低位、角度高位、角度低位按特定顺序排列在一帧里。比如某个典型型号的数据帧是40位8位状态/命令回读 12位圈数 20位角度。不过在不同型号上位数分配差异很大有的把状态和CRC放在最后有的把CRC放在数据之前必须严格按手册定义移位拼接。我自己的做法是先按照一个结构体接收原始数据typedef struct { uint8_t status; uint16_t turns; uint32_t position; uint8_t crc; } IC_MU_Frame_t; IC_MU_Frame_t frame; uint8_t rxBuf[8];实际读取时主机先拉低CSB然后发送一个空字节或读命令同时接收从机返回的第一个字节接着继续发送时钟接收剩余字节接收完成后拉高CSB。整个过程可以用HAL的收发函数完成uint8_t txBuf[8] {0}; HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET); delay_us(10); HAL_SPI_TransmitReceive(hspi1, txBuf, rxBuf, 8, 100); delay_us(10); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);然后从rxBuf中解析frame.status rxBuf[0]; frame.turns ((uint16_t)rxBuf[1] 8) | rxBuf[2]; frame.position ((uint32_t)rxBuf[3] 16) | ((uint32_t)rxBuf[4] 8) | rxBuf[5]; frame.crc rxBuf[6];这里有几个容易踩的坑第一SPI是全双工通信发送和接收是同时进行的MOSI发送空字节的同时MISO返回数据这个空字节不要随便发成0xFF否则有些芯片会把这个字节当成命令最好发0x00或者按手册发读命令第二帧长度如果不是8的整数倍拼接时要注意位偏移不能简单按字节组合第三CSB拉低的提前量和拉高后的保持时间要保证否则数据容易整体移位一位或两位。关于CRC校验部分IC-MU型号支持CRC常见的是CRC-8多项式0x31x^8x^5x^41。如果芯片带CRC建议一定开启校验尤其是用于伺服闭环或者高可靠设备时。因为SPI在长线传输时偶尔会受到电机驱动器干扰数据跳变是客观存在的有CRC能在上层直接丢弃坏帧避免位置跳变引发设备误动作。CRC计算逻辑在STM32上可以按位写也可以用查表法100Hz的读取频率下按位计算的开销可以忽略不计。uint8_t icmu_crc8(uint8_t *data, uint8_t len) { uint8_t crc 0; for (uint8_t i 0; i len; i) { crc ^ data[i]; for (uint8_t bit 0; bit 8; bit) { if (crc 0x80) crc (crc 1) ^ 0x31; else crc 1; } } return crc; }校验不通过时我的策略是直接丢弃这一帧用上一帧数据继续运行同时计数坏帧超过阈值就报通信异常而不是把坏数据当成真实位置去执行控制逻辑。这个处理方式在电机闭环里非常重要可以避免位置突变导致电流冲击或者机构碰撞。3. 码盘数据处理核心实现3.1 零点校准和多圈数据拼接SPI读回来的是原始码盘数据不能直接拿来做位置控制第一件事是确定零点。机械装配时把设备转到需要作为零点的位置比如机器人的机械原点然后读取当前编码器值把这个值记为zero_offset。后面所有实际位置都用原始值减去zero_offset再进行归一化处理这样零点就和工作坐标对齐了。有些IC-MU型号支持通过SPI命令写入零位直接把芯片内部的圈数和角度寄存器清零。这类操作要谨慎因为零位一旦写入芯片后续拆装或机械变动时需要重新校零。我更推荐软件零位偏移法只在控制器里保存zero_offset不动芯片内部数据。这样即使机械位置调整了改一个参数就能重新标定不会把芯片锁死。多圈数据拼接是另一个核心点。单圈位置值会在0到最大值之间循环如果没有圈数信息转一圈后位置值无法区分和第一圈是否相同。IC-MU的多圈数据在帧里通常是独立字段比如12位圈数可以表示0到4095圈加上单圈角度就能组成一个完整的绝对位置。我习惯把圈数和角度组合成一个32位的位置量int32_t absolute_raw (int32_t)frame.turns * single_turn_resolution (int32_t)frame.position;其中single_turn_resolution是单圈位置最大值加1比如14位编码器就是16384。这里有一个非常容易错的地方如果圈数递增的时刻和单圈位置回零时刻在逻辑上不完全同步拼接后的绝对位置会在过零点附近出现突跳。解决办法是先将角度从最大值回0那一瞬的圈数变化对齐。实际调试时我在过零附近连续打印转过的绝对位置如果发现有一个台阶跳变就去检查圈数字段是在角度达到最大值之前变化还是之后变化然后针对性地在拼接公式里加一个修正量。拼接完成后的绝对位置还要注意符号问题。如果机械运动方向是双向的也就是可以正转和反转圈数数据一般是无符号的。通过零点标定操作需要把某个机械方向映射为正向具体做法在下一节说明。3.2 数据滤波、跳变处理和方向归一化磁编码器的原始数据在高精度应用里往往有小幅抖动体现为静止时位置值在几个LSB之间跳动。这种抖动在位置环里会被放大成速度噪声。解决思路是区分“静止抖动”和“真实微小运动”。静止时可以用滑动均值滤波降低噪声但如果设备确实在低速运动过重的滤波会造成位置延迟相位裕度变差。我的经验是分两级处理第一级用滑动窗口均值窗口长度根据SPI读取速率动态调整。比如读取频率200Hz时窗口取5个点延迟约25ms在多数云台和关节应用里可以接受第二级做死区判断采集到的原始位置和目标位置的差值小于设定阈值时输出保持上一次的值大于阈值时直接输出当前值。这个死区阈值实际上就是速度环的死区相当于把噪声挡在控制器外面同时不阻碍真正的运动。跳变处理主要针对数据帧错误或者过零翻转。过零翻转的意思是单圈角度从最大值回到0如果代码里直接用后一帧减前一帧正常情况下结果是负的或者一个巨大的正向差值。正确做法是int32_t delta new_pos - old_pos; if (delta half_range) delta - full_range; else if (delta -half_range) delta full_range;这样即使位置绕过了0点delta依然能反映真实位移方向和大小。这个技巧在做速度和增量位移计算时几乎必须使用否则转一圈就会误报一次转速突变。方向归一化则是把机械方向映射到业务逻辑上。假设编码器顺时针读数增大而你的应用希望顺时针对应正向那什么都不用做如果希望顺时针是负向可以在输出位置时取反或者用“full_range - 原始位置”的方式取镜像。不要在底层反复改方向标志而是在统一出口做一次方向变换否则后续所有标定都会乱掉。3.3 单位换算和实际位置输出码盘最终要输出的是工程单位的值比如角度、圈数或者直线位移。如果是角度换算公式很简单float angle_deg (float)position_after_zero * 360.0f / (float)single_turn_resolution; float angle_rad angle_deg * 0.0174532925f;如果是累计圈数带小数比如要做丝杠位置换算就需要把多圈位置转换为线位移float linear_pos_mm ((float)turns (float)position / (float)single_turn_resolution) * lead_mm;这个公式里lead_mm是丝杠导程也就是电机转一圈平台移动的距离。注意turns要先对零点后的圈数偏移做归一化否则零点位置不对算出来的线位移整个偏移。输出位置时我一般使用double计算中间结果最终输出根据控制周期要求做类型转换。很多控制器只需要整数脉冲或整数角度直接用float可能导致分辨率和精度下降尤其换算累计圈数超过几千圈时float的24位有效精度在超过1677万之后就开始丢失低位。如果项目范围很大建议用int64保存原始累加位置只在显示或者协议输出时转成float。另外一个细节是如果你通过SPI读到的位置值最后几位稳定跳动不要盲目加大滤波。先用示波器或者上位机查看原始值的分布如果跳动是因为磁铁和芯片旋转中心偏移带来的周期性误差滤波根本滤不掉必须返回去调整机械安装把磁铁和编码器芯片的同轴度控制在公差范围内。4. 从CubeMX配置到整机联调的完整实操4.1 CubeMX工程快速搭建步骤搭建工程我用的是STM32CubeMX Keil MDKF405也兼容F103的思路主要区别在时钟树和外设时钟频率。步骤按顺序来第一步选择MCU型号先在System Core里配置RCCHSE选择Crystal/Ceramic Resonator方便后面跑精确波特率。第二步配置时钟树把APB2外设时钟设为84MHz左右SPI1的输入时钟就看这里。第三步SPI1模式选择Full-Duplex Master参数按IC-MU手册来波特率预分频按照输入时钟除以希望得到的SCK频率选择。第四步配置CSB片选引脚为GPIO_Output初始电平设为高因为从机片选低有效。第五步在Project Manager中生成工程工具链选MDK-ARM。生成之后一定要检查MISO引脚是不是复用模式CubeMX自动生成的代码有时会把MISO设置成普通输入导致SPI通信失败或者只在第一次读取时正常。我遇到过一次CubeMX版本生成的GPIO初始化把MISO配成了Analog模式调试了半天才发现数据线根本没接到外设上。初始化代码在手写SPI配置时已经给出来了如果CubeMX生成过一遍直接在用户代码区补充片选控制和读写函数就行。HAL库提供的HAL_SPI_TransmitReceive是阻塞型的适合读取频率不高的场合如果控制周期要求1kHz以上可以考虑用中断或DMA。但就IC-MU这种编码器而言SPI传输几微秒就能完成阻塞方式在绝大多数应用里已经足够。4.2 用逻辑分析仪和示波器排查时序问题SPI项目调试逻辑分析仪是刚需。我用的是24MHz采样的逻辑分析仪连接CSB、SCK、MISO和MOSI四根线在读取函数里打断点或者连续轮询抓取完整的一帧时序然后对照手册看数据对齐关系。常见时序问题大致有几类。第一类是CPOL/CPHA不匹配表现为读回来的数据要么全部是0xFF要么全部是0x00或者数据位整体错开一位变成乱码。这个通过逻辑分析仪一眼就能看出来SCK空闲电平和数据变化沿的位置对不对一目了然。第二类是CSB拉低时间太短从机还没准备好主机就开始发时钟最前面的几个数据位会丢失。第三类是SCK频率过高从机输出数据跟不上表现为数据高电平幅度不够或者MISO数据边沿严重变缓示波器上可以看到梯形波而不是方波。调试时我的节奏是先用最低分频比如256分频让通信稳稳跑通逻辑分析仪确认数据解析正确后再把分频逐步调低每调一档都要连续采集几千帧数据看是否有偶发错误。用这个方法我最后把SCK稳定在了2MHz左右再高就会出现偶发CRC错误。另外一个小技巧调试阶段可以在解析函数里实时统计CRC错误次数在调试串口上周期性打印。如果错误次数随电机运转速度升高而增加大概率是机械干扰或者供电纹波问题不是SPI配置问题。这时候不要在软件里无止境地滤波优先检查编码器供电是否干净、线缆屏蔽层是否良好接地、电机动力线和编码器信号线是否分开布线。4.3 机械安装与电气布局的现场心得IC-MU磁编码器是一颗灵敏度很高的器件磁铁的安装质量直接决定数据质量。磁铁和芯片之间的距离太远磁场强度不够输出噪声变大距离太近可能超出芯片的线性感应范围导致角度计算失真。实际安装时我一般先用卡尺测量轴端和芯片表面的间隙对照手册推荐的安装气隙范围调整到中值附近。同轴度问题更隐蔽。如果磁铁旋转中心和编码器芯片中心不同心读出的单圈角度会出现一次正弦波动也就是转一圈数据误差明显呈现周期性。这个误差很难靠软件完全修复因为它不是固定偏移而是随角度变化。现场处理办法是先不固定编码器电路板让电机慢速旋转同时上位机看原始位置输出是否平滑如果输出有周期性波峰波谷微调电路板位置让波形变平。这个过程听起来原始但比任何数据处理都好使。电气布局上编码器的电源引脚建议加一个0.1uF陶瓷电容和一个10uF电解电容靠近芯片引脚放有条件的话在供电入口加磁珠或LC滤波可以显著降低电机启停时的电压跌落。信号线建议采用双绞线或者屏蔽线屏蔽层单端接地。SCK和MISO不要长距离并行走线否则SCK的边沿会耦合到MISO上把数据带坏。我的经验是30厘米以内的短线普通杜邦线就能正常工作超过50厘米最好换屏蔽线并降低SPI速率。5. 常见问题排查技巧实录5.1 一台样机调试中遇到的典型问题把这段时间碰到的问题整理成一个速查表方便大家对照排查现象可能原因排查方法读到的数据始终是0xFFMISO没接对芯片没供电CSB没拉低万用表测引脚电平逻辑分析仪看CSB是否有效读到的数据始终是0x00MISO被拉低或者SPI配置成发送模式没切到接收检查MISO初始化和SPI方向配置数据有值但整体错位一位SPI时钟极性和相位配置和手册不一致对照逻辑分析仪逐个bit核对采样沿单圈数据跳动几十个LSB磁铁与芯片间隙过大、电机干扰、供电纹波大调整间隙示波器看电源纹波检查屏蔽转一圈数据出现正弦波动磁铁和芯片不同轴旋转时连续采集位置微调安装同轴度断电再上电后多圈圈数不对芯片内部保存圈数需要额外命令或供电保持查看手册确认是否需要主动写EEPROM读取频率高时偶发CRC错误SCK过高、线缆过长、接触不良降低SPI分频缩短飞线可靠连接过零时位置跳变异常圈数和角度拼接逻辑对齐错误打印过零前后原始值修正拼接公式5.2 调试中总结的几条独家经验第一片选之后的起始延时不要省。IC-MU从CSB下降沿到数据准备好需要一段很短的时间如果主机立刻发时钟第一字节的数据往往不可靠。我实际加的是10微秒左右延时这个量级在高动态控制里也没有明显影响却能把通信可靠性提升一个档次。第二零点校准不要只做一次。机械结构经过一段时间的运行后联轴器或者轴套可能发生微小松动零位会漂移。如果设备对位置精度要求高建议每次上电后做一次“回零校验”比如让设备回到机械挡块位置读取此时编码器值是否和存储的零点一致。如果不一致要么提醒机械松了要么重新标定零点。第三滤波参数不要套死。同样的IC-MU用在转速缓慢的云台上和用在高速转台上滤波策略完全不同。低速应用可以加大窗口平滑高速应用必须减小窗口保证响应。更好的做法是把原始位置和解算速度分开位置环用长窗口滤波速度环用短窗口滤波两个环节各取所需。第四如果发现读到的圈数在正反转切换时偶尔会多一圈或少一圈先检查磁铁是否装反了极性。磁铁N/S装反后编码器依然能输出位置但方向判断会完全反掉拼接后的绝对位置在过零处容易出现圈数错误。这个坑非常隐蔽我在样机上踩过一次花了不少时间才定位到是磁铁方向的问题。第五SPI通信偶尔出一帧错数据并不可怕可怕的是把错数据当成真数据用进去。所有读取结果都要经过CRC校验或者合理性判断比如位置变化量超过电机物理上能达到的最大速度就认为是坏帧并丢弃。这个“合理性门限”比CRC还好用因为CRC只能检测字节错误检测不了机械逻辑上明显不合理的跳变。5.3 关于数据率与实时性的一点平衡建议编码器读取频率取决于控制回路带宽。做云台角度闭环200Hz足够做伺服电机FOC位置环最好1kHz以上如果只是做低速分度定位50Hz到100Hz也可以。不要盲目追求读得快IC-MU内部数据的更新率有限读取频率超过芯片本身的刷新率读到的只是重复旧值反而浪费CPU。我实际采用的方案是读取频率500HzSPI每次读取耗时几微秒CPU占用几乎可以忽略。数据在定时器中断里读取和解析通过全局变量共享给主循环和控制任务。500Hz对于多圈拼接和滤波来说都比较从容也不会让STM32负担过重。SPI传输本身是阻塞的如果你在高优先级中断里调用HAL_SPI_TransmitReceive注意这个函数会等待传输完成如果在中断里被更高优先级的中断频繁打断实际等待时间会拉长。这种情况下建议把SPI读取放在主循环或者中等优先级任务里或者改用DMA传输。DMA方式需要两个缓冲区一个发送零字节一个接收数据中断完成回调里再做解析逻辑上复杂一些但实时性更好。最后分享一点个人的体会。编码器调试这件事百分之七十的问题出在硬件连接和安装精度上剩下的百分之三十才轮到协议和软件。把机械间隙、同轴度、供电质量这三个基础打牢SPI通讯和码盘数据处理反而变得很顺手。如果一上来就遇到数据乱跳先别急着改滤波代码把示波器探头夹在MISO和电源上看一眼很多时候答案已经写在波形里了。IC-MU这款编码器和STM32的组合在成本、尺寸和性能上找到了一个很好的平衡点后续如果在项目里发现更刁钻的问题我会再补一篇后续记录。