STM32 HAL库I2C轮询读取AS5600磁编码器实战指南
简介这份资源面向使用STM32 HAL库进行编码器开发的嵌入式工程师与学生聚焦于通过I2C总线读取AS5600磁编码器角度数据且采用非DMA的阻塞式读取方式适合需要理解I2C底层时序、不依赖DMA中断的入门与进阶场景。压缩包共111个文件以73个.h头文件与26个.c源文件为核心涵盖STM32F4xx HAL驱动、I2C与定时器外设配置另含.ioc工程配置、.uvprojx与.uvoptx Keil工程文件、.hex烧录固件及.bat辅助脚本整体约1016KB结构完整可直接编译。代码中已封装AS5600_Read_DMA接口与角度、圈数打印逻辑读者可据此快速验证传感器通信、理解寄存器读取流程并在此基础上扩展多圈计数或闭环控制。目前已有1071人学习下载适合作为I2C编码器采集的参考工程。1. 磁编码器选型与 I2C 轮询方案的适用边界AS5600 这颗 12 位磁旋转编码器在电机控制、旋钮交互、云台角度反馈里出现频率很高原因很直接非接触、体积小、成本低输出 04095 的绝对角度掉电后位置不丢。但很多人第一次把它接到 STM32 上用 HAL 库的 I2C 轮询去读会发现角度跳变、读数偶尔卡死、甚至总线直接锁住。问题往往不在芯片而在「不加 DMA 的轮询读法」这件事本身的边界没搞清楚。这篇笔记只讲一件事用 STM32 HAL 库纯轮询方式通过 I2C 读取 AS5600 的原始角度和状态寄存器不引入 DMA、不引入中断回调把最小可复现的读法、寄存器配置、时序参数和踩坑点讲透。适合两类人一是刚上手磁编码器、想先把数据读稳的新手二是已经在用 DMA 或中断、但想搞清楚底层寄存器交互到底发生了什么的老手。轮询方案在低速采样几十到几百赫兹下完全够用代价是占用 CPU收益是逻辑简单、没有并发问题、调试时不会因为 DMA 搬运和主循环抢资源而出现玄学现象。2. AS5600 寄存器映射与 HAL I2C 轮询读的底层逻辑2.1 先搞清楚要读哪几个寄存器AS5600 的 I2C 从机地址是 0x367 位地址HAL 里要左移一位变成 0x6C。它内部有一组配置寄存器和一组输出寄存器轮询读角度只需要碰输出区。核心寄存器如下寄存器地址名称含义读法0x0CRAW_ANGLE_H原始角度高 8 位连续读 2 字节0x0DRAW_ANGLE_L原始角度低 4 位有效同上0x0EANGLE_H经过 ZPOS/MPOS 处理后的角度高 8 位连续读 2 字节0x0FANGLE_L处理后角度低 4 位有效同上0x0BSTATUS磁铁状态标志单字节读0x16AGC自动增益值单字节读RAW_ANGLE 是传感器直接输出的 04095 原始值ANGLE 是经过零点ZPOS和最大角度MPOS映射后的值。如果你不做零点标定两者数值一样。STATUS 寄存器里 MD 位表示检测到磁铁ML 位表示磁铁太弱MH 位表示磁铁太强这三个位是排查「读出来一直是 0 或一直是 4095」的第一手线索。2.2 HAL_I2C_Mem_Read 的调用方式和参数含义HAL 库读寄存器最顺手的是HAL_I2C_Mem_Read它把「写寄存器地址 重复起始 读数据」这套时序封装好了。最小读角度函数如下#include stm32f1xx_hal.h // 按实际芯片系列替换头文件 #define AS5600_ADDR (0x36 1) // HAL 使用 8 位地址7 位地址左移一位 #define AS5600_RAW_H 0x0C #define AS5600_STATUS 0x0B I2C_HandleTypeDef hi2c1; // 假设 I2C1 已由 CubeMX 初始化 /* 读取 12 位原始角度返回 0~4095失败返回 0xFFFF */ uint16_t AS5600_ReadRawAngle(void) { uint8_t buf[2] {0}; HAL_StatusTypeDef st; // 从 0x0C 开始连续读 2 字节AS5600 支持地址自增 st HAL_I2C_Mem_Read(hi2c1, AS5600_ADDR, AS5600_RAW_H, I2C_MEMADD_SIZE_8BIT, buf, 2, 10); if (st ! HAL_OK) { return 0xFFFF; // 用非法值标记失败调用方自行处理 } // 高 8 位在 buf[0]低 4 位在 buf[1] 的高半字节 return ((uint16_t)buf[0] 8) | (buf[1] 0x0F); } /* 读取 STATUS 寄存器判断磁铁状态 */ uint8_t AS5600_ReadStatus(void) { uint8_t s 0; if (HAL_I2C_Mem_Read(hi2c1, AS5600_ADDR, AS5600_STATUS, I2C_MEMADD_SIZE_8BIT, s, 1, 10) ! HAL_OK) { return 0xFF; } return s; }逻辑说明HAL_I2C_Mem_Read的第二个参数是 8 位从机地址第三个参数是寄存器起始地址第四个参数说明寄存器地址宽度是 8 位第五个是接收缓冲区第六个是字节数第七个是超时毫秒数。AS5600 的寄存器地址会自动递增所以从 0x0C 读 2 字节就能一次拿到高 8 位和低 4 位不需要分两次读。低字节只有低 4 位有效必须用 0x0F屏蔽掉高 4 位的无效数据否则角度会出现 16 的倍数跳变。参数说明超时值 10ms 是轮询场景下的经验值。I2C 标准模式 100kHz 下读 2 字节大约 0.3ms快速模式 400kHz 下更短10ms 足够覆盖绝大多数情况。如果总线上挂了多个从机或者走线较长可以放宽到 2050ms但不要设成HAL_MAX_DELAY否则总线异常时主循环会被永久阻塞这是轮询方案最典型的翻车方式。2.3 轮询周期和 I2C 速率的匹配关系不加 DMA 意味着每次读角度都要 CPU 全程参与。AS5600 内部采样率典型值是 150Hz 左右不同滤波配置下会变也就是说传感器每 67ms 才更新一次内部角度值。如果你的主循环 1ms 读一次会读到大量重复值白白占用总线如果 100ms 读一次又会丢失快速旋转时的角度变化。常见做法是把读取周期设在 520ms对应 50200Hz 的采样率和传感器内部更新率大致匹配。I2C 速率建议用 400kHz 快速模式CubeMX 里 I2C 的 Clock Speed 直接填 400000。100kHz 也能用但读一次的时间会拉长到 1ms 量级在高频轮询时 CPU 占用会明显上升。提示轮询周期不要用HAL_Delay硬等那会把整个主循环卡死。用HAL_GetTick()做非阻塞时间片判断主循环里其他任务才能正常跑。3. 从 CubeMX 配置到读角度的完整落地步骤3.1 CubeMX 里 I2C 参数怎么填在 CubeMX 里选中 I2C1模式选 I2CSpeed Mode 选 Fast ModeClock Speed 填 400000。GPIO 会自动分配到 PB6/PB7 或 PB8/PB9取决于芯片型号。上拉电阻这块要注意AS5600 模块板上通常已经带了 4.7k10k 上拉如果模块自带CubeMX 里 GPIO 的上拉就不需要再开否则并联后阻值偏低上升沿变陡但功耗增加长走线时反而容易振铃。时钟配置里确认 I2C 的时钟源频率F1 系列是 APB1F4 系列也是 APB1确保分频后能得到接近 400kHz 的实际速率。CubeMX 会自动算但如果你手动改了 PCLK1要回头检查 I2C 的 Timing 寄存器值有没有跟着变。3.2 初始化顺序和第一个读通的最小验证生成代码后先别急着写业务逻辑用下面这段最小验证确认总线通了void AS5600_Test(void) { uint8_t status AS5600_ReadStatus(); uint16_t angle AS5600_ReadRawAngle(); if (status 0xFF) { printf(I2C no response\r\n); // 总线没通查接线和上拉 return; } if (!(status 0x20)) { printf(No magnet detected, status0x%02X\r\n, status); return; } printf(Angle%u, status0x%02X\r\n, angle, status); }逻辑说明先读 STATUS如果返回 0xFF 说明HAL_I2C_Mem_Read失败大概率是从机没应答检查 SDA/SCL 有没有接反、上拉有没有、地址有没有写错。STATUS 的 bit5 是 MD 位为 1 表示检测到磁铁。如果 MD 为 0角度寄存器读出来会是无效值这时候转磁铁或者调磁铁距离才有意义。参数说明磁铁和芯片表面的推荐气隙是 0.53mm太远 MD 不置位太近 MH 置位、AGC 饱和角度线性度会变差。轴向充磁的圆磁铁效果最好直径 6mm 左右、厚度 23mm 是常见规格。3.3 把角度读进主循环并做简单滤波确认能读到数之后把读取逻辑放进主循环的时间片里uint32_t last_tick 0; uint16_t angle_buf[4] {0}; uint8_t buf_idx 0; while (1) { if (HAL_GetTick() - last_tick 10) { // 10ms 周期100Hz last_tick HAL_GetTick(); uint16_t a AS5600_ReadRawAngle(); if (a ! 0xFFFF) { angle_buf[buf_idx 0x03] a; } // 四点滑动平均抑制单次读数抖动 uint32_t sum 0; for (int i 0; i 4; i) sum angle_buf[i]; uint16_t angle_avg sum 2; // angle_avg 就是可用的角度值0~4095 对应 0~360 度 } // 其他任务继续跑 }逻辑说明用HAL_GetTick()做 10ms 时间片读失败时保留上一次的缓冲区数据不更新避免 0xFFFF 污染平均值。四点滑动平均对磁编码器的白噪声抑制效果明显代价是引入约 2 个周期的相位延迟在 100Hz 采样下约 20ms对大多数角度反馈场景可以接受。参数说明滑动平均的窗口长度按噪声水平调。窗口 4 适合噪声 ±3LSB 以内的情况如果抖动超过 ±10LSB先查磁铁安装偏心、气隙过大或电源纹波而不是一味加大窗口窗口越大动态响应越差。角度转度数用angle * 360.0f / 4096.0f注意用浮点或者先乘后除避免整数截断。4. 不加 DMA 时最容易翻车的几个点4.1 现象读数偶尔返回 0xFFFF重试又好了原因I2C 总线在电磁干扰下出现仲裁丢失或从机忙HAL_I2C_Mem_Read返回非 HAL_OK。轮询方案没有中断兜底一次失败就是一次失败。解决在读取函数里加一次重试重试前先调用HAL_I2C_DeInit和HAL_I2C_Init复位 I2C 外设或者用HAL_I2C_IsDeviceReady确认从机在线再读。更彻底的做法是在 SDA/SCL 上加 100nF 到地的滤波电容缩短走线远离电机驱动线。4.2 现象角度在某个位置固定跳变 16 的倍数原因低字节的高 4 位无效数据没屏蔽。AS5600 的 ANGLE_L 寄存器只有低 4 位有效高 4 位是保留位某些批次会读出随机值。解决读回来之后强制 0x0F这一点在 2.2 的代码里已经处理。如果还有跳变检查是不是把 RAW_ANGLE 和 ANGLE 两个寄存器搞混了两者地址只差 2。4.3 现象主循环卡死看门狗复位原因HAL_I2C_Mem_Read的超时设成了HAL_MAX_DELAY总线被拉低时函数永远不返回。解决所有轮询 I2C 调用的超时必须是有限值1050ms 之间。同时在主循环里喂看门狗一旦 I2C 卡住超过看门狗周期系统能自动复位恢复。4.4 现象角度线性度差两端压缩中间拉伸原因磁铁没对中或者气隙太大导致磁场边缘非线性。AGC 值可以量化这个问题AGC 正常范围在 0x400x80 之间太小说明磁场太强太大说明太弱。解决读 0x16 寄存器的 AGC 值调整磁铁位置让 AGC 落在中间范围。同时确认磁铁是轴向充磁、芯片在磁铁正下方偏心超过 1mm 线性度就会明显恶化。4.5 现象多个 AS5600 挂同一条 I2C 读不出来原因AS5600 的 I2C 地址固定为 0x36不可通过引脚改变同一条总线上挂两个就会地址冲突。解决要么用 I2C 多路复用器分路要么用两条独立 I2C 总线要么改用 SPI 接口的同类编码器。这是选型阶段就要确认的事不要等画完板子才发现。5. 用 STATUS 和 AGC 做在线自检的进阶技巧轮询方案虽然简单但可以顺手把 AS5600 的诊断寄存器用起来做成一个在线自检机制这在长期运行的设备里比读角度本身更有价值。具体做法是每读 N 次角度穿插读一次 STATUS 和 AGC把 MD、ML、MH 三个标志和 AGC 值一起判断一旦异常就置位一个错误标志供上层逻辑决定是否降级运行或报警。typedef struct { uint16_t angle; uint8_t agc; uint8_t magnet_ok; uint8_t error; } AS5600_State; AS5600_State as_state; void AS5600_Update(void) { uint8_t st AS5600_ReadStatus(); uint8_t agc 0; if (st 0xFF) { as_state.error 1; // I2C 通信失败 return; } as_state.magnet_ok (st 0x20) ? 1 : 0; as_state.error 0; // 磁铁过弱或过强都标记异常 if ((st 0x08) || (st 0x10)) { // ML 或 MH 置位 as_state.error 2; } HAL_I2C_Mem_Read(hi2c1, AS5600_ADDR, 0x16, I2C_MEMADD_SIZE_8BIT, agc, 1, 10); as_state.agc agc; // AGC 超出合理范围也标记异常 if (agc 0x40 || agc 0x80) { as_state.error 3; } uint16_t a AS5600_ReadRawAngle(); if (a ! 0xFFFF) { as_state.angle a; } }逻辑说明STATUS 的 bit3 是 ML磁铁太弱bit4 是 MH磁铁太强bit5 是 MD检测到磁铁。三个位组合起来能区分「没磁铁」「磁铁太远」「磁铁太近」三种安装问题。AGC 是自动增益控制值反映内部放大器的增益档位磁铁越远增益越高、AGC 值越大。把 AGC 的正常范围卡在 0x400x80超出就说明安装状态在漂移可能是磁铁松动或者温度导致气隙变化。参数说明自检的调用频率不用和角度读取一样高每 100ms 跑一次足够因为磁铁状态和 AGC 是慢变量。这样既不影响角度采样率又能持续监控传感器健康度。错误码分级也有讲究error1 是通信层问题优先查硬件error2 是安装问题查磁铁位置error3 是裕量问题系统还能跑但精度在下降可以提前预警。我自己的习惯是任何用磁编码器的项目第一版固件一定先把 STATUS 和 AGC 打印出来盯着这两个值调磁铁位置调到 AGC 落在 0x500x70 之间再开始调角度逻辑。这个习惯帮我省掉了大量「角度不对就怀疑代码」的来回折腾——十次里有七次是磁铁没装好代码本身没问题。轮询方案的价值就在于它足够透明每一步读写在示波器上都能对上出问题时不会在 DMA 和中断的层层封装里迷路。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

