简介:面向需要实现传感器信号采集与有效值计算的嵌入式开发者,这套V1.0固件工程给出了基于STM8S105K3、MCP3202、ACS758LCB-050B-PFF-T与AD637的完整参考设计。工程通过12位ADC芯片MCP3202实时读取电流与电压采样值,配合AD637完成有效值计算,进而解算出当前阻抗,并将电流值和阻抗值在LED数码管上动态刷新显示,覆盖了从模拟前端采样、SPI通信、参数计算到人机显示的完整软硬件链路。资源共262个文件,主体为STM8S标准外设库与应用代码(.c/.h),同时包含IAR工程文件(.ewp/.eww)、Source Insight辅助配置(.siproj及相关工程文件)以及编译链接输出(.map/.hex)等,压缩包约4.05MB,目录按工程模块划分,可直接导入开发环境查看和重新编译。目前已有663人学习下载,适合嵌入式初学者深入研究ADC驱动、电流传感器接口、有效值算法思路与数码管扫描实现,也可作为仪器仪表类课程设计或产品原型的基础代码复用。
1. ACS758LCB 与 STM8S 组合算有效电流,卡点不在采样精度而在带宽
做电流监测的人容易被“精度”两个字带偏:ACS758LCB 灵敏度误差只有 1%,MCP3202 是 12 位 ADC,STM8S 主频也够,就觉得有效电流计算是小事。但把输出接到示波器上看一眼可控硅调光或者开关电源输入波形就知道,RMS 绝不是“把采样值平均”那么简单。这个 V1.0 方案把 ACS758LCB 的输出拆成两路:一路进 MCP3202,由 STM8S 定时器驱动 50kSPS 采样做数字 RMS;另一路 AC 耦合进 AD637 真有效值转直流芯片,覆盖数字链路够不到的高频分量,两路结果交叉验证后合成。适合做电机驱动、充电设备、配电监控的嵌入式工程师。下文按信号链顺序拆偏置处理、SPI 时序、模拟 RMS 链路和最终合成公式。
2. ACS758LCB 输出特性与信号调理:先处理 Vcc/2 偏置再做有效值
2.1 ACS758LCB 的输出公式与灵敏度选型
ACS758LCB 是霍尔闭环?不,它是开环霍尔电流传感器,输出不是电流而是电压:Vout = Vcc/2 + Ip × Sens。零电流时输出严格等于电源电压的一半,这是它最容易被误用的地方——拿万用表量到 2.5V 就以为有电流。
常用型号对比如下:
| 型号 | 量程 | 灵敏度 | 满量程输出摆幅 |
|---|---|---|---|
| ACS758LCB-025B | ±25 A | 40 mV/A | ±1.0 V |
| ACS758LCB-050B | ±50 A | 20 mV/A | ±1.0 V |
| ACS758LCB-100B | ±100 A | 10 mV/A | ±1.0 V |
选型时先看灵敏度。050B 每安培只有 20mV,用它做 1A 级别的负载监测,量化噪声就会吃掉信号;025B 适合几十安培以内的家电和工控场合。另一个关键是比例输出(ratiometric):Vcc 漂移 1%,2.5V 偏置就跟着漂 25mV,换算到 050B 就是 1.25A 的虚假电流。所以传感器供电必须取自稳压输出,不能直接从 STM8S 的 3.3V 或者纹波较大的 DC-DC 输出上拉。
2.2 双路信号分线与 AD637 的 AC 耦合
ACS758LCB 的 VIOUT 引脚出来直接分两条支路。第一条串一个 100Ω 电阻进 MCP3202 的 CH0,保留完整波形(含直流偏置和负载直流分量)给数字链路;第二条经 C1-R1 高通进 AD637 的 Vin,把 2.5V 偏置滤掉,让 AD637 只面对交流分量。
高通截止频率取 fc = 1/(2π·R1·C1)。R1=3.3kΩ、C1=10µF 时 fc≈4.8Hz,对 50/60Hz 工频足够平坦,代价是上电后要等耦合电容充到稳态,大约 0.3s 内 AD637 输出不可信。C1 必须选无极性薄膜电容或双极性电解,普通铝电解在 ±1V 交流摆幅下有明显失真,低频误差直接进 RMS 结果。AD637 输出端再接一级同相放大,增益 G=1+Rf/Rg,放大后送 MCP3202 的 CH1,增益取值在第四章讲。
为什么不把 2.5V 偏置直接送 AD637 做“AC+DC 真有效值”?因为偏置是 2.5V 直流,它的 RMS 贡献高达 2.5V,而真实电流信号满量程才 1V 峰值,偏置会把有效值结果淹没,所以必须先把偏置滤干净。
2.3 零电流偏置采样与断电保存
数字链路的核心是“每次上电后重新测偏置”,而不是用万用表量一次写死在代码里。上电瞬间无负载时,对 CH0 连续读 16 次取平均,作为本次运行的零点:
#define ACS_OFFSET_ADDR ((uint32_t)0x004000) /* STM8S103 数据 EEPROM 起始地址 */ volatile int16_t acs_offset; /* 零电流时 CH0 的 12 位 ADC 值 */ void cal_acs_zero(void) { uint32_t sum = 0; for (uint8_t i = 0; i < 16; i++) { sum += mcp3202_read(0); /* CH0 接 ACS758LCB VIOUT */ } acs_offset = (int16_t)(sum / 16); FLASH_Unlock(FLASH_MEMTYPE_DATA); /* 解锁数据区 */ FLASH_ProgramByte(ACS_OFFSET_ADDR, (uint8_t)(acs_offset & 0xFF)); FLASH_ProgramByte(ACS_OFFSET_ADDR + 1, (uint8_t)(acs_offset >> 8)); FLASH_Lock(FLASH_MEMTYPE_DATA); }说明:mcp3202_read(0) 返回 0..4095 的 12 位结果,5V 参考下每个 LSB 约 1.22mV,对应 050B 大约 61mA。16 次平均能压掉一半以上的随机噪声,上电开销也只有几百微秒。偏置写进 EEPROM 是为了掉电重启后能做自检对比,但运行时每次都用新测的值,防止温度漂移把零点带偏。这里存的原始 ADC 计数值而不是换算后的电压,换算放到主循环,避免在初始化阶段引入浮点。
3. STM8S 驱动 MCP3202 双通道读取:SPI 配置与 24 时钟采样
3.1 SPI 引脚分配与寄存器初值
STM8S103 的 SPI1 在 PC5/SCK、PC6/MISO、PC7/MOSI,片选我用 PD4 普通 GPIO 软件控制,不用硬件 NSS。SPI_CR1 初始化为 0x4C,各位置含义:
| CR1 位域 | 值 | 含义 |
|---|---|---|
| BR[2:0] | 001 | 时钟 = f_MASTER/4,16MHz 主频下为 4MHz |
| MSTR | 1 | 主机模式 |
| CPOL, CPHA | 0, 0 | SPI Mode 0 |
| SPE | 1 | SPI 使能 |
注意 MCP3202 的最高 SCLK 与供电电压强相关:5V 供电时约 5MHz,3.3V 供电只有 1.6MHz 附近,硬往上跑会出现偶发跳码。若板子整体跑 3.3V,BR 要改成 011(f/16=1MHz)。初始化代码:
void spi_master_init(void) { PC_DDR = (1 << 5) | (1 << 7); /* SCK/MOSI 输出 */ PC_CR1 = (1 << 5) | (1 << 7) | (1 << 6); /* MISO 上拉输入 */ PC_CR2 = (1 << 5) | (1 << 7); /* 输出斜率 10MHz 档 */ PD_DDR |= (1 << 4); /* CS 输出 */ PD_ODR |= (1 << 4); /* CS 默认高 */ SPI_CR1 = 0x4C; /* SPE=1, BR=f/4, MSTR=1, Mode 0 */ SPI_CR2 = 0x03; /* SSM=1, SSI=1,软件管理 NSS */ }代码逻辑:先把 SPI 三根线配好,MISO 设成带上拉的输入,避免浮空时读到随机电平;片选用普通 IO 的好处是不受 NSS 模式干扰,想拉多久就拉多久。CR2 里把 SSM 和 SSI 都置 1,完全交给软件控制片选。
提示:如果换成 STM8S207/208 这类 24MHz 主频的型号,f_MASTER/4 会变成 6MHz,超过 MCP3202 的 5V 上限,必须把 BR 改成 010(f/8=3MHz),别沿用 103 的配置。
3.2 MCP3202 读一帧需要 24 个时钟,别只发两个字节
MCP3202 的命令很短:起始位 1、单端位 1、通道选择位 ODD,共 3 个配置位,后面跟 1 个空位,然后才是 12 位转换结果从 MISO 串行输出。我的做法是把配置位放在第一字节的低三位,让空位落在第 8 拍,数据从第 9 拍开始返回,这样一帧至少 17 拍,稳妥起见发 3 字节共 24 拍。
网上常见的“两个字节收完”写法只能拿到高 8 位数据,低 4 位丢掉后分辨率退化成 8 位,噪声底从 ±1LSB 变成 ±16LSB,对 050B 来说就是 ±0.98A 的假抖动,完全不可接受。
uint16_t mcp3202_read(uint8_t channel) { uint8_t b[3], r[3], i; b[0] = 0x0C | ((channel & 0x01) << 1); /* 0000 1 SGL/DIFF ODD */ b[1] = 0x00; b[2] = 0x00; PD_ODR &= (uint8_t)~(1 << 4); /* CS 拉低,启动一帧 */ for (i = 0; i < 3; i++) { SPI_DR = b[i]; /* 写入即启动发送 */ while (!(SPI_SR & 0x01)); /* RXNE=1 表示收到一字节 */ r[i] = SPI_DR; /* 读 DR 同时清 RXNE */ } PD_ODR |= (1 << 4); /* CS 拉高,结束一帧 */ return ((uint16_t)r[1] << 4) | (r[2] >> 4);/* 12 位数据跨在第 2、3 字节 */ }参数说明:channel 为 0 时读 ACS758LCB 原始输出,为 1 时读 AD637 输出。第一字节写 0x0C,发送顺序是 MSB 在前,所以起始位、单端位、通道位分别落到第 5、6、7 个时钟;第 8 拍是空位,第 9 拍开始从 MISO 返回 D11。r[1] 包含 D11..D4,r[2] 的高四位是 D3..D0,所以拼接公式是(r[1] << 4) | (r[2] >> 4)。也有用 0xC0 开头两字节收完的写法,本质是配置位提前到帧头,两条路都通,但别混用,否则数据错位一个时钟就是整帧作废。
3.3 定时器定频采样与 RMS 累加器的溢出边界
用 TIM2 的更新中断把采样率锁定在 50kSPS:16MHz 主频、预分频 1、自动重载 319,20µs 一次中断,中断里只读 CH0、减偏置、累加,RMS 换算推到主循环做。关键是累加器位宽:满偏时 v=2047,v²≈4.19M,窗口取 1000 点(20ms,两个完整工频周期)累计约 4.19e9,离 uint32 上限 4.29e9 只剩 2% 余量。窗口加到 2000 点必溢出,这是最隐蔽的坑——溢出后 RMS 不升反降,查半天查不出原因。
volatile uint32_t sum_sq = 0; volatile uint32_t sum_i = 0; volatile uint16_t n = 0; float g_i_rms_a = 0.0f; float g_i_dc_a = 0.0f; void TIM2_OVF_ISR(void) __interrupt(13) /* 中断号以芯片头文件为准 */ { int16_t v = (int16_t)mcp3202_read(0) - acs_offset; /* 先转有符号再减 */ sum_sq += (uint32_t)v * (uint32_t)v; /* 双 32 位乘法防 16 位溢出 */ sum_i += (uint32_t)v; if (++n >= 1000) { float rms = sqrtf((float)sum_sq / 1000.0f); float dc = (float)sum_i / 1000.0f; g_i_rms_a = rms * 5.0f / 4096.0f / 0.020f; /* 050B 灵敏度 20mV/A */ g_i_dc_a = dc * 5.0f / 4096.0f / 0.020f; sum_sq = 0; sum_i = 0; n = 0; } TIM2_SR1 = (uint8_t)~0x01; /* 清更新标志 */ }中断服务函数每 20µs 跑一次,单次 SPI 读取在 4MHz 时钟下约 6µs,加上入栈和累加,CPU 占用四成左右。v 是 int16,读回来的 12 位 ADC 值是 0..4095,直接无符号减会出下溢出变成大正数,所以先掐成 int16 再做减法。sqrtf 是软浮点,一次调用几十微秒,放主循环没问题,绝不能塞进这个中断。若还要叠加读 CH1(AD637 输出),把 CH1 放到主循环 10ms 慢速轮询,别挤进同一个 20µs 中断。
4. AD637 模拟有效值链路与双通道合成计算
4.1 数字 RMS 的带宽短板,正是 AD637 的位置
50kSPS 采样率的奈奎斯特频率是 25kHz。MCP3202 前端不加抗混叠滤波,高于 25kHz 的开关纹波会折叠回低频段污染结果;加了低通,又把真实的高次谐波能量滤掉,RMS 照样偏小。数字链路在“带宽完整”和“抗混叠”之间只能二选一。
AD637 是模拟域的平方-平均-开方电路,输入带宽在 2V RMS 时可到 8MHz,1V 量级也有 MHz 上下。AC 耦合后它测到的就是“从几赫兹到几百千赫兹全部交流分量的真有效值”,输出一个缓慢变化的直流电压,恰好补齐数字链路够不到的那段。代价是响应速度慢,平均电容决定建立时间,所以它适合稳态计量,不适合做过流保护的快速判据。保护动作靠数字链路 20ms 一窗口的 RMS,计量显示靠 AD637,两个角色分开,这就是这个 V1.0 方案的双路架构逻辑。
4.2 平均电容 CAV 与增益级参数
AD637 的外围只有一个关键取舍:CAV 越大,输出纹波越小,建立越慢。工程参考值(50/60Hz 输入实测):
| CAV | 输出纹波 | 0.1% 建立时间 |
|---|---|---|
| 0.47 µF | 约 0.2% | 约 0.5 s |
| 1 µF | 约 0.1% | 约 1 s |
| 4.7 µF | 可忽略 | 约 2 s |
动态负载场合选 0.47µF,稳态计量用 1µF 以上。注意 CAV 首选薄膜电容或 C0G,X7R 的直流偏压特性会让实际容值缩水,建立时间比理论值长。增益级方面,AD637 输出摆幅到不了 5V,要把满量程推到 ADC 量程的 70% 左右。050B 在 50A 正弦有效值下输出 0.707V,G=3 后约 2.12V,既保住 12 位分辨力,又给波峰因数留了余量。Rf/Rg 用 0.1% 精度电阻,增益温漂会被直接算进电流误差。
CH1 的换算代码:
uint16_t ad = mcp3202_read(1); /* 读 AD637 增益后输出 */ float v_ad = (float)ad * 5.0f / 4096.0f; float i_rms_ac = v_ad / 3.0f / 0.020f; /* G=3,050B 灵敏度 20mV/A */这里没有减偏置这一步。AD637 前端的 C1-R1 高通已经把 2.5V 偏置滤干净,输出直接对应交流分量的电流有效值。换算公式里出现的是 G 和灵敏度,不是 2.5V,很多人在这一步又想把偏置减一遍,反而引入二次误差。
4.3 有效电流合成:总 RMS 由 DC 与 AC 分量合成
负载是整流后直流脉动、电机 PWM 电流这类“DC+AC”混合波形时,两条链路各测一半,合起来才等于全部有效值:
I_rms = sqrt(I_dc² + I_ac²)
I_dc 用数字链路的窗口均值,I_ac 用 AD637 读数。数字链路自己的 RMS 含直流分量,正好作为整机输出;AD637 只贡献交流部分。主循环里的合并计算:
float i_dc = g_i_dc_a; /* 数字窗口均值,含真实负载直流 */ float i_ac = i_rms_ac; /* AD637 交流有效值 */ float i_total = sqrtf(i_dc * i_dc + i_ac * i_ac); g_i_rms_final = i_total;两路对表规则:纯正弦工频负载下,数字 RMS 与 AD637 换算结果的差值应在 ±2% 以内。偏差大了,优先查零点偏置和增益电阻,而不是怀疑算法。正弦验证通过后,再用一个占空比 50%、峰值 10A 的方波验证合成公式:数字 RMS 应为 7.07A,均值 5A,AD637 测得交流分量 5A,合成回去正好 7.07A,说明 DC 与 AC 两条路径都没有丢能量。
5. 有效电流计算的上电自检、方波验证与 ACS758LCB 标定细节
5.1 上电自检:空载条件下先给两个通道“对表”
上电无负载时,CH0 应当落在上次保存的偏移 ±5 LSB 以内,CH1(AD637 增益后)应小于 20 LSB。自检失败就沿用上次偏移并点亮告警,而不是把偏移清零重新标。清零的后果在数学上很直观:零点从 2048 变成 0,采样值直接当信号算,RMS 会虚增到满量程的一半,下一次过流判断立刻误动作。自检通过后,把本次实测偏移更新进 EEPROM,注意写完后回读一次,连续两次一致才确认写入成功。
5.2 方波与已知负载的“对表”验证步骤
按下面三步把两条链路各校一遍:
- 电子负载设 DC 10A,数字 RMS 应读出 10.0±0.1A,AD637 通道应接近 0A,验证零点与增益。
- 自耦调压器带大功率电阻,用钳形表读取电流,与两通道输出对照,验证交流路径的一致性。
- 用 MOS 管把 10A 直流斩成 50% 占空比方波,数字 RMS 应为 7.07A、均值 5A、AD637 为 5A,合成后回到 7.07A,验证合成公式。
第三步最有价值:方波含大量高次谐波,数字链路的抗混叠短板和 AD637 的带宽优势会在这一步同时暴露,两条链路数值对不上时,问题几乎都出在 AD637 的 AC 耦合电容或 CAV 取值上。
5.3 三个容易忽略的标定细节
第一个是 ACS758LCB 大电流引脚的 PCB 处理。电流路径铜皮要够宽,两个大电流引脚附近的压降不要串进信号地;VIOUT 走线远离功率开关节点和电感,VCC 与 GND 就近放 0.1µF 陶瓷电容和 10µF 电解。
第二个是 MCP3202 输入端的采样毛刺。它的输入是开关电容结构,每次采样都会抽取电荷,前置 RC 的截止频率要放到 10 倍采样率以上,我取 R=1kΩ、C=470pF,约 339kHz,既抑制高频噪声又不影响 50kSPS 的完整带宽。别用大电容做滤波,否则 12 位精度全丢在 RC 压降上。
第三个是 EEPROM 里偏移量的校验。写入后回读不一致,说明写穿或供电跌落,此时应保持上次有效偏移并连续告警三次,而不是重新标定。把“校验失败”和“上电无负载”区分开,能避免交流负载刚断电、又被误判成过流跳闸的尴尬。
本文还有配套的精品资源,点击获取