前言

本篇博客基于 STM32F103C8T6 最小系统板,搭配 TTP223 电容触摸模块以及 I2C‑OLED 显示屏,实现触摸检测、触摸状态显示、触摸次数统计功能。 很多同学会把 TTP223 当成普通按键,其实它是电容触摸芯片,检测的是电容变化,不是触点导通。本文从硬件接线、工作原理、软件逻辑、实物演示、源码说明几个方面完整讲解,工程可以直接移植使用。

视频讲解:STM32F103C8T6驱动TTP223电容触摸模块+OLED显示(完整工程讲解)https://www.bilibili.com/video/BV1Rb8c6TEJf/?share_source=copy_web&vd_source=91b967b41a17a5686913521545a8c3fb

硬件平台:STM32F103C8T6 最小系统板 外设:TTP223电容触摸模块、0.96 寸 I2C OLED 屏幕

一、项目实现效果

手指靠近 / 接触 TTP223 触摸电极 → 电极对地电容发生改变 → TTP223 芯片检测电容变化,修改 OUT 引脚输出电平 → STM32 通过 PB12 引脚读取电平信号 → 软件做消抖确认,识别触摸、松开事件 → OLED 屏幕实时刷新触摸状态,同时统计触摸总次数。

简单概括:TTP223 把手指带来的电容变化转换成高低电平;STM32 读取电平,软件校验之后,更新 OLED 显示以及触摸计数。

二、硬件接线说明

⚠️重点:所有模块 GND 必须接到一起,也就是单片机和外设必须共地,否则会出现检测异常、乱触发问题。

模块引脚STM32F103C8T6 引脚功能说明
TTP223 OUT(SIG)PB12触摸信号输出,本工程高电平代表触摸
TTP223 VCC5VTTP223 模块供电
TTP223 GNDGND电源地
OLED SCLPB8软件 I2C 时钟
OLED SDAPB9软件 I2C 数据
OLED VCC3.3VOLED 屏幕供电
OLED GNDGNDOLED 电源地

引脚配置注意事项

  1. TTP223‑BA6 芯片工作电压 2.0V‑5.5V,这里使用 5V 供电,OUT 输出高电平接近 5V。
  2. PB12 属于 FT 类型 5V 容忍 IO 口,可以接收 5V 电平;根据芯片手册,FT 引脚接收高于 VDD+0.3V 信号时,要关闭内部上拉下拉。
  3. 工程源码默认使用下拉输入GPIO_Mode_IPD,可以悬空时保持低电平,但是不符合手册规范,长期使用建议修改为浮空输入GPIO_Mode_IN_FLOATING
  4. 如果你的单片机 IO 不支持 5V 容忍,OUT 引脚必须增加分压或者电平转换电路,防止烧坏 IO。

TTP223 模块模式匹配

TTP223 成品模块板上有焊盘,可以修改工作模式,我们本工程软件默认配置:

  • ✅高电平有效 + 直接模式
  • 未触摸:OUT 输出低电平
  • 触摸按住:OUT 输出高电平
  • 松手释放:OUT 回到低电平

如果你的模块是低电平有效,OLED 显示状态会完全颠倒;如果是切换模式,触摸计数逻辑会失效,使用前确认模块硬件模式。

三、TTP223 电容触摸工作原理

TTP223 属于单通道电容触摸检测芯片,和机械按键原理完全不一样。机械按键依靠金属触点导通断开,而 TTP223 检测触摸电极的对地电容变化

  1. 触摸铜箔本身就存在很小的寄生电容,人体可以和大地形成电容耦合。当手指靠近或者接触触摸电极,电极等效对地电容就会变大。
  2. 手指离得越近、接触面积越大,电容变化越明显;它可以隔着薄玻璃、亚克力、塑料完成触摸检测,不需要手指直接接触铜箔。
  3. 如果覆盖材料太厚、触摸面积太小、周围电磁干扰大,触摸灵敏度会明显下降。

芯片内部会周期性检测电极充放电特性,上电生成环境基准电容值,实时对比当前电容。当电容变化达到判定阈值,直接修改 OUT 引脚电平。手指移开电容恢复,OUT 电平复原。

💡重点:TTP223 输出的是已经处理完成的数字电平,不是模拟信号,STM32 不需要 ADC 采集;芯片不能检测压力大小,按压效果差异来自接触面积改变。芯片还会自动校准基准,适配温度、湿度缓慢变化。

四、STM32 软件逻辑讲解

4.1 GPIO 读取逻辑

PB12 作为数字输入引脚,只识别高低两种逻辑电平:

  • PB12 为低电平:状态为释放,没有触摸
  • PB12 为高电平:状态为触摸

直接读取引脚输入寄存器,就可以获取瞬时触摸信号,不需要 ADC。

4.2 轮询扫描与软件消抖

虽然电容触摸没有机械按键的触点抖动,但是电源噪声、长线干扰、信号跳变瞬间,依然会产生毛刺干扰。 工程中主循环每 10ms 调用一次触摸扫描函数:

  1. 读取 PB12 当前电平,和上一次确认稳定状态对比;
  2. 如果电平没有变化,直接返回无事件;
  3. 如果电平发生改变,延时 20ms 再次读取引脚;
  4. 两次读取状态一致,才判定状态真正发生切换;否则判定为干扰毛刺,直接丢弃。

参数说明:10ms 是主循环轮询间隔,20ms 是状态变化复核消抖时间。最终整体响应延迟包含芯片本身响应时间、轮询间隔、OLED 刷新耗时。

4.3 事件与触摸计数逻辑

程序区分状态事件两个概念:

  • 状态:当前持续是触摸还是释放;
  • 事件:状态发生跳变的一瞬间产生的信号。
事件类型触发条件程序动作
EVENT_NONE状态无变化,或者消抖复核失败不做任何处理
EVENT_TOUCH状态从释放切换到触摸触摸计数 + 1,OLED 显示 TOUCHED
EVENT_RELEASE状态从触摸切换到释放计数不变,OLED 显示 RELEASED

👉 按住触摸不放的时候,只会触发一次 EVENT_TOUCH,计数只加 1;必须松开之后再次触摸,才会再次计数。计数最大 99999,溢出之后自动归零,防止五位显示溢出。

4.4 OLED 屏幕显示效果

屏幕输出内容:

TTP223 TOUCH DEMO
OUT PIN: PB12
STATE:TOUCHED
COUNT:00001

松开触摸之后 STATE 行变为STATE:RELEASED

五、源代码存放区

工程文件结构参考:

  • TTP223.h:引脚、事件、消抖参数宏定义
  • TTP223.c:初始化、读取、扫描处理函数
  • main.c:主循环,事件处理、OLED 状态刷新函数

main.c代码

/* TTP223 touch teaching demonstration for STM32F103C8T6. */
#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "TTP223.h"

#define TOUCH_POLL_TIME_MS  10U
#define TOUCH_COUNT_MAX     99999UL

static void TTP223_ShowStatus(u8 touch_state, u32 touch_count);

int main(void)
{
	TTP223_EventTypeDef event;
	u32 touch_count = 0U;

	delay_init();
	NVIC_Configuration();
	OLED_Init();
	OLED_ColorTurn(0);
	OLED_DisplayTurn(0);
	TTP223_Init();
	TTP223_ShowStatus(TTP223_GetState(), touch_count);

	while(1)
	{
		event = TTP223_Scan();
		if(event == TTP223_EVENT_TOUCH)
		{
			if(touch_count < TOUCH_COUNT_MAX)
			{
				touch_count++;
			}
			else
			{
				touch_count = 0U;
			}

			TTP223_ShowStatus(TTP223_TOUCHED, touch_count);
		}
		else if(event == TTP223_EVENT_RELEASE)
		{
			TTP223_ShowStatus(TTP223_RELEASED, touch_count);
		}

		delay_ms(TOUCH_POLL_TIME_MS);
	}
}

static void TTP223_ShowStatus(u8 touch_state, u32 touch_count)
{
	OLED_Clear();
	OLED_ShowString(0, 0, (u8 *)"TTP223 TOUCH DEMO", 12);
	OLED_ShowString(0, 16, (u8 *)"OUT PIN: PB12", 12);
	OLED_ShowString(0, 32, (u8 *)"STATE:", 12);
	if(touch_state == TTP223_TOUCHED)
	{
		OLED_ShowString(42, 32, (u8 *)"TOUCHED ", 12);
	}
	else
	{
		OLED_ShowString(42, 32, (u8 *)"RELEASED", 12);
	}
	OLED_ShowString(0, 48, (u8 *)"COUNT:", 12);
	OLED_ShowNum(42, 48, touch_count, 5, 12);
	OLED_Refresh();
}

TTP223.c代码

#include "TTP223.h"
#include "delay.h"

static u8 TTP223_StableState = TTP223_RELEASED;

/**
  * @brief  初始化 TTP223 触摸模块。
  * @note   将 PB12 配置为下拉输入。模块未连接时,下拉电阻可以使输入
  *         保持为确定的低电平;标准 TTP223 模块在检测到触摸时输出高电平。
  */
void TTP223_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	/* 开启 GPIOB 外设时钟。 */
	RCC_APB2PeriphClockCmd(TTP223_GPIO_CLK, ENABLE);

	/* 将连接 TTP223 OUT 的 PB12 配置为下拉输入。 */
	GPIO_InitStructure.GPIO_Pin = TTP223_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(TTP223_GPIO_PORT, &GPIO_InitStructure);

	/* 保存上电后的初始电平,防止首次扫描产生错误事件。 */
	TTP223_StableState = TTP223_Read();
}

/**
  * @brief  读取 TTP223 OUT 引脚的瞬时电平。
  * @retval TTP223_TOUCHED:检测到触摸;TTP223_RELEASED:未触摸。
  */
u8 TTP223_Read(void)
{
	if(GPIO_ReadInputDataBit(TTP223_GPIO_PORT, TTP223_GPIO_PIN) == Bit_SET)
	{
		return TTP223_TOUCHED;
	}

	return TTP223_RELEASED;
}

/**
  * @brief  获取最近一次经过消抖确认的稳定触摸状态。
  * @retval TTP223_TOUCHED 或 TTP223_RELEASED。
  */
u8 TTP223_GetState(void)
{
	return TTP223_StableState;
}

/**
  * @brief  扫描 TTP223,并判断是否发生触摸状态切换。
  * @note   本函数应在主循环中反复调用。当检测到 PB12 电平发生变化时,
  *         延时 TTP223_DEBOUNCE_MS 后再次读取;只有两次结果一致,才确认
  *         本次状态切换有效。
  * @retval TTP223_EVENT_NONE:状态未改变或消抖失败;
  *         TTP223_EVENT_TOUCH:确认发生触摸;
  *         TTP223_EVENT_RELEASE:确认触摸被释放。
  */
TTP223_EventTypeDef TTP223_Scan(void)
{
	u8 current_state;

	/* 当前电平与稳定状态相同时,不产生新事件。 */
	current_state = TTP223_Read();
	if(current_state == TTP223_StableState)
	{
		return TTP223_EVENT_NONE;
	}

	/* 延时后再次采样,以滤除状态切换瞬间的干扰。 */
	delay_ms(TTP223_DEBOUNCE_MS);
	current_state = TTP223_Read();
	if(current_state == TTP223_StableState)
	{
		return TTP223_EVENT_NONE;
	}

	/* 更新稳定状态,并根据新状态返回对应事件。 */
	TTP223_StableState = current_state;
	if(current_state == TTP223_TOUCHED)
	{
		return TTP223_EVENT_TOUCH;
	}

	return TTP223_EVENT_RELEASE;
}

TTP223.h代码

#ifndef __TTP223_H
#define __TTP223_H

#include "stm32f10x.h"

/* TTP223 的 OUT 引脚连接至 PB12,模块默认为高电平有效。 */
#define TTP223_GPIO_PORT        GPIOB
#define TTP223_GPIO_PIN         GPIO_Pin_12
#define TTP223_GPIO_CLK         RCC_APB2Periph_GPIOB
/* 触摸状态切换时的消抖时间,单位:毫秒。 */
#define TTP223_DEBOUNCE_MS      20U

/* 消抖后的稳定触摸状态。 */
#define TTP223_RELEASED         0U
#define TTP223_TOUCHED          1U

/* TTP223 扫描事件:状态不变、触摸按下、触摸释放。 */
typedef enum
{
	TTP223_EVENT_NONE = 0,
	TTP223_EVENT_TOUCH,
	TTP223_EVENT_RELEASE
} TTP223_EventTypeDef;

/* 初始化 PB12,并记录模块上电后的初始状态。 */
void TTP223_Init(void);
/* 直接读取 PB12 的瞬时电平,不进行消抖。 */
u8 TTP223_Read(void);
/* 获取最近一次经过消抖确认的稳定状态。 */
u8 TTP223_GetState(void);
/* 扫描并返回经过消抖确认的触摸或释放事件。 */
TTP223_EventTypeDef TTP223_Scan(void);

#endif

六、实物演示章节

6.1 实物接线搭建

把 STM32 最小系统板、TTP223 触摸模块、OLED 屏幕按照上面表格接线,接线完成检查:

  1. 确认所有 GND 共地;TTP223 接 5V 供电,OLED 接 3.3V;
  2. TTP223 OUT 引脚确认接 PB12;OLED SCL 接 PB8,SDA 接 PB9;
  3. 确认 TTP223 硬件模式为高电平有效、直接输出模式。

烧录程序之后,上电,上电前手指不要放在触摸片上,TTP223 上电需要约 0.5 秒做基准校准,校准阶段触摸检测不正常。

6.2 现象观察

  1. 没有触摸:OLED 显示STATE:RELEASED,COUNT 保持不变;
  2. 手指触摸感应片:屏幕立刻切换STATE:TOUCHED,触摸计数 + 1;
  3. 手指持续按住不放:状态保持 TOUCHED,计数不会持续上涨;
  4. 松开手指:变回 RELEASED,计数保持不变;
  5. 反复触摸松开,COUNT 数字不断累加,到 99999 之后再次触摸自动归零。

拓展观察:可以用万用表测量 TTP223 的 OUT 引脚电压,触摸时输出接近 5V 高电平,松开回到低电平。

6.3 常见问题排查

遇到触摸乱触发、触摸没反应,按照下面顺序排查:

  1. 检查供电、地线,确认全部共地,接线牢固;
  2. 确认模块硬件模式:高电平有效、直接模式;
  3. 重新上电,上电 0.5s 校准时间手不要碰触摸电极;
  4. OUT 信号线尽量缩短,远离开关电源、电机等强干扰源;
  5. 修改触摸铜箔面积,调整模块灵敏度,减少遮挡覆盖层厚度。

七、补充知识点

  1. TTP223 是硬件完成触摸判断,STM32 只做信号读取和软件抗干扰,不负责电容采样;
  2. FT 引脚虽然支持 5V 输入,但不代表可以长期乱接,尽量遵循芯片手册要求关闭内部上下拉;
  3. 电容触摸很容易受电源、地线、环境湿度影响,实际产品开发必须重视电源滤波和布线。

八、总结

手指靠近触摸电极,改变电极等效电容;TTP223 芯片把电容变化转换成 OUT 引脚高低电平;STM32 读取 PB12 引脚电平,软件做消抖复核识别触摸、释放事件;最后把触摸状态和触摸次数输出到 OLED 屏幕,完成整套工程功能。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