当AI接管了执行,我们在团队里到底该干什么?

当AI接管了执行,我们在团队里到底该干什么?

AI时代分工里最先变化的到底是什么 我最近和几个带团队的朋友聊天,发现大家都有同一种焦虑:AI时代分工正在被悄悄改写。以前开会分活儿,谁写文案、谁做图、谁跑数据,清清楚楚。现在这些执行动作AI几秒钟就能交差,人反…

2026/10/15 18:26:40 阅读更多 →
1ml 到 20ml:安瓿规格如何决定灌封机机型选择

1ml 到 20ml:安瓿规格如何决定灌封机机型选择

1ml 到 20ml:安瓿规格如何决定灌封机机型选择“你们机器能灌多大规格?”——这几乎是每个客户开口的第二句话。但真正决定买哪台机的,不是"能不能灌",而是你的主力规格落在哪一段。1ml 和 20ml 虽然都叫安瓿&#xff0c…

2026/10/15 18:26:40 阅读更多 →
用 Python 画一张成绩分布图:教孩子从直方图里读数据

用 Python 画一张成绩分布图:教孩子从直方图里读数据

为什么讲这个知识点 孩子学完列表、循环、求和之后,最容易卡在"这些数字到底能干什么"。成绩分布图是个很好的落点:数据是孩子自己班里的,图是孩子自己画的,结论也是孩子自己读出来的。 比起直接讲统计学定义&#xff0…

