应广单片机比较器测电压代码详解:低压检测、分压计算与调试优化
目录
一、应广单片机比较器测电压,本质上是在做什么?
在很多低成本电子产品中,经常会遇到电压检测需求,例如电池低压提醒、输入电源检测、充电电压判断、过压保护、掉电检测等。对于这些场景,很多工程师会想到使用 ADC 采样,但在一些应广单片机项目中,使用内部比较器往往也是一种更简单、更经济的方案。
需要先说明一点:比较器并不能像 ADC 一样读取具体电压值。
ADC 可以得到类似这样的结果:
当前电池电压 = 3.72V
而比较器只能判断两个电压谁高谁低:
待测电压 > 参考电压
或
待测电压 < 参考电压
所以,所谓“应广单片机比较器测电压”,更准确地说,应该叫:
应广单片机利用比较器进行电压阈值检测
也就是说,它适合判断电压是否超过某个门槛,而不是用来做高精度电压测量。
如果产品只需要知道“电池是否低压”“电源是否插入”“电压是否超过保护点”,比较器方案非常合适;如果需要显示电压数值、电池百分比或进行精细控制,则应优先考虑 ADC 方案。
二、为什么应广单片机项目常用比较器检测电压?
应广单片机常见于小家电、玩具、LED 控制、充电器、消费电子、电池供电产品等成本敏感场景。这类产品往往有几个特点:
- 成本要求低;
- 程序资源有限;
- IO 和 ADC 资源有限;
- 功耗要求较高;
- 电压检测只需要判断高低,不需要精确数值。
在这种情况下,使用比较器检测电压有明显优势。
1. 成本低,外围简单
如果芯片内部已经集成比较器和参考电压模块,外部通常只需要两个分压电阻,就可以完成电池低压检测或输入电压判断。相比外置比较器芯片,BOM 成本更低,PCB 面积也更小。
2. 程序逻辑简单
ADC 检测电压通常需要:
启动 ADC → 等待转换 → 读取采样值 → 换算电压 → 滤波 → 判断阈值
而比较器只需要判断输出状态:
if (CMP_OUT) {
// 电压高于阈值
} else {
// 电压低于阈值
}
对低成本 MCU 来说,这种逻辑更轻量。
3. 响应速度快
比较器属于硬件模拟判断,电压跨过阈值后输出状态会快速变化。对于过压保护、掉电检测、短路保护等场景,比较器比纯软件 ADC 轮询更直接。
4. 有利于低功耗设计
部分应广 MCU 型号支持比较器中断或低功耗唤醒。如果产品平时处于休眠状态,可以利用比较器检测外部电压变化,从而唤醒单片机。
5. 节省 ADC 资源
一些应广单片机型号 ADC 通道有限,或者产品中 ADC 已经用于 NTC、电位器、光敏电阻等采样。此时将低压检测交给比较器,可以释放 ADC 资源。
三、比较器和 ADC 检测电压有什么区别?
很多初学者会把比较器和 ADC 混在一起,但二者定位完全不同。
| 对比项目 | 比较器检测电压 | ADC 检测电压 |
|---|---|---|
| 输出结果 | 高于/低于阈值 | 具体数字量 |
| 是否能得到电压值 | 不能 | 可以 |
| 程序复杂度 | 低 | 较高 |
| 响应速度 | 快 | 取决于采样周期 |
| 成本和资源占用 | 低 | 较高 |
| 适合场景 | 欠压、过压、电源插入检测 | 电压显示、电量估算、传感器采样 |
| 精度 | 取决于参考源和比较器误差 | 取决于 ADC 位数和参考源 |
简单总结:
只判断门槛,用比较器;要读取数值,用 ADC。
例如:
- 判断电池是否低于 3.0V → 用比较器
- 显示电池电压为 3.68V → 必须用 ADC
- 判断 USB 5V 是否插入 → 用比较器
- 做电池百分比显示 → 建议用 ADC
四、应广单片机比较器检测电压的典型电路思路
比较器通常有两个输入端:
CMP+:正输入端
CMP-:负输入端
当:
CMP+ > CMP- → 比较器输出为高电平
CMP+ < CMP- → 比较器输出为低电平
实际项目中,常见连接方式如下:
待测电压 → 电阻分压 → 比较器一个输入端
参考电压 → 比较器另一个输入端
例如检测电池是否低压:
电池电压 Vin
|
R1
|
+---- 接入比较器输入端
|
R2
|
GND
分压点电压为:
Vcmp = Vin × R2 / (R1 + R2)
然后让比较器将 Vcmp 与内部参考电压或外部参考电压进行比较。
五、分压电阻和检测阈值怎么计算?
计算前必须先确认芯片型号是否支持内部固定基准。 应广不同型号(如 PMS150C、PFS154、PMS132B 等)的比较器参考电压档位不同,部分型号只有 VDD 分压,没有 1.2V 固定基准。若芯片没有固定基准,请改用外部参考电压或 ADC 方案。
假设希望检测电池电压是否低于 3.0V,当前芯片型号支持 1.2V 内部参考电压,那么可以让电池电压经过分压后,在 3.0V 时刚好等于 1.2V。
公式如下:
Vcmp = Vin × R2 / (R1 + R2)
设定:
Vin = 3.0V
Vcmp = 1.2V
则:
1.2 = 3.0 × R2 / (R1 + R2)
也就是:
R2 / (R1 + R2) = 0.4
可以选择:
R1 = 150K
R2 = 100K
此时:
Vcmp = Vin × 100K / (150K + 100K)
Vcmp = Vin × 0.4
当电池电压为 3.0V 时:
Vcmp = 3.0V × 0.4 = 1.2V
这样比较器就可以根据分压点与参考电压的大小关系,判断电池是否低于设定阈值。
功耗提示: 150K + 100K 分压在 3V 电池下消耗约 12µA。如需进一步降低功耗,可选用 MΩ 级电阻(如 1.5M + 1M),但需注意高阻分压更容易受干扰,建议增加滤波电容。
六、分压设计时不能忽略输入电压范围
这是比较器测电压方案中非常重要的一点。
无论是普通 IO、ADC 输入,还是比较器输入,引脚电压都不能超过芯片规格书允许范围。特别是检测高于 MCU 供电电压的信号时,不能直接接到单片机引脚。
例如单片机供电为 3V,而外部输入可能是 5V、12V 甚至更高,就必须通过电阻分压、限流、滤波或钳位保护后再接入比较器输入端。
实际设计时需要关注:
- 分压后最大电压不能超过比较器输入允许范围;
- 输入电流不能超过 IO 钳位能力;
- 高阻分压容易受干扰;
- 必要时增加 RC 滤波;
- 强干扰环境下可增加限流电阻或保护器件;
- 具体限制必须以应广对应型号规格书为准。
这一点不能只靠代码解决,硬件设计必须先安全。
七、参考电压来源会直接影响检测精度
很多文章在讲应广单片机比较器测电压代码时,会默认芯片内部有 1.2V 参考电压。但实际情况并不一定如此。
不同应广单片机型号的比较器资源差异较大,可能存在以下几种情况:
- 支持内部固定参考电压;
- 支持内部 Bandgap 基准;
- 支持 VDD 分压作为参考;
- 支持外部参考电压输入;
- 参考电压档位有限;
- 某些低成本型号比较器功能较简单。
如果参考电压来自稳定的内部基准,适合做电池低压判断。
如果参考电压来自 VDD 分压,而待测电压本身又是 VDD,那么比较关系可能会随电源一起变化,无法得到稳定的绝对低压点。
因此,做低压检测时要特别注意:
- 参考源是否稳定?
- 参考源是否随 VDD 变化?
- 检测对象是否就是 VDD 本身?
- 芯片内部参考误差有多大?
- 量产时是否需要留余量?
如果产品对阈值要求较高,应考虑校准、外部基准,或者使用 ADC 结合软件修正。
八、应广单片机比较器初始化代码思路
由于应广单片机型号较多,不同芯片的寄存器名称、比较器输入通道、参考电压选择方式、开发环境都可能不同。 以下代码更适合作为比较器电压检测代码框架,实际项目中应以具体型号的 数据手册、官方头文件、官方例程、Padauk IDE 或对应开发环境说明、Mini-C 或汇编支持情况为准进行移植。
/*
* 应广单片机比较器电压检测代码框架
*
* 说明:
* 1. 不同型号寄存器名称不同,以下代码用于说明配置流程;
* 2. 实际项目请根据具体芯片规格书和官方例程修改;
* 3. 比较器只能判断电压高低,不能读取具体电压值;
* 4. 若芯片不支持内部参考电压,需要改用外部参考或其他方案。
*/
void Comparator_Init(void)
{
/*
* 1. 配置待测电压输入脚
* - 设置为输入模式;
* - 关闭上拉/下拉;
* - 如芯片要求,切换为模拟功能;
* - 确认该引脚支持比较器输入复用。
*/
// Set_CMP_Input_Pin();
// Disable_Pullup();
// Select_Analog_Function();
/*
* 2. 开启比较器模块
*/
// Comparator_Enable();
/*
* 3. 选择比较器正输入端
* 例如:CMP+ = 外部分压电压
*/
// Comparator_Positive_Input_Select(EXTERNAL_PIN);
/*
* 4. 选择比较器负输入端
* 例如:CMP- = 内部参考电压或外部参考电压
*/
// Comparator_Negative_Input_Select(INTERNAL_VREF);
/*
* 5. 开启并选择参考电压
* 注意:并非所有型号都支持可选内部参考电压。
*/
// Vref_Enable();
// Vref_Select(VREF_1_2V);
/*
* 6. 设置输出极性
* 根据实际逻辑选择是否反相。
*/
// Comparator_Output_Polarity_Normal();
/*
* 7. 等待比较器和参考电压稳定
* 实际延时根据芯片手册和测试结果决定。
*/
DelayMs(2);
/*
* 8. 清除比较器标志位
* 如需中断,再开启比较器中断。
*/
// Comparator_Clear_Flag();
// Comparator_Interrupt_Enable();
}
九、轮询方式读取比较器结果
如果产品对响应速度要求不高,比如普通电池低压提醒,可以在主循环中定时读取比较器输出状态。
typedef enum {
VOLTAGE_NORMAL = 0,
VOLTAGE_LOW
} voltage_state_t;
volatile voltage_state_t voltage_state = VOLTAGE_NORMAL;
void Voltage_Check_Task(void)
{
/*
* 假设:
* CMP_OUT = 1 表示待测电压高于参考电压;
* CMP_OUT = 0 表示待测电压低于参考电压。
*
* 实际逻辑要根据比较器输入端连接方式和输出极性确认。
*/
if (CMP_OUT) {
voltage_state = VOLTAGE_NORMAL;
} else {
voltage_state = VOLTAGE_LOW;
}
}
主循环示例:
int main(void)
{
System_Init();
Comparator_Init();
while (1) {
Voltage_Check_Task();
if (voltage_state == VOLTAGE_LOW) {
Low_Voltage_Process();
} else {
Normal_Work_Process();
}
DelayMs(10);
}
}
轮询方式优点是简单、稳定、容易调试。缺点是响应速度取决于查询周期。
十、增加软件滤波,避免临界点误判
实际产品中,电池电压、电源输入、电机负载、电感开关、LED 驱动都会带来纹波和干扰。如果只判断一次比较器输出,可能会误判。更可靠的方式是连续多次确认。
#define LOW_CONFIRM_COUNT 5
#define NORMAL_CONFIRM_COUNT 5
void Voltage_Check_Filter(void)
{
static unsigned char low_count = 0;
static unsigned char normal_count = 0;
if (CMP_OUT == 0) {
/*
* 电压低于阈值
*/
if (low_count < LOW_CONFIRM_COUNT) {
low_count++;
}
normal_count = 0;
if (low_count >= LOW_CONFIRM_COUNT) {
voltage_state = VOLTAGE_LOW;
}
} else {
/*
* 电压高于阈值
*/
if (normal_count < NORMAL_CONFIRM_COUNT) {
normal_count++;
}
low_count = 0;
if (normal_count >= NORMAL_CONFIRM_COUNT) {
voltage_state = VOLTAGE_NORMAL;
}
}
}
如果每 10ms 检测一次,连续 5 次确认,相当于需要约 50ms 才改变状态。这样可以有效过滤瞬间抖动。
十一、迟滞设计:比较器稳定性的关键
比较器检测电压时,最常见的问题是阈值附近反复跳变。
比如低压阈值设为 3.0V,当电池电压在 2.99V 到 3.01V 之间波动时,比较器输出可能频繁变化,导致系统一会儿低压、一会儿正常。解决这个问题,最好加入迟滞。
1. 软件迟滞
软件迟滞的思路是:进入低压和恢复正常使用不同阈值。
低于 3.0V:判定为低压
高于 3.2V:恢复为正常
这样电压必须明显回升后才恢复,系统不会在临界点来回抖动。
如果只有一个比较器固定阈值,也可以通过状态机、延时确认、不同处理策略实现类似效果。
2. 硬件迟滞
硬件迟滞通常通过正反馈电阻实现,使比较器翻转后阈值发生轻微变化。它的响应更直接,但需要额外计算电阻参数,并验证实际波形。
3. 内部迟滞
部分 MCU 比较器可能支持内部迟滞功能。如果应广具体型号支持,应优先查看规格书中的比较器迟滞配置位。
工程经验上,低压检测不建议只做一次判断,至少要有以下其中一种措施:
- 软件多次确认;
- 软件迟滞;
- 硬件 RC 滤波;
- 硬件迟滞;
- 芯片内部迟滞。
十二、中断方式检测电压变化
如果需要快速响应电压变化,例如掉电检测、过压保护、外部电源插入唤醒,可以使用比较器中断。
示例代码框架如下:
void Comparator_ISR(void)
{
/*
* 不同应广型号中断入口和标志位名称不同,
* 以下仅表示处理流程。
*/
if (Comparator_Get_Flag()) {
Comparator_Clear_Flag();
if (CMP_OUT) {
voltage_state = VOLTAGE_NORMAL;
} else {
voltage_state = VOLTAGE_LOW;
low_voltage_flag = 1;
}
}
}
中断方式的优点:
- 响应速度快;
- 不需要频繁轮询;
- 适合保护类功能;
- 可用于低功耗唤醒。
中断方式的缺点:
- 阈值附近抖动可能导致频繁中断;
- 需要正确清除中断标志;
- 需要处理误触发;
- 中断内不宜执行复杂任务。
建议中断中只设置标志位,具体处理放到主循环中完成。
if (low_voltage_flag) {
low_voltage_flag = 0;
/*
* 在主循环中执行低压处理
* 例如关闭负载、停止 PWM、提示用户等。
*/
Low_Voltage_Process();
}
十三、完整软件框架示例
下面给出一个更接近实际项目的框架,包含初始化、滤波和状态处理。依旧需要根据具体应广单片机型号修改底层寄存器。
/*
* 应广单片机比较器电压检测完整框架
*
* 功能:
* - 使用比较器检测电压是否低于设定阈值
* - 包含软件滤波,防止误触发
* - 状态变化回调处理
* - 适用于电池低压检测、电源插入检测等场景
*/
#include
#include
/* ===== 硬件配置 ===== */
#define CMP_OUT (P0 & (1 << 0)) // 比较器输出引脚
#define LOW_CONFIRM_COUNT 5
#define NORMAL_CONFIRM_COUNT 5
/* ===== 状态定义 ===== */
typedef enum {
VOLTAGE_NORMAL = 0,
VOLTAGE_LOW
} voltage_state_t;
/* ===== 全局变量 ===== */
voltage_state_t voltage_state = VOLTAGE_NORMAL;
volatile bool low_voltage_pending = false;
/* ===== 比较器初始化 ===== */
void Comparator_Init(void)
{
/*
* 1. 配置输入引脚
* 2. 开启比较器
* 3. 选择输入通道和参考电压
* 4. 设置输出极性
* 5. 等待稳定
*/
// 具体寄存器配置请参考对应型号数据手册
}
/* ===== 滤波检测 ===== */
void Voltage_Check_Filter(void)
{
static uint8_t low_count = 0;
static uint8_t normal_count = 0;
if (CMP_OUT == 0) {
if (low_count < LOW_CONFIRM_COUNT) {
low_count++;
}
normal_count = 0;
if (low_count >= LOW_CONFIRM_COUNT) {
if (voltage_state != VOLTAGE_LOW) {
voltage_state = VOLTAGE_LOW;
low_voltage_pending = true;
}
}
} else {
if (normal_count < NORMAL_CONFIRM_COUNT) {
normal_count++;
}
low_count = 0;
if (normal_count >= NORMAL_CONFIRM_COUNT) {
if (voltage_state != VOLTAGE_NORMAL) {
voltage_state = VOLTAGE_NORMAL;
low_voltage_pending = true;
}
}
}
}
/* ===== 低压处理函数 ===== */
void Low_Voltage_Process(void)
{
// 关闭非关键负载
// 停止 PWM 输出
// 点亮低压指示灯
// 或进入低功耗模式
}
/* ===== 恢复正常处理函数 ===== */
void Normal_Voltage_Process(void)
{
// 恢复负载
// 恢复 PWM
// 关闭低压指示灯
}
/* ===== 主循环 ===== */
int main(void)
{
System_Init();
Comparator_Init();
while (1) {
// 定时检测(约 10ms)
Voltage_Check_Filter();
if (low_voltage_pending) {
low_voltage_pending = false;
if (voltage_state == VOLTAGE_LOW) {
Low_Voltage_Process();
} else {
Normal_Voltage_Process();
}
}
DelayMs(10);
}
}
十四、实际项目调试技巧
以下3条调试经验来自实际项目,可帮助缩短开发周期:
技巧1:确认阈值前先测量分压点真实电压
电阻精度、NTC 误差、PCB 漏电都会影响分压比。建议在焊接后先用万用表测量比较器输入引脚的实际电压,确认是否与理论计算一致。如果偏差超过 ±5%,需调整电阻或修正阈值。
技巧2:量产时预留阈值调整位
不同批次芯片的比较器偏移电压可能有差异。建议在 PCB 上预留一个 0Ω 电阻或小型电位器,方便量产时微调分压比,确保阈值一致性。
技巧3:用示波器观察比较器输出波形
在阈值附近,比较器输出可能出现高频振荡(尤其是输入信号有纹波时)。使用示波器观察 CMP_OUT 引脚波形,确认是否有异常抖动。如有抖动,优先增加软件滤波次数(如从 5 次改为 10 次),其次考虑增加硬件 RC 滤波。
重要提醒:以上代码为框架说明,不可直接复制编译。应广单片机不同型号的寄存器定义、比较器通道、参考电压选项均有差异,请务必以具体型号的 数据手册(Datasheet) 和 官方开发环境 为准进行移植。
结语
应广单片机内部比较器是一个被很多工程师低估的模块。在电池低压检测、电源插入检测、过压保护、掉电预警等场景中,它可以用最少的硬件成本和最简洁的代码完成可靠的电压阈值判断。
关键不在于“怎么写代码”,而在于:
- 硬件上——分压安全、参考稳定、滤波合理;
- 软件上——防抖可靠、迟滞合理、状态切换平滑;
- 工程上——确认芯片型号支持、验证量产一致性。



