传感器 · 霍尔磁力 · KY-003
KY-003 霍尔磁力传感器 STM32F103 接线与代码
KY-003 霍尔磁力传感器就是给单片机装一个「有没有磁铁靠近」的开关——有效磁极靠近感应面,输出就拉低;磁铁移开或换另一面靠近,输出都保持高电平。本页是 STM32F103C8T6 + KY-003 + 0.96 OLED 的完整实机验证记录,含接线表、完整代码和 PlatformIO 工程下载,全部实测通过。最值得看的是「单极霍尔为什么只有磁铁的一面能触发」和「感应面在下面、磁铁要从下面对准」这两段。
Quick Facts
- 模块:KY-003,国产 3144 单极霍尔开关
- 接线:S→PA0,+/- 共 3 根线
- 输出:低电平=有效磁极靠近,高电平=远离
- 显示:0.96 寸 OLED,SSD1306,地址 0x3C
- 消抖:30ms 稳定判定
- 实测:B 面靠近触发,A 面 / 远离不触发
Verified
实机验证结果
本项目实测 下面的结果全部来自我们自己的实机测试,更新于 2026-08-17。
有无磁铁实测
| 测试动作 | 预期 | 实测结果 |
|---|---|---|
| 上电,磁铁远离 | OLED 显示 CLEAR,串口 OUT:1,LED 灭 | CLEAR / OUT:1 / LED 灭 |
| A 面靠近感应面(下面) | 无变化(无效磁极) | 仍 CLEAR / OUT:1 |
| B 面(有效磁极)靠近感应面 | OLED 显示 MAGNET DETECTED,串口 OUT:0,LED 亮,CNT +1 | MAGNET DETECTED / OUT:0 / LED 亮 / CNT 每次 +1 |
| 磁铁移开 | 恢复 CLEAR,LED 灭 | 恢复 CLEAR / LED 灭 |
串口输出(115200)
KY-003 hall demo v1.0 starting...
OLED 0x3C: found
STATE: CLEAR | OUT: 1 | CNT: 0
STATE: MAGNET DETECTED | OUT: 0 | CNT: 1
STATE: CLEAR | OUT: 1 | CNT: 1
STATE: MAGNET DETECTED | OUT: 0 | CNT: 2
编译烧录记录
PlatformIO 上传输出 ** Programming Finished ** → ** Verified OK ** → [SUCCESS],烧录成功。Flash 占用 49.9%,RAM 占用 8.5%。
| 项目 | 本项目实际使用 |
|---|---|
| 主控 | STM32F103C8T6(BluePill) |
| 模块 | KY-003 霍尔磁力传感器(国产 3144 单极霍尔开关,无电位器,S 数字输出) |
| 显示 | 0.96 寸 OLED,SSD1306,128×64,地址 0x3C |
| 状态灯 | BluePill 板载 LED,PC13(低电平点亮) |
| 开发工具 | VS Code + PlatformIO |
| 框架 | Arduino / STM32duino |
| 烧录 | ST-Link |
| 串口 | 115200 |
| 依赖库 | Adafruit SSD1306、Adafruit GFX Library |
What it does
这个模块干什么
KY-003 霍尔磁力传感器就是给单片机装一个「有没有磁铁靠近」的开关。核心是一颗 3144 单极霍尔开关(认磁极):磁铁只有一个磁极靠近感应面时,输出才被拉低;磁铁移开、或换另一面靠近,输出都保持高电平。门磁检测、计数、到位检测、转速测量都用这一类模块。
它只输出「有 / 没有磁铁」这一个二值结果,不测磁场强度,也不告诉你磁铁离多远。感应面在模块下面(电路板背面那一面),磁铁要从下面对准才有效。
三个要点
| 要点 | 意思 |
|---|---|
| 单极霍尔开关(认磁极) | 磁铁正反两面不是都能触发,只有「有效磁极」那一面靠近才触发 |
| 感应面在下面 | 只有电路板背面这一面认磁性,磁铁要从下面对准,不是贴正面 |
| 不是测磁场强度 | 只输出「过没过阈值」一个二值结果,不知道磁铁多强、离多远 |
和测磁场强度的传感器怎么选
| KY-003 霍尔开关 | 线性霍尔(如 49E) | 磁力计(如 QMC5883) | |
|---|---|---|---|
| 输出 | 高低电平(有 / 无磁铁) | 模拟电压(随磁场强弱变) | 三轴磁场数值 |
| 能不能测强度 | 不能,只判有无 | 能,模拟量 | 能,数字量 |
| 适合场景 | 门磁、计数、到位检测、转速 | 测磁场强弱、接近度 | 指南针、姿态、磁场分布 |
对比依据为三类器件的工作原理,由我们整理成表。资料参考
Wiring
接线
本项目实测 KY-003 模块和 OLED 分开接,模块 3 根线 + OLED 4 根线。下面接法实机跑通。
KY-003 模块(正面三针顺序 - / + / S)
| 信号 | 模块丝印 | STM32F103C8T6 |
|---|---|---|
| 地 | - | GND |
| 电源 | + | 3.3V |
| 输出 | S | PA0 |
OLED
| 信号 | OLED | STM32F103C8T6 |
|---|---|---|
| 电源 | VCC / VDD | 3.3V |
| 地 | GND | GND |
| 时钟 | SCL / SCK | PB8 |
| 数据 | SDA | PB9 |
烧录与串口(不接也能跑,接上方便看数据)
| 用途 | 连接 | 说明 |
|---|---|---|
| 烧录 | ST-Link → SWD 四线(3.3V / GND / SWDIO / SWCLK) | 烧录用 |
| 串口 | PA9(TX) / PA10(RX) → CP2102 等 | 看串口数据(115200) |
1. OLED 的 I2C 用的是重映射引脚 PB8/PB9,不是默认的 PB7/PB6。代码里必须先
Wire.setSDA(PB9)、Wire.setSCL(PB8),再 Wire.begin(),顺序反了也读不到屏幕。2. 磁铁要从模块「下面」对准感应面,而且要用有效磁极那一面(B 面)。换 A 面靠近、或从正面靠近都不会触发,这不是模块坏了,是单极霍尔只认一个磁极。
3. 必须共地。模块、OLED、STM32 的 GND 要接在一起,否则信号对不上。
| 要点 | 说明 |
|---|---|
| S 走内部上拉 | 代码用 INPUT_PULLUP 读 PA0,3144 是开漏输出,内部上拉兜底保证悬空为高 |
| 供电 3.3V | 本项目用 3.3V 与主控同电平 |
| PA13 / PA14 | 留给 ST-Link 调试,不要占用 |
| 丝印为准 | 本块正面三针顺序是 - / + / S,按丝印接,不要照着照片数孔位 |
Principle
工作原理
资料参考 这一节来自公开资料和常规工程实践,不是我们实测的结论,用来帮助理解。实测结果只在上面「实机验证结果」一节。
霍尔效应:磁铁怎么变成电信号
一块通着电流的导体放进磁场里,里面的载流子会受洛伦兹力往一边偏,在导体两侧产生一个微弱电压,这个电压随磁场增强而变大。霍尔开关就是把这个微弱电压放大后,和内部设定好的阈值比较——超过阈值就翻转输出。
3144 单极霍尔开关为什么「认磁极」
3144 内部是霍尔元件 + 放大器 + 施密特触发器 + 开漏输出。它只对磁场的一个方向(一个磁极)有反应:有效磁极靠近时磁场超过阈值,输出被拉低;磁场方向反过来(另一磁极靠近)、或磁场移开,输出都恢复高。所以表现就是「磁铁只有一面能触发」。
| 项目 | 说明 | 来源 |
|---|---|---|
| 核心器件 | 国产 3144 单极霍尔开关(开漏输出) | 本项目实测 |
| 输出 | 数字电平:远离=高,有效磁极靠近=低 | 本项目实测 |
| 感应面 | 模块下面(电路板背面) | 本项目实测 |
| 有无电位器 | 无(没有灵敏度调节) | 本项目实测 |
| 有效磁极是 N 还是 S | 待确认(磁铁无丝印,暂标 A / B 面) | 待确认 |
为什么不是测磁场强度
这个模块只输出「过没过阈值」这一个二值结果,没有把磁场强弱换算成数值。要测磁场强弱,得用线性霍尔(输出随磁场强弱变化的模拟电压),或磁力计(输出三轴数字磁场值)。
Program logic
程序逻辑
本项目实测 下面是对实际代码 src/main.cpp(v1.0)逻辑的说明,不展开语法,只讲「程序是怎么想的」。
整体是一套「读电平 → 消抖 → 更新状态 → 输出」的状态机
主循环 loop() 里只做三件事:读模块电平并消抖、有变化才刷新串口、有变化才刷新 OLED。没有变化时什么都不做,CPU 几乎全空转。
| 步骤 | 做什么 | 对应代码 |
|---|---|---|
| 1. 读电平 | 读 PA0,低电平 = 有效磁极靠近 | readHallDetected() → digitalRead(PA0) == LOW |
| 2. 消抖 | 电平变化只记时刻,稳定 30ms 才更新状态 | updateHallState() |
| 3. 计触发 | 远离 → 检测到,计一次 triggerCount | 状态翻转时 g_triggerCount++ |
| 4. 状态灯 | 检测到点灯(PC13 低电平),远离熄灭 | digitalWrite(LED_PIN, ...) |
| 5. 输出 | 只在状态变化时(dirty 标记)刷新串口和 OLED | g_serialDirty / g_oledDirty |
为什么 30ms 消抖必须做
磁铁正好停在临界距离上时,磁场就在阈值上下抖,OUT 会快速来回跳。如果每次跳变都当一次状态变化,串口会刷屏、计数会乱涨。代码的做法是:电平一变化先记下时刻、不更新状态,等它稳定 30ms 才认这次变化。
上电那一刻为什么不能误触发计数
如果上电时把初始状态硬编码成「远离」,而实际磁铁正好贴着感应面,那么第一次读到的电平会和初始值不同,就会被当成一次「远离 → 检测到」的触发,计数凭空 +1。代码的处理是:上电时直接以真实电平作为初始状态,不把它当成一次触发。
输出为什么「有变化才发」
串口和 OLED 刷新都靠两个 dirty 标记:状态变了才置位,主循环里看到标记才输出、输出完清掉。这样磁铁一直在、或一直不在的时候,串口不会一直刷、OLED 不会一直整屏重写。
Code
完整代码
本项目实测 下面是实际烧录进板子、实机测试通过的那一份(v1.0),一字未改。环境:VS Code + PlatformIO + Arduino(STM32duino)。
关键处说明
1. 把 I2C 指到 PB8 / PB9。STM32duino 默认 I2C 引脚是 PB7/PB6,必须手动指到重映射的 PB8/PB9。setSDA/setSCL 必须在 begin() 之前调用。本项目 I2C 用 100kHz。
2. 模块 S 用「低电平 = 有效磁极靠近」判断。S 接 PA0 并开内部上拉(INPUT_PULLUP),3144 是开漏输出,内部上拉兜底保证悬空为高。
3. 30ms 消抖状态机。磁铁正好停在临界距离时输出会抖,电平变化只记时刻、不更新状态,稳定 30ms 才认。
4. 上电以真实电平作初始状态。避免上电那一瞬间因为初始值不对而误触发一次计数。
5. 板载 LED 低电平点亮。PC13 在 BluePill 上是低电平点亮,检测到磁铁时写 LOW、远离写 HIGH。
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define FIRMWARE_VERSION "v1.0"
// =========================
// GPIO 配置
// =========================
// KY-003 霍尔磁力传感器(国产 3144 单极霍尔开关,无电位器,正面三针 - / + / S)
// S -> PA0(数字输入,内部上拉)
// + -> 3.3V
// - -> GND
// 3144 是开漏输出,内部上拉兜底保证悬空时为高。
//
// 实测极性(以实机测试为准,不凭网图):
// 远离 / A 面靠近 -> OUT 高电平 (RAW=1) -> CLEAR
// B 面(有效磁极)靠近 -> OUT 低电平 (RAW=0) -> MAGNET DETECTED
constexpr uint8_t HALL_OUT_PIN = PA0;
constexpr uint8_t LED_PIN = PC13; // bluepill 板载 LED,低电平点亮
// OLED 使用 I2C1 重映射引脚:SCL=PB8 / SDA=PB9(与 01~06 系列公共标准一致)
constexpr uint8_t I2C_SCL_PIN = PB8;
constexpr uint8_t I2C_SDA_PIN = PB9;
constexpr uint8_t OLED_ADDRESS = 0x3C;
constexpr uint8_t SCREEN_WIDTH = 128;
constexpr uint8_t SCREEN_HEIGHT = 64;
constexpr int OLED_RESET = -1;
// 消抖:磁铁正好停在临界距离时会抖,要求稳定 30ms 才更新,避免 CNT 乱跳
constexpr uint32_t DEBOUNCE_MS = 30UL;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// 当前状态
bool g_magnet = false; // true = 有效磁极靠近(MAGNET DETECTED)
bool g_hasOled = false; // OLED 是否初始化成功
uint32_t g_triggerCount = 0; // 触发次数(CLEAR -> DETECTED)
bool g_oledDirty = false; // OLED 需要重绘
bool g_serialDirty = false; // 串口需要输出(有变化)
// 去抖用:上次采到的原始电平与变化时刻
static bool g_rawState = false;
static uint32_t g_lastChangeMs = 0;
// =========================
// 读取
// =========================
// OUT 低电平 = 有效磁极靠近
static bool readHallDetected() {
return digitalRead(HALL_OUT_PIN) == LOW;
}
// =========================
// 状态机(带消抖)
// =========================
static void updateHallState() {
const bool raw = readHallDetected();
// 电平抖动阶段:只记变化时刻,不更新状态
if (raw != g_rawState) {
g_rawState = raw;
g_lastChangeMs = millis();
return;
}
// 稳定达到 30ms 才真正更新状态
if (millis() - g_lastChangeMs >= DEBOUNCE_MS) {
if (g_magnet != g_rawState) {
g_magnet = g_rawState;
if (g_magnet) {
g_triggerCount++; // CLEAR -> DETECTED 计一次触发
}
digitalWrite(LED_PIN, g_magnet ? LOW : HIGH); // 检测到点灯
g_oledDirty = true;
g_serialDirty = true;
}
}
}
// =========================
// OLED UI
// =========================
static const char* getStateText() {
return g_magnet ? "MAGNET DETECTED" : "CLEAR";
}
static void drawOledUi() {
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextWrap(false);
display.setTextSize(1);
display.setCursor(0, 0);
display.print(F("07 HALL " FIRMWARE_VERSION));
display.setTextSize(2);
if (g_magnet) {
display.setCursor(0, 16);
display.print(F("MAGNET"));
display.setCursor(0, 32);
display.print(F("DETECTED"));
} else {
display.setCursor(0, 24);
display.print(F("CLEAR"));
}
display.setTextSize(1);
display.setCursor(0, 52);
display.print(F("OUT:"));
display.print(g_magnet ? 0 : 1);
display.print(F(" CNT:"));
display.print(g_triggerCount);
display.display();
}
// =========================
// 串口输出(有变化才输出)
// =========================
static void printSerialState() {
Serial.printf("STATE: %s | OUT: %d | CNT: %lu\n",
getStateText(),
g_magnet ? 0 : 1,
(unsigned long)g_triggerCount);
}
void setup() {
Serial.begin(115200);
delay(100);
Serial.printf("KY-003 hall demo %s starting...\n", FIRMWARE_VERSION);
pinMode(HALL_OUT_PIN, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
// I2C1 重映射:PB9=SDA / PB8=SCL,必须先 setSDA/setSCL 再 begin()
Wire.setSDA(I2C_SDA_PIN);
Wire.setSCL(I2C_SCL_PIN);
Wire.begin();
Wire.setClock(100000); // I2C 100kHz,与 01~06 系列公共标准一致
if (display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
g_hasOled = true;
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.display();
Serial.println(F("OLED 0x3C: found"));
} else {
Serial.println(F("OLED 0x3C: NOT found (continue with serial only)"));
}
// 以真实上电电平作为初始状态,避免上电误触发计数
g_rawState = readHallDetected();
g_magnet = g_rawState;
g_lastChangeMs = millis();
digitalWrite(LED_PIN, g_magnet ? LOW : HIGH);
// 首帧:串口 + OLED 都显示一次当前状态
printSerialState();
if (g_hasOled) {
drawOledUi();
}
}
void loop() {
updateHallState();
if (g_serialDirty) {
g_serialDirty = false;
printSerialState();
}
if (g_oledDirty && g_hasOled) {
g_oledDirty = false;
drawOledUi();
}
}
依赖库:Adafruit SSD1306 和 Adafruit GFX Library,用 PlatformIO 打开工程会按 platformio.ini 自动下载,不用手动安装。
Download
下载
解压后用 VS Code + PlatformIO 打开这个文件夹就能 Build,一般不需要改代码,也不需要改 platformio.ini。
第一次使用
- 按上面接线表接好:模块 S→PA0、+→3.3V、-→GND;OLED SDA→PB9、SCL→PB8、VCC→3.3V、GND→GND
- 下载 ZIP,解压,用 VS Code + PlatformIO 打开这个文件夹
- 等 Adafruit SSD1306 和 GFX 库自动装完,点 Build,看到 SUCCESS
- 接上 ST-Link,点 Upload,看到
** Verified OK **和[SUCCESS] - 打开串口监视器(115200),磁铁 B 面从模块下面靠近感应面,OLED 应从 CLEAR 变 MAGNET DETECTED,串口 OUT 从 1 变 0,CNT +1
正常应该看到什么
| 动作 | OLED / 串口 / LED 上的表现 |
|---|---|
| 上电 | OLED 显示 07 HALL v1.0 与 CLEAR;串口 STATE: CLEAR | OUT: 1;LED 灭 |
| 磁铁 B 面靠近感应面 | OLED 变 MAGNET DETECTED;串口 OUT: 0;LED 亮;CNT +1 |
| 磁铁移开 | OLED 恢复 CLEAR;LED 灭;CNT 保持不变 |
pio device list,找 CP2102 / CP210x / CH340 对应的那个。
Troubleshooting
常见问题排查
下面每一条都是这个模块真实会遇到的。标注「实测」的是我们自己踩过的。
磁铁靠近没反应(OUT 一直是高电平)
按这个顺序查三处:
- 磁铁是不是从模块「下面」对准感应面。感应面在电路板背面,贴正面不会触发。
- 磁铁换一面试试。单极霍尔只认一个磁极,一面不触发是正常的,换另一面(有效磁极)靠近再试。
- S 是否真接在 PA0。接错引脚或共地不良都会导致读不到。
磁铁一面触发、另一面不触发,是不是坏了
不是坏。这块 KY-003 用的是单极霍尔开关,本来就只认一个磁极。磁铁只有「有效磁极」那一面靠近才触发,另一面靠近、或从正面靠近都不触发。
做门磁、计数、到位检测这类应用,用有效磁极那一面(本块是 B 面)靠近感应面即可。
临界距离时 LED 乱跳 / 计数乱涨
磁铁正好停在触发临界距离上,OUT 在抖。磁场就在阈值上下抖,OUT 快速来回跳。
怎么办:代码已做 30ms 消抖能挡掉大部分,但机械上还是建议把磁铁拿近一点或拿远一点,离开临界距离。
OLED 不亮 / 串口打印 OLED 0x3C: NOT found
十有八九是 I2C 引脚或地址。按这个顺序查:
- 确认 SDA 接 PB9、SCL 接 PB8,不是默认的 PB7/PB6。
- 确认代码里
setSDA/setSCL在Wire.begin()之前,顺序反了配置不生效。 - 确认 OLED 地址。常见 0.96 寸 SSD1306 大多是
0x3C,少数是0x3D。 - 确认共地。OLED 的 GND 必须和 STM32 接在一起。
检测到磁铁时板载 LED 反而灭、远离反而亮
PC13 是低电平点亮。BluePill 的板载 LED 接在 PC13 上,低电平亮、高电平灭,所以代码里「检测到写 LOW、远离写 HIGH」。
如果表现正好反了,检查是否抄错极性,或者换用别的高电平点亮的状态灯引脚。
串口看不到数据
先查当前串口:
pio device list
找到 CP2102 / CP210x / CH340 对应的那个 COM,然后在自己电脑的 platformio.ini 里加:
monitor_port = COMx
monitor_speed = 115200
COMx 要换成你自己电脑实际识别到的端口号,不要直接抄。只有需要看串口时才加这两行,正常用 ST-Link 烧录不需要。
状态反了:有效磁极靠近显示 CLEAR
不同厂家模块的输出极性可能不同。确认模块 S 接的是 PA0,且代码用「低电平=有效磁极靠近」判断。如果反了,改代码里 readHallDetected() 的判断(把 == LOW 改成 == HIGH),或换一根逻辑相反的引脚接线。
References
资料来源
本页信息来源声明
| 内容 | 来源 |
|---|---|
| 接线、代码、实测结果、编译烧录记录、串口输出 | 本项目实测 2026-08-17 实机测试 |
| 3144 单极霍尔开关工作原理、霍尔效应、开漏输出 | 资料参考 公开资料与常规工程实践 |
| 与线性霍尔 / 磁力计的对比 | 资料参考 三类器件的工作原理,由我们整理成表 |
| 有效磁极是 N 还是 S | 待确认 本块磁铁无丝印,暂标 A 面 / B 面 |
本页所有「资料参考」内容均由我们重新整理表述,未直接复制原文。不同厂家 / 批次模块可能存在引脚顺序或检测逻辑差异,参数以你手上模块的实际丝印和卖家说明为准。