2026/10/15 18:26:40 阅读更多 →

最新新闻

【ESP32笔记】ESP中的分区表

【ESP32笔记】ESP中的分区表

本节目标:看懂分区表 CSV 的每一列,会给工程添加自定义分区,并用 esp_partition API 完成擦除、写入、读取一、本节要解决的问题 ESP32 的程序不是"整个 bin 一股脑烧到某个固定地址",而是把整块 flash 划分成多个分区&…

2026/10/15 19:04:55 阅读更多 →
亲和路由(inspect)

亲和路由(inspect)

Inspect 要解决的是“多轮对话在普通选路下被分到不同渠道”导致缓存失效和兼容性出错的问题;它通过回答“值不值得亲和、按什么粒度分组、用哪些稳定字段”三个问题,输出分类、触发原因和内容哈希;它被设计成无外部状态的纯分析器&#xff0…

2026/10/15 19:04:55 阅读更多 →
AI本体建模-------GB/T 48000.3—2026 标准详解

AI本体建模-------GB/T 48000.3—2026 标准详解

AI本体建模之 GB/T 48000.3—2026 标准详解 当大模型与 Agent 逐渐成为企业基础设施,真正的护城河正从「数据」转向「可被机器理解、推理、校验、共享的知识」。而 本体(Ontology) 正是这套知识体系的「模式层」与「契约层」。 2026 年 1 月&…

2026/10/15 19:04:55 阅读更多 →
线上 Redis 慢查询,不一定就是命令本身慢

线上 Redis 慢查询,不一定就是命令本身慢

上周值班的时候,监控突然弹出 Redis 慢查询告警,慢查询日志里全是 HGETALL,单条耗时经常冲到 80ms 以上。第一反应,又是经典问题,大 Hash 别用 HGETALL,这个知识点群里都说烂了。 一开始我也是这么处理的&a…

2026/10/15 19:04:55 阅读更多 →
有效三角形个数:排序+双指针经典算法详解

有效三角形个数:排序+双指针经典算法详解

做算法题的时候有一种快乐是:题目本身只有一行描述,但背后藏着一整类优化思路。有效三角形个数就是这样——表面上是几何问题,实际上是排序和双指针的经典练兵场,也是我算法题解系列第五篇想重点讲透的题目。题目很直白&#xff1…

2026/10/15 19:04:55 阅读更多 →
SDSegmentedControl避坑指南:iOS 6/7兼容处理、IB集成技巧与现代化改造建议

SDSegmentedControl避坑指南:iOS 6/7兼容处理、IB集成技巧与现代化改造建议

【免费下载链接】SDSegmentedControl A drop-in remplacement for UISegmentedControl that mimic iOS 6 AppStore tab controls 项目地址: https://gitcode.com/gh_mirrors/sd/SDSegmentedControl 点击查看 免费下载 SDSegmentedControl 是一个可以直接替换系统 U…

2026/10/15 19:02:09 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →