【CW32无线抄表项目】模拟电压(VC)比较器
招标
发布时间:
2026-04-27
发布于
--
收藏
公告内容
项目编号
立即查看
项目预算
立即查看
采购单位
立即查看
供应商
立即查看
采购代理
立即查看
公告详情
您当前为:【游客状态】,公告详情仅对登录用户开放,
登录/注册
后查看完整商机。全国免费咨询热线:400-888-7022

【CW32无线抄表项目】模拟电压(VC)比较器

原创 CW32 CW32生态社区

一 VC介绍
特性 ADC (模数转换) VC (电压比较器)
反应速度 慢(需要采样、转换、计算) 极快(纳秒级响应,几乎瞬间)
功耗 高(ADC 模块很费电) 极低(微安级,适合电池供电)
省心程度 需要 CPU 不停地去“问”结果 自动触发(电压一变立马发中断,CPU 可以睡觉)

纯硬件电路(电压比较器 VC)来代替软件算法(ADC 采样),从而解放 CPU。

框图详情

整体结构

VC1 和 VC2 是两套几乎对称的比较器通道,每路都有 正输入 INP、负输入 INN、比较器核心、极性控制、窗口模式、数字滤波和中断逻辑。

比较结果最终既可以作为内部状态/标志使用,也可以形成 VC1_OUT / VC2_OUT 输出信号,并触发 VC1中断 / VC2中断。

输入部分怎么理解

INP、INN 前面都有一个多路选择器,说明比较器输入源不是固定的,而是可以选不同通道。

外部通道可以选 VCx_CH0 ~ VCx_CH7 之类的模拟输入。

内部参考源也能接进来,图里明确给了:

温度传感器

1.2V基准电压

ADC参考电压

中间还有一个 电阻分压器,输入可来自 VDDA 或 ADC参考电压,输出为 VCx_DIV.DIV,这说明芯片内部还能先做一次分压,再送去比较器当阈值参考。

比较器核心

中间三角形带 +/- 的就是模拟比较器本体。

VCx_CR0.EN:使能比较器。

VCx_CR0.HYS:迟滞控制。这个很重要,输入电压靠近阈值时容易抖动,开迟滞可以减少误翻转。

输出整形

VCx_CR0.POL:输出极性翻转。也就是你可以选择“正向输出”还是“反向输出”。

后面还有 VCx_CR0.WINDOW:这表示 VC1 和 VC2 不一定完全独立,也可以组合成一个“窗口比较器”。

窗口模式的意义

正常模式下,VC1、VC2 各比各的。

窗口模式下,通常一个比较器作为“下限阈值”,另一个作为“上限阈值”:

低于下限

落在窗口内

高于上限

这样就很适合做“区间检测”,比如电池电压是否在正常范围内,而不只是“高于某个点”。

图中两个比较器中间交叉和逻辑门的部分,就是在做这个窗口逻辑组合。

数字滤波和状态输出

数字滤波 模块说明比较结果不是直接裸输出,而是可以经过稳定化处理。

VCx_CR1.FLTCLK:滤波时钟。

VCx_CR1.FLTTIME:滤波时间/滤波长度。

这类设计通常用于抑制噪声、毛刺、慢速抖动。

滤波后的结果会反映到:

VCx_SR.FLTV:滤波后的状态标志

VCx_OUT:最终输出信号

中断部分

触发条件选择 由 VCx_CR1[7:5] 控制,通常就是配置:

上升沿触发

下降沿触发

双沿触发

或某种电平/状态触发

VCx_CR0.IE:中断使能。

条件满足并且中断使能后,输出 VCx中断。

这张图对应的典型用途

电压阈值检测:比如某路电压是否超过设定值

欠压/过压检测:配合内部参考和分压器

窗口检测:判断电压是否落在某个安全区间

零点检测/波形边沿检测

传感器阈值比较:温度、模拟量越界判断

读图时最关键的信号链

输入选择:INP/INN 选谁

比较器配置:EN、HYS

输出逻辑:POL

是否启用窗口:WINDOW

是否做数字滤波:FLTCLK、FLTTIME

是否开中断:IE 和触发条件选择

配置流程

第一步,确定使用 VC1 还是 VC2,以及是“单路比较”还是“窗口比较”

第二步,配置比较器输入源:谁接 INP,谁接 INN

第三步,如果要用内部参考/分压阈值,先配置分压器

第四步,配置比较器本体参数:使能前先设 HYS、POL、WINDOW

第五步,配置数字滤波:FLTCLK、FLTTIME

第六步,配置中断触发条件和中断使能

第七步,最后打开比较器 EN,再读取输出标志或等待中断

按寄存器拆开看

VCx_CR0.INP

选择比较器正端输入 VCx_INP

可选外部通道,如 VCx_CH0 ~ VCx_CH7

VCx_CR0.INN

温度传感器

1.2V基准电压

ADC参考电压

分压器输出

选择比较器负端输入 VCx_INN

除外部通道外,从图上看还能选内部源:

VCx_DIV.VIN

选择分压器输入源

图里给出的候选是 VDDA 或 ADC参考电压

VCx_DIV.DIV

设置分压比

作用是把内部电压先缩放,再作为比较阈值送入比较器

VCx_CR0.HYS

设置迟滞

输入靠近阈值有噪声时建议打开,能减少抖动误翻转

VCx_CR0.POL

设置输出极性

正常输出还是反向输出,由这个位控制

VCx_CR0.WINDOW

设置是否进入窗口比较模式

单独使用某一路时通常关闭

VC1 + VC2 组成上下限窗口检测时打开相关配置

VCx_CR1.FLTCLK

选择数字滤波时钟

VCx_CR1.FLTTIME

选择数字滤波时间/长度

值越大,一般抗毛刺越强,但响应更慢

VCx_CR1[7:5]

选择中断触发条件

从框图看,是“触发条件选择”,通常会是上升沿/下降沿/双沿一类

VCx_CR0.IE

使能该路比较器中断输出

VCx_CR0.EN

最后一步使能比较器

VCx_SR.FLTV

读取滤波后的比较结果状态

VCx_OUT

比较器最终输出信号

推荐的实际配置顺序

1) 先关闭 VCx_CR0.EN

2) 配置 VCx_CR0.INP,选择正端输入

3) 配置 VCx_CR0.INN,选择负端输入

4) 如果负端或正端要用内部阈值,先配 VCx_DIV.VIN 和 VCx_DIV.DIV

5) 配置 VCx_CR0.HYS

6) 配置 VCx_CR0.POL

7) 如果要做窗口比较,再配置 VCx_CR0.WINDOW

8) 配置 VCx_CR1.FLTCLK 和 VCx_CR1.FLTTIME

9) 如果要中断,配置 VCx_CR1[7:5] 和 VCx_CR0.IE

10) 清状态标志(如果手册定义有清标志位)

11) 打开 VCx_CR0.EN

12) 通过 VCx_SR.FLTV 轮询,或者等待 VCx 中断

翻成“使用场景”会更好理解

普通比较模式

目标:判断某个输入电压是否高于阈值

配法:

INP 接被测信号

INN 接 1.2V基准 或分压后的内部参考

配 HYS

可选 POL

WINDOW=0

需要稳定输出就开滤波

需要事件响应就开中断

窗口比较模式

目标:判断输入值是否位于某个区间内

配法:

一路比较下限

一路比较上限

两路共享同一被测量,或按手册要求分别接入

打开窗口逻辑 WINDOW

输出就不再只是单纯“大于/小于”,而是参与区间判断

典型思路:

VC1:输入与“下限阈值”比较

VC2:输入与“上限阈值”比较

两路通过窗口逻辑组合后,判断:

低于下限

落在区间内

高于上限

一个更实用的寄存器思维导图

输入从哪来:INP / INN

阈值怎么来:内部基准 / 分压器 / 外部脚

输出要不要翻转:POL

抖动要不要抑制:HYS + FLTCLK + FLTTIME

是不是要做区间检测:WINDOW

要不要中断:CR1[7:5] + IE

最后开机:EN

几个配置经验

阈值附近信号有噪声时,优先开 HYS,再决定是否加数字滤波

想快速响应边沿,用较弱滤波;想避免误触发,用较强滤波

如果只做软件轮询,重点看 VCx_SR.FLTV

如果做中断检测边沿,重点配 VCx_CR1[7:5] 和 VCx_CR0.IE

如果输入源切换频繁,建议先关 EN 再改输入选择

霍尔模块介绍

AH812D

特性

高带带宽 (120kHz):反应极快,不管是水表慢转还是电机快转都能抓到。

静态 2.5V 输出:这是最重要的信息。意思是当周围没有磁场时,它默认吐出 2.5V 电压

耐热抗压:能在 -40°C 到 150°C 工作,工业级水准。

应用场景:除了咱们做的水表计圈,它还能测电流、控电机。

1脚 (VCC):电源正极。

2脚 (GND):电源负极(地)。

3脚 (VOUT):信号输出。它会把磁场的大小变成“变化的电压”从这里传给单片机的 PB00(也就是咱们连 VC 的地方)。

$$C_{BYPASS$$ (0.1uF):这是“电源滤镜”,必须靠近传感器的电源脚,防止电源杂波干扰测量。

$$C_$$ (0.5nF):这是“输出滤镜”,能让输出的电压信号更平滑,配合咱们之前代码里的数字滤波,效果翻倍。

工作电压 (4.5V - 5.5V):典型值是 5V

静态输出 (2.5V):再次强调,没磁场时就是 2.5V。

伏笔:这就是为什么你之前测到 2.5V 的原因,也是为什么咱们要把 VC 阈值设在 2.67V 的物理依据。

中心点:横轴(输出电压)在 2.5V 时,纵轴(磁场强度)是 0。

线性关系

磁铁的南极 (S) 靠近,电压往 4.5V 爬。

磁铁的北极 (N) 靠近,电压往 0.5V 掉。

型号区别:A、B、C、D 四条线斜率不同,代表灵敏度不同。斜率越陡(如 AH812-A),磁铁稍微动一点,电压跳得越厉害。

手册虽然推荐 0.5nF,但在低速计数的场景下(如水表、转速计),我们可以换成 470nF (104电容)。这样做相当于给信号加了一个‘减震器’,能有效防止因为磁铁手抖导致的误触发计数。

好处一:强力硬件“去噪”

霍尔传感器的输出信号比较微弱,容易受到电机或电源的杂波干扰。

0.5nF:只能滤掉极高频的电磁波。

470nF:形成了一个更强的低通滤波器。它能把绝大部分的高频毛刺直接掐死在硬件阶段,让传给单片机 PB00 的电压信号像丝绸一样顺滑。

好处二:自带硬件“防抖”

之前晃动磁铁时数值猛跳,很大一部分原因是电压在阈值附近抖动。

当用了 470nF 后,这个电容像一个“储能水槽”,它会让电压的变化变得缓慢而圆滑。

即使磁铁在边缘轻微抖动,电压也不会立刻跟着剧烈跳变。这种硬件级的延迟配合咱们代码里的数字滤波,能极大地解决“晃一下跳 145 圈”的问题。

唯一的代价:

牺牲了响应速度:用大电容会让传感器的反应变慢。如果用来检测每秒转几万转的电机,470nF 会让波形失真;但水表叶轮每秒钟可能才转几圈,这种微秒级的延迟完全可以忽略不计。

实物

钕磁铁

钕磁铁(Neodymium magnet),全称钕铁硼磁铁(NdFeB),在电子爱好者圈子里有个响亮的称号——“磁王”

它是目前人类能制造出的磁性最强的永久磁铁。别看它通常只有一颗纽扣甚至一颗米粒那么大,它能吸起自身重量 600 倍以上的物体。同等体积下,它的磁力最强;同等磁力下,它的体积最小。

在智能水表计圈这个具体的场景下,选用钕磁铁主要有三个“非它不可”的理由:

穿透力强(体积小): 水表的叶轮封装在充满水的密封腔内,而我们的传感器 PB00 是装在干燥的电路板上的。磁力必须穿过加厚的塑料外壳才能被感应到。普通磁铁如果想穿透这么厚的壳,体积会变得巨大,根本塞不进叶轮;而钕磁铁只需要一小块,就能释放出足够的磁通量。

极高的稳定性(寿命长): 水表一装就是几年甚至十年。普通磁铁容易随着时间流逝或温度变化而退磁(磁力变弱),导致计圈越来越不准。钕磁铁具有极高的矫顽力,只要不遇到极高温(通常高于 80℃),它的磁性能保持很多年不衰减。

线性响应更清晰: 配合选用的 AH812 线性霍尔传感器,钕磁铁能提供非常稳定的磁场梯度。这意味着当它靠近时,产生的电压变化非常干脆、线性度好,不会给单片机的 VC 模块带来模糊的“灰色地带”。

建立“磁力桥梁”

当叶轮旋转时,嵌在上面的钕磁铁会随之转动。磁铁周围存在着看不见的磁感线。当磁铁靠近 AH812 时,这些磁感线会垂直穿过传感器的芯片表面。

激发“霍尔效应”

AH812 内部有一个半导体薄片。平时电流平稳流过,输出 2.5V。当钕磁铁的磁场扫过时,磁力会把薄片里的电子推向一边。根据磁场强弱,电子偏转的程度不同,输出端的电压就会随之起伏(比如从 2.5V 爬升到 3.2V)。

触发“逻辑闸门”

这就是咱们之前代码干的事了:

钕磁铁远在天边:传感器输出 2.5V —> VC 比较器发现 2.5V < 2.67V 没动静。

钕磁铁近在眼前:传感器输出 3.2V —>VC 比较器发现 3.2V > 2.67V 触发中断,计数器 WaterPulseCount 加 1。

程序详情

第一步:确定使用模式 & 关使能

流程图节点:单路比较模式 -> 选 VC2 -> 关闭比较器使能

对应代码:我们选择了 VC2,因为你的霍尔传感器物理引脚 PB00 就是焊在 VC2 的通道 3 上。

第二步:是否使用内部分压阈值? (造一把尺子)

流程图节点:是 -> 配置 VCx_DIV.VIN (选源) -> 配置 VCx_DIV.DIV (设分压比)

对应代码

VC_DivStruct.VC_DivVref = VC_DivVref_VDDA; // 原料:选 3.3V 供电VC_DivStruct.VC_DivValue = 51; // 比例:切成 63 份,取 51 份

第三步:配置输入源 (牵线搭桥)

流程图节点:配置 VCx_CR0.INP (正端) / INN (负端)

对应代码

VC_InitStruct.VC_InputP = VC_InputP_Ch3; // 正端:接外面的水表 PB00VC_InitStruct.VC_InputN = VC_InputN_DivOut; // 负端:接内部的 2.67V 标尺

第四步:配置比较器本体 (定规矩)

流程图节点:配置 HYS (迟滞) / POL (极性) / WINDOW (窗口)

对应代码

VC_InitStruct.VC_Hys = VC_Hys_20mV; // 迟滞:20mV 缓冲带VC_InitStruct.VC_Polarity = VC_Polarity_Low; // 极性:正常输出VC_InitStruct.VC_Window = VC_Window_Disable; // 窗口:不搞花里胡哨的双重比较

第五步:是否启用数字滤波? (戴上降噪耳机)

流程图节点:是 -> 配置 FLTCLK (时钟) -> 配置 FLTTIME (时间)

对应代码

VC_InitStruct.VC_FilterEn = VC_Filter_Enable; VC_InitStruct.VC_FilterTime = VC_FltTime_63Clk; // 听大约 0.4 毫秒

第六步:是否启用中断? & 清标志

流程图节点:是 -> 选触发条件 -> 使能中断 -> 清除标志位

对应代码

VC2_ITConfig(VC_IT_FALL | VC_IT_RISE, ENABLE); // 靠近和离开都喊我VC2_EnableIrq(VC_INT_PRIORITY); // 打开法官办公室的门VC2_ClearIrq(); // 把以前的旧案子档案全撕了

第七步:使能比较器 & 等待

流程图节点:VCx_CR0.EN = 1 -> 等待中断

对应代码

VC2_EnableChannel(); // 法官,醒醒,开工了!VC_EnableNvic(ADC_IRQn, VC_INT_PRIORITY); // 打开老板办公室的对讲机接收端 vc.h #ifndef __VC_H#define __VC_H#include "cw32f030.h"#include "cw32f030_vc.h"#include "cw32f030_gpio.h"#include "cw32f030_rcc.h"// 1. 使用 extern 声明全局变量,告诉 main.c 这些变量的存在// 注意:这里绝对不能写 "= 0",只声明,不赋值!extern volatile boolean_t gFlagIrq;extern volatile uint32_t WaterPulseCount;// 2. 声明初始化函数void WaterMeter_VC_Init(void);#endif /* __VC_H */ vc.c #include "vc.h"// 1. 在这里真正地定义并初始化变量(它们的主人是 vc.c)volatile boolean_t gFlagIrq = FALSE;volatile uint32_t WaterPulseCount = 0;/** * @brief 水表计圈 VC 模块完整初始化 * 包含 GPIO、分压器、比较器、滤波器以及中断的配置 */void WaterMeter_VC_Init(void){ VC_InitTypeDef VC_InitStruct; VC_DivTypeDef VC_DivStruct; VC_BlankTypeDef VC_BlankStruct; VC_OutTypeDef VC_OutStruct; // 1. 开启时钟 __RCC_GPIOB_CLK_ENABLE(); __RCC_VC_CLK_ENABLE(); // 2. 将 PB00 设置为模拟输入模式 (VC2_CH3) PB00_ANALOG_ENABLE(); // 3. 配置内部分压器产生 2.67V 阈值 (3.3V * 51 / 63) VC_DivStruct.VC_DivVref = VC_DivVref_VDDA; VC_DivStruct.VC_DivEn = VC_Div_Enable; VC_DivStruct.VC_DivValue = 51; VC1VC2_DIVInit(&VC_DivStruct); // 4. 配置比较器通道 VC_InitStruct.VC_InputP = VC_InputP_Ch3; VC_InitStruct.VC_InputN = VC_InputN_DivOut; VC_InitStruct.VC_Hys = VC_Hys_20mV; VC_InitStruct.VC_Resp = VC_Resp_High; // 开启硬件滤波 (63个时钟周期) VC_InitStruct.VC_FilterEn = VC_Filter_Enable; VC_InitStruct.VC_FilterClk = VC_FltClk_RC150K; VC_InitStruct.VC_FilterTime = VC_FltTime_63Clk; VC_InitStruct.VC_Window = VC_Window_Disable; VC_InitStruct.VC_Polarity = VC_Polarity_Low; VC2_ChannelInit(&VC_InitStruct); // 5. 初始化空白窗口和输出连接 VC1VC2_BlankInit(&VC_BlankStruct); VC2_BlankCfg(&VC_BlankStruct); VC1VC2_OutInit(&VC_OutStruct); VC2_OutputCfg(&VC_OutStruct); // 6. 开启中断 VC2_ITConfig(VC_IT_FALL | VC_IT_RISE, ENABLE); VC2_EnableIrq(VC_INT_PRIORITY); VC2_ClearIrq(); VC2_EnableChannel(); // 7. 开启 NVIC 中断通道 VC_EnableNvic(ADC_IRQn, VC_INT_PRIORITY); } main.c #include "main.h"#include "cw32f030_gpio.h"#include "cw32f030_rcc.h"#include "init.h"#include "buffer.h"#include "fun.h"#include "radio.h"#include "delay.h"#include "flashhoufun.h" #include "cw32_eval_spi_flash.h"#include "dma.h"#include "vc.h"// 全局中断标志 (fun.c 也要用)volatile uint8_t g_bIrqTriggered = 0; void System_Init_Config(void);//int32_t main(void)//{ // // 1. 硬件初始化// System_Init_Config();// // // 2. 射频初始化// if (rf_init() != OK) // {// while(1); // 失败报警// }// rf_set_default_para();// // 3. 初始状态设置 (编译时决定)// #ifdef SLAVE_MODE// // [从机] 上电必须开启接收,否则听不到第一句// rf_enter_single_timeout_rx(15000);// #endif// // // [主机] 不需要预先接收,它会主动发送// while (1)// {// // === 1. 优先处理中断 (公共逻辑) ===// if (g_bIrqTriggered)// {// g_bIrqTriggered = 0;// rf_irq_process(); // SPI 读取状态// }// // === 2. 业务逻辑 (编译时二选一) ===// #ifdef MASTER_MODE// OnMaster();// #endif// #ifdef SLAVE_MODE// OnSlave();// #endif// }// //}//int32_t main(void)//{ // System_Init_Config();// // SPI_FLASH_Init();// // flash_fun();// while (1)// {// }//}//int32_t main(void)//{// System_Init_Config(); // 初始化时钟和串口// SPI_FLASH_Init(); // 初始化 SPI 硬件// SPI2_DMA_Init(); // 初始化 DMA 配置// printf("开始 DMA 验证...\r\n");// // 第一步:写入 kunkun// W25Q_DMA_Write_Kunkun(0x0000); // // // // // 第二步:读取回来// W25Q_DMA_Read_Back(0x0000);// // 第三步:验证// if (strcmp((char*)CW_DMA_RxBuf1, "kunkun") == 0) {// printf("验证通过!收到了:%s\r\n", CW_DMA_RxBuf1);// } else {// printf("验证失败,收到了垃圾数据。\r\n");// }// while(1);//}int main(void){ // 1. 初始化基础外设 //LED_Init(); // 2. 调用封装好的 VC 模块初始化 WaterMeter_VC_Init(); // 3. 开启内核全局中断总闸 (所有配置就绪后,最后开总闸) __enable_irq(); // 4. 主循环 while (1) { // 直接使用 vc.h 里 extern 声明过来的标志位 if(gFlagIrq) { PB09_TOG(); gFlagIrq = FALSE; // 这里可以加一句串口打印,用来观察 WaterPulseCount // printf("当前水表圈数: %d\r\n", WaterPulseCount); } }}void System_Init_Config(void){ RCC_Configuration(); GPIO_Configuration(); SPI_Configuration(); EXTI_Configuration(); ADC_Configuration();}

实物展示

效果演示

如图所示传感器和磁铁在至于同一平面时,只有从下往上计数会加1,从上往下则不会。

磁极的“盲区”与极性 (Magnet Orientation)

这一串磁铁,磁极通常是在圆面的两头(轴向充磁)。

磁场角度:AH812 这种线性霍尔传感器最喜欢垂直穿过它身体的磁力线。

当你由下往上划时,磁铁底部的磁场刚好以最佳角度“切”过了传感器。而当你换个方向划,磁场的角度发生了细微偏转,导致垂直分量变小,电压就达不到 2.67V 了。

往期推荐:

【产品应用】基于CW32的智能充电宝(方案开源)

【产品方案】基于CW32L010低成本电动工具方案

【产品方案】CW32L010低成本工业仪表

【产品方案】基于CW32F003E4P7的数字电压电流表产品方案

【产品方案】基于CW32的无刷直流空心杯电机无感方波控制驱动方案

【产品方案】基于CW32L010的低成本USB充电检测仪产品方案

【产品方案】基于CW32的无刷直流空心杯电机有感控制驱动方案

【产品方案】基于CW32F030C8的低压无刷风机无感控制器

【产品应用】基于CW32的角磨机控制器产品方案

CW32生态社区微信交流群

扫码加入QQ群 3群| 610403240

获取资料及“开发者扶持计划”第一手资讯

求点赞

求分享

求喜欢

微信扫一扫关注该公众号

继续滑动看下一个

潜在客户预测
点击查看详情>
合作机会