简介:围绕单片机应用技术中的可调光台灯设计,这份PPT面向电子、自动化及相关专业的学习者与初学者,聚焦A/D、D/A转换与I2C总线通信的入门实践。内容以PCF8591为核心,梳理模拟电压输出驱动LED并调节亮度的完整思路,涉及A/D与D/A转换原理、PCF8591控制字与0~+5V输出范围、I2C启动/停止及字节收发时序、1KΩ限流电阻选型、按键增减输出电压的控制程序,以及主程序流程和产生正弦波、数字万用表等举一反三案例,可帮助读者理解单片机与外围芯片间的通信过程,并对照硬件电路与程序代码完成从原理到实现的梳理。压缩包仅含1个pptx文件,约751KB,以图文页面呈现任务导入、硬件电路、程序设计与扩展应用,便于课堂演示或自学查阅。目前已有274人学习下载。
1. 从 AD 转换芯片和 DA 转换芯片看可调光台灯的闭环调光
把 AD 转换芯片和 DA 转换芯片放进一盏可调光台灯里,最直接的价值不是把旋钮换成数字编码器,而是让亮度设定、环境光补偿和驱动电压形成可标定的模拟链路。AD 转换芯片负责把电位器分压、光敏电阻或触摸滑条的电压变成数字量;MCU 用这个数字量查表、滤波、限幅;DA 转换芯片再把目标亮度还原成 0~3.3V 或 0~10V 的模拟电压,送进恒流驱动或调光接口。这样做的台灯,旋钮手感由软件定义,低亮度不闪、不同批次灯珠亮度一致。适合做课程设计、桌面照明产品原型,以及需要把 AD 转换和 DA 转换芯片写进方案里的嵌入式开发者。
2. AD 转换芯片在可调光台灯里的采样链路与参数标定
2.1 可调光台灯里 AD 转换芯片要采什么信号
可调光台灯里最常见的被测量是电位器中间抽头的电压。电位器两端接 3.3V 和 GND,中间抽头随旋钮角度在 0~3.3V 之间变化,AD 转换芯片读到的码值就是亮度设定值。另一类信号是环境光,用光敏电阻和固定电阻分压,或者用光电二极管加跨阻放大器,输出仍是 0~VREF 的电压。触摸滑条则可能直接输出模拟电压,经过 AD 转换芯片采样后做位置判断。
采样率不用追求高。人手转动旋钮的带宽通常低于 20Hz,环境光变化更慢,AD 转换芯片每秒采 50~200 次就够。分辨率要够,8 位立即可见阶跃,12 位在 0~3.3V 下每 LSB 约 0.8mV,配合软件映射能做出细腻手感。参考电压要稳,如果直接用 MCU 的 3.3V 供电当参考,灯珠电流变化会通过地弹和电源纹波影响读数,常见做法是外接 2.5V 或 3.0V 基准,或者至少给 AD 转换芯片的 VREF 加 1uF 加 100nF 退耦。
还有一个容易忽略的点:电位器不是理想分压器。碳膜电位器在低亮度端接触电阻会变大,读数可能跳。我一般会在电位器抽头和 AD 输入之间串 1k 电阻,再并 100nF 到地,截止频率约 1.6kHz,不影响旋钮响应,还能压住高频噪声。
2.2 AD 转换芯片选型与外围电路参数
选 AD 转换芯片先看接口、通道和参考方式。台灯里通常只有 1~2 路模拟量,I2C 或 SPI 外置芯片比 MCU 内置 ADC 更贴近“基于 AD 转换芯片”这个标题,也更容易做高分辨率。下面这张表是我常用的几类选择。
| 型号 | 分辨率 | 接口 | 通道 | 参考 | 适合场景 |
|---|---|---|---|---|---|
| ADS1115 | 16 位 | I2C | 4 单端/2 差分 | 内部 2.048V 或外部 | 低速高精度,电位器、光敏 |
| MCP3201 | 12 位 | SPI | 1 单端 | 外部 VREF | 成本低,采样率可到 100ksps |
| ADS7828 | 12 位 | I2C | 8 单端 | 内部/外部 | 多路模拟量,带光敏和温度 |
| MCU 内置 ADC | 10~12 位 | 片内 | 多路 | VDD 或 VREF | 成本最省,精度要求不高 |
外围电路按“分压、限流、滤波、退耦”四步走。电位器阻值取 10k~100k,太大容易受 AD 输入漏电流影响,太小则静态功耗高。分压后如果超过 VREF,要用电阻分压到量程内。ADS1115 的 AIN 输入范围由 PGA 决定,±2.048V 或 ±4.096V 要算清楚,别让 3.3V 直接灌进去。
下面是用 Python 在树莓派一类 Linux 板上读 ADS1115 的最小代码,寄存器配置按单端 AIN0、±4.096V、128SPS 设置。
import time import smbus2 I2C_BUS = 1 ADS1115_ADDR = 0x48 # 配置寄存器:单端 AIN0,±4.096V,128SPS,关比较器 CONFIG_MSB = 0xC3 # 1100 0011 CONFIG_LSB = 0x83 # 1000 0011 bus = smbus2.SMBus(I2C_BUS) def read_ads1115(): # 写配置寄存器 bus.write_i2c_block_data(ADS1115_ADDR, 0x01, [CONFIG_MSB, CONFIG_LSB]) time.sleep(0.01) data = bus.read_i2c_block_data(ADS1115_ADDR, 0x00, 2) raw = (data[0] << 8) | data[1] if raw > 32767: raw -= 65536 # ±4.096V 量程,16 位有符号,1LSB = 4.096/32768 voltage = raw * 4.096 / 32768.0 return raw, voltage while True: raw, voltage = read_ads1115() print(f"raw={raw:6d} voltage={voltage:.4f} V") time.sleep(0.1)逻辑说明:先向配置寄存器 0x01 写入两个字节,选择 AIN0 单端输入、PGA 为 ±4.096V、采样率 128SPS。再读转换寄存器 0x00,把两个字节拼成 16 位有符号数,负数做补码还原。最后按 1LSB=4.096V/32768 换算电压。参数说明:ADS1115_ADDR由 ADDR 引脚决定,接地为 0x48,接 VDD 为 0x49,接 SDA 为 0x4A,接 SCL 为 0x4B;CONFIG_MSB和CONFIG_LSB要按数据手册的位定义改,特别是输入多路选择位和 PGA 位。如果读数一直是 0x7FFF 或 0x8000,先量 AIN0 对地电压,再查 I2C 地址。
提示:ADS1115 的模拟输入不能超过 VDD+0.3V,台灯里如果电位器用 5V 供电,必须先用两个电阻分压到 3.3V 以内,否则会打坏输入保护二极管。
2.3 用软件滤波把抖动压到 1 个 LSB 以内
AD 转换芯片的原始码值不会完全安静,电源纹波、LED 驱动开关噪声、人手触碰都会让最后几位跳。直接在原始值上做映射,低亮度端会出现肉眼可见的闪烁。常见做法是“中值滤波去毛刺,再加一阶低通或滑动平均”。中值窗口取 5 点,能干掉单次尖峰;滑动平均取 8~16 点,牺牲一点响应换稳定。
下面是一段 Python 滤波代码,适合在每 10ms 采样一次的主循环里调用。
from collections import deque class AdFilter: def __init__(self, median_win=5, avg_win=8, alpha=0.15): self.median_buf = deque(maxlen=median_win) self.avg_buf = deque(maxlen=avg_win) self.alpha = alpha self.low_pass = None def update(self, raw): # 1. 中值滤波 self.median_buf.append(raw) med = sorted(self.median_buf)[len(self.median_buf)//2] # 2. 滑动平均 self.avg_buf.append(med) avg = sum(self.avg_buf) / len(self.avg_buf) # 3. 一阶低通 if self.low_pass is None: self.low_pass = avg else: self.low_pass = self.low_pass * (1 - self.alpha) + avg * self.alpha return int(self.low_pass)逻辑说明:每来一个原始码值,先压入中值窗口,取排序后的中间值;再把中值压入平均窗口,求均值;最后用alpha做一阶低通,输出平滑后的整数码值。参数说明:median_win=5对连续尖峰有效,如果噪声是持续高频,窗口可加到 7;avg_win=8在 100SPS 下约 80ms 延迟,手感可以接受;alpha=0.15越小越稳但越迟钝,调到 0.3 会跟手一些。注意不要在中值窗口还没填满时做映射,否则上电瞬间亮度会冲一下,我一般等len(median_buf) == median_win再输出。
2.4 AD 采样值到亮度等级的映射和校准
AD 转换芯片给出的是电压码值,台灯需要的是亮度等级,比如 0~1000 或 0~255。直接线性映射有两个问题:电位器两端有机械死区,码值到不了 0 和满量程;不同台灯的电位器角度和灯珠亮度曲线不一致。所以我一般做两点校准:上电时记录电位器最低端和最高端的稳定码值,存到 Flash 或 EEPROM,再用分段线性映射。
假设校准后得到ad_min和ad_max,当前滤波值为ad_val,先限幅:
def ad_to_level(ad_val, ad_min, ad_max, level_max=1000): if ad_val <= ad_min: return 0 if ad_val >= ad_max: return level_max ratio = (ad_val - ad_min) / (ad_max - ad_min) # 低亮度端留 2% 死区,避免刚离开最低端就跳亮 if ratio < 0.02: return 0 return int(ratio * level_max)逻辑说明:先把码值限幅到校准区间,再归一化到 0~1,最后乘以最大亮度等级。参数说明:ad_min和ad_max不是理论值 0 和 32767,而是实测稳定值,通常留 2%~5% 余量;level_max取 1000 方便后面做 Gamma 映射,取 255 则适合 8 位 DA 或 PWM。如果电位器是反接的,交换ad_min和ad_max即可。
注意:校准值不要每次上电都重算,否则用户转到中间位置开机,会把中间位置当成最低端,亮度全乱。常见做法是长按某个隐藏按键 3 秒进入校准,或者只在首次上电且检测到电位器在最低端时记录。
3. DA 转换芯片驱动可调光台灯的模拟调光输出
3.1 DA 转换芯片选型:MCP4725、DAC8551、PWM+RC 的边界
可调光台灯的 DA 转换芯片通常输出 0~3.3V、0~5V 或 0~10V 的模拟电压,去控制恒流驱动芯片的 DIM 引脚。选型时先看驱动接口要电压还是电流,再看分辨率、接口和上电默认输出。MCP4725 是 I2C 12 位 DAC,带 EEPROM,上电可以恢复保存值,适合小台灯;DAC8551 是 SPI 16 位,精度高,但需要外部基准和运放;PWM 加 RC 滤波成本最低,但纹波和建立时间难控制,低亮度容易闪。下面表格把边界列清楚。
| 方案 | 分辨率 | 接口 | 上电默认 | 低亮度表现 | 适用 |
|---|---|---|---|---|---|
| MCP4725 | 12 位 | I2C | EEPROM 可保存 | 较好,需滤波 | 桌面台灯、原型 |
| DAC8551 | 16 位 | SPI | 0V 或中间值 | 很好 | 高精度调光 |
| PWM+RC | 取决于 PWM | GPIO | 取决于定时器 | 易纹波,需二级 RC | 成本极敏感 |
| 内置 DAC | 8~12 位 | 片内 | 不确定 | 一般 | MCU 自带 DAC |
如果台灯驱动要求 0~10V,DA 转换芯片输出 0~3.3V 后要加同相放大器,增益约 3.03 倍,把 0~3.3V 拉到 0~10V。运放选轨到轨输出,供电至少 12V,否则输出摆不到 10V。DAC8551 这类电流输出型 DAC 还需要外部运放做 I/V 转换,不能直接接 DIM 引脚。
3.2 DA 转换芯片与 LED 驱动的接口电路
DA 转换芯片输出到 LED 驱动之间,常见接口有三类:模拟电压调光、PWM 调光、电阻调光。模拟电压调光最贴合 DA 转换芯片,DIM 引脚接受 0.5~2.5V 或 0~10V,对应 0~100% 电流。接口电路要做三件事:限幅、滤波、阻抗匹配。
限幅用肖特基二极管钳位到驱动芯片的 VDD 和 GND,防止上电顺序异常时打坏 DIM 引脚。滤波用 1k 加 1uF 低通,截止频率约 160Hz,能压住 DA 更新带来的台阶。阻抗匹配要看 DIM 引脚输入阻抗,如果只有几十 k,DA 输出串 100 欧姆后直接接没问题;如果输入阻抗低,要加运放缓冲。
提示:DA 转换芯片的模拟地和 LED 驱动的功率地要单点连接,最好在驱动芯片的 GND 引脚附近汇合。把 DA 的地和功率地分开走,再在电源入口连,低亮度时不容易出现随电流变化的亮度抖动。
还有一个细节:很多恒流驱动芯片的 DIM 引脚在 0V 附近不是完全关断,而是有 1%~3% 的残余电流。DA 输出 0V 时灯珠可能微亮。解决方法是把 DA 最低输出设在 20~50mV,或者用 MCU 的 GPIO 控制驱动的 EN 引脚,在亮度等级为 0 时彻底关断。
3.3 用 MCP4725 输出 0~3.3V 控制恒流驱动
MCP4725 的 I2C 地址通常是 0x60 或 0x61,写 12 位数据加 2 位命令。下面代码把 0~1000 的亮度等级映射到 0~4095 的 DAC 码值,并写入 MCP4725 的寄存器。
import smbus2 MCP4725_ADDR = 0x60 bus = smbus2.SMBus(1) def set_dac_level(level, level_max=1000, vref=3.3): if level < 0: level = 0 if level > level_max: level = level_max # 12 位 DAC,码值 0~4095 dac_code = int(level / level_max * 4095) # 命令字节:写 DAC 寄存器,不保存到 EEPROM cmd = 0x40 data = [(dac_code >> 4) & 0xFF, (dac_code << 4) & 0xF0] bus.write_i2c_block_data(MCP4725_ADDR, cmd, data) voltage = dac_code / 4095.0 * vref return dac_code, voltage逻辑说明:先把亮度等级限幅,再按 12 位满量程 4095 换算成 DAC 码值。MCP4725 写寄存器时,第一个字节高 4 位是码值高 4 位,第二个字节高 4 位是码值低 8 位,低 4 位补 0。命令字节 0x40 表示只写 DAC 寄存器,掉电不保存;0x60 表示写 DAC 并保存到 EEPROM。参数说明:level_max与上一章映射输出保持一致,如果上一章输出 0~1000,这里也取 1000;vref按 MCP4725 的 VDD 实际电压填,3.3V 供电时满量程接近 3.3V,但输出高端会有几十 mV 压降,要求高时用外部基准。如果写完后电压不动,先量 VDD,再查 I2C 地址是否被 A0 引脚改变。
3.4 DA 输出非线性与最低亮度死区的处理
DA 转换芯片本身线性度通常很好,12 位 MCP4725 的 INL 在几个 LSB 以内,真正非线性来自 LED 驱动和灯珠。人眼对亮度感知近似 Gamma 2.2,如果 DA 码值线性增加,低亮度端变化太快,高亮度端又看不出差别。常见做法是在 AD 映射和 DA 输出之间插入 Gamma 查表。假设亮度等级 0~1000,Gamma 2.2 映射可以写成:
def gamma_correct(level, level_max=1000, gamma=2.2): ratio = level / level_max corrected = ratio ** gamma return int(corrected * level_max) for level in [0, 50, 100, 200, 500, 800, 1000]: print(level, gamma_correct(level))逻辑说明:把归一化亮度做 Gamma 幂运算,再恢复到等级量程。参数说明:gamma=2.2是常用视觉曲线,如果灯珠和驱动本身已经做了 Gamma,这里可以取 1.0 或 1.8;输出等级再送set_dac_level。最低亮度死区要单独处理:当level < 1%时直接输出 0 并关 EN,当level在 1%~5% 时用查表值,不要把 DAC 码值从 0 直接跳到几十,否则会看到“咔”一下亮起来。
注意:Gamma 查表不要每次控制循环都算浮点幂,低端 MCU 上耗时且抖动。上电时生成 1001 个 uint16 的查表数组,运行时只做查表。
4. AD 与 DA 转换芯片联调:可调光台灯控制逻辑与整定
4.1 主循环状态机:采样、滤波、映射、输出
AD 转换芯片和 DA 转换芯片联调时,主循环不要写成“读一次、算一次、写一次”的裸循环,否则 I2C 阻塞和滤波延迟会让旋钮手感发涩。我一般用 10ms 定时器做采样节拍,主循环里做状态机:采样态读 AD,滤波态更新滤波器,映射态做校准和 Gamma,输出态写 DA。亮度等级变化小于 1 个等级时不写 DA,减少 I2C 流量。下面是一个简化状态机代码。
import time class LampController: def __init__(self, ad_filter, ad_min, ad_max): self.ad_filter = ad_filter self.ad_min = ad_min self.ad_max = ad_max self.last_level = -1 self.current_level = 0 def tick(self, raw_ad): filtered = self.ad_filter.update(raw_ad) level = ad_to_level(filtered, self.ad_min, self.ad_max, 1000) level = gamma_correct(level, 1000, 2.2) # 迟滞:变化超过 2 个等级才更新,避免边界抖动 if abs(level - self.last_level) >= 2: self.current_level = level self.last_level = level set_dac_level(level, 1000, 3.3) return self.current_level ctrl = LampController(AdFilter(), ad_min=1200, ad_max=31000) while True: raw = read_ads1115()[0] ctrl.tick(raw) time.sleep(0.01)逻辑说明:每个 10ms 节拍读一次 AD 原始值,经过滤波器,做 AD 到等级映射、Gamma 校正,再和上次输出比较,超过 2 个等级才写 DA。参数说明:ad_min=1200和ad_max=31000是示例校准值,实际要按实测改;迟滞阈值 2 可以改到 3~5,旋钮越差可以越大;time.sleep(0.01)决定采样周期,配合 AD 转换芯片的 128SPS 时要注意别读太快,否则读到重复值。
4.2 调光曲线:线性、Gamma 2.2 与视觉亮度匹配
调光曲线决定“手感”。线性曲线在低亮度端变化剧烈,高亮度端迟钝;Gamma 2.2 接近人眼,但最低端会偏暗;有些产品用分段曲线,前 20% 等级映射到 0~5% 电流,后 80% 映射到 5%~100%。下面表格对比三种曲线在典型等级下的 DA 码值,假设 DA 满量程 4095。
| 亮度等级 | 线性 DA 码值 | Gamma 2.2 DA 码值 | 分段曲线 DA 码值 |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 50 | 205 | 2 | 10 |
| 100 | 410 | 9 | 25 |
| 300 | 1228 | 105 | 180 |
| 500 | 2047 | 341 | 500 |
| 800 | 3276 | 1064 | 1800 |
| 1000 | 4095 | 4095 | 4095 |
从表里能看出,Gamma 2.2 在 50 级时只输出 2 个码值,如果 DA 有噪声,低亮度会闪。所以低亮度端要么用分段曲线把码值抬起来,要么提高 DA 分辨率到 16 位。另一个做法是在 DA 输出后加 RC 滤波,但 RC 不能消除码值量化噪声,只能压高频。
4.3 采样频率、DA 更新率与闪烁的权衡
AD 采样频率、DA 更新率和滤波器长度三者互相牵制。采样太快,噪声进得多;采样太慢,旋钮响应迟;DA 更新太快,I2C 流量大且可能写坏 EEPROM;DA 更新太慢,调光有台阶。下面这张参数表是我在 12 位 AD、12 位 DA、I2C 400kHz 下的常用起点。
| 参数 | 典型值 | 可调范围 | 影响 |
|---|---|---|---|
| AD 采样周期 | 10ms | 5~50ms | 越小越跟手,噪声越大 |
| 中值窗口 | 5 | 3~7 | 越大去毛刺越强,延迟越大 |
| 平均窗口 | 8 | 4~16 | 越大越稳,旋钮越钝 |
| DA 更新阈值 | 2 等级 | 1~5 | 越大越省 I2C,台阶越明显 |
| I2C 速率 | 400kHz | 100~400kHz | 越高写 DA 越快,抗干扰越差 |
| Gamma | 2.2 | 1.8~2.5 | 越大低端越暗 |
整定时先固定 AD 采样 10ms、中值 5、平均 8,然后转旋钮看低亮度有没有闪烁。如果闪,先加 DA 输出 RC 滤波,再把 Gamma 降到 1.8,最后才考虑换 16 位 DA。不要一上来就加长平均窗口,那会把旋钮变成“橡皮筋”。
4.4 常见故障排查:读数跳、亮度阶跃、DA 无输出
联调阶段的问题大多集中在 I2C 地址、参考电压、地线和驱动接口。下面表格按现象列检查点。
| 现象 | 可能原因 | 检查点 |
|---|---|---|
| AD 读数乱跳 | 参考电压不稳、地线长、电位器接触不良 | 量 VREF 纹波,短接地线,换电位器 |
| AD 读数固定 0 或满量程 | 输入超范围、I2C 没应答、配置寄存器错 | 量 AIN 电压,扫 I2C 地址,读配置回写 |
| 低亮度闪烁 | DA 码值太小、Gamma 太陡、驱动 DIM 有纹波 | 看 DA 输出波形,改分段曲线,加 RC |
| 亮度阶跃 | DA 更新阈值太大、查表步进粗 | 减小阈值,增加等级数,检查 Gamma 表 |
| DA 无输出 | 地址错、VDD 没电、命令字节错 | 量 VDD,扫 I2C,用万用表量 OUT |
| 高亮度上不去 | DA 满量程不够、运放没轨到轨、DIM 上限低 | 量 DA 满码输出,查运放供电,查驱动规格 |
排查时先分清楚是 AD 侧还是 DA 侧。把 AD 原始值打印出来,如果原始值稳定而亮度不对,问题在映射或 DA;如果原始值就跳,先治 AD。DA 侧可以用万用表直流档量 OUT 引脚,写 0、2048、4095 三个码值,看电压是否接近 0、VDD/2、VDD。如果 4095 时电压只有 2.8V,而 VDD 是 3.3V,说明输出接近轨但没到轨,驱动 DIM 的上限要重新算。
提示:I2C 总线上挂 AD 和 DA 时,上拉电阻不要重复焊。很多模块自带 4.7k 上拉,两个模块并联后等效 2.35k,400kHz 下可能上升沿太陡,反而振铃。用示波器看 SCL 上升沿,如果过冲明显,拆掉一个模块的上拉。
5. 可调光台灯低亮度验证:AD 采样校准与 DA 输出保持技巧
5.1 上电自动校准 AD 采样上下限
可调光台灯批量做的时候,电位器角度和 AD 转换芯片的零点会有差异。手动写死ad_min和ad_max会导致每台灯低亮度端不一样。我一般在上电时做一次快速校准:检测到电位器在最低端停留 500ms,记录 20 次 AD 采样的中值作为ad_min;检测到最高端停留 500ms,记录中值作为ad_max。校准值写入 EEPROM,下次上电直接读。如果没有 EEPROM,可以用 MCU 的 Flash 模拟,或者用 MCP4725 自带的 EEPROM 存一个标志位。
校准代码片段:
def auto_calibrate(read_raw, save_func): low_samples = [] high_samples = [] # 简化:连续 20 次低于 5% 量程认为在最低端 for _ in range(20): raw = read_raw() if raw < 32768 * 0.05: low_samples.append(raw) time.sleep(0.02) if len(low_samples) >= 15: ad_min = sorted(low_samples)[len(low_samples)//2] save_func("ad_min", ad_min) # 最高端同理,高于 95% 量程 for _ in range(20): raw = read_raw() if raw > 32768 * 0.95: high_samples.append(raw) time.sleep(0.02) if len(high_samples) >= 15: ad_max = sorted(high_samples)[len(high_samples)//2] save_func("ad_max", ad_max)逻辑说明:在最低端和最高端各采 20 次,用中值作为校准点,避免单次噪声。参数说明:5% 和 95% 是判断阈值,如果电位器机械角度小,可以改成 10% 和 90%;20 次采样在 20ms 周期下约 400ms,用户感觉不到。注意校准期间不要写 DA,先把亮度关到 0,否则用户会看到亮度乱跳。
5.2 DA 输出保持与掉电保存亮度
MCP4725 支持把 DAC 值写入 EEPROM,上电自动恢复。这样即使 MCU 还没启动,台灯也能保持上次亮度。写入 EEPROM 不能太频繁,MCP4725 的 EEPROM 擦写寿命约 100 万次,如果每次旋钮都写,几天就写坏了。常见做法是“运行中只写 DAC 寄存器,检测到亮度稳定 3 秒后再写 EEPROM”,或者掉电检测时用储能电容供电写一次。DAC8551 没有 EEPROM,可以在 MCU 的 Flash 里存亮度等级,上电后先写 DA 再开 EN。
5.3 低亮度验证:示波器、万用表和眼睛的组合
低亮度是台灯最难调的部分。验证时不要只靠眼睛,眼睛会适应暗环境。我一般用示波器看 DA 输出纹波,用万用表看直流平均电压,再用眼睛看 1%、3%、5% 三个等级有没有闪烁和色偏。示波器探头接 DA 输出,交流耦合,带宽限制 20MHz,如果看到几十 mV 的周期性纹波,先查 I2C 写入频率和电源。万用表直流档量 DIM 引脚,确认 1% 等级电压不在驱动芯片的死区内。眼睛验证要在暗室适应 5 分钟后进行,如果 1% 等级看不见但 2% 突然亮,说明 Gamma 低端太陡,把分段曲线的前 10% 等级再压低。把示波器探头从 DA 输出移到 LED 驱动电流采样电阻两端,看电流波形是否随亮度等级单调变化,这是判断 AD 到 DA 整条链路是否线性的最后一步。
本文还有配套的精品资源,点击获取