3、坐标映射基础:从ADC原始值到屏幕像素坐标的线性变换模型(比例因子与偏移量)
触摸屏按下时,ADC 给我们的是什么?是一堆数字。比如 1850、3720 这样的原始值。但屏幕要的是像素坐标,比如 (x=120, y=240)。
从原始值到像素坐标,中间需要一个桥梁。这个桥梁就是线性变换模型。说白了,就是一条直线方程。
我个人习惯把这个问题拆成两步:先搞清楚 X 轴怎么变,再搞清楚 Y 轴怎么变。因为触摸屏的 X 和 Y 通道是独立的,互不干扰。
3.1 为什么是线性模型?
你想想看,电阻触摸屏的工作原理是分压。电压和触摸位置之间,理论上就是线性关系。ADC 采样到的电压值,和触摸点的物理位置成正比。
所以,我们只需要两个参数就能描述这个关系:
- 比例因子(Scale Factor):也叫增益。它决定了 ADC 值变化多少,像素坐标变化 1 个单位。
- 偏移量(Offset):也叫截距。它解决了 ADC 的零点对应屏幕哪个像素的问题。
公式很简单:
像素坐标 = ADC原始值 × 比例因子 + 偏移量
嗯,这里要注意。这个公式是理想情况。实际项目中,ADC 的参考电压、触摸屏的物理尺寸、屏幕的分辨率,都会影响这两个参数的值。
3.2 比例因子:决定灵敏度
比例因子怎么算?举个例子。
假设我的屏幕分辨率是 320×240。触摸屏 X 方向的最大 ADC 值是 4000,最小是 200。那么 X 方向的比例因子就是:
ScaleX = (320 - 0) / (4000 - 200) = 320 / 3800 ≈ 0.0842
这个值意味着:ADC 每变化 1 个单位,像素坐标变化约 0.0842 个像素。反过来,如果你想移动 1 个像素,ADC 需要变化大约 11.88 个单位。
我在项目中遇到过一个问题:比例因子算出来很小,比如 0.05 以下。这时候触摸会变得很“迟钝”,手指划了很长一段距离,光标才动一点点。后来发现是 ADC 的参考电压设置太高了,导致 ADC 值范围过大。
3.3 偏移量:解决零点漂移
偏移量解决的是“原点对齐”问题。
理论上,触摸屏左上角对应的 ADC 值应该是 (0, 0)。但实际不是。因为触摸屏的物理原点、ADC 的零点、屏幕的像素原点,这三者很难天然对齐。
偏移量的计算公式:
OffsetX = 0 - (ADC最小值 × ScaleX)
或者更直观的写法:
OffsetX = 目标像素值 - (ADC值 × ScaleX)
我曾经踩过一个坑。有一批触摸屏,ADC 最小值在 50 左右,但另一批在 150 左右。我一开始写死了偏移量,结果第二批屏触摸位置整体偏了十几个像素。后来改成动态校准,才彻底解决。
3.4 完整的线性变换公式
把比例因子和偏移量合在一起,就是完整的变换公式:
// X 轴变换
PixelX = (ADC_X - ADC_X_Min) * ScaleX
// Y 轴变换
PixelY = (ADC_Y - ADC_Y_Min) * ScaleY
或者用我前面提到的带偏移量的形式:
PixelX = ADC_X * ScaleX + OffsetX
PixelY = ADC_Y * ScaleY + OffsetY
两种写法本质一样。我个人习惯用第二种,因为代码里只需要存 Scale 和 Offset 两个参数,计算起来更直观。
3.5 参数获取:两点校准法
比例因子和偏移量怎么得到?最简单的方法:两点校准。
步骤很简单:
- 在屏幕左上角显示一个校准点,用户点击。记录 ADC 值 (adc_x1, adc_y1),对应的像素坐标是 (0, 0)。
- 在屏幕右下角显示另一个校准点,用户点击。记录 ADC 值 (adc_x2, adc_y2),对应的像素坐标是 (ScreenWidth, ScreenHeight)。
- 代入公式求解。
X 轴参数计算:
ScaleX = (ScreenWidth - 0) / (adc_x2 - adc_x1)
OffsetX = 0 - adc_x1 * ScaleX
Y 轴同理。
两点校准法虽然简单,但精度有限。如果触摸屏本身有非线性误差,或者安装有倾斜,两点校准就不够用了。这时候需要三点、五点甚至九点校准。
3.6 坐标系方向问题
还有一个容易忽略的点:坐标系方向。
ADC 值通常是“左上角最小,右下角最大”。但有些触摸屏的 X 轴方向是反的,或者 Y 轴方向是反的。这时候比例因子会变成负数。
比如:
ScaleX = (320 - 0) / (200 - 4000) = 320 / (-3800) ≈ -0.0842
比例因子为负数,意味着 ADC 越大,像素坐标越小。这是正常的,只要你的公式里正确处理了符号,触摸方向就是对的。
我曾经接手过一个项目,触摸方向左右是反的。前任工程师把 ADC 值的正负搞反了,结果比例因子算出来是正数,但触摸方向就是不对。我花了半天才找到问题——不是比例因子错了,是 ADC 值的读取顺序反了。
3.7 知识体系图
下面这张图总结了从 ADC 原始值到像素坐标的完整映射关系:
3.8 代码实现示例
最后,给一个 LVGL 中坐标映射的代码片段。这是我在实际项目中用过的:
// 触摸校准参数结构体
typedef struct {
float scale_x;
float scale_y;
int16_t offset_x;
int16_t offset_y;
} touch_calib_t;
// 全局校准参数
static touch_calib_t g_calib = {
.scale_x = 0.0842f,
.scale_y = 0.1125f,
.offset_x = -16,
.offset_y = -22
};
// ADC原始值转像素坐标
void touch_adc_to_pixel(int16_t adc_x, int16_t adc_y,
int16_t *pixel_x, int16_t *pixel_y)
{
*pixel_x = (int16_t)(adc_x * g_calib.scale_x + g_calib.offset_x);
*pixel_y = (int16_t)(adc_y * g_calib.scale_y + g_calib.offset_y);
// 边界裁剪,防止越界
if (*pixel_x < 0) *pixel_x = 0;
if (*pixel_x >= LV_HOR_RES) *pixel_x = LV_HOR_RES - 1;
if (*pixel_y < 0) *pixel_y = 0;
if (*pixel_y >= LV_VER_RES) *pixel_y = LV_VER_RES - 1;
}
这段代码看起来简单,但边界裁剪那一步很重要。ADC 值偶尔会超出校准范围,如果不做裁剪,触摸点可能会跑到屏幕外面去,导致 UI 组件响应异常。
嗯,坐标映射的基础就这些。比例因子和偏移量,两个参数,一条直线方程。理解了这个模型,后面再讲多点校准、非线性补偿,你就知道根在哪里了。