这篇文章来讲一下如何使用ESP8266单片机与触摸芯片—BS8112A(可测12个按键)进行IIC通信测量9个按键,因为自己之前没有接触过类似的触摸芯片,所以就选择先挑选网上教程和售卖比较多的芯片和模块,但是我发现使用ESP系列单片机来控制芯片的教程比较少。于是,写下这篇文章,希望对大家有帮助。

1、起因:最近在做一个项目,需要测量9个触摸按键,于是上网开始挑选合适的触摸芯片和模块。

(1)TTP2x系列芯片——TTP229-BSF(可测量16个按键)

        这款芯片比较热门(自身感觉!),网上它的教程(但有关ESP8266的程序代码都是一样的)相对来说比较多,并且TB卖的触摸按键模块大部分都是使用它。

图1  TTP229-BSF芯片

        我在大致读了一下芯片手册后,便购买了它,同时还画了PCB准备验证,结果因为自己只是粗略地读了英文版的芯片手册,导致对该芯片的了解不准确,导致画的板子出错了,索性直接买一个模块来测试一下。

图2  TTP229触摸按键模块

        模块到来后便跟着网上教程直接测试触摸效果,ESP8266烧录好代码后测试一下果然有反应了,但是不知道为啥当按下按键“1”、“2”、“3”时,串口打印出来的却是其它按键的返回值,跟教程里展示的不一样。

        于是重新开始在网上地毯式搜索相关芯片的代码编写,发现该芯片是有中文版的手册,重新看了一遍后,发现它有多种模式来控制,感觉对我来说比较复杂,而且TTP229-BSF是用2线串行接口通信的,有些博主说可以用SPI通信,但是我只了解过IIC,SPI没了解过。

        所以我想着要不直接找IIC通信的触摸芯片吧,于是放弃了这款芯片。

(2)BS81xA系列芯片——BS8112A-3(可测量12个按键)

        在某个视频评论区里看到说合泰的触摸芯片比较容易,查了一下芯片手册发现是IIC通信的,并且推荐的外围电路十分简单,只有一个滤波电容!于是选择了它,再认真看完手册后,便画了PCB准备再次测试。

芯片手册网址:Holtek-simBS81xA-xv170.pdf

图3  BS8112A-3芯片

       图4  BS8112A外围电路图

2、过程:

        找了一些网上的教程,虽然没有搜到用ESP8266实现触摸的代码,但是IIC的代码大致都是相同的,便开始学习。前期主要是复习一下IIC的通信原理,再学习ESP8266的wire.h库(用来IIC通信调用的),并看看别人写的代码。想着结合芯片手册给的IIC传输示意图来调用wire.h来写,但花了一天都没成功,暂时放弃这种写法了。

图5  BS8112A的从机七位地址、八位地址 

图6  BS8112A读取按键状态寄存器 

        于是想着使用I/O口来模拟IIC,去B站看了一下江科大的软件IIC的视频,尝试先用STM32来实现,花了大概几个小时成功实现了触摸按键,将所写的代码移植到ESP8266上,改一改代码格式(C语言变成C++),过程十分顺利,当然结果也顺利实现了。

图7  STM32实现触摸按键

图8  ESP8266实现触摸按键

软件IIC代码如下:

//模拟SCL
void MyI2C_W_SCL(uint8_t BitValue)
{
    digitalWrite(SCL_PIN, (BitValue? 1 : 0));
    delayMicroseconds(5);
}

//模拟SDA(主机向从机写数据)
void MyI2C_W_SDA(uint8_t BitValue)
{
    digitalWrite(SDA_PIN, (BitValue? 1 : 0));
    delayMicroseconds(5);
}

//模拟SDA(主机读取从机发来的数据)
uint8_t MyI2C_R_SDA(void)
{
    uint8_t BitValue;
    BitValue = digitalRead(SDA_PIN);
    delayMicroseconds(5);
    return BitValue;
}

//起始
void MyI2C_Start(void)
{
  //MyI2C_W_SCL(0);
    MyI2C_W_SDA(1);
    MyI2C_W_SCL(1);
    MyI2C_W_SDA(0);
    MyI2C_W_SCL(0);   
}

//结束
void MyI2C_Stop(void)
{
    MyI2C_W_SDA(0);
    MyI2C_W_SCL(1);
    MyI2C_W_SDA(1);
}

//主机发送数据
void MyI2C_SendByte(uint8_t Byte)
{
    uint8_t i;
    for (i = 0; i < 8; i ++)
    {
      MyI2C_W_SDA(Byte & (0x80 >> i));
      MyI2C_W_SCL(1);
      MyI2C_W_SCL(0);
    }
}

//主机接收数据
uint8_t MyI2C_ReceiveByte(void)
{
    uint8_t i, Byte = 0x00;
    MyI2C_W_SDA(1);
    for (i = 0; i < 8; i ++)
    {
      MyI2C_W_SCL(1);
      if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}
      MyI2C_W_SCL(0);
    }
    return Byte;
}

//主机发送应答
void MyI2C_SendAck(uint8_t AckBit)
{
    MyI2C_W_SDA(AckBit);
    MyI2C_W_SCL(1);
    MyI2C_W_SCL(0);
}
//主机接收从机的应答
uint8_t MyI2C_ReceiveAck(void)
{
    uint8_t AckBit;
    MyI2C_W_SDA(1);
    MyI2C_W_SCL(1);
    AckBit = MyI2C_R_SDA();
    MyI2C_W_SCL(0);
    return AckBit;
}

/*
函 数 名:BSI2C_SendByte
函数功能:IIC发送字节数据
返 回 值:ack 接受到的响应
形    参:data 待发送的数据
备    注:
*/
uint8_t BSI2C_SendByte(uint8_t data)
{
	
	MyI2C_SendByte(data);
	uint8_t ack = MyI2C_ReceiveAck();
	return ack;
	
}

/*
函 数 名:BSI2C_RevByte
函数功能:IIC接收字节数据
返 回 值:接收到的数据
形    参:ack 待发送的响应
备    注:
*/
uint8_t BSI2C_RevByte(uint8_t ack)
{
	uint8_t data;
	data = MyI2C_ReceiveByte();
	MyI2C_SendAck(ack);
	return data;
}

// BS8112A检测函数
uint8_t BS8112_Read_Key(void)
{

	MyI2C_Start();
	if(BSI2C_SendByte(0xA0))
	{
		return 1;
	}
	if(BSI2C_SendByte(0x08))
	{
		return 2;
	}
	
	MyI2C_Start();
	if(BSI2C_SendByte(0xA1))
	{
		return 3;
	}
	Touch_Key = BSI2C_RevByte(0);
	Touch_Key = BSI2C_RevByte(1)<<8|Touch_Key;
	MyI2C_Stop();
	switch(Touch_Key)
	{
		case 0x0001:return '1';
		case 0x0008:return '2';
		case 0x0040:return '3';
		case 0x0002:return '4';
		case 0x0010:return '5';
		case 0x0080:return '6';
		case 0x0004:return '7';
		case 0x0020:return '8';
		case 0x0100:return '9';
	}
	return 0;
}   

ESP8266完整代码如下:

#include <Arduino.h>
#include <stdint.h>
#include <Ticker.h>

#define SDA_PIN 4                           // SDA引脚,默认gpio4(D2)
#define SCL_PIN 5                           // SCL引脚,默认gpio5(D1)

uint16_t Actual_Key,Touch_Key = 0;

Ticker key_tick;                            //定时器

void KEY_GET(void)
{
    Actual_Key = BS8112_Read_Key();         //定时器执行函数
}

void setup() 
{
    Serial.begin(115200);    //初始化串口

    pinMode(SDA_PIN, OUTPUT_OPEN_DRAIN);       //初始化SCL、SDA引脚
    pinMode(SCL_PIN, OUTPUT_OPEN_DRAIN);

    key_tick.attach_ms(10,KEY_GET);            //配置好定时器

    delay(100);
}

void loop() 
{
    if (Actual_Key != 0) 
    {
        Serial.print("Read key: ");            //串口打印按键值
        Serial.println(Actual_Key);
    }
    
    delay(50);
}

//模拟SCL
void MyI2C_W_SCL(uint8_t BitValue)
{
    digitalWrite(SCL_PIN, (BitValue? 1 : 0));
    delayMicroseconds(5);
}

//模拟SDA(主机向从机写数据)
void MyI2C_W_SDA(uint8_t BitValue)
{
    digitalWrite(SDA_PIN, (BitValue? 1 : 0));
    delayMicroseconds(5);
}

//模拟SDA(主机读取从机发来的数据)
uint8_t MyI2C_R_SDA(void)
{
    uint8_t BitValue;
    BitValue = digitalRead(SDA_PIN);
    delayMicroseconds(5);
    return BitValue;
}

//起始
void MyI2C_Start(void)
{
  //MyI2C_W_SCL(0);
    MyI2C_W_SDA(1);
    MyI2C_W_SCL(1);
    MyI2C_W_SDA(0);
    MyI2C_W_SCL(0);   
}

//结束
void MyI2C_Stop(void)
{
    MyI2C_W_SDA(0);
    MyI2C_W_SCL(1);
    MyI2C_W_SDA(1);
}

//主机发送数据
void MyI2C_SendByte(uint8_t Byte)
{
    uint8_t i;
    for (i = 0; i < 8; i ++)
    {
      MyI2C_W_SDA(Byte & (0x80 >> i));
      MyI2C_W_SCL(1);
      MyI2C_W_SCL(0);
    }
}

//主机接收数据
uint8_t MyI2C_ReceiveByte(void)
{
    uint8_t i, Byte = 0x00;
    MyI2C_W_SDA(1);
    for (i = 0; i < 8; i ++)
    {
      MyI2C_W_SCL(1);
      if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}
      MyI2C_W_SCL(0);
    }
    return Byte;
}

//主机发送应答
void MyI2C_SendAck(uint8_t AckBit)
{
    MyI2C_W_SDA(AckBit);
    MyI2C_W_SCL(1);
    MyI2C_W_SCL(0);
}
//主机接收从机的应答
uint8_t MyI2C_ReceiveAck(void)
{
    uint8_t AckBit;
    MyI2C_W_SDA(1);
    MyI2C_W_SCL(1);
    AckBit = MyI2C_R_SDA();
    MyI2C_W_SCL(0);
    return AckBit;
}

/*
函 数 名:BSI2C_SendByte
函数功能:IIC发送字节数据
返 回 值:ack 接受到的响应
形    参:data 待发送的数据
备    注:
*/
uint8_t BSI2C_SendByte(uint8_t data)
{
	
	MyI2C_SendByte(data);
	uint8_t ack = MyI2C_ReceiveAck();
	return ack;
	
}

/*
函 数 名:BSI2C_RevByte
函数功能:IIC接收字节数据
返 回 值:接收到的数据
形    参:ack 待发送的响应
备    注:
*/
uint8_t BSI2C_RevByte(uint8_t ack)
{
	uint8_t data;
	data = MyI2C_ReceiveByte();
	MyI2C_SendAck(ack);
	return data;
}

// BS8112A检测函数
uint8_t BS8112_Read_Key(void)
{

	MyI2C_Start();
	if(BSI2C_SendByte(0xA0))
	{
		return 1;
	}
	if(BSI2C_SendByte(0x08))
	{
		return 2;
	}
	
	MyI2C_Start();
	if(BSI2C_SendByte(0xA1))
	{
		return 3;
	}
	Touch_Key = BSI2C_RevByte(0);
	Touch_Key = BSI2C_RevByte(1)<<8|Touch_Key;
	MyI2C_Stop();
	switch(Touch_Key)
	{
		case 0x0001:return '1';
		case 0x0008:return '2';
		case 0x0040:return '3';
		case 0x0002:return '4';
		case 0x0010:return '5';
		case 0x0080:return '6';
		case 0x0004:return '7';
		case 0x0020:return '8';
		case 0x0100:return '9';
	}
	return 0;
}   

3、总结:

        记录了自己使用ESP8266单片机实现了对BS8112A触摸按键芯片的IIC通信,以上内容希望对大家也有帮助!

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