Note 02 of 02: 笔记 02 / 02:Light Calibration and Lookup Control光照标定与查表控制
Working through an ADC-to-PWM interpolation example, uneven calibration intervals and a possible correction for changing ambient light.从目标 ADC 到 PWM 的一次插值计算,研究采样点分布、标定条件和环境光变化后的修正思路。
In the Tianjin lighting exercise, an encoder set the target and the STM32 adjusted an LED. The code raises a very specific question: with a target of 1000, what goes into TIM4's compare register? Back then I measured 16 PWM and ADC pairs and put them into a lookup function. Following that question through ties together where the answer 209 comes from, which stretches of the table deserve extra measurements, and how the output should be corrected once the ambient light changes.
天津实习的光照小实验里,编码器设定目标,STM32 去调 LED。代码里有个特别具体的问题:目标填 1000,TIM4 的比较值该填多少?当时我测了 16 组 PWM 和 ADC 数据,写进了查表函数。顺着这个问题算下去,209 是怎么来的、哪几段值得补测、环境光一变输出该怎么修,就能串在一起看了。
PWM and ADC Calibration DataPWM 与 ADC 标定数据
The table starts at PWM = 0, ADC = 1600 and ends at PWM = 1000, ADC = 480. The brighter the LED, the lower the ADC reading. In this wiring a small ADC code means strong light, so moving the target from 1200 to 1000 actually asks for a brighter LED. The screen says light, but the program only ever handles ADC codes; converting to lux would need a relationship between the sensor reading and illuminance.
这张表从 PWM = 0, ADC = 1600 开始,到 PWM = 1000, ADC = 480 结束。LED 越亮,ADC 读数反而越小。在这套接法里,ADC 码小代表光强,所以目标从 1200 调到 1000,其实是要把灯调亮。屏幕上写的是光照,程序里处理的始终是 ADC 采样码,想换成 lux,还得有传感器读数和照度之间的换算关系。
The readings also depend on how they were taken. How far the LED sits from the sensor, which way it faces and how bright the room already is all end up in the number. Holding those conditions fixed during calibration is what lets a PWM change line up with an ADC change. A few adjacent records from the table are enough for the calculations that follow.
表里的读数也离不开测量环境。LED 离传感器多远、朝哪个方向、房间本来有多亮,都会掺进最后的读数。标定时把这些条件固定住,PWM 的变化和 ADC 的变化才对得上。下面挑几组相邻记录,后面的计算就用它们。
| PWM setting | ADC code |
|---|---|
| 100 | 1180 |
| 200 | 1015 |
| 300 | 850 |
| 800 | 551 |
| 900 | 515 |
| PWM 设置 | ADC 采样码 |
|---|---|
| 100 | 1180 |
| 200 | 1015 |
| 300 | 850 |
| 800 | 551 |
| 900 | 515 |
Linear Interpolation and Output Limits线性插值与输出限幅
A target of 1000 falls between ADC 1015 and 850, whose PWM settings are 200 and 300. First find how far into that ADC interval the target sits, then apply the same fraction to the PWM interval. Both ADC differences are taken in table order, so numerator and denominator are both negative and the fraction comes out positive.
目标 1000 落在 ADC 1015 和 850 之间,对应的 PWM 是 200 和 300。先算目标在这段 ADC 区间里走了几成,再把同样的比例套到 PWM 区间上。两边的 ADC 都按表里的顺序相减,分子分母同时为负,比例还是正的。
fraction = (1000 - 1015) / (850 - 1015)
= 15 / 165
PWM = 200 + fraction * (300 - 200)
= 209.0909...The function adds 0.5f before converting to an integer and returns 209, the command the table calculates. Rounding moves this particular result by only about 0.09 compare counts; how far the light actually lands from the target is only known after setting 209 and reading the ADC again. How many decimal places the arithmetic carries and how precisely the hardware can be controlled are two separate matters.
函数最后加上 0.5f 再转整数,返回 209,这是按表算出的指令值。四舍五入只让这次结果差了约 0.09 个比较计数;灯实际离目标有多远,还得把输出调到 209 以后再读一次 ADC。算式算到小数点后几位,和实物能控制得多准,是两码事。
Following the call further down turns up one more easy-to-miss detail. The end of the table can return 1000, but Set_LED_Brightness() clips anything above 999 to 999 before writing TIM4. If I recollected this table, I would record the compare value actually written, so calibration, display and actuation all share one numerical range. Turning that value into a duty cycle also needs the timer period and output mode.
顺着调用再往下看,还有一处很容易漏。表格末端会返回 1000,但 Set_LED_Brightness() 会把大于 999 的输入截成 999 再写进 TIM4。要是重新采这张表,我会记下真正写进去的比较值,让标定、显示和执行用同一套数值范围。比较值对应多少占空比,还要结合定时器的周期和输出模式来换算。
Calibration Coverage标定点与补测
The 16 points are spread very unevenly. Between PWM 0 and 100 there is a point every 10 counts, yet later the table jumps from 300 straight to 800. Linear interpolation draws a straight line between those two points, and the function will happily return values in between, but whether the real curve bends across that long stretch can only be found by measuring somewhere in the middle.
这 16 个点分布得很不均匀。PWM 0 到 100 之间每隔 10 就有一个点,到了后面,却从 300 一下跳到 800。线性插值等于在这两个点之间拉一条直线,函数照样能返回中间值,但这一大段真实曲线弯不弯,得在中间挑点测一测。
At PWM 550, for example, the line between the two ends predicts an ADC value of (850 + 551) / 2 = 700.5. That 700.5 is only a prediction. With the LED and sensor fixed in place, I would let the reading settle, take several samples and compare their average with it to see whether the straight line holds up. Extra points belong where the error is large or the curve changes quickly; growing 16 points to 32 just for a rounder number would not guide anything.
比如把 PWM 设成 550,按两端连的直线算,ADC 应该是 (850 + 551) / 2 = 700.5。这个 700.5 只是预测。把 LED 和传感器的位置固定好,等读数稳定后多读几次,用平均值和它比,就知道这段直线靠不靠谱。补点我会优先放在偏差大、曲线变化快的地方,不会为了凑个整数把 16 个点扩成 32 个。
Converting in the other direction, sensitivity also differs along the table. PWM 0 to 10 moves the ADC by 70, while PWM 300 to 800 moves it by only 299. Each one-code change of target therefore needs about 10 / 70 = 0.143 compare counts of PWM in the first interval and 500 / 299 = 1.672 in the second. The same click of the knob can produce very different output steps in different intervals. That ratio explains how the adjustment feels, though on its own it cannot say which interval carries the largest calibration error.
反过来换算时,各段的灵敏度也不一样。PWM 从 0 到 10,ADC 变了 70;PWM 从 300 到 800,ADC 只变了 299。也就是说,目标 ADC 每改 1,前一段 PWM 大约动 10 / 70 = 0.143 个比较计数,后一段要动 500 / 299 = 1.672 个。旋钮转同样一格,不同区间的输出步长能差出一大截。这个比值能解释调节手感,光凭它却看不出哪一段的标定误差最大。
Before adding points, the sampling itself needs a check. ST's ADC application note AN2834 relates source impedance to the time the sampling capacitor needs to charge. A light-sensing circuit with fairly high output impedance needs enough ADC sampling time and a stable reference voltage. Repeated samples show how much the reading wobbles, but if the sampling conditions are wrong to begin with, the same offset measured a hundred times is still there after averaging.
补点之前,还得先查采样本身。ST 的 ADC 应用说明 AN2834讨论了信号源阻抗和采样电容充电时间的关系。光敏电路输出阻抗偏高时,要给 ADC 留够采样时间,参考电压也得稳。多次取样能看出读数在晃;可要是采样条件本身就不对,同一个偏差测上一百遍,平均下来它还在那儿。
Ambient Light and Feedback环境光与反馈修正
The main loop hands the target set_light_value to the lookup function and sends the measured light_value only to the display. With the target held at 1000, the lookup keeps returning 209. Suppose the room gets brighter and the sensor reading drops to 900: the number on screen changes, and the LED command does not move at all. The calibrated mapping is fixed while the surroundings change, and that is exactly how feedforward behaves.
主循环把目标 set_light_value 交给查表函数,实测的 light_value 只拿去显示。目标一直是 1000,查表就一直给 209。假设这时房间变亮,传感器读数降到 900,屏幕上的数字变了,LED 的指令却纹丝不动。标定关系是固定的,环境却在变,前馈就是这个样子。
Making the output follow starts with getting the direction of the error right. Here stronger light means a smaller ADC code, so a reading of 900 against a target of 1000 calls for dimming the LED. One possible extension keeps the table's result as the starting output and adds a proportional term, measured ADC − target ADC times a positive coefficient, so a negative error reduces PWM. That sign has to be derived from the actual wiring; borrowing a habitual error formula without checking could easily push the light further from the target.
想让输出跟着修,先得把误差的方向想清楚。这里光越强 ADC 码越小,实测 900、目标 1000,灯应该调暗。可以设想在查表结果的基础上加一个比例修正项,用 实测 ADC − 目标 ADC 乘一个正系数再加上去,误差为负时 PWM 就跟着减小。这个符号必须按实际接法推,照搬一个惯用的误差公式,很可能越调越偏。
The correction should run only when a new sample arrives, its coefficient chosen with the sampling interval and the LED and sensor response in mind, and the resulting output limited to its allowed range. When ambient light is strong enough, even an LED switched fully off may leave the reading short of the target, and reducing the command further does nothing. The table supplies a quick starting point and feedback trims it against the current reading; how bright and how dim the light can actually go is, in the end, up to this particular hardware.
修正要等新的采样到了再算,系数也要结合采样间隔和 LED、传感器的响应来选,算出来的输出还得限幅。环境光够强时,就算把 LED 关掉,读数也可能够不着目标,再往下减指令也没用。查表负责快速给一个起点,反馈负责按当前读数微调,至于灯最亮能多亮、最暗能多暗,到头来还是这套实物说了算。