传感器 · 霍尔磁力 · 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。

低=磁OUT 低电平判有效磁极
30 ms消抖稳定窗口
PC13板载状态灯
0x3COLED I2C 地址

有无磁铁实测

测试动作预期实测结果
上电,磁铁远离OLED 显示 CLEAR,串口 OUT:1,LED 灭CLEAR / OUT:1 / LED 灭
A 面靠近感应面(下面)无变化(无效磁极)仍 CLEAR / OUT:1
B 面(有效磁极)靠近感应面OLED 显示 MAGNET DETECTED,串口 OUT:0,LED 亮,CNT +1MAGNET 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
未验证的环境不当成支持。本项目没有在 Arduino IDE、Keil、STM32CubeIDE 下编译或验证过,所以不提供这些环境的支持说明。

What it does

这个模块干什么

KY-003 霍尔磁力传感器就是给单片机装一个「有没有磁铁靠近」的开关。核心是一颗 3144 单极霍尔开关(认磁极):磁铁只有一个磁极靠近感应面时,输出才被拉低;磁铁移开、或换另一面靠近,输出都保持高电平。门磁检测、计数、到位检测、转速测量都用这一类模块。

它只输出「有 / 没有磁铁」这一个二值结果,不测磁场强度,也不告诉你磁铁离多远。感应面在模块下面(电路板背面那一面),磁铁要从下面对准才有效。

三个要点

要点意思
单极霍尔开关(认磁极)磁铁正反两面不是都能触发,只有「有效磁极」那一面靠近才触发
感应面在下面只有电路板背面这一面认磁性,磁铁要从下面对准,不是贴正面
不是测磁场强度只输出「过没过阈值」一个二值结果,不知道磁铁多强、离多远

和测磁场强度的传感器怎么选

KY-003 霍尔开关线性霍尔(如 49E)磁力计(如 QMC5883)
输出高低电平(有 / 无磁铁)模拟电压(随磁场强弱变)三轴磁场数值
能不能测强度不能,只判有无能,模拟量能,数字量
适合场景门磁、计数、到位检测、转速测磁场强弱、接近度指南针、姿态、磁场分布

对比依据为三类器件的工作原理,由我们整理成表。资料参考

磁铁一面触发、一面不触发,不是模块坏了。这是单极霍尔开关的正常表现——它只认一个磁极。做门磁、计数这类「有磁/无磁」的应用,用有效磁极那一面(本块是 B 面)靠近感应面即可。

Wiring

接线

本项目实测 KY-003 模块和 OLED 分开接,模块 3 根线 + OLED 4 根线。下面接法实机跑通。

KY-003 模块(正面三针顺序 - / + / S)

信号模块丝印STM32F103C8T6
地-GND
电源+3.3V
输出SPA0

OLED

信号OLEDSTM32F103C8T6
电源VCC / VDD3.3V
地GNDGND
时钟SCL / SCKPB8
数据SDAPB9

烧录与串口(不接也能跑,接上方便看数据)

用途连接说明
烧录ST-Link → SWD 四线(3.3V / GND / SWDIO / SWCLK)烧录用
串口PA9(TX) / PA10(RX) → CP2102 等看串口数据(115200)
KY-003 霍尔模块实测接线照片
实测接线:KY-003 S→PA0、+→3.3V、-→GND;OLED SCL→PB8、SDA→PB9
最容易接错的三处,接错了都不会报错,就是表现不对。
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 标记)刷新串口和 OLEDg_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。

PlatformIO 工程 ZIP 含 platformio.ini、src/main.cpp、README.md。已去掉本机构建路径,解压即可用。约 3.9 KB
下载 ZIP

第一次使用

  1. 按上面接线表接好:模块 S→PA0、+→3.3V、-→GND;OLED SDA→PB9、SCL→PB8、VCC→3.3V、GND→GND
  2. 下载 ZIP,解压,用 VS Code + PlatformIO 打开这个文件夹
  3. 等 Adafruit SSD1306 和 GFX 库自动装完,点 Build,看到 SUCCESS
  4. 接上 ST-Link,点 Upload,看到 ** Verified OK ** 和 [SUCCESS]
  5. 打开串口监视器(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 保持不变
这块板子不能用网页烧录器。网页烧录(WebSerial)主要面向 ESP32 / ESP8266 这类自带 USB 串口下载的芯片。STM32F103C8T6 本项目用的是 ST-Link + SWD,浏览器接触不到 SWD 接口。
不要照抄 COM 号。截图里出现的串口号是我们这台电脑的,你的电脑多半不一样。查当前串口:pio device list,找 CP2102 / CP210x / CH340 对应的那个。

Troubleshooting

常见问题排查

下面每一条都是这个模块真实会遇到的。标注「实测」的是我们自己踩过的。

磁铁靠近没反应(OUT 一直是高电平)

按这个顺序查三处:

  1. 磁铁是不是从模块「下面」对准感应面。感应面在电路板背面,贴正面不会触发。
  2. 磁铁换一面试试。单极霍尔只认一个磁极,一面不触发是正常的,换另一面(有效磁极)靠近再试。
  3. S 是否真接在 PA0。接错引脚或共地不良都会导致读不到。
磁铁一面触发、另一面不触发,是不是坏了

不是坏。这块 KY-003 用的是单极霍尔开关,本来就只认一个磁极。磁铁只有「有效磁极」那一面靠近才触发,另一面靠近、或从正面靠近都不触发。

做门磁、计数、到位检测这类应用,用有效磁极那一面(本块是 B 面)靠近感应面即可。

临界距离时 LED 乱跳 / 计数乱涨

磁铁正好停在触发临界距离上,OUT 在抖。磁场就在阈值上下抖,OUT 快速来回跳。

怎么办:代码已做 30ms 消抖能挡掉大部分,但机械上还是建议把磁铁拿近一点或拿远一点,离开临界距离。

OLED 不亮 / 串口打印 OLED 0x3C: NOT found

十有八九是 I2C 引脚或地址。按这个顺序查:

  1. 确认 SDA 接 PB9、SCL 接 PB8,不是默认的 PB7/PB6。
  2. 确认代码里 setSDA/setSCL 在 Wire.begin() 之前,顺序反了配置不生效。
  3. 确认 OLED 地址。常见 0.96 寸 SSD1306 大多是 0x3C,少数是 0x3D。
  4. 确认共地。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 面

本页所有「资料参考」内容均由我们重新整理表述,未直接复制原文。不同厂家 / 批次模块可能存在引脚顺序或检测逻辑差异,参数以你手上模块的实际丝印和卖家说明为准。