3. 驱动层优化:中断优先级设置、ISR 瘦身与DMA传输
驱动层是触摸响应优化的第一道关卡。说白了,硬件信号到了这里,如果处理不好,上层再怎么优化也是白搭。我个人习惯把驱动层比作「水龙头」——你拧开它,水要能顺畅流出来,而不是滴滴答答半天才来。
这一节,我们聚焦三个核心手段:中断优先级设置、ISR 瘦身、DMA 传输。这三板斧砍下去,触摸延迟能降一个数量级。
3.1 中断优先级:别让触摸被「插队」
嵌入式系统里,中断优先级决定了谁先被 CPU 伺候。触摸这种对实时性要求高的外设,优先级必须排在前列。
我遇到过这样一个项目:GUI 界面偶尔卡顿,排查了半天,发现是 UART 中断优先级比触摸中断高。UART 一收数据,触摸就被晾在一边。你想想看,用户手指都划过去了,CPU 还在处理串口数据,这体验能好吗?
设置优先级时,记住一个原则:触摸中断优先级 > 普通外设中断优先级。具体做法如下:
- 确认中断控制器:是 NVIC(Cortex-M 系列)还是其他?
- 分配抢占优先级:触摸用最高抢占优先级(数值最小)。
- 避免优先级反转:如果触摸中断里要访问共享资源,记得用临界区保护。
以 STM32 为例,设置触摸中断优先级:
// 设置触摸中断优先级为 0(最高)
HAL_NVIC_SetPriority(TOUCH_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TOUCH_IRQn);
// 其他外设中断优先级设为 2 或更低
HAL_NVIC_SetPriority(UART_IRQn, 2, 0);
HAL_NVIC_SetPriority(TIM_IRQn, 3, 0);
3.2 ISR 瘦身:中断服务程序越短越好
ISR 里最忌讳什么?做复杂运算、调用 printf、甚至操作文件系统。这些操作会阻塞中断,导致后续触摸事件丢失。
我见过一个案例:工程师在触摸 ISR 里做了滤波算法,结果中断执行时间超过 1ms。触摸采样率一高,CPU 就卡死在中断里。嗯,这里要注意:ISR 只做最必要的事。
ISR 瘦身的核心原则:
- 只读数据:从触摸控制器寄存器读取坐标数据。
- 设置标志:用一个全局变量或信号量通知主循环。
- 快速退出:不做任何数据处理,不调用耗时函数。
优化前后的对比:
| 操作 | 优化前(ISR 内) | 优化后(ISR 内) |
|---|---|---|
| 读取坐标 | ✅ 保留 | ✅ 保留 |
| 滤波处理 | ❌ 在 ISR 内做 | ✅ 移到主循环 |
| 更新 GUI | ❌ 直接调用 GUI 函数 | ✅ 设置标志,主循环处理 |
| 打印日志 | ❌ 使用 printf | ✅ 用 DMA 或环形缓冲区 |
优化后的 ISR 示例:
volatile uint8_t touch_flag = 0;
volatile uint16_t touch_x, touch_y;
void TOUCH_IRQHandler(void) {
// 只做两件事:读数据 + 设标志
touch_x = TOUCH->X_REG;
touch_y = TOUCH->Y_REG;
touch_flag = 1;
// 清除中断标志
TOUCH->ICR = 0x01;
}
3.3 DMA 传输:让数据自己「跑」到内存
DMA 是嵌入式系统的「快递员」。它不需要 CPU 参与,就能把触摸数据从外设搬到内存。CPU 可以腾出手来做 GUI 渲染或其他任务。
我记得第一次用 DMA 传输触摸数据时,效果立竿见影。原本 CPU 要轮询读取每个坐标点,现在 DMA 自动完成,CPU 负载直接降了 30%。
DMA 传输触摸数据的流程:
- 配置 DMA 通道:源地址指向触摸控制器数据寄存器,目标地址指向内存缓冲区。
- 设置传输长度:每次传输 4 字节(x 和 y 坐标各 2 字节)。
- 触发方式:触摸中断触发 DMA 传输,或者定时器周期性触发。
- 传输完成中断:DMA 传输完成后,通知 CPU 处理数据。
以 STM32 的 DMA 配置为例:
// 配置 DMA 传输触摸数据
DMA_HandleTypeDef hdma_touch;
hdma_touch.Instance = DMA1_Channel1;
hdma_touch.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_touch.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_touch.Init.MemInc = DMA_MINC_ENABLE;
hdma_touch.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_touch.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_touch.Init.Mode = DMA_CIRCULAR; // 循环模式,持续采集
HAL_DMA_Init(&hdma_touch);
// 启动 DMA 传输
HAL_DMA_Start(&hdma_touch, (uint32_t)&TOUCH->DATA_REG,
(uint32_t)touch_buffer, 2); // 每次传输 2 个半字
3.4 整体优化流程
这三步不是孤立的,它们需要协同工作。下面这张图展示了驱动层优化的完整流程:
从图中可以看出,触摸硬件触发中断后,有两个选择:
- 走 ISR 瘦身路径:适合简单场景,ISR 只读数据、设标志,主循环处理。
- 走 DMA 传输路径:适合高采样率场景,DMA 自动搬运数据,CPU 几乎零负担。
两条路径最终都汇聚到主循环,由主循环统一处理数据并更新 GUI。
嗯,驱动层优化就讲到这里。下一层是协议层优化,我们到时候再聊。