一、BS81x 特征
• 工作电压:2.2V~5.5V
• 低待机电流
• 自动校准功能
• 可靠的触摸按键检测
• 自动切换待机 / 工作模式
• 最长按键输出时间检测
• 具备抗电压波动功能
• Level Hold,可选高有效或低有效
• NMOS 输出内建上拉电阻 /CMOS 直接 输出
• 支持串行和并行输出
• 外接电容调整感度
• 极少的外围组件
二、BS81x 概述
BS81x 系列芯片具有 2~16 个触摸按键,可用来检测外部触摸按键上人手的触摸动作。该系列的芯片具有较高的集成度,仅需极少的外部组件便可实现触摸按键的检测。
BS81x 系列提供了串行及并行输出功能,可方便与外部 MCU 之间的通讯,实现设备安装及触摸引脚监测目的。芯片内部采用特殊的集成电路,具有高电源电压抑制比,可减少按键检测错误的发生,此特性保证在不利环境条件的应用中芯片仍具有很高的可靠性。
此系列的触摸芯片具有自动校准功能,低待机电流,抗电压波动等特性,为各种触摸按键的应用提供了一种简单而又有效的实现方法。
三、BS8166介绍
我们这里使用的是BS8166A-3,下图所示:
1、引脚特征
按键:16个触摸按键
通信方式:IIC
2、接口电气特征
3、读写时序流程
写设置:起始条件+一个字节(触摸芯片器件地址+W)+从机回响应信号+主机发送命令字节+从机回响应信号+主机发送起始信号+一个字节(触摸芯片器件地址+R)+从机回响应信号+读取输出(从机发送)+主机发送停止信号
读1个字节设置:起始条件+一个字节(触摸芯片器件地址+R)+从机回响应信号+主机发送命令字节+从机回响应信号+主机发送起始信号+一个字节(触摸芯片器件地址+R)+从机回响应信号+读取输出(从机发送)+主机发送停止信号
4、IRQ功能
输出模式:IRQ_OMS = 0 (Level hold,低有效)主机在 IRQ 低电平时读取按键数据,当按键数据为 0 时停止读取。
输出模式:IRQ_OMS = 1 (One-shot,低有效 )按键状态发生改变时,发一脉冲信号。
不使用 IRQ 功能时:Key16 (BS8116A-3) 是触摸按键
当主机读取所有按键为松键 (KeyStatus=0x00) 后,主机可以降低读取速度,使功耗降低,降低读取速度时按键反应速度会变慢。
最长按键持续时间:为尽量减少如不小心碰触到感应电极等此类的无意按键检测,芯片内部设置了最长按键持续时间功能。当某个触摸按键按下时,内部定时器开始计时,一旦按键按下的时间过长,超过大约 64s 后,触摸芯片会忽略该被触摸键的状态,重新校准,获取新的基准值,同时输出状态重置为初始状态。
自动校准功能:上电后,芯片会进行初始化,取得第一次基准值,接下来,若在正常模式下 1s 内,待机模式下 32s 内,没有按键被按下,触摸芯片在固定的时间周期到后,将自动校准基准值,使得基准值可以根据外界环境进行动态的变化。
四、代码例程
1、硬件原理图
2、完整代码
main.c
#include "led.h"
#include "delay.h"
#include "key.h"
#include "usart.h"
#include "stdio.h"
#include "myiic.h"
#include "at24c0x.h"
#include "BS8116_IIC1.h"
#include "BS8116.h"
int main(void)
{
uint8_t key;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
SysTick_Config(SystemCoreClock/1000); //配置1ms的中断
Led_Config();
USART1_Config();
// At24c02_Config();
BS8116_Config();
while(1)
{
// if(LED_Period[0]>=LED_Period[1])
// {
// LED_Period[0]=0;
// led3_T();
// led4_T();
// }
if(!BS8116_IRQ)//判断按键是否按下
{
key=BS8116ReadKey();
if(key&&key!=0xFF)
{
printf("%c\r\n",key);
}
while(!BS8116_IRQ);
}
}
}
IIC1.c
#include "BS8116_IIC1.h"
#include "delay.h"
/***************
SCL PB6 开漏输出
SDA PB7 开漏输出
***************/
void I2C1_config(void)
{
//开启GPIOB的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
//定义结构体
GPIO_InitTypeDef GPIO_InitStruct;
//给结构体内容赋值
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //输出模式
GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; //开漏模式
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed;
GPIO_Init(GPIOB,&GPIO_InitStruct);
/*设置默认电平*/
GPIO_SetBits(GPIOB, GPIO_Pin_6);
GPIO_SetBits(GPIOB, GPIO_Pin_7);
}
/*
****************************************************************************************
* Function: IIC1_Start
* Description: 起始条件
* Input: None
* Output: None
* Return: None
* Author: weihaoMo
* Others: None
* Date of completion: 2019-11-29
* Date of last modify: 2019-11-29
****************************************************************************************
*/
void IIC1_Start(void)
{
IIC1_SCL(0);
Delay_us(6);
IIC1_SDA_OUT(1);
IIC1_SCL(1);
Delay_us(6);//延时---起始条件的建立时间
IIC1_SDA_OUT(0);//---产生起始条件
Delay_us(6);//延时---起始条件的保持时间
IIC1_SCL(0); //---结束起始条件
}
/*
****************************************************************************************
* Function: IIC1_Stop
* Description: 停止条件
* Input: None
* Output: None
* Return: None
* Author: weihaoMo
* Others: None
* Date of completion: 2019-11-29
* Date of last modify: 2019-11-29
****************************************************************************************
*/
void IIC1_Stop(void)
{
IIC1_SDA_OUT(0);
IIC1_SCL(1);
Delay_us(6);//延时---停止条件的建立时间
IIC1_SDA_OUT(1); //---产生了停止条件
Delay_us(6);//延时---本次通信结束到下次通信开始的时间
}
/*
****************************************************************************************
* Function: IIC1_Send_Ack
* Description: 主机发送应答信号
* Input: ack--0表示有应答 1表示非应答
* Output: None
* Return: None
* Author: weihaoMo
* Others: None
* Date of completion: 2019-11-29
* Date of last modify: 2019-11-29
****************************************************************************************
*/
void IIC1_Send_Ack(uint8_t ack)
{
IIC1_SCL(0);
if(ack)//(主机准备数据)
IIC1_SDA_OUT(1);
else
IIC1_SDA_OUT(0);
Delay_us(6);//延时(数据稳定在数据线上)
IIC1_SCL(1);//(从机在时钟线上升沿从SDA上采集数据)
Delay_us(6);//延时(给时间从机读取数据)
// IIC1_SCL=0;//方便后续的操作;防止意外产生了停止条件
}
/*
****************************************************************************************
* Function: IIC1_Revice_Ack
* Description: 主机读取应答信号
* Input: None
* Output: None
* Return: 0--有应答 1--非应答
* Author: weihaoMo
* Others: None
* Date of completion: 2019-11-29
* Date of last modify: 2019-11-29
****************************************************************************************
*/
uint8_t IIC1_Revice_Ack(void)
{
uint8_t ack=0;
IIC1_SCL(0);//(从机准备数据)
IIC1_SDA_OUT(1);//读模式-----让输出电路与管脚断开!!!!!!!!!!
Delay_us(6);//延时(给时间从机准备数据并且数据稳定在数据线上)
IIC1_SCL(1);
Delay_us(6);//延时 (给时间主机读取数据)
if(IIC1_SDA_IN)//主机读取SDA线上的数据
ack=1;
//IIC1_SCL=0;//方便后续的操作;防止意外产生了停止条件
return ack;
}
/*
****************************************************************************************
* Function: IIC1_Send_Byte
* Description: 主机发送一个字节给从机并且读取一次应答信号
* Input: 待发送的一个字节数据
* Output: None
* Return: 应答信号 0--有应答 1--非应答
* Author: weihaoMo
* Others: None
* Date of completion: 2019-11-29
* Date of last modify: 2019-11-29
****************************************************************************************
*/
uint8_t IIC1_Send_Byte(uint8_t data)
{
uint8_t i=0;
for(i=0;i<8;i++)
{
IIC1_SCL(0);
if((data<<i)&0x80)//(主机准备数据)
IIC1_SDA_OUT(1);
else
IIC1_SDA_OUT(0);
Delay_us(6);//延时(数据稳定在数据线上)
IIC1_SCL(1);//(从机在时钟线上升沿从SDA上采集数据)
Delay_us(6);//延时(给时间从机读取数据)
}
//IIC1_SCL=0;//方便后续的操作;防止意外产生了停止条件
return IIC1_Revice_Ack( );
}
/*
****************************************************************************************
* Function: IIC1_Revice_Byte
* Description: 主机读取一个字节并且发送一次应答信号
* Input: 应答信号 0--有应答 1--非应答
* Output: None
* Return: 读取的一个字节数据
* Author: weihaoMo
* Others: None
* Date of completion: 2019-11-29
* Date of last modify: 2019-11-29
****************************************************************************************
*/
uint8_t IIC1_Revice_Byte(uint8_t ack)
{
uint8_t i=0;
uint8_t data=0;
for(i=0;i<8;i++)
{
IIC1_SCL(0);//(从机准备数据)
IIC1_SDA_OUT(1);//读模式-----让输出电路与管脚断开!!!!!!!!!!
Delay_us(6);//延时(给时间从机准备数据并且数据稳定在数据线上)
IIC1_SCL(1);
data = (data<<1) | (IIC1_SDA_IN);
Delay_us(6);//延时 (给时间主机读取数据)
}
//IIC1_SCL=0;//方便后续的操作;防止意外产生了停止条件
IIC1_Send_Ack(ack);
return data;
}
IIC1.h
#ifndef _BS8116_IIC1_H_
#define _BS8116_IIC1_H_
#include "stm32f4xx.h"
#define IIC1_SCL(x) GPIO_WriteBit(GPIOB,GPIO_Pin_6,(BitAction)x)
#define IIC1_SDA_OUT(x) GPIO_WriteBit(GPIOB,GPIO_Pin_7,(BitAction)x)
#define IIC1_SDA_IN GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7)
#define IIC1_SCL_IN GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6)
void I2C1_config(void);
void IIC1_Start(void);
void IIC1_Stop(void);
uint8_t IIC1_Send_Byte(uint8_t data);
uint8_t IIC1_Revice_Byte(uint8_t ack);
#endif
BS8116.c
#include "BS8116.h"
#include "BS8116_IIC1.h"
#include "delay.h"
#include "stdio.h"
void BS8116_Config(void)
{
I2C1_config();
//打开GPIOA的时钟 BS8116_IRQ-PA1
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
}
uint8_t BS8116ReadKey(void)
{
uint16_t data;
IIC1_Start(); //起始条件
IIC1_Send_Byte(0xA0); //主机发送一个字节:1010 0000 前7位是从机地址 最后是‘写’
Delay_us(5);
while(!IIC1_SCL_IN);
IIC1_Send_Byte(0x08); //触摸按键的输出寄存器地址
Delay_us(5);
while(!IIC1_SCL_IN);
IIC1_Start(); //起始条件
IIC1_Send_Byte(0xA1); //1010 0001
Delay_us(5);
while(!IIC1_SCL_IN);
data = IIC1_Revice_Byte(0);
data|=IIC1_Revice_Byte(1)<<8;
IIC1_Stop( );
switch(data)
{
case 0X8081:return '1';
case 0X8480:return '2';
case 0X8080:return '3';
case 0X8082:return '4';
case 0X8880:return '5';
case 0X80C0:return '6';
case 0X8088:return '7';
case 0X8180:return '8';
case 0X80A0:return '9';
case 0X8084:return '*';
case 0X8280:return '0';
case 0X8090:return '#';
}
return 0;
}
BS8116.h
#ifndef _BS8116_H_
#define _BS8116_H_
#include "stm32f4xx.h"
#define BS8116_IRQ (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1))
void BS8116_Config(void);
uint8_t BS8116ReadKey(void);
#endif
3、实验现象
按下按键会显示对应的数字。