STM32多圈绝对值编码器测试:从SSI数据解析到掉电记忆
2026/9/9 18:47:51 网站建设 项目流程

简介:面向STM32开发者的多圈绝对值编码器测试程序,基于串行同步接口SSI协议实现位置与圈数同步读取,适用于工业自动化、机器人定位、数控设备等需要精确角度检测的场景。整个工程以RAR压缩包形式发布,共计一百六十三个文件,其中四十六个C源文件与四十六个H头文件构成核心代码,配套Keil工程配置、编译生成的HEX与AXF、链接映射文件、调试配置文件等,压缩包大小仅为三点二兆字节,便于快速下载与导入开发环境。目前已有三千一百零四人学习。工程结构清晰,从初始化代码、SSI通信收发、编码器数据解析到错误处理与日志输出均有完整实现,并预留串口打印接口,可直接编译运行;开发者既能对照代码理解硬件时序,也能基于现有模块做功能扩展或平台移植,是掌握绝对式编码器及STM32外设应用的实用参考。 第一次把多圈绝对值编码器接到STM32开发板上,我习惯性地去找Z相脉冲中断,翻完整个接线表才发现根本没有这根信号线。这个小小的不适应,恰恰说明多圈绝对值编码器和增量编码器在工程思路上的分水岭:你要测的不再是频率和脉冲数,而是一份断电后依然记得自己转到第几圈的位置数据。

这篇文章记录的是我基于STM32给多圈绝对值编码器写测试程序的完整过程,覆盖的范围包括接口协议确认、硬件接线、数据解析、稳定性判断,以及圈数溢出、掉电记忆这些多圈器件特有的现象。文章里给的代码和排查思路,我按HAL库体系整理过,适合刚入手绝对值编码器、第一次用STM32读SSI或SPI数据的开发者参考。如果你需要给这类编码器做导入检验或者做产线工装,直接拿走改参数也能用。

1. 为什么测试多圈绝对值编码器需要单独写程序

很多做运动控制的工程师第一次接触绝对值编码器,潜意识里还是会沿用增量编码器的思路去测试:接上A相B相,开个定时器,数脉冲。这套方法放到多圈绝对值编码器上完全行不通,因为它的输出根本不是一个脉冲序列,而是一串带位置信息的数字量。

1.1 三种编码器的本质差异

先把概念理清楚。增量编码器输出的是相位差90度的两路脉冲,位置靠外部计数器累加得到,断电之后计数清零,重新上电必须找原点。单圈绝对值编码器可以直接读出当前角度,但这个角度只覆盖0到360度,一旦转出这一圈,它就不知道自己在第几圈了。多圈绝对值编码器在单圈绝对值的基础上,内部多维护了一套圈数记录,读出来的数据直接就是“第几圈加圈内角度”的完整绝对位置。

增量编码器单圈绝对值编码器多圈绝对值编码器
输出内容A/B脉冲,需计数一圈内的绝对角度圈数+一圈内的绝对角度
断电后位置丢失角度保留,圈数丢失圈数和角度都保留
上电是否找零需要同一圈内不需要不需要
典型场景电机测速、手轮转台、机械臂关节垂直升降轴、堆垛机、旋转分度台

我做设备调试时遇到过一种很典型的情况:设备装了垂直升降轴,用的增量编码器,每次断电重启都要先回到原点,否则位置等于零。后来换成了多圈绝对值编码器,上电直接读圈数和角度,设备省掉了一次回零动作,整个开机流程缩短了好几秒。这就是这类编码器的核心价值——位置数据本身就是完整的,不需要依赖外部计数。

1.2 测试程序到底要回答什么问题

既然器件本身的价值在“断电记忆”和“位置即得”,那测试程序就不能只把数据读出来看个热闹。我给自己定的测试目标是回答下面五个问题,少一个都不算测完:

  • 硬件链路是否通畅:时钟线、数据线、片选线的时序配合是否正确,能不能稳定读到数据。
  • 数据解析是否正确:多圈字段和单圈字段的拼接顺序对不对,掩码有没有做对。
  • 静态稳定性如何:转轴静止时,连续读取是否跳变,跳变幅度是否在分辨率以内。
  • 断电记忆是否生效:切断供电再上电,圈数和角度是否保持原值。
  • 方向定义是否一致:按安装方向正转时,读数应该增大还是减小,程序里需不需要做方向取反。

后面所有章节的内容,本质上都是在回答这五个问题。带着这些目标去写测试程序,就不会写完一段读取代码就以为万事大吉。

2. 动手前先把接口协议和数据格式敲定

这块是测试程序最容易翻车的地方。很多人拿到编码器就急着接STM32,结果读回来的数据要么是全零,要么是乱跳,排查半天发现是协议没搞对。

2.1 SSI、SPI还是RS485,先分清接口类型

多圈绝对值编码器常见的数字接口有三种:SSI、SPI、RS485。它们用起来差别很大,测试程序的写法也完全不同。

SSI是工业绝对值编码器最常见的接口。主机发一串时钟脉冲,从机在时钟驱动下把数据位同步输出,一次性把整包数据推完。它的数据位长比较随意,13位、17位、25位、29位都有,完全由具体型号决定。SPI接口的读取逻辑和SSI类似,但工业编码器上的SPI时序不一定和STM32标准SPI外设完全一致,片选信号的行为、时钟空闲电平、数据采样边沿都需要对着数据手册确认。RS485接口多走Modbus RTU协议,主机发读寄存器帧,从机回数据帧,测试程序的核心就变成了串口收发和CRC16校验,而不是SPI时序。

我在选型阶段一般先看编码器标签上的接口标识,再翻数据手册的时序图。SSI和SPI容易搞混,因为它们都是主机提供时钟、从机吐数据。最简单的区分办法是看手册里数据位长:如果写的是“25位SSI”,那就是连续时钟输出一整包;如果写的是“SPI兼容”,通常意味着可以按字节读,但要注意片选和时钟极性的参数。

2.2 数据位长和拼接顺序决定了解析代码怎么写

以常见的配置举个例子:单圈分辨率17位,多圈12位,总数据位长就是29位。意思是单圈角度被量化成2^17等于131072份,圈数范围是2^12等于4096圈。读取到的原始数据里,有一段代表圈数,有一段代表角度,测试程序要做的就是正确地把这两个字段拆出来。

不同厂家的字段排列顺序不一样,最常见的是多圈在前、单圈在后,但也有一小部分厂家反过来。判断方法很土但很有效:用手缓慢旋转编码器轴,观察读回来哪个字段在变。如果转一圈,圈数位没有任何变化,而低位角度在走满整个范围,那顺序就没搞错。反过来,如果变化量出现在高位的圈数字段,说明拼接顺序反了。

代码里务必用掩码把圈数和角度分离开,不要直接把整个uint32_t当作位置值去用。两个字段一起变化的时候,不做掩码很容易解读出莫名其妙的数字。

2.3 电平、差分与接线的几个硬性注意点

编码器供电常见5V,STM32的IO是3.3V,信号电平能不能直接对接,要看编码器的输出形式。推挽输出的3.3V兼容型号可以直接接,集电极开路输出的需要外部上拉,5V TTL输出则要考虑分压或电平转换。

另一个容易踩的坑是差分信号。一部分工业SSI编码器为了抗干扰,数据线走RS422差分输出,A+和A-两个引脚。这种编码器不能直接接到STM32的GPIO上,中间必须加差分接收芯片,比如AM26LV32这类TTL转换芯片。如果手头只有单端输入的MCU,硬接的结果就是数据完全读不到。

接线时还有一个经验:编码器供电不要和电机驱动共用一个电源,尤其是测试阶段,电机一启动,电源毛刺就可能让读到的数值跳一个分辨率。编码器线的屏蔽层建议单端接地,测试用的杜邦线尽量短,线越长,数据线受到干扰的概率越大。

3. STM32测试程序的代码骨架(HAL库版)

我用的是STM32F103和STM32F407两块板子做过同样的测试,代码主体基本通用。下面这套基于HAL库的框架,核心思想是把读取、解析、判稳三个动作拆开,方便单独调。

3.1 CubeMX里的基础配置

-CubeMX里把SPI配成主机模式,速率先压在500kHz到1MHz之间,等时序确认稳定了再尝试拉高。

  • 片选信号用普通GPIO控制,不要在SPI外设里自动管理NSS,这样时序可控,也方便后面改成软件模拟。
  • 串口开一个,用于打印观测数据,波特率115200,重定向printf到UART即可。

时钟极性和相位的选择,先按数据手册推荐值来。如果手册没写,测试阶段优先试CPOL=0、CPHA=0,不行再换CPOL=0、CPHA=1。相位不对最典型的症状是数据全是0xAA、0x55这类规律性错位数据。

3.2 一次完整的读取与解析

下面的代码用一个数组接收原始字节,再把字节拼成uint32_t,最后按预定位数拆出圈数和角度。不同型号的编码器只需要修改开头的三个宏。

#include "main.h" #define ENC_ANGLE_BITS 17 /* 单圈分辨率位数,按数据手册修改 */ #define ENC_TURN_BITS 12 /* 多圈范围位数,按数据手册修改 */ #define ENC_TOTAL_BITS (ENC_ANGLE_BITS + ENC_TURN_BITS) #define ENC_BYTES ((ENC_TOTAL_BITS + 7) / 8) /* 读取一次原始数据 */ uint32_t read_encoder_raw(void) { uint8_t buf[ENC_BYTES] = {0}; uint32_t raw = 0; int i; HAL_GPIO_WritePin(ENC_CS_GPIO_Port, ENC_CS_Pin, GPIO_PIN_RESET); HAL_SPI_Receive(&hspi1, buf, ENC_BYTES, 10); HAL_GPIO_WritePin(ENC_CS_GPIO_Port, ENC_CS_Pin, GPIO_PIN_SET); for (i = 0; i < ENC_BYTES; i++) { raw = (raw << 8) | buf[i]; } /* 如果数据左对齐,需要右移掉多余的填充位 */ raw >>= (ENC_BYTES * 8 - ENC_TOTAL_BITS); return raw; } /* 从原始数据里拆出圈数和角度 */ void parse_encoder_data(uint32_t raw, uint32_t *turns, uint32_t *angle) { *turns = (raw >> ENC_ANGLE_BITS) & ((1UL << ENC_TURN_BITS) - 1); *angle = raw & ((1UL << ENC_ANGLE_BITS) - 1); }

如果SPI外设怎么调都读不到数据,不要死磕,直接改成GPIO模拟时序。工业SSI的时序和标准SPI未必兼容,GPIO模拟反而更灵活,测试早期的排错效率更高。

uint32_t read_encoder_gpio_bitbang(void) { uint32_t raw = 0; int i; HAL_GPIO_WritePin(ENC_CS_GPIO_Port, ENC_CS_Pin, GPIO_PIN_RESET); for (int d = 0; d < 100; d++); /* 片选稳定等待 */ for (i = ENC_TOTAL_BITS - 1; i >= 0; i--) { HAL_GPIO_WritePin(ENC_SCK_GPIO_Port, ENC_SCK_Pin, GPIO_PIN_SET); for (int d = 0; d < 100; d++); if (HAL_GPIO_ReadPin(ENC_DATA_GPIO_Port, ENC_DATA_Pin) == GPIO_PIN_SET) { raw |= (1UL << i); } HAL_GPIO_WritePin(ENC_SCK_GPIO_Port, ENC_SCK_Pin, GPIO_PIN_RESET); for (int d = 0; d < 100; d++); } HAL_GPIO_WritePin(ENC_CS_GPIO_Port, ENC_CS_Pin, GPIO_PIN_SET); return raw; }

GPIO模拟版的好处是能看到每一位的读取过程,配合示波器很容易定位问题。测试程序跑通之后,如果需要更高的采样率,再回到SPI外设模式也不迟。

3.3 判稳逻辑与串口打印

静态测试时,编码器轴不动,连续读取值理论上应该完全一致。我习惯在循环里连续读5次,如果5次结果都一样,才认为当前数据有效,否则标记一次不稳定计数。

uint8_t is_enc_data_stable(uint32_t *stable_val, uint8_t sample_times) { uint32_t last = 0, cur = 0; uint8_t i, same_count = 0; for (i = 0; i < sample_times; i++) { HAL_Delay(2); cur = read_encoder_raw(); if (i > 0 && cur == last) { same_count++; } last = cur; } if (same_count == sample_times - 1) { *stable_val = cur; return 1; } return 0; }

主循环里的打印格式建议把原始值和解析值都输出。原始值用于排查字节拼接问题,解析值用于观察位置变化。格式类似:raw=0x1A2B3C turn=128 angle=8912,一眼就能看出数据有没有异常。

注意别在定时器中断里调用HAL_Delay做判稳,中断里延时会导致系统卡顿,我踩过这个坑,后来把判稳逻辑全放到了主循环。

4. 读数不对时的排查链路

如果说前面是测试程序的正常流程,那这一章就是测试程序存在的意义。多圈绝对值编码器第一次上电读数不对,几乎是必然经历。

4.1 现象与优先怀疑点

读到的现象优先怀疑点处理方式
读数固定不变,但电压正常时钟没发出来或片选没拉低示波器抓SCK和CS,确认波形
读数随机乱跳电平不匹配、接触不良、SPI速率过高降速到500kHz以下,重新压线
数据高低位整体错位字段顺序或数据位长理解错误缓慢转轴,观察变化的是高位还是低位
上电第一次读数异常,之后正常上电稳定时序不足初始化后先丢弃一帧,延时50ms再读

其中最隐蔽的就是上电第一次读数异常。有些编码器内部有自检流程,上电后需要一段时间才能稳定输出,这时候去读,读回来的可能是中间态。处理办法很粗暴:初始化完成后延时一段时间,或者直接丢弃第一次读取结果。

4.2 多读几帧,区分干扰和真实位置

我在前面加了判稳函数,实际使用中有个体会:当转轴静止时,如果读数频繁跳变,多半是硬件问题而非软件问题。此时不要急着改代码,先看电源纹波,再看信号线屏蔽,最后考虑时钟极性。软件上能做的只是多读几帧取众数,但这治标不治本。

当转轴匀速转动时,读数应该按照分辨率步进递增或递减,比如17位编码器每圈131072个码值。如果发现读数不是逐步变化,而是偶尔跳变几百甚至几千,那很大概率是时钟频率太高导致采样落在数据位的边沿附近,把SPI速率调低一档往往就解决了。

4.3 校验位与CRC的处理策略

部分编码器的数据包里带奇偶校验位或CRC校验码,这类编码器在测试程序里反而好办:读到数据先过一遍校验,校验失败直接算无效帧。没有校验位的编码器,只能靠多读判稳来兜底。

比较麻烦的是那种校验逻辑不公开的型号。遇到这种情况,我的处理方式是把原始值和解析值都记录下来,观察稳定状态下原始值是否恒定。如果原始值恒定但解析值异常,那问题一定出在解析代码;如果原始值本身就不稳定,那是传输层面的问题,先处理硬件链路。

5. 多圈特有的行为:溢出、掉电、零位与方向

测试多圈绝对值编码器和测试普通传感器最大的不同,在于要主动去验证那些增量编码器永远不会出现的行为。这一节里的每一条,都是我在实际项目中真正遇到过的。

5.1 圈数溢出不是程序bug

12位多圈对应4096圈,16位多圈对应65536圈。如果你一直朝一个方向转,转到最大圈数附近时,有些型号的圈数会回绕到0重新开始,有些会饱和在最大值不动,还有些会置一个溢出标志位。这三种行为都是设计使然,不是编码器坏了,也不是测试程序写错了。

测试时我的做法是:查手册确认最大圈数,然后故意转到边界去,把回绕或饱和的行为记录在测试结果里。这一条看起来简单,但不少设备调试时因为圈数溢出导致数据突变,现场排查半天才发现是应用层没考虑边界。

5.2 断电记忆:上电读值必须保持

多圈绝对值编码器之所以值钱,核心就是断电记忆。测试方法很简单:记录当前圈数和角度,切断编码器供电,等一秒,重新上电,比较前后读值。如果一致,记忆正常。如果不一致,先排查供电引脚是否真的断开了,再确认型号确实支持多圈记忆,而不是被商家拿单圈编码器冒充了多圈。

这里要提一句多圈记忆的实现方式。老式产品用机械齿轮记忆圈数,纯物理结构,不需要电池。后来出现了电池备份型,断电后靠电池维持计数状态,这种型号电池没电就会丢圈数。现在还有基于韦根传感器的电子式多圈,不用电池也能记录圈数。拿到编码器时先搞清楚属于哪种,测试方法略有差异:机械式和韦根式直接断电验证即可,电池备份型的还要检查电池接没接。

5.3 零位写入和方向取反的处理

很多多圈绝对值编码器支持零位写入功能,把当前机械位置标记为坐标原点。测试程序里应该留一个按键触发零位写入的入口,按下后发送写零位指令,再把读数清零作为验证条件。

方向问题则是另一个高频坑。顺时针转动时,如果读到的数值在减小,可能是编码器安装方向反了,也可能是数据输出采用了补码格式。测试程序里预留一个方向取反的宏,比如#define ENC_DIR_REVERSE 1,在解析后做一次raw = MAX_CODE - raw的处理。软件取反只适合临时验证,最终项目里还是建议通过安装方式统一方向,否则以后维护会非常头大。

6. 测试到这一步,怎么才算验收通过

测试程序既然叫“测试程序”,最终要能回答一个结论性的问题:这颗编码器能不能用,测试过程中有没有异常。我在做导入验证时,会按下面的标准判断。

  • 重复精度验证:固定一个机械位置,断电上电重复10次,读值极差应该为0。如果每次都差一点,要么是供电不稳,要么是编码器本身有问题。
  • 连续转动验证:用手匀速转轴一整圈,串口打印的角度值应该连续单调变化,不允许跳变。
  • 静态稳定性验证:转轴静止,连续读1000次,记录跳变次数和最大跳变量。
  • 方向一致性验证:正转读值递增,反转读值递减,不能出现忽增忽减。
  • 掉电记忆验证:按第5章的方法,断电上电读值保持一致。

这些结果建议整理成一张简单的测试记录表,包含型号、接口类型、数据位长、供电电压、SPI速率、测试时间、结论这几列。后面如果编码器在设备上出了问题,这张表能帮你快速排查到底是批次差异还是程序问题。

我从测试程序切到正式工程程序时,一般会做三处调整:把GPIO模拟时序替换成SPI外设以提升读取速率;把轮询读取改成定时器触发并加入错误重试机制;把解析后的位置数据封装成结构体,方便给运动控制逻辑调用。测试程序的定位是验证链路和器件,正式程序的定位是满足控制周期,两者目标不同,代码不必强行共用。

最后再说一个实测心得:程序第一次能读到数据的时候,先不要急着把编码器装到设备上,也不要拿伺服电机驱动它转。我习惯拿在手里,缓速转轴,盯着串口助手看数值。转到圈数边界时确认溢出行为,反向转确认方向逻辑,断电再上电确认记忆保持。把这些人工测试全部验证通过后,再考虑装机和上高速采样,否则排查问题的难度会成倍上升。一套好的测试程序,不只是把数据读出来,更是在替你把整个链路的质量提前问一遍。

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

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

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

立即咨询