精华内容
下载资源
问答
  • 上拉电阻、下拉电阻、旁路电容,上拉电阻、下拉电阻、旁路电容
  • 电容耦合是电路中信号传输的方式之一,和滤波有区别。 在电路中经常需要将本级信号传输到下一级电路中去,这种传输就叫耦合。根据所用的元件不同,耦合方式有电感耦合、电容耦合、光电耦合、直流耦合等多种。用电容...

    电容耦合是电路中信号传输的方式之一,和滤波有区别。
      在电路中经常需要将本级信号传输到下一级电路中去,这种传输就叫耦合。根据所用的元件不同,耦合方式有电感耦合电容耦合光电耦合、直流耦合等多种。用电容来实现这种信号传输的方式叫电容耦合。扩音机是常见的一种电器,前级放大器将话筒微弱的音频信号放大后,需要传输给功率放大级,输出足够的功率推动喇叭发出声音。在前级和功放之间往往用一只电容来连接,根据电容“通交流,隔直流”的特性,把有用的音频信号(交流电)传输给功率放大级。直流会被隔断,因而两级之间没有直流电的传输,互不影响工作状态。这就是电容耦合的具体含义。
      滤波电路经常用到电容,虽然都是利用电容“通交流,隔直流”的特性,但是有区别的,表现在:
      1、作用不同。耦合是把有用的交流成分传输到下一级,而滤波则是把有害的交流成分去掉。
      2、容量大小不同。耦合往往用10微法及以下容量,而滤波则经常用到几百、几千微法的容量。
      3、耐压不同。耦合电容耐压一般比较低,而滤波电容耐压一般比较高。
      4、体积不同。耦合电容体积比较小,而滤波电容由于容量大,体积也会大许多。
      当然,这些区别是大体而言,并不是绝对,如,有时滤波电路用一个很小容量的电容来滤除高频有害成分。

    转载于:https://www.cnblogs.com/isAndyWu/p/9577326.html

    展开全文
  • 电容触摸按键简介02. 硬件设计03. 功能描述04. 程序示例05. 结果验证06. 附录07. 声明 01. 电容触摸按键简介 触摸按键相对于传统的机械按键有寿命长、占用空间少、易于操作等诸多优点。大家看看如今的手机,触摸屏...

    00. 目录

    01. 电容触摸按键简介

    触摸按键相对于传统的机械按键有寿命长、占用空间少、易于操作等诸多优点。大家看看如今的手机,触摸屏、触摸按键大行其道,而传统的机械按键,正在逐步从手机上面消失。接下来给大家介绍一种简单的触摸按键:电容式触摸按键

    利用探索者 STM32F4 开发板上的触摸按键(TPAD)来实现对 DS1 的亮灭控制。这里 TPAD 其实就是探索者 STM32F4 开发板上的一小块覆铜区域,实现原理如图所示:
    在这里插入图片描述

    我们使用的是检测电容充放电时间的方法来判断是否有触摸,图中 R 是外接的电容充电电阻,Cs 是没有触摸按下时 TPAD 与 PCB 之间的杂散电容。而 Cx 则是有手指按下的时候,手指与 TPAD 之间形成的电容。图中的开关是电容放电开关(由实际使用时,由 STM32F4 的IO 代替)。

    先用开关将 Cs(或 Cs+Cx)上的电放尽,然后断开开关,让 R 给 Cs(或 Cs+Cx)充电,当没有手指触摸的时候,Cs 的充电曲线如图中的 A 曲线。而当有手指触摸的时候,手指和 TPAD之间引入了新的电容 Cx,此时 Cs+Cx 的充电曲线如图中的 B 曲线。从上图可以看出,A、B两种情况下,Vc 达到 Vth 的时间分别为 Tcs 和 Tcs+Tcx。

    其中,除了 Cs 和 Cx 我们需要计算,其他都是已知的,根据电容充放电公式:

    Vc=V0(1-e^(-t/RC))*

    其中 Vc 为电容电压,V0 为充电电压,R 为充电电阻,C 为电容容值,e 为自然底数,t 为充电时间。根据这个公式,我们就可以计算出 Cs 和 Cx。利用这个公式,我们还可以把探索者开发板作为一个简单的电容计,直接可以测电容容量了。

    我们只要能够区分 Tcs 和 Tcs+Tcx,就已经可以实现触摸检测了,当充电时间在 Tcs 附近,就可以认为没有触摸,而当充电时间大于 Tcs+Tx 时,就认为有触摸按下(Tx为检测阀值)。

    我们使用 PA5(TIM2_CH1)来检测 TPAD 是否有触摸,在每次检测之前,我们先配置PA5 为推挽输出,将电容 Cs(或 Cs+Cx)放电,然后配置 PA5 为浮空输入,利用外部上拉电阻给电容 Cs(Cs+Cx)充电,同时开启 TIM2_CH1 的输入捕获,检测上升沿,当检测到上升沿的时候,就认为电容充电完成了,完成一次捕获检测。

    在 MCU 每次复位重启的时候,我们执行一次捕获检测(可以认为没触摸),记录此时的值,记为 tpad_default_val,作为判断的依据。在后续的捕获检测,我们就通过与 tpad_default_val 的对比,来判断是不是有触摸发生。

    02. 硬件设计

    用到的硬件资源有:
    1) 指示灯 DS0 和 DS1
    2) 定时器 TIM2
    3) 触摸按键 TPAD

    需要通过 TIM2_CH1(PA5)采集 TPAD 的信号,所以需要用跳线帽短接多功能端口(P12)的 TPAD 和 ADC,以实现 TPAD 连接到PA5。如图所示:
    在这里插入图片描述

    03. 功能描述

    实现一个简单的电容触摸按键,通过该按键控制 DS1 的亮灭。

    04. 程序示例

    tpad.h

    #ifndef __TPAD_H
    #define __TPAD_H
    #include "sys.h"
    	   
    //空载的时候(没有手按下),计数器需要的时间
    //这个值应该在每次开机的时候被初始化一次
    extern vu16 tpad_default_val;
    							   	    
    void TPAD_Reset(void);
    u16  TPAD_Get_Val(void);
    u16 TPAD_Get_MaxVal(u8 n);
    u8   TPAD_Init(u8 systick);
    u8   TPAD_Scan(u8 mode);
    void TIM2_CH1_Cap_Init(u32 arr,u16 psc);     
    #endif
    
    

    tpad.c

    #define TPAD_ARR_MAX_VAL  0XFFFFFFFF	//最大的ARR值(TIM2是32位定时器)	  
    vu16 tpad_default_val=0;				//空载的时候(没有手按下),计数器需要的时间
    //初始化触摸按键
    //获得空载的时候触摸按键的取值.
    //psc:分频系数,越小,灵敏度越高.
    //返回值:0,初始化成功;1,初始化失败
    u8 TPAD_Init(u8 psc)
    {
    	u16 buf[10];
    	u16 temp;
    	u8 j,i;
    	TIM2_CH1_Cap_Init(TPAD_ARR_MAX_VAL,psc-1);//设置分频系数
    	for(i=0;i<10;i++)//连续读取10次
    	{				 
    		buf[i]=TPAD_Get_Val();
    		delay_ms(10);	    
    	}				    
    	for(i=0;i<9;i++)//排序
    	{
    		for(j=i+1;j<10;j++)
    		{
    			if(buf[i]>buf[j])//升序排列
    			{
    				temp=buf[i];
    				buf[i]=buf[j];
    				buf[j]=temp;
    			}
    		}
    	}
    	temp=0;
    	for(i=2;i<8;i++)temp+=buf[i];//取中间的8个数据进行平均
    	tpad_default_val=temp/6;
    	printf("tpad_default_val:%d\r\n",tpad_default_val);	
    	if(tpad_default_val>TPAD_ARR_MAX_VAL/2)return 1;//初始化遇到超过TPAD_ARR_MAX_VAL/2的数值,不正常!
    	return 0;		     	    					   
    }
    //复位一次
    //释放电容电量,并清除定时器的计数值
    void TPAD_Reset(void)
    {		
    	GPIO_InitTypeDef GPIO_InitStructure;
    	
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;  //PA5
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//速度100MHz
    	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽 
    	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉
    	GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA5
    	 
    	GPIO_ResetBits(GPIOA,GPIO_Pin_5);//输出0,放电
    
    	delay_ms(5);
    	TIM_ClearITPendingBit(TIM2, TIM_IT_CC1|TIM_IT_Update); //清除中断标志
    	TIM_SetCounter(TIM2,0);		//归0
    	
    
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //PA5
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//速度100MHz
    	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽 
    	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//不带上下拉 
    	GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA5
    	
    }
    //得到定时器捕获值
    //如果超时,则直接返回定时器的计数值.
    //返回值:捕获值/计数值(超时的情况下返回)
    u16 TPAD_Get_Val(void)
    {				   
    	TPAD_Reset();
    	while(TIM_GetFlagStatus(TIM2, TIM_IT_CC1) == RESET)//等待捕获上升沿
    	{
    		if(TIM_GetCounter(TIM2)>TPAD_ARR_MAX_VAL-500)return TIM_GetCounter(TIM2);//超时了,直接返回CNT的值
    	};	
    	return TIM_GetCapture1(TIM2);	 
    } 	  
    //读取n次,取最大值
    //n:连续获取的次数
    //返回值:n次读数里面读到的最大读数值
    u16 TPAD_Get_MaxVal(u8 n)
    {
    	u16 temp=0;
    	u16 res=0; 
    	while(n--)
    	{
    		temp=TPAD_Get_Val();//得到一次值
    		if(temp>res)res=temp;
    	};
    	return res;
    }  
    //扫描触摸按键
    //mode:0,不支持连续触发(按下一次必须松开才能按下一次);1,支持连续触发(可以一直按下)
    //返回值:0,没有按下;1,有按下;										  
    #define TPAD_GATE_VAL 	100	//触摸的门限值,也就是必须大于tpad_default_val+TPAD_GATE_VAL,才认为是有效触摸.
    u8 TPAD_Scan(u8 mode)
    {
    	static u8 keyen=0;	//0,可以开始检测;>0,还不能开始检测	 
    	u8 res=0;
    	u8 sample=3;		//默认采样次数为3次	 
    	u16 rval;
    	if(mode)
    	{
    		sample=6;	//支持连按的时候,设置采样次数为6次
    		keyen=0;	//支持连按	  
    	}
    	rval=TPAD_Get_MaxVal(sample); 
    	if(rval>(tpad_default_val+TPAD_GATE_VAL)&&rval<(10*tpad_default_val))//大于tpad_default_val+TPAD_GATE_VAL,且小于10倍tpad_default_val,则有效
    	{							 
    		if((keyen==0)&&(rval>(tpad_default_val+TPAD_GATE_VAL)))	//大于tpad_default_val+TPAD_GATE_VAL,有效
    		{
    			res=1;
    		}	   
    		//printf("r:%d\r\n",rval);		     	    					   
    		keyen=3;				//至少要再过3次之后才能按键有效   
    	} 
    	if(keyen)keyen--;		   							   		     	    					   
    	return res;
    }	 
    //定时器2通道2输入捕获配置	 
    //arr:自动重装值
    //psc:时钟预分频数
    void TIM2_CH1_Cap_Init(u32 arr,u16 psc)
    {
    	GPIO_InitTypeDef  GPIO_InitStructure; 
      TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    	TIM_ICInitTypeDef  TIM2_ICInitStructure;
    	
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);  	  //TIM2时钟使能    
    	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); 	//使能PORTA时钟	
    
    	
    	GPIO_PinAFConfig(GPIOA,GPIO_PinSource5,GPIO_AF_TIM2); //GPIOA5复用位定时器2
    
    	  
     	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //GPIOA5
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//速度100MHz
    	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
    	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//不带上下拉 
    	GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA5
    
    	
      //初始化TIM2  
    	TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值   
    	TIM_TimeBaseStructure.TIM_Prescaler =psc; 	//预分频器 	   
    	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
    	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
    	
    	//初始化通道1
      TIM2_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 	选择输入端 IC1映射到TIM2上
      TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//上升沿捕获
      TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; 
      TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //配置输入分频,不分频 
      TIM2_ICInitStructure.TIM_ICFilter = 0x00;//IC2F=0000 配置输入滤波器 不滤波
      TIM_ICInit(TIM2, &TIM2_ICInitStructure);//初始化TIM2 IC1
    															 
    	TIM_Cmd(TIM2,ENABLE ); 	//使能定时器2
    }
    
    
    

    main.c

    #include "sys.h"
    #include "delay.h"
    #include "usart.h"
    #include "led.h"
    #include "tpad.h"
    
    
    int main(void)
    { 
    	u8 t=0; 
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
    	delay_init(168);     //初始化延时函数
    	uart_init(115200);	 //初始化串口波特率为115200
    	LED_Init();					 //初始化LED
    	
    	TPAD_Init(8);				//初始化触摸按键,以84/4=21Mhz频率计数
       	while(1)
    	{					  						  		 
     		if(TPAD_Scan(0))	//成功捕获到了一次上升沿(此函数执行时间至少15ms)
    		{
    			LED1=!LED1;		//LED1取反
    		}
    		t++;
    		if(t==15)		 
    		{
    			t=0;
    			LED0=!LED0;		//LED0取反,提示程序正在运行
    		}
    		delay_ms(10);
    	}
    }
    

    05. 结果验证

    06. 附录

    6.1 【STM32】STM32系列教程汇总

    网址:【STM32】STM32系列教程汇总

    07. 声明

    该教程参考了正点原子的《STM32 F4 开发指南》

    展开全文
  • 电容电感的选型

    2021-03-28 00:10:16
    负载瞬间电流越大,容值选择越大,如果容值偏小,瞬间无法提供足够大的电流,电压将被下拉,产生纹波,影响其它电路。 另外,由于寄生参数的存在,存在频率响应,电容不是越大越好,合适最佳。 比如集成电

    电容
    1、选型依据
    容值:电容值;
    电容类型:陶瓷电容,铝电解电容,钽电解电容等;
    寄生参数:ESR,影响滤波效果;
    封装:插件封装,贴片封装;
    价格:影响产品成本;
    尺寸:影响结构;
    精度:陶瓷电容受温度影响较大,电解电容变化小些。

    2、选型方法
    ①、优先考虑容值。 电容是储能器件,容值越大,瞬间可以提供更多的能量。负载瞬间电流越大,容值选择越大,如果容值偏小,瞬间无法提供足够大的电流,电压将被下拉,产生纹波,影响其它电路。 另外,由于寄生参数的存在,存在频率响应,电容不是越大越好,合适最佳。 比如集成电路内部主要是开关电路,频率较高,一般选用0.1uF的;
    ②、容值确定后,选择电容的类型。 一般小于10uF的,优先选择陶瓷电容,超过10uF,小于几百uF的,可以选择铝电解与钽电解电容, 超过几百uF的,一般选择铝电解电容;
    ③、再选择封装,封装选择需要考虑耐压、寿命、结构、加工、成本等,比如铝电解电容,插件的比较便宜,贴片的好加工;
    ④、耐压方面,陶瓷电容耐压较好,钽电解电容耐压较差,钽电解电容的耐压值最好大于电路电压的2倍左右;
    ⑤、寿命方面,铝电解电容较差,内部电解质受温度影响较大,设计时,不要靠近热源;
    ⑥、其它方面,钽电解电容价格较高,但封装小,滤波效果好;铝电解电容价格便宜,滤波效果稍差,一般与陶瓷电容并联,减小ESR,提升滤波效果。

    3、选型实例
    以STC15实战项目MP2451电源图说明:
    ①、C24,MP2451输入滤波电容,容值尽量大些,输入电压为5-28V,此处选择了22uF/35V的铝电解电容,满足要求;
    ②、C22与C23并联,提升滤波效果。

    二、电感
    1、选型依据
    感值:电感值;
    电感类型:功率电感,普通电感,共模电感等;
    电感参数;DCR,饱和电流,温升电流;
    封装:插件封装,贴片封装(常用);
    尺寸:影响结构。
    2、选型方法
    ①、优先选择电感类型:信号滤波,一般选用0603,0402等常用的贴片封装;功率电路,一般选择功率电感,功率电感屏蔽型的EMC效果更好,但价格稍贵;
    ②、选择感值。滤波使用的,根据信号频率计算;功率电感,一般根据电源IC手册推荐的即可,影响纹波,负载动态响应等;
    ③、根据DCR,饱和电流,温升电流选择合适的尺寸,一般来说,尺寸越大,参数越好,但是价格越贵,同时,结构也会受到限制;
    ④、DCR越小越好,根据功率的I²R,DCR越大,发热越大,电源功率转换效率越低;
    ⑤、饱和电流必须大于电感流过的最大电流,如果电感饱和了,感值将急剧减小,失去储能的作用,相当于一根导线,损坏电路;
    ⑥、温升电流越大越好,发热越小。

    展开全文
  • 数字电路中功能有上拉电阻和下拉电阻。  电容器具有隔直流、提供容抗参数和贮存电能等作用,广泛地被用于隔直流、谐振、信号耦合、滤波、移相、能量转换和传感等电路中。  电阻,电路中对电流通过有...
  • 电容触摸屏

    2021-08-17 23:35:51
    电容屏具有支持多点触控、检测精度高的特点,电容屏通过与导电物体产生的电容效应来检测触摸动作, 只能感应导电物体的触摸,湿度较大或屏幕表面有水珠时会影响电容屏的检测效果。区分电阻屏和电容屏一般通过绝缘...

    电容触摸屏

    触摸屏又称触控面板,它是一种把触摸位置转化成坐标数据的输入设备,根据触摸屏的检测原理,主要分为电阻式触摸屏和电容式触摸屏。 相对来说,电阻屏造价便宜,能适应较恶劣的环境,但它只支持单点触控(一次只能检测面板上的一个触摸位置),触摸时需要一定的压力, 使用久了容易造成表面磨损,影响寿命;而电容屏具有支持多点触控、检测精度高的特点,电容屏通过与导电物体产生的电容效应来检测触摸动作, 只能感应导电物体的触摸,湿度较大或屏幕表面有水珠时会影响电容屏的检测效果。区分电阻屏和电容屏一般通过绝缘物体点击是否响应来区别。
    电阻触摸屏一般会引出四根线,通过XPT2046触摸控制芯片驱动;f407可用GT5688驱动电容屏。
    电阻屏结构及其原理

    电阻屏结构
    在这里插入图片描述
    它主要由表面硬涂层、两个ITO层、间隔点以及玻璃底层构成, 这些结构层都是透明的,整个触摸屏覆盖在液晶面板上,透过触摸屏可看到液晶面板。表面涂层起到保护作用, 玻璃底层起承载的作用,而两个ITO层是触摸屏的关键结构,它们是涂有铟锡金属氧化物的导电层。 两个ITO层之间使用间隔点使两层分开,当触摸屏表面受到压力时,表面弯曲使得上层ITO与下层ITO接触,在触点处连通电路。

    电阻屏电压原理

    两个ITO涂层的两端分别引出X-、X+、Y-、Y+四个电极,见下图 XY的ITO层结构 , 这是电阻屏最常见的四线结构,通过这些电极,外部电路向这两个涂层可以施加匀强电场或检测电压。在这里插入图片描述
    通过按压点和xy轴的比例关系,来确定按压点
    在这里插入图片描述
    计算X坐标时,在X+电极施加驱动电压Vref,X-极接地,所以X+与X-处形成了匀强电场,而触点处的电压通过Y+电极采集得到, 由于ITO层均匀导电,触点电压与Vref之比等于触点X坐标与屏宽度之比,从而:
    在这里插入图片描述
    计算Y坐标时,在Y+电极施加驱动电压Vref,Y-极接地,所以Y+与Y-处形成了匀强电场,而触点处的电压通过X+电极采集得到, 由于ITO层均匀导电,触点电压与Vref之比等于触点Y坐标与屏高度之比,从而:
    在这里插入图片描述
    注:可计算出坐标的基础是ITO上的电压是均匀的。上文说的四根线就是X、Y的正负。
    为了方便检测触摸的坐标,一些芯片厂商制作了电阻屏专用的控制芯片,控制上述采集过程、采集电压, 外部微控制器直接与触摸控制芯片通讯直接获得触点的电压或坐标。XPT2046芯片控制4线电阻触摸屏,STM32与XPT2046采用SPI通讯获取采集得的电压,然后转换成坐标。

    电容屏原理
    与电阻式触摸屏不同,电容式触摸屏不需要通过压力使触点变形,再通过触点处电压值来检测坐标,它的基本原理是利用充电时间检测电容大小,从而通过检测出电容值的变化来获知触摸信号。
    电容屏的最上层是玻璃(不会像电阻屏那样形变), 核心层部分也是由ITO材料构成的,导电材料在屏幕里构成了静电网,静电网由多行X轴电极和多列Y轴电极构成,两个电极之间会形成电容。 触摸屏工作时,X轴电极发出AC交流信号,而交流信号能穿过电容,即通过Y轴能感应出该信号,当交流电穿越时电容会有充放电过程,检测该充电时间可获知电容量。 若手指触摸屏幕,会影响触摸点附近两个电极之间的耦合,从而改变两个电极之间的电容量,若检测到某电容的电容量发生了改变, 即可获知该电容处有触摸动作。

    电容屏ITO工作过程及有效原理
    在这里插入图片描述
    X轴电极与Y轴电极在交叉处形成电容,即这两组电极构成了电容的两极,这样的结构覆盖了整个电容屏,每个电容单元在触摸屏中都有其特定的物理位置, 即电容的位置就是它在触摸屏的XY坐标。检测触摸的坐标时,第1条X轴的电极发出激励信号,而所有Y轴的电极同时接收信号, 通过检测充电时间可检测出各个Y轴与第1条X轴相交的各个互电容的大小,各个X轴依次发出激励信号,重复上述步骤,即可得到整个触摸屏二维平面的所有电容大小。 当手指接近时,会导致局部电容改变,根据得到的触摸屏电容量变化的二维数据表,可以得知每个触摸点的坐标,因此电容触摸屏支持多点触控。最终STM32通过IIC协议与电容屏芯片进行通讯来获得具体的坐标。
    电容触摸屏可看作是多个电容按键组合而成,就像机械按键中独立按键和矩阵按键的关系一样,甚至电容触摸屏的坐标扫描方式与矩阵按键都是很相似的。

    电容触摸屏芯片

    内部结构框图在这里插入图片描述
    解释如下
    在这里插入图片描述
    ADC用来检测电压;DSP处理数据。

    上电时序与I2C设备地址:GT5688触控芯片有两个备选的I2C通讯地址,这是由芯片的上电时序设定的。上电时序有Reset引脚和INT引脚生成,若Reset引脚从低电电平转变到高电平期间,INT引脚为高电平的时候,触控芯片使用的I2C设备地址为0x28/0x29(8位写、读地址), 7位地址为0x14;若Reset引脚从低电电平转变到高电平期间,INT引脚一直为低电平,则触控芯片使用的I2C设备地址为0xBA/0xBB(8位写、读地址),7位地址为0x5D。

    选择设备地址
    在这里插入图片描述

    寄存器配置
    上电复位后,GT5688芯片需要通过外部主控芯片加载寄存器配置,设定它的工作模式,这些配置通过I2C信号线传输到GT5688, 它的配置寄存器地址都由两个字节来表示,这些寄存器的地址从0x8047-0x8135,一般来说,我们实际配置的时候会按照GT5688生产厂商给的默认配置来控制芯片, 仅修改部分关键寄存器。

    (此处使用旧型号芯片GT9157的寄存器表截图来进行说明,这两个型号大部分兼容)在这里插入图片描述
    寄存器介绍如下:
    (1) 配置版本寄存器
    0x8047配置版本寄存器,它包含有配置文件的版本号,若新写入的版本号比原版本大,或者版本号相等,但配置不一样时, 才会更新配置文件到寄存器中。其中配置文件是指记录了寄存器0x8048-0x80FE控制参数的一系列数据。
    为了保证每次都更新配置,我们一般把配置版本寄存器设置为“0x00”,这样版本号会默认初始化为‘A’, 这样每次我们修改其它寄存器配置的时候,都会写入到GT5688中。
    (2) X、Y分辨率
    0x8048-0x804B寄存器用于配置触控芯片输出的XY坐标的最大值,为了方便使用,我们把它配置得跟液晶面板的分辨率一致, 这样就能使触控芯片输出的坐标一一对应到液晶面板的每一个像素点了。
    (3) 触点个数
    0x804C触点个数寄存器用于配置它最多可输出多少个同时按下的触点坐标,这个极限值跟触摸屏面板有关, 如我们本章实验使用的触摸面板最多支持5点触控。
    (4) 模式切换
    0x804D模式切换寄存器中的X2Y位可以用于交换XY坐标轴;而INT触发方式位可以配置不同的触发方式, 当有触摸信号时,INT引脚会根据这里的配置给出触发信号。
    (5) 配置校验
    0x80FF配置校验寄存器用于写入前面0x8047-0x80FE寄存器控制参数字节之和的补码,GT5688收到前面的寄存器配置时, 会利用这个数据进行校验,若不匹配,就不会更新寄存器配置。
    (6) 配置更新
    0x8100配置更新寄存器用于控制GT5688进行更新,传输了前面的寄存器配置并校验通过后, 对这个寄存器写1,GT5688会更新配置。

    读取坐标
    坐标寄存器
    上述寄存器主要是由外部主控芯片给GT5688写入配置的,而它则使用图 坐标信息寄存器 中的寄存器向主控器反馈信息。
    在这里插入图片描述
    (1) 产品ID及版本
    0x8140-0x8143 寄存器存储的是产品ID,上电后我们可以利用I2C读取这些寄存器的值来判断I2C是否正常通讯, 这些寄存器中包含有“5688”字样; 而0x8144-0x8145则保存有固件版本号,不同版本可能不同。
    (2) X/Y分辨率
    0x8146-0x8149寄存器存储了控制触摸屏的分辨率,它们的值与我们前面在配置寄存器写入的XY控制参数一致。 所以我们可以通过读取这两个寄存器的值来确认配置参数是否正确写入。
    (3) 状态寄存器
    0x814E地址的是状态寄存器,它的Buffer status位存储了坐标状态,当它为1时,表示新的坐标数据已准备好,可以读取, 0表示未就绪,数据无效,外部控制器读取完坐标后,须对这个寄存器位写0 。number of touch points位表示当前有多少个触点。
    (4) 坐标数据
    从地址0x814F-0x8156的是触摸点1的坐标数据,从0x8157-0x815E的是触摸点2的坐标数据,依次还有存储3-10触摸点坐标数据的寄存器。 读取这些坐标信息时,我们通过它们的track id来区分笔迹,多次读取坐标数据时,同一个track id号里的数据属于同一个连续的笔划轨迹。
    读坐标流程
    上电、配置完寄存器后,GT5688就会开监测触摸屏,若我们前面的配置使INT采用中断上升沿报告触摸信号的方式,整个读取坐标信息的过程如下:
    (1) 待机时INT引脚输出低电平;
    (2) 有坐标更新时,INT引脚输出上升沿;
    (3) INT输出上升沿后,INT 脚会保持高直到下一个周期(该周期可由配置 Refresh_Rate 决定)。 外部主控器在检测到INT的信号后,先读取状态寄存器(0x814E)中的number of touch points位获当前有多少个触摸点, 然后读取各个点的坐标数据,读取完后将 bufferstatus位写为 0。外部主控器的这些读取过程要在一个周期内完成,该周期由0x8056地址的Refresh_Rate寄存器配置;
    (4) 上一步骤中INT输出上升沿后,若主控未在一个周期内读走坐标, 下次 GT5688即使检测到坐标更新会再输出一个 INT 脉冲但不更新坐标;
    (5) 若外部主控一直未读走坐标, 则 GT5688会一直输出 INT 脉冲。

    在这里插入图片描述
    其中的PD3和PD7没有硬件I2C功能,所以需要软件模拟的方式模拟I2C
    代码相关
    1.I2C配置
    I2C的GPIO配置时(下为I2C__GPIO_Config()部分函数体)

        /*配置SCL引脚 */   
        GPIO_InitStructure.GPIO_Pin = GTP_I2C_SCL_PIN;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
        GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
        GPIO_Init(GTP_I2C_SCL_GPIO_PORT, &GPIO_InitStructure);
        /*配置SDA引脚 */
        GPIO_InitStructure.GPIO_Pin = GTP_I2C_SDA_PIN;
        GPIO_Init(GTP_I2C_SDA_GPIO_PORT, &GPIO_InitStructure); 
      /*配置RST引脚,下拉推挽输出 */   
      GPIO_InitStructure.GPIO_Pin = GTP_RST_GPIO_PIN;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
      GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_DOWN;
      GPIO_Init(GTP_RST_GPIO_PORT, &GPIO_InitStructure);  
      /*配置 INT引脚,下拉推挽输出,方便初始化 */   
      GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    

    SDA和SCL引脚都应该设置为浮空开漏输出,而RST和INT应该设置为下拉推挽输出。INT引脚需要接收选择时序的,因此需要设置为推挽输出模式。(下拉即为默认值为低电平,方便初始化)

    配置完GPIO,选择好时序后,再次配置I2C,将INT设置为下拉推挽输出,方便初始化(下为I2C_ResetChip()部分函数体)

    GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN;
    	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    	  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    	  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_DOWN;  
    	  GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure);
    

    初始化完后紧接着就把INT引脚设置为浮空输入模式,方便接收中断信号

    /*把INT引脚设置为浮空输入模式,以便接收触摸中断信号*/
    	  GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN;
    	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    	  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
    	  GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure);
    

    对于整个I2C_ResetChip()函数:

    void I2C_ResetChip(void)
    {
    	  GPIO_InitTypeDef GPIO_InitStructure;
    
      /*配置 INT引脚,下拉推挽输出,方便初始化 */   
    	  GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN;
    	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    	  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    	  	  //设置为下拉,方便初始化
    	  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_DOWN;       
    
    	  //INT的GPIO初始化配置
    	  GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure);
    
    	  /*初始化GT5688,rst为高电平,int为低电平,
    	  则gt5688的设备地址被配置为0xBA*/
    
    	  /*设置复位引脚 复位为低电平,为初始化做准备*/
    	  GPIO_ResetBits (GTP_RST_GPIO_PORT,GTP_RST_GPIO_PIN);
    	  Delay(0x0FFFFF);
    
    	  /*拉高一段时间,进行初始化*/
    	  GPIO_SetBits (GTP_RST_GPIO_PORT,GTP_RST_GPIO_PIN);
    	  Delay(0x0FFFFF);
    //上面两个操作实现了选择设置地址为0xBA/0xBB时序
    
    	  /*把INT引脚设置为浮空输入模式,以便接收触摸中断信号*/
    	  GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN;
    	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    	  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
    	  GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure);
    }
    
    //复位引脚中断引脚设置部分define如下
    /*复位引脚*/
    #define GTP_RST_GPIO_PORT                GPIOD
    #define GTP_RST_GPIO_CLK                 RCC_AHB1Periph_GPIOD
    #define GTP_RST_GPIO_PIN                 GPIO_Pin_6
    /*中断引脚*/
    #define GTP_INT_GPIO_PORT                GPIOG
    #define GTP_INT_GPIO_CLK                 RCC_AHB1Periph_GPIOG
    #define GTP_INT_GPIO_PIN                 GPIO_Pin_8
    #define GTP_INT_EXTI_PORTSOURCE          EXTI_PortSourceGPIOG
    #define GTP_INT_EXTI_PINSOURCE           EXTI_PinSource8
    #define GTP_INT_EXTI_LINE                EXTI_Line8
    #define GTP_INT_EXTI_IRQ                 EXTI9_5_IRQn
    

    I2C中断引脚配置

    void I2C_GTP_IRQEnable(void)
    {
      EXTI_InitTypeDef EXTI_InitStructure;  
      NVIC_InitTypeDef NVIC_InitStructure;
      GPIO_InitTypeDef GPIO_InitStructure;  
      /*配置 INT 为浮空输入 */   
      GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
      GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure);  
      
      /* 连接 EXTI 中断源 到INT 引脚 */
      SYSCFG_EXTILineConfig(GTP_INT_EXTI_PORTSOURCE, GTP_INT_EXTI_PINSOURCE);
    
      /* 选择 EXTI 中断源 */
      EXTI_InitStructure.EXTI_Line = GTP_INT_EXTI_LINE;
      EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
      EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;  
      EXTI_InitStructure.EXTI_LineCmd = ENABLE;
      EXTI_Init(&EXTI_InitStructure);  
      
      /* 配置中断优先级 */
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
      
    	/*使能中断*/
      NVIC_InitStructure.NVIC_IRQChannel = GTP_INT_EXTI_IRQ;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      NVIC_Init(&NVIC_InitStructure);
    //中断define见上文
    }
    

    上三个函数运行

    void I2C_Touch_Init(void)
    {
      I2C_GPIO_Config(); 
     
      I2C_ResetChip();
    
      I2C_GTP_IRQEnable();
    }
    

    写软件I2C时,需要自己写函数。下面介绍几个相关函数。

    void i2c_Start(void)
    {
    	/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
    	I2C_SDA_1();//设置SDA引脚为高电平
    	I2C_SCL_1();//设置SCL引脚为高电平
    	i2c_Delay();
    	I2C_SDA_0();//设置SDA引脚为低电平
    	i2c_Delay();
    	I2C_SCL_0();//设置SCL引脚为低电平
    	i2c_Delay();
    }
    //相关宏
    //软件IIC使用的宏
    #define I2C_SCL_1()  GPIO_SetBits(GTP_I2C_SCL_GPIO_PORT, GTP_I2C_SCL_PIN)		/* SCL = 1 */
    #define I2C_SCL_0()  GPIO_ResetBits(GTP_I2C_SCL_GPIO_PORT, GTP_I2C_SCL_PIN)		/* SCL = 0 */
    
    #define I2C_SDA_1()  GPIO_SetBits(GTP_I2C_SDA_GPIO_PORT, GTP_I2C_SDA_PIN)		/* SDA = 1 */
    #define I2C_SDA_0()  GPIO_ResetBits(GTP_I2C_SDA_GPIO_PORT, GTP_I2C_SDA_PIN)		/* SDA = 0 */
    
    #define I2C_SDA_READ()  GPIO_ReadInputDataBit(GTP_I2C_SDA_GPIO_PORT, GTP_I2C_SDA_PIN)	/* 读SDA口线状态 */
    //i2c_Delay()函数:
    static void i2c_Delay(void)
    {
    	uint8_t i;
    
    	/* 
    	 	下面的时间是通过逻辑分析仪测试得到的。
        工作条件:CPU主频180MHz ,MDK编译环境,1级优化
          
    		循环次数为50时,SCL频率 = 333KHz 
    		循环次数为30时,SCL频率 = 533KHz,  
    	 	循环次数为20时,SCL频率 = 727KHz, 
      */
    	for (i = 0; i < 10*2; i++);
    }
    

    I2C硬件电路时序图在这里插入图片描述

    根据时序图,要先把SDA和SCL线都拉成高电平。延时后SDA由高变低,SCL没变化,则Start信号成功产生,为进行后续操作,SCL线也拉成低电平。SCL为高电平时SDA由高变低产生的这个时序信号就是I2C启动信号。
    类似的停止信号:

    void i2c_Stop(void)
    {
    	/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
    	I2C_SDA_0();
    	I2C_SCL_1();
    	i2c_Delay();
    	I2C_SDA_1();
    }
    

    软件I2C实际就是CPU控制引脚电平发送I2C信号;硬件I2C就是通过控制STM32的寄存器产生I2C信号。
    其他常用软件I2C函数如下
    (1)
    i2c_SendByte(uint8_t _ucByte) CPU向I2C总线设备发送8bit数据
    形参:_ucByte : 等待发送的字节

    void i2c_SendByte(uint8_t _ucByte)
    {
    	uint8_t i;
    
    	/* 先发送字节的高位bit7 */
    	for (i = 0; i < 8; i++)
    	{		
    		if (_ucByte & 0x80)
    		{
    			I2C_SDA_1();
    		}
    		else
    		{
    			I2C_SDA_0();
    		}
    		i2c_Delay();
    		I2C_SCL_1();
    		i2c_Delay();	
    		I2C_SCL_0();
    		if (i == 7)
    		{
    			 I2C_SDA_1(); // 释放总线
    		}
    		_ucByte <<= 1;	/* 左移一个bit */
    		i2c_Delay();
    	}
    }
    

    形参为要发送的数据,8个数据位,如果这个数据位是1,SDA线产生高电平,是0产生低电平。
    (2)
    函数名: i2c_ReadByte(); CPU从I2C总线设备读取8bit数据
    返回值: 读到的数据

    uint8_t i2c_ReadByte(void)
    {
    	uint8_t i;
    	uint8_t value;
    
    	/* 读到第1个bit为数据的bit7 */
    	value = 0;
    	for (i = 0; i < 8; i++)
    	{
    		value <<= 1;
    		I2C_SCL_1();
    		i2c_Delay();
    		if (I2C_SDA_READ())
    		{
    			value++;
    		}
    		I2C_SCL_0();
    		i2c_Delay();
    	}
    	return value;
    }
    //通过SCL由高变低,对SDA引脚进行采样,采样其每个数据位,采样八个数据位则读到一个字节
    

    (3)
    i2c_WaitAck() ;功能: CPU产生一个时钟,并读取器件的ACK应答信号
    返回值: 返回0表示正确应答,1表示无器件响应

    uint8_t i2c_WaitAck(void)
    {
    	uint8_t re;
    
    	I2C_SDA_1();	/* CPU释放SDA总线 */
    	i2c_Delay();
    	I2C_SCL_1();	/* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
    	i2c_Delay();
    	if (I2C_SDA_READ())	/* CPU读取SDA口线状态 */
    	{
    		re = 1;
    	}
    	else
    	{
    		re = 0;
    	}
    	I2C_SCL_0();
    	i2c_Delay();
    	return re;
    }
    

    (4)
    i2c_Ack();功能 CPU产生一个ACK信号

    void i2c_Ack(void)
    {
    	I2C_SDA_0();	/* CPU驱动SDA = 0 */
    	i2c_Delay();
    	I2C_SCL_1();	/* CPU产生1个时钟 */
    	i2c_Delay();
    	I2C_SCL_0();
    	i2c_Delay();
    	I2C_SDA_1();	/* CPU释放SDA总线 */
    }
    

    (5)
    i2c_NAck();功能说明: CPU产生1个NACK信号

    void i2c_NAck(void)
    {
    	I2C_SDA_1();	/* CPU驱动SDA = 1 */
    	i2c_Delay();
    	I2C_SCL_1();	/* CPU产生1个时钟 */
    	i2c_Delay();
    	I2C_SCL_0();
    	i2c_Delay();	
    }
    

    (6)ReadBytes和WriteBytes

    #define I2C_DIR_WR	0		/* 写控制bit */
    #define I2C_DIR_RD	1		/* 读控制bit */
    
    /**
      * ClientAddr:从设备地址
      *	pBuffer:存放由从机读取的数据的缓冲区指针
      *	NumByteToRead:读取的数据长度
      */
    uint32_t I2C_ReadBytes(uint8_t ClientAddr,uint8_t* pBuffer, 
    uint16_t NumByteToRead)
    {
    	
    	/* 第1步:发起I2C总线启动信号 */
    	i2c_Start();
    	
    	/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,
    	0表示写,1表示读 */
    	i2c_SendByte(ClientAddr | I2C_DIR_RD);	/* 此处是读指令 */
    	
    	/* 第3步:等待ACK */
    	if (i2c_WaitAck() != 0)
    	{
    		goto cmd_fail;	/* 器件无应答 */
    	}
    
    	while(NumByteToRead) 
      {
       if(NumByteToRead == 1)
        {
    			i2c_NAck();	/* 最后1个字节读完后,CPU产生NACK信号
    			(驱动SDA = 1) */
          
          /* 发送I2C总线停止信号 */
          i2c_Stop();
        }
        
       *pBuffer = i2c_ReadByte();
        
        /* 读指针自增 */
        pBuffer++; 
          
        /*计数器自减 */
        NumByteToRead--;
        
        i2c_Ack();	/* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */  
      }
    
    	/* 发送I2C总线停止信号 */
    	i2c_Stop();
    	return 0;	/* 执行成功 */
    
    cmd_fail: /* 命令执行失败后,切记发送停止信号,
                  避免影响I2C总线上其他设备 */
    	/* 发送I2C总线停止信号 */
    	i2c_Stop();
    	return 1;
    }
    
    /**
      * ClientAddr:从设备地址
      *	pBuffer:缓冲区指针
      * NumByteToWrite:写的字节数
      */
    uint32_t I2C_WriteBytes(uint8_t ClientAddr,uint8_t* pBuffer,
      uint8_t NumByteToWrite)
    {
    	uint16_t m;	
    
      /* 第0步:发停止信号,启动内部写操作 */
      i2c_Stop();
      
      /* 通过检查器件应答的方式,判断内部写操作是否完成, 一般小于 10ms 			
        CLK频率为200KHz时,查询次数为30次左右
      */
      for (m = 0; m < 1000; m++)
      {				
        /* 第1步:发起I2C总线启动信号 */
        i2c_Start();
        
        /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,
        0表示写,1表示读 */
        i2c_SendByte(ClientAddr | I2C_DIR_WR);	/* 此处是写指令 */
        
        /* 第3步:发送一个时钟,判断器件是否正确应答 */
        if (i2c_WaitAck() == 0)
        {
          break;
        }
      }
      if (m  == 1000)
      {
        goto cmd_fail;	/* EEPROM器件写超时 */
      }	
    	
      while(NumByteToWrite--)
      {
      /* 第4步:开始写入数据 */
      i2c_SendByte(*pBuffer);
    
      /* 第5步:检查ACK */
      if (i2c_WaitAck() != 0)
      {
        goto cmd_fail;	/* 器件无应答 */
      }
      
          pBuffer++;	/* 地址增1 */		
      }
    	
    	/* 命令执行成功,发送I2C总线停止信号 */
    	i2c_Stop();
    	return 0;
    
    cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
    	/* 发送I2C总线停止信号 */
    	i2c_Stop();
    	return 1;
    	}
    

    驱动GT5688芯片
    1.I2C_Transfer(struct i2c_msg *msgs,int num)函数

    static int I2C_Transfer( struct i2c_msg *msgs,int num)
    {
    	int im = 0;
    	int ret = 0;
    
    	GTP_DEBUG_FUNC();
    //下面的for实际就是执行num次
    	for (im = 0; ret == 0 && im != num; im++)
    	{
    		if ((msgs[im].flags&I2C_M_RD))																//根据flag判断是读数据还是写数据
    		{
    			ret = I2C_ReadBytes(msgs[im].addr, msgs[im].buf, msgs[im].len);		//IIC读取数据
    		} else
    		{
    			ret = I2C_WriteBytes(msgs[im].addr,  msgs[im].buf, msgs[im].len);	//IIC写入数据
    		}
    	}
    	if(ret)
    		return ret;
    	return im;   //正常完成的传输结构个数
    }
    
    //mag结构体定义
    struct i2c_msg {
    	uint8_t addr;		/*从设备的I2C设备地址 */
    	uint16_t flags;	/*控制标志*/
    	uint16_t len;		/*读写数据的长度			*/
    	uint8_t *buf;		/*存储读写数据的指针	*/
    };
    
    

    此函数就是STM32使用I2C传输数据必要的函数,应用关键在于读数据和写数据两个函数。此读取函数应用在其他方面时主要替换的是就是实现读数据写数据的这两个函数,让I2C读取数据和写入数据。
    GTP的输入输出函数:

    /**
      * @brief   从IIC设备中读取数据
      * @param
      *		@arg client_addr:设备地址
      *		@arg  buf[0~1]: 读取数据寄存器的起始地址
      *		@arg buf[2~len-1]: 存储读出来数据的缓冲buffer
      *		@arg len:    GTP_ADDR_LENGTH + read bytes count(寄存器地址长度+读取的数据字节数)
      * @retval  i2c_msgs传输结构体的个数,2为成功,其它为失败
      */
      #define GTP_ADDR_LENGTH       2
    static int32_t GTP_I2C_Read(uint8_t client_addr, uint8_t *buf, int32_t len)
    {
        struct i2c_msg msgs[2];
        int32_t ret=-1;
        int32_t retries = 0;
    
        GTP_DEBUG_FUNC();
        /*一个读数据的过程可以分为两个传输过程:
         * 1. IIC  写入 要读取的寄存器地址
         * 2. IIC  读取  数据
         * */
    
        msgs[0].flags = !I2C_M_RD;					//写入(非读即意思为写入)
        msgs[0].addr  = client_addr;					//IIC设备地址
        msgs[0].len   = GTP_ADDR_LENGTH;	//寄存器地址一般为2字节(即写入两字节的数据)
        msgs[0].buf   = &buf[0];						//buf[0~1]存储的是要读取的寄存器地址
        
        msgs[1].flags = I2C_M_RD;					//读取
        msgs[1].addr  = client_addr;					//IIC设备地址
        msgs[1].len   = len - GTP_ADDR_LENGTH;	//要读取的数据长度
        msgs[1].buf   = &buf[GTP_ADDR_LENGTH];	//buf[GTP_ADDR_LENGTH]之后的缓冲区存储读出的数据
    
        while(retries < 5)
        {
            ret = I2C_Transfer( msgs, 2);					//调用IIC数据传输过程函数,有2个传输过程
            if(ret == 2)break;
            retries++;
        }
        if((retries >= 5))
        {
            GTP_ERROR("I2C Read: 0x%04X, %d bytes failed, errcode: %d! Process reset.", (((uint16_t)(buf[0] << 8)) | buf[1]), len-2, ret);
        }
        return ret;
    }
    
    
    
    /**
      * @brief   向IIC设备写入数据
      * @param
      *		@arg client_addr:设备地址
      *	
      *		@arg  buf[0~1]: 要写入的数据寄存器的起始地址
      *		@arg buf[2~len-1]: 要写入的数据
      *		@arg len:    GTP_ADDR_LENGTH + write bytes count(寄存器地址长度+写入的数据字节数)
      * @retval  i2c_msgs传输结构体的个数,1为成功,其它为失败
      */
    static int32_t GTP_I2C_Write(uint8_t client_addr,uint8_t *buf,int32_t len)
    {
        struct i2c_msg msg;
        int32_t ret = -1;
        int32_t retries = 0;
    
        GTP_DEBUG_FUNC();
        /*一个写数据的过程只需要一个传输过程:
         * 1. IIC连续 写入 数据寄存器地址及数据
         * */
        msg.flags = !I2C_M_RD;			//写入
        msg.addr  = client_addr;			//从设备地址
        msg.len   = len;							//长度直接等于(寄存器地址长度+写入的数据字节数)
        msg.buf   = buf;						//直接连续写入缓冲区中的数据(包括了寄存器地址)
    //尝试发送五次
        while(retries < 5)
        {
            ret = I2C_Transfer(&msg, 1);	//调用IIC数据传输过程函数,1个传输过程
            if (ret == 1)break;
            retries++;
        }
        if((retries >= 5))
        {
    
            GTP_ERROR("I2C Write: 0x%04X, %d bytes failed, errcode: %d! Process reset.", (((uint16_t)(buf[0] << 8)) | buf[1]), len-2, ret);
    
        }
        return ret;
    }
    
    

    buf[0-1]: 读取数据寄存器的起始地址,buf[2~len-1]: 存储读出来数据的缓冲buffer。例如,往0x8143中写入0xFE,三个参数为——0xBA,
    (buf 0 0x81;buf 1 0x43;buf 2 0xFE),len 3;若要向0x8144写入0xFF向0x8143写入0xFE,则写如下:0xBA,(buf 0 0x81;buf 1 0x43;buf 2 0xFE,buf 3 0xFF),len 4.则读到了两个字节。
    GTP_I2C_Read(uint8_t client_addr, uint8_t *buf, int32_t len)函数的

     msgs[0].flags = !I2C_M_RD;					//写入(非读即意思为写入)
        msgs[0].addr  = client_addr;					//IIC设备地址
        msgs[0].len   = GTP_ADDR_LENGTH;	//寄存器地址一般为2字节(即写入两字节的数据)
        msgs[0].buf   = &buf[0];						//buf[0~1]存储的是要读取的寄存器地址
        
        msgs[1].flags = I2C_M_RD;					//读取
        msgs[1].addr  = client_addr;					//IIC设备地址
        msgs[1].len   = len - GTP_ADDR_LENGTH;	//要读取的数据长度
        msgs[1].buf   = &buf[GTP_ADDR_LENGTH];	//buf[GTP_ADDR_LENGTH]之后的缓冲区存储读出的数据
    

    部分,msgs[0]储存的是设备地址,msgs[1]储存的是后面的数据,而设备地址占两个字节,因此后面的数据占len - GTP_ADDR_LENGTH,读的数据也是地址后的 msgs[1].buf = &buf[GTP_ADDR_LENGTH];

    GTP_Init_Panel()函数分析

    int32_t GTP_Init_Panel(void)
    {
        int32_t ret = -1;
    
        int32_t i = 0;
        uint16_t check_sum = 0;
        int32_t retry = 0;
    
        const uint8_t* cfg_info;
        uint8_t cfg_info_len  ;
    		uint8_t* config;
    
        uint8_t cfg_num =0 ;		//需要配置的寄存器个数
    
        GTP_DEBUG_FUNC();
    	
    //uint8_t config[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH]
    //                = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff};
    
    		config = (uint8_t *)malloc (GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH);
    
    		config[0] = GTP_REG_CONFIG_DATA >> 8;
    		config[1] =  GTP_REG_CONFIG_DATA & 0xff;
    	
        I2C_Touch_Init();//I2C初始化
    
        ret = GTP_I2C_Test();//数据测试
        if (ret < 0)
        {
            GTP_ERROR("I2C communication ERROR!");
    				return ret;
        } 
    		
    		
        GTP_Read_Version(); //获取触摸IC的版本号和型号
    		
    #if UPDATE_CONFIG
    		//根据IC的型号指向不同的配置
        if(touchIC == GT5688)
    		{
    			cfg_info =  CTP_CFG_GT5688; //指向寄存器配置
    			
    //#define CFG_GROUP_LEN(p_cfg_grp)  (sizeof(p_cfg_grp) / sizeof(p_cfg_grp[0]))
    //计算数组元素的宏
    			cfg_info_len = CFG_GROUP_LEN(CTP_CFG_GT5688);//计算配置表的大小
    		}
    		/*
            #define GTP_ADDR_LENGTH       2
            #define GTP_CONFIG_MIN_LENGTH 186
            #define GTP_CONFIG_MAX_LENGTH 256
            */
        memset(&config[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH);
        //memset函数是c语言库函数,会把内存空间清零
        //在此处作用就是把指针指向的该数据地址的内存空间清零
        memcpy(&config[GTP_ADDR_LENGTH], cfg_info, cfg_info_len);
        //memcpy函数是c语言库函数,
        //在上文中的作用是复制cfg_info的内容到config[2]后面的内容里去
    
    		cfg_num = cfg_info_len;
    		
    		GTP_DEBUG("cfg_info_len = %d ",cfg_info_len);
    		GTP_DEBUG("cfg_num = %d ",cfg_num);
    		GTP_DEBUG_ARRAY(config,6);
    		
    /*
    //根据液晶扫描方向而变化的XY像素宽度
    //调用ILI9806G_GramScan函数设置方向时会自动更改
    uint16_t LCD_X_LENGTH = ILI9806G_MORE_PIXEL;
    uint16_t LCD_Y_LENGTH = ILI9806G_LESS_PIXEL;
    */
    		/*根据LCD的扫描方向设置分辨率*/
    		config[GTP_ADDR_LENGTH+1] = LCD_X_LENGTH & 0xFF;//取低字节
    		config[GTP_ADDR_LENGTH+2] = LCD_X_LENGTH >> 8;//取高字节
    		config[GTP_ADDR_LENGTH+3] = LCD_Y_LENGTH & 0xFF;//取低字节
    		config[GTP_ADDR_LENGTH+4] = LCD_Y_LENGTH >> 8;//取高字节
    		
    /*
    //液晶屏扫描模式,本变量主要用于方便选择触摸屏的计算参数
    //参数可选值为0-7
    //调用ILI9806G_GramScan函数设置方向时会自动更改
    //LCD刚初始化完成时会使用本默认值
    uint8_t LCD_SCAN_MODE =6;
    */
    		/*根据扫描模式设置X2Y交换*/
    		switch(LCD_SCAN_MODE)
    		{
    		//其实就是配置控制xy坐标交换的寄存器,让其坐标交换
    		//#define X2Y_LOC        				(1<<3)
    			case 0:case 2:case 4: case 6:
    				config[GTP_ADDR_LENGTH+6] &= ~(X2Y_LOC);
    				break;
    			
    			case 1:case 3:case 5: case 7:
    				config[GTP_ADDR_LENGTH+6] |= (X2Y_LOC);
    				break;		
    		}
    
        //计算要写入checksum寄存器的值
        check_sum = 0;
    		
    		for (i = GTP_ADDR_LENGTH; i < (cfg_num+GTP_ADDR_LENGTH -3); i += 2) 
    		{
    			check_sum += (config[i] << 8) + config[i + 1];
    		}
    		
    		check_sum = 0 - check_sum;
    		GTP_DEBUG("Config checksum: 0x%04X", check_sum);
    		//更新checksum
    		//下面是往倒数第一个、第二个分别赋值check_sum的高字节
    		//最后在倒数第一个写1,寄存器更新上述数据,至此config变量重新配置
    		config[(cfg_num+GTP_ADDR_LENGTH -3)] = (check_sum >> 8) & 0xFF;
    		config[(cfg_num+GTP_ADDR_LENGTH -2)] = check_sum & 0xFF;
    		config[(cfg_num+GTP_ADDR_LENGTH -1)] = 0x01;
        //写入配置信息
        for (retry = 0; retry < 5; retry++)
        {
            ret = GTP_I2C_Write(GTP_ADDRESS, config , cfg_num + GTP_ADDR_LENGTH+2);
            if (ret > 0)
            {
                break;
            }
        }
        Delay(0xfffff);				//延迟等待芯片更新		
    
    
        //下面代码平时不使用,调试模式才时使用
        //使用时if后的0改为1,使用调试模式
    #if 0	//读出写入的数据,检查是否正常写入
        //检验读出的数据与写入的是否相同
    	{
        uint16_t i;
        uint8_t buf[300];
         buf[0] = config[0];
         buf[1] =config[1];    //寄存器地址
         
        GTP_DEBUG_FUNC();
    
        ret = GTP_I2C_Read(GTP_ADDRESS, buf, sizeof(buf));
       
        GTP_DEBUG("read ");
    
        GTP_DEBUG_ARRAY(buf+2,cfg_num+2);
    
        GTP_DEBUG("write ");
    
        GTP_DEBUG_ARRAY(config,cfg_num);
    
        //不对比版本号
        for(i=1;i<cfg_num+GTP_ADDR_LENGTH-3;i++)
        {
    
          if(config[i] != buf[i])
          {
            GTP_ERROR("Config fail ! i = %d ",i);
            free(config);
            return -1;
          }
        }
        if(i==cfg_num+GTP_ADDR_LENGTH-3)
        GTP_DEBUG("Config success ! i = %d ",i);
    	}
    #endif
      free(config);//释放调用的空间
      #endif
      /*使能中断,这样才能检测触摸数据*/
      I2C_GTP_IRQEnable();//发生触摸时Init引脚产生上升沿或下降沿,引起中断
    
      GTP_Get_Info();//获取配置后的信息:分辨率版本号等,具体函数体见下文
    
      return 0;
    }
    

    debug 的define

    // Log define
    #define GTP_INFO(fmt,arg...)           printf("<<-GTP-INFO->> "fmt"\n",##arg)
    #define GTP_ERROR(fmt,arg...)          printf("<<-GTP-ERROR->> "fmt"\n",##arg)
    #define GTP_DEBUG(fmt,arg...)          do{\
                                             if(GTP_DEBUG_ON)\
                                             printf("<<-GTP-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
    																					}while(0)
    
    #define GTP_DEBUG_ARRAY(array, num)    do{\
                                             int32_t i;\
                                             uint8_t* a = array;\
                                             if(GTP_DEBUG_ARRAY_ON)\
                                             {\
                                                printf("<<-GTP-DEBUG-ARRAY->>\n");\
                                                for (i = 0; i < (num); i++)\
                                                {\
                                                    printf("0x%02X,", (a)[i]);\
                                                    if ((i + 1 ) %10 == 0)\
                                                    {\
                                                        printf("\n");\
                                                    }\
                                                }\
                                                printf("\n");\
                                            }\
                                           }while(0)
    
    #define GTP_DEBUG_FUNC()               do{\
                                             if(GTP_DEBUG_FUNC_ON)\
                                             printf("<<-GTP-FUNC->> Func:%s@Line:%d\n",__func__,__LINE__);\
                                           }while(0)
    
    																			 
    																			 
    #define GTP_SWAP(x, y)                 do{\
                                             typeof(x) z = x;\
                                             x = y;\
                                             y = z;\
                                           }while (0)
    
    //
    

    check_sum计算的校验码位置
    在这里插入图片描述

    上面函数中的GTP_Read_Version()函数:
    此函数就是初始化时读取设备的ID号版本号打印出来。

    int32_t GTP_Read_Version(void)
    {
        int32_t ret = -1;
        uint8_t buf[8] = {GTP_REG_VERSION >> 8, GTP_REG_VERSION & 0xff};    
        //寄存器地址,定义第一字节第二字节为设备寄存器地址,读取设备id号
    
        GTP_DEBUG_FUNC();
    
        ret = GTP_I2C_Read(GTP_ADDRESS, buf, sizeof(buf));
        if (ret < 0)
        {
            GTP_ERROR("GTP read version failed");
            return ret;
        }
        if (buf[2] == '5')//从两个字节的设备地址后的字节开始读取
        {
            GTP_INFO("IC1 Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);//打印IC的版本号lowbyte和highbyte
    				
    				//GT5688芯片
    				if(buf[2] == '5' && buf[3] == '6' && buf[4] == '8'&& buf[5] == '8')
    					touchIC = GT5688;
        }        
        else if (buf[5] == 0x00)
        {
            GTP_INFO("IC2 Version: %c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[7], buf[6]);
    				
    				//GT911芯片
    				if(buf[2] == '9' && buf[3] == '1' && buf[4] == '1')
    					touchIC = GT911;
        }
        else
        {
            GTP_INFO("IC3 Version: %c%c%c%c_%02x%02x", buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);
    				
    				//GT9157芯片
    				if(buf[2] == '9' && buf[3] == '1' && buf[4] == '5' && buf[5] == '7')
    					touchIC = GT9157;
    		}
        return ret;
    }
    
    

    GTP_Get_Info()函数

    static int32_t GTP_Get_Info(void)
    {
        uint8_t opr_buf[10] = {0};
        int32_t ret = 0;
    
        uint16_t abs_x_max = GTP_MAX_WIDTH;
        uint16_t abs_y_max = GTP_MAX_HEIGHT;
        uint8_t int_trigger_type = GTP_INT_TRIGGER;
            
        opr_buf[0] = (uint8_t)((GTP_REG_CONFIG_DATA+1) >> 8);
        opr_buf[1] = (uint8_t)((GTP_REG_CONFIG_DATA+1) & 0xFF);
        
        ret = GTP_I2C_Read(GTP_ADDRESS, opr_buf, 10);
        if (ret < 0)
        {
            return FAIL;
        }
        
        abs_x_max = (opr_buf[3] << 8) + opr_buf[2];
        abs_y_max = (opr_buf[5] << 8) + opr_buf[4];
    		GTP_DEBUG("RES");   
    		GTP_DEBUG_ARRAY(&opr_buf[0],10);
    
        opr_buf[0] = (uint8_t)((GTP_REG_CONFIG_DATA+6) >> 8);
        opr_buf[1] = (uint8_t)((GTP_REG_CONFIG_DATA+6) & 0xFF);
        ret = GTP_I2C_Read(GTP_ADDRESS, opr_buf, 3);
        if (ret < 0)
        {
            return FAIL;
        }
        int_trigger_type = opr_buf[2] & 0x03;
        
        GTP_INFO("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x",
                abs_x_max,abs_y_max,int_trigger_type);
        
        return SUCCESS;    
    }
    

    上文产生中断时,执行中断服务函数

    void GTP_IRQHandler(void)
    {
    	if(EXTI_GetITStatus(GTP_INT_EXTI_LINE) != RESET) //确保是否产生了EXTI Line中断
    	{
    		LED2_TOGGLE;
        GTP_TouchProcess();    
    		EXTI_ClearITPendingBit(GTP_INT_EXTI_LINE);     //清除中断标志位
    	}  
    }
    

    其中执行 GTP_TouchProcess();

    void GTP_TouchProcess(void)
    {
      GTP_DEBUG_FUNC();//测试函数,不管
      Goodix_TS_Work_Func();//坐标获取函数,获取各个触点的坐标,寄存器如下
      
    }
    

    不完全图:不完全
    Goodix_TS_Work_Func();函数体如下

    #define GTP_READ_COOR_ADDR    0x814E//就是上文所说的寄存器地址
    static void Goodix_TS_Work_Func(void)
    {
        uint8_t  end_cmd[3] = {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF, 0};
        uint8_t  point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1]={GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF};
        uint8_t  touch_num = 0;
        uint8_t  finger = 0;
        static uint16_t pre_touch = 0;
        static uint8_t pre_id[GTP_MAX_TOUCH] = {0};
    
        uint8_t client_addr=GTP_ADDRESS;
        uint8_t* coor_data = NULL;
        int32_t input_x = 0;
        int32_t input_y = 0;
        int32_t input_w = 0;
        uint8_t id = 0;
     
        int32_t i  = 0;
        int32_t ret = -1;
    
        GTP_DEBUG_FUNC();
    
        ret = GTP_I2C_Read(client_addr, point_data, 12);//10字节寄存器加2字节地址
        if (ret < 0)
        {
            GTP_ERROR("I2C transfer error. errno:%d\n ", ret);
    
            return;
        }
        
        finger = point_data[GTP_ADDR_LENGTH];//状态寄存器数据
    
        if (finger == 0x00)		//没有数据,退出
        {
            return;
        }
    
        if((finger & 0x80) == 0)//判断buffer status位
        {
            goto exit_work_func;//坐标未就绪,数据无效
        }
    
        touch_num = finger & 0x0f;//坐标点数
        if (touch_num > GTP_MAX_TOUCH)
        {
            goto exit_work_func;//大于最大支持点数,错误退出
        }
    
        if (touch_num > 1)//不止一个点
        {
            uint8_t buf[8 * GTP_MAX_TOUCH] = {(GTP_READ_COOR_ADDR + 10) >> 8, (GTP_READ_COOR_ADDR + 10) & 0xff};
    
            ret = GTP_I2C_Read(client_addr, buf, 2 + 8 * (touch_num - 1));
            memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));			//复制其余点数的数据到point_data
        }
    
        
        
        if (pre_touch>touch_num)				//pre_touch>touch_num,表示有的点释放了
        {
            for (i = 0; i < pre_touch; i++)						//一个点一个点处理
             {
                uint8_t j;
               for(j=0; j<touch_num; j++)
               {
                   coor_data = &point_data[j * 8 + 3];
                   id = coor_data[0] & 0x0F;									//track id
                  if(pre_id[i] == id)
                    break;
    
                  if(j >= touch_num-1)											//遍历当前所有id都找不到pre_id[i],表示已释放
                  {
                     GTP_Touch_Up( pre_id[i]);
                  }
               }
           }
        }
    
    
        if (touch_num)
        {
            for (i = 0; i < touch_num; i++)						//一个点一个点处理
            {
                coor_data = &point_data[i * 8 + 3];
    
                id = coor_data[0] & 0x0F;									//track id
                pre_id[i] = id;
    
                input_x  = coor_data[1] | (coor_data[2] << 8);	//x坐标
                input_y  = coor_data[3] | (coor_data[4] << 8);	//y坐标
                input_w  = coor_data[5] | (coor_data[6] << 8);	//size
            
                {
    								/*根据扫描模式更正X/Y起始方向*/
    								switch(LCD_SCAN_MODE)
    								{
    									case 0:case 7:
    										input_y  = LCD_Y_LENGTH - input_y;
    										break;
    									
    									case 2:case 3: 
    										input_x  = LCD_X_LENGTH - input_x;
    										input_y  = LCD_Y_LENGTH - input_y;
    										break;
    									
    									case 1:case 6:
    										input_x  = LCD_X_LENGTH - input_x;
    										break;	
    									
    									default:
    									break;
    								}
    								
                    GTP_Touch_Down( id, input_x, input_y, input_w);//数据处理
                }
            }
        }
        else if (pre_touch)		//touch_ num=0 且pre_touch!=0
        {
          for(i=0;i<pre_touch;i++)
          {
              GTP_Touch_Up(pre_id[i]);
          }
        }
    
    
        pre_touch = touch_num;
    
    
    exit_work_func:
        {
            ret = GTP_I2C_Write(client_addr, end_cmd, 3);
            if (ret < 0)
            {
                GTP_INFO("I2C write end_cmd error!");
            }
        }
    
    }
    static void GTP_Touch_Up( int32_t id)
    {
    	
    
        /*处理触摸释放,用于触摸画板*/
        Touch_Button_Up(pre_x[id],pre_y[id]);
    
    		/*****************************************/
    		/*在此处添加自己的触摸点释放时的处理过程即可*/
    		/* pre_x[id],pre_y[id] 即为最新的释放点 ****/
    		/*******************************************/	
    		/***id为轨迹编号(多点触控时有多轨迹)********/
    	
    	
        /*触笔释放,把pre xy 重置为负*/
    	  pre_x[id] = -1;
    	  pre_y[id] = -1;		
      
        GTP_DEBUG("Touch id[%2d] release!", id);
    
    }
    
    static void GTP_Touch_Down(int32_t id,int32_t x,int32_t y,int32_t w)
    {
      
    	GTP_DEBUG_FUNC();
    
    	/*取x、y初始值大于屏幕像素值*/
        GTP_DEBUG("ID:%d, X:%d, Y:%d, W:%d", id, x, y, w);
    
    	
        /* 处理触摸按钮,用于触摸画板 */
        Touch_Button_Down(x,y); 
    	
    
        /*处理描绘轨迹,用于触摸画板 */
        Draw_Trail(pre_x[id],pre_y[id],x,y,&brush);
    	
    		/************************************/
    		/*在此处添加自己的触摸点按下时处理过程即可*/
    		/* (x,y) 即为最新的触摸点 *************/
    		/************************************/
    	
    		/*prex,prey数组存储上一次触摸的位置,id为轨迹编号(多点触控时有多轨迹)*/
        pre_x[id] = x; pre_y[id] =y;
    	
    }
    

    应用层
    Palette_Init(uint8_t LCD_Mode)函数

    void Palette_Init(uint8_t LCD_Mode)
    {
      
      uint8_t i;
    	
    	ILI9806G_GramScan ( LCD_Mode );
    
      /* 整屏清为白色 */
    	LCD_SetBackColor(CL_WHITE);//设置背景为白色	
      ILI9806G_Clear(0,0,LCD_X_LENGTH,LCD_Y_LENGTH);//清屏	
    
       
      /* 初始化按钮 */
      Touch_Button_Init();
      
      /* 描绘按钮 */
      for(i=0;i<BUTTON_NUM;i++)
      {
        button[i].draw_btn(&button[i]);
      }
      
    	
      /* 初始化画笔 */
      brush.color = CL_BLACK;
      brush.shape = LINE_SINGLE_PIXCEL;
    	
    	LCD_SetTextColor(brush.color);
    
    
    }
    void LCD_SetBackColor(uint16_t Color)
    {
      CurrentBackColor = Color;
    }
    
    void ILI9806G_Clear ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight )
    {
    	ILI9806G_OpenWindow ( usX, usY, usWidth, usHeight );
    
    	ILI9806G_FillColor ( usWidth * usHeight, CurrentBackColor );		
    	
    }
    /*画刷形状列表*/
    typedef enum 
    {
      LINE_SINGLE_PIXCEL = 0,   //单像素线
      
      LINE_2_PIXCEL,  //2像素线
      
      LINE_4_PIXCEL,  //4像素线
      
      LINE_6_PIXCEL,  //6像素线
      
      LINE_8_PIXCEL,  //8像素线
      
      LINE_16_PIXCEL, //16像素线
      
      LINE_20_PIXCEL, //20像素线
      
      LINE_WITH_CIRCLE,  //珠子连线
        
      RUBBER,           //橡皮
    
    }SHAPE;
    
    
    #if 1     //按钮栏在左边
      #define BUTTON_START_X      0
      #define PALETTE_START_X   (COLOR_BLOCK_WIDTH*2+1)
      #define PALETTE_END_X     LCD_X_LENGTH
    
    #else     //按钮栏在右边,(存在触摸按键时也会的bug仅用于测试触摸屏左边界)
      #define BUTTON_START_X     ( LCD_X_LENGTH-2*COLOR_BLOCK_WIDTH)
      #define PALETTE_START_X   0
      #define PALETTE_END_X     (LCD_X_LENGTH-2*COLOR_BLOCK_WIDTH)
    
    void Touch_Button_Init(void)//只列出部分button,button是按钮结构体数组
    {
      /*第一列,主要为颜色按钮*/
      button[0].start_x = BUTTON_START_X;
      button[0].start_y = 0;
      button[0].end_x = BUTTON_START_X+COLOR_BLOCK_WIDTH ;
      button[0].end_y = COLOR_BLOCK_HEIGHT;
      button[0].para = CL_BLACK;
      button[0].touch_flag = 0;  
      button[0].draw_btn = Draw_Color_Button ;
      button[0].btn_command = Command_Select_Color ;
      
      button[1].start_x = BUTTON_START_X;
      button[1].start_y = COLOR_BLOCK_HEIGHT;
      button[1].end_x = BUTTON_START_X+COLOR_BLOCK_WIDTH ;
      button[1].end_y = COLOR_BLOCK_HEIGHT*2;
      button[1].para = CL_GREY;
      button[1].touch_flag = 0;  
      button[1].draw_btn = Draw_Color_Button ;
      button[1].btn_command = Command_Select_Color ;
    //button结构体
    typedef struct 
    {
      uint16_t start_x;   //按键的x起始坐标  
      uint16_t start_y;   //按键的y起始坐标
      uint16_t end_x;     //按键的x结束坐标 
      uint16_t end_y;     //按键的y结束坐标
      uint32_t para;      //颜色按钮中表示选择的颜色,笔迹形状按钮中表示选择的画刷
      uint8_t touch_flag; //按键按下的标志
        
      void (*draw_btn)(void * btn);     //按键描绘函数
      void (*btn_command)(void * btn);  //按键功能执行函数,例如切换颜色、画刷
     
    }Touch_Button;
    
    //绘制函数
    /**
    * @brief  Draw_Color_Button 颜色按钮的描绘函数
    * @param  btn Touch_Button 类型的按键参数
    * @retval 无
    */
    static void Draw_Color_Button(void *btn)
    {
      Touch_Button *ptr = (Touch_Button *)btn;
      
      /*释放按键*/
      if(ptr->touch_flag == 0)
      {
    		/*背景为功能键相应的颜色*/
    		LCD_SetColors(ptr->para,CL_WHITE);
        ILI9806G_DrawRectangle(ptr->start_x,
                          ptr->start_y,
                          ptr->end_x - ptr->start_x,
    											ptr->end_y - ptr->start_y,1);
      }
      else  /*按键按下*/
      {
        /*白色背景*/
    		LCD_SetColors(CL_WHITE,CL_WHITE);
        ILI9806G_DrawRectangle(ptr->start_x,
                          ptr->start_y,
                          ptr->end_x - ptr->start_x,
    											ptr->end_y - ptr->start_y,1);
      } 
        /*按钮边框*/
    		LCD_SetColors(CL_BLUE4,CL_WHITE);
        ILI9806G_DrawRectangle(ptr->start_x,
    									ptr->start_y,
    									ptr->end_x - ptr->start_x,
    									ptr->end_y - ptr->start_y,0);
      
    }
    /**
     * @brief  在 ILI9806G 显示器上画一个矩形
     * @param  usX_Start :在特定扫描方向下矩形的起始点X坐标
     * @param  usY_Start :在特定扫描方向下矩形的起始点Y坐标
     * @param  usWidth:矩形的宽度(单位:像素)
     * @param  usHeight:矩形的高度(单位:像素)
     * @param  ucFilled :选择是否填充该矩形
      *   该参数为以下值之一:
      *     @arg 0 :空心矩形
      *     @arg 1 :实心矩形 
     * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
     * @retval 无
     */
    void ILI9806G_DrawRectangle ( uint16_t usX_Start, uint16_t usY_Start, uint16_t usWidth, uint16_t usHeight, uint8_t ucFilled )
    {
    	if ( ucFilled )
    	{
    		ILI9806G_OpenWindow ( usX_Start, usY_Start, usWidth, usHeight );
    		ILI9806G_FillColor ( usWidth * usHeight ,CurrentTextColor);	
    	}
    	else
    	{
    		ILI9806G_DrawLine ( usX_Start, usY_Start, usX_Start + usWidth - 1, usY_Start );
    		ILI9806G_DrawLine ( usX_Start, usY_Start + usHeight - 1, usX_Start + usWidth - 1, usY_Start + usHeight - 1 );
    		ILI9806G_DrawLine ( usX_Start, usY_Start, usX_Start, usY_Start + usHeight - 1 );
    		ILI9806G_DrawLine ( usX_Start + usWidth - 1, usY_Start, usX_Start + usWidth - 1, usY_Start + usHeight - 1 );		
    	}
    
    }
    
    
    展开全文
  • 去耦电容

    2020-11-02 14:22:51
    旁路电容是把输入信号中的干扰作为滤除对象,而去耦电容是把输出信号的干扰作为滤除对象,防止干扰信号返回电源。这应该是他们的本质区别。去耦电容相当于电池,避免由于电流的突变而使电压下降,相当于滤纹波。具体...
  • 上拉、下拉以及对应上拉电阻和下拉电阻的作用原理 一、什么是上拉和下拉电路 上拉(Pull Up )或下拉(Pull Down)电阻两者统称为拉电阻 上拉就是单片机的IO口串联一个电阻到VDD; 下拉就是单片机的IO口串联一个...
  • 上拉、下拉电阻的原理和作用

    万次阅读 多人点赞 2019-04-21 00:48:55
    下拉电阻:将一个不确定的信号(高或低电平),通过一个电阻与地GND相连,固定在低电平。 上、下拉电阻的作用: 一般说法是上拉增大电流,下拉电阻是用来吸收电流。 1、当 TTL 电路驱动 CMOS 电路时,如果电路输出的...
  • gpio上拉下拉区别

    千次阅读 2019-08-13 20:57:07
    gpio上拉下拉区别  GPIO是一颗芯片(MCU)必须具备的最基本外设功能。 GPIO通常有三种状态:高电平、低电平和高阻态。高阻态换句话说就是断开状态或浮空态。因此上拉和下拉其中一个强大的理由就是为了防止输入端...
  • 锂离子电容Preparing your mobile app for an emulator can seem like an overwhelming task at first. However, once the specifics are laid out, it’s actually a fairly painless process and an exciting time...
  •  什么是耦合电容?什么是去耦电路?  耦合指信号由级向第二级传递的过程,一般不加注明时往往是指交流耦合。  退耦是指对电源采取进一步的滤波措施,去除两级间信号通过电源互相干扰的影响。耦合常数是指耦合...
  • 什么是电容?什么是去耦电路? 指信号由第一级向第二级传递的过程,一般不加注明时往往是指交流耦合。 是指对电源采取进一步的滤波措施,去除两级间信号通过电源互相干扰的影响。耦合常数是指耦合电容值与第二级输入...
  • 上拉电阻和下拉电阻作用、区别及应用

    万次阅读 多人点赞 2018-07-17 21:43:54
    上拉电阻和下拉电阻有什么用? 1、提高驱动能力: 例如,用单片机输出高电平,但由于后续电路的影响,输出的高电平不高,就是达不到VCC,影响电路工作。所以要接上拉电阻。下拉电阻情况相反,让单片机引脚输出低...
  • 基于战舰V3的4.3寸电容触摸屏 原理简介 4.3寸电容屏可以等效为800*480个点,当我们触碰到LCD屏幕时,触摸点的电容值会发生变化,此时内置MPU可以读取到这一点的准确坐标我们访问其中的寄存器就可以获取该信息...
  • I2C的总线电容

    千次阅读 2014-06-09 22:08:31
    由于规定了上升时间,这个电容限制了上拉电阻Rp 的最大值,而电源电压限制了上拉电阻Rp 的最小值,输出级在VOLmax=0.4V 时指定的最小下拉电流是3mA。 如果总线线路的电容负载升高,位速率将逐渐下降,总线的电容...
  • 烧录口的电容

    2019-11-26 17:19:16
    特别注意这里的C111与C112都是错的,应该选用电容值为1NF的电容,烧录口的电容起着滤波的作用,通交隔直,但是容值太大会导致烧录失败
  • 上拉下拉电阻

    千次阅读 2011-07-05 14:15:07
    在网上看到一些对电阻的上拉和下拉不太明白的,输入端的上拉及下拉非常简单但也非常重要。 上拉:通过一个电阻对电源相连。下拉:通过一个电阻到地。 上下拉一般有两个用处:提高输出信号的驱动能力、确定输入信号...
  • 三极管基极下拉电阻的作用

    千次阅读 2014-10-25 22:34:33
    三极管的基极不能出现悬空,当输入信号不确定时(如输入信号为高阻态时),加下拉电阻,就能使有效接地。 特别是GPIO连接此基极的时候,一般在GPIO所在IC刚刚上电初始化的时候,此GPIO的内部也处于一种上电状态,很...
  • JL223B单点电容触摸

    2019-04-02 11:19:24
    硬件接线:J17可不接(内部下拉),或接左边2个接线柱,或接右边2个接线柱 J17只是影响初始输出电平,对本案例实现现象无大影响。 注意事项:(1) J17不接或接右边2个接线柱,则初始输出高电平;J17接左边2个 接线...
  • 三极管基极下拉电阻作用 1)防止三极管受噪声信号的影响而产生误动作,使晶体管截止更可靠!三极管的基极不能出现悬空,当输入信号不确定时(如输入信号为高阻态时),加下拉电阻,就能使有效接地。 特别是GPIO连接...
  • 耦合与退耦什么是电容?什么是去耦电路?指信号由第一级向第二级传递的过程,一般不加注明时往往是指交流耦合。是指对电源采取进一步的滤波措施,去除两级间信号通过电源互相干扰的影响。耦合常数是指耦合电容值与第二...
  • 上拉(Pull Up )或下拉(Pull Down)电阻(两者统称为“拉电阻”)最基本的作用是:将状态不确定的信号线通过一个电阻将其箝位至高电平(上拉)或低电平(下拉),无论它的具体用法如何,这个基本的作用都是相同的...
  • 触摸感应区与四周的铜片区域就形成了一个电容,通过检测电容充放 电时间即可判断是否有触摸。实现原理: 容充放电公式:Vc=V0*(1-e^(-t/RC)) 我们使用TIM2的通道1(PA5)来检测触摸按键是否按下,每次检测前,我们...
  • js 下拉列表级联

    2010-06-30 11:10:15
    JS级联式下拉列表 二级联动: 请选择一级大类 家用电器 电子元器件 请选择二级小类
  • 上拉下拉电阻总结

    2016-05-10 16:49:33
    下拉同理! 上拉是对器件注入电流,下拉是输出电流;弱强只是上拉电阻的阻值不同,没有什么严格区分;对于非集电极(或漏极)开路输出型电路(如普通门电路)提升电流和电压的能力是有限的,上拉电阻的功能主要是...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,201
精华内容 1,280
关键字:

下拉电容