? ? ? ? 首先,我先介绍电容触摸按键的原理。
? ? ? ? 一个电容充满电的时候是需要一定时间,当两个电容并联的时候,此时,总电容大小就变成两个电容之和。导致充电的时间变成。电容触摸实验就是基于此,来根据时间的长短变换来确定是否有按键按下,并做出相应的反应。
我们从主函数开始看
????????int main(void) ?{?? ? ??? ?u8 t=0; ??? ? ?? ?delay_init();?? ? ? ??? ? //延时函数初始化?? ? ? ?? ?NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置NVIC中断分组2:2位抢占优先级,2位响应优先级 ?? ?uart_init(115200);?? ? //串口初始化为115200 ??? ?LED_Init();?? ??? ??? ? ? ? //LED端口初始化 ? TPAD_Init(6);?? ??? ??? ?//初始化触摸按键 ? ??? ?while(1) ?? ?{?? ??? ??? ??? ??? ? ??? ??? ??? ??? ??? ??? ? ??? ??? ?? ??? ??? ?if(TPAD_Scan(0))?? ?//成功捕获到了一次上升沿(此函数执行时间至少15ms) ?? ??? ?{ ?? ??? ??? ?LED1=!LED1;?? ??? ?//LED1取反 ?? ??? ?} ?? ??? ?t++; ?? ??? ?if(t==15)?? ??? ?? ?? ??? ?{ ?? ??? ??? ?t=0; ?? ??? ??? ?LED0=!LED0;?? ??? ?//LED0取反,提示程序正在运行 ?? ??? ?} ?? ??? ?delay_ms(10); ?? ?}
while里面的先不看。当程序初始化好,走到TPAD_Init(6);?? ??
#define TPAD_ARR_MAX_VAL ?? ?0XFFFF?? ?//最大的ARR值 vu16 tpad_default_val=0; u8 TPAD_Init(u8 psc) { ?? ?u16 buf[10]; ?? ?u16 temp; ?? ?u8 j,i; ?? ?TIM5_CH2_Cap_Init(TPAD_ARR_MAX_VAL,psc-1);//以12Mhz的频率计数? ?? ?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];//取中间的6个数据进行平均 ?? ?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;?? ??? ? ? ? ?? ? ? ??? ??? ??? ??? ??? ? ?? }
? ? ? ? 开始,程序创建一个数组,为了取平均值,获取没有按键按下时的准确时间;
?TIM5_CH2_Cap_Init(TPAD_ARR_MAX_VAL,psc-1);//以12Mhz的频率计数 (定时器初始化),这里就不说了
这里的定时器时钟频率是72Mhz,所以计数频率是72/(psc-1+1)=12,这是计数频率 ,不是定时器周期。
? ?buf[i]=TPAD_Get_Val();跳转定时器捕获上升沿时间
u16 TPAD_Get_Val(void) {?? ??? ??? ??? ? ?? ?? ?TPAD_Reset(); ?? ?while(TIM_GetFlagStatus(TIM5, TIM_IT_CC2) == RESET)//等待捕获上升沿 ?? ?{ ?? ??? ?if(TIM_GetCounter(TIM5)>TPAD_ARR_MAX_VAL-500)return TIM_GetCounter(TIM5);//超时了,直接返回CNT的值 ?? ?};?? ? ?? ?return TIM_GetCapture2(TIM5);?? ? ? } ?? ??
void TPAD_Reset(void) { ? ?? ?GPIO_InitTypeDef ?GPIO_InitStructure;? ?? ?RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);?? ? //使能PA端口时钟 ?? ? ?? ?//设置GPIOA.1为推挽使出 ??? ?GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;?? ??? ??? ??? ? //PA1 端口配置 ??? ?GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; ?? ??? ? //推挽输出 ??? ?GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; ??? ?GPIO_Init(GPIOA, &GPIO_InitStructure); ??? ?GPIO_ResetBits(GPIOA,GPIO_Pin_1);?? ??? ??? ??? ??? ??? ? //PA.1输出0,放电
?? ?delay_ms(5);
?? ?TIM_SetCounter(TIM5,0);?? ??? ?//归0 ?? ?TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志 ?? ?//设置GPIOA.1为浮空输入 ?? ?GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;?? ? //浮空输入 ?? ?GPIO_Init(GPIOA, &GPIO_InitStructure);
? ?? }
?? ?TPAD_Reset();
这个函数的目的是为了先将通过引脚输出低电平来达到电容的电放掉的目的,之后,在将引脚设为输入模式,清除中断标志位,这里不需要对中断进行初始化,因为我们用不到中断服务函数,我们只需要确定是否发生中断。
???????? ?while(TIM_GetFlagStatus(TIM5, TIM_IT_CC2) == RESET)//等待捕获上升沿 ?? ?{ ?? ??? ?if(TIM_GetCounter(TIM5)>TPAD_ARR_MAX_VAL-500)return TIM_GetCounter(TIM5);//超时了,直接返回CNT的值 ?? ?};?? ? ?? ?return TIM_GetCapture2(TIM5);?? ? ? } ?? ??
如果一直没有捕获上升沿,则
?if(TIM_GetCounter(TIM5)>TPAD_ARR_MAX_VAL-500)return TIM_GetCounter(TIM5);//超时了,直接返回CNT的值(防止非法状态,比如一直是低电平,导致死循环。)这里为什么减500,我也不知道,查资料说是平经验得到的,鬼知道是什么经验;
如果,捕获高电平则返回定时器5 的计数器值
TIM_GetCapture2(TIM5);?
TIM_GetCounter(TIM5);
这两句话其实差别不大,第一句话,适当定时器5 通道处于输入模式下,CCR存放的上次捕获事件 的计数器的值返回。第二句话,是直接返回计数器值。只不过第一个数字更准确点。
? ? ? ? 通过TPAD_Init函数的第一次循环,获取十次没有按下的时间,之后,进行大小排序,去掉最大和最小,求平均,最后输出;
?? ?if(tpad_default_val>TPAD_ARR_MAX_VAL/2)return 1;//初始化遇到超过TPAD_ARR_MAX_VAL/2的数值,不正常! ?? ?return 0;? ? ? ? 这里是为了有需要的准备的,因为主函数没有接受,所以也没用。
????????tpad_default_val>TPAD_ARR_MAX_VAL/2 至于这句话怎么得到的,也是凭经验,我也查不到相关资料,只知道凭经验。
? ? ? ? 这样,就彻底得到了没有按键按下的时候的时间T1
接下来,就是获取按键按下的时间
?? ?while(1) ?? ?{?? ??? ??? ??? ??? ? ??? ??? ??? ??? ??? ??? ? ??? ??? ?? ??? ??? ?if(TPAD_Scan(0))?? ?//成功捕获到了一次上升沿(此函数执行时间至少15ms) ?? ??? ?{ ?? ??? ??? ?LED1=!LED1;?? ??? ?//LED1取反 ?? ??? ?} ?? ??? ?t++; ?? ??? ?if(t==15)?? ??? ?? ?? ??? ?{ ?? ??? ??? ?t=0; ?? ??? ??? ?LED0=!LED0;?? ??? ?//LED0取反,提示程序正在运行 ?? ??? ?} ?? ??? ?delay_ms(10); ?? ?} ?}
TPAD_Scan(0)
#define TPAD_GATE_VAL ?? ?100?? ?//触摸的门限值,也就是必须大于tpad_default_val+TPAD_GATE_VAL,才认为是有效触摸.
这里tpad_default_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))//大于tpad_default_val+TPAD_GATE_VAL,有效 ?? ?{?? ??? ??? ??? ??? ??? ??? ?? ?? ??? ?if(keyen==0)res=1;?? ??? ?//keyen==0,有效? ?? ??? ?//printf("r:%d\r\n",rval);?? ??? ? ? ? ?? ? ? ??? ??? ??? ??? ??? ? ?? ?? ??? ?keyen=3;?? ??? ??? ??? ?//至少要再过3次之后才能按键有效 ?? ?? ?}? ?? ?if(keyen)keyen--;?? ??? ? ? ?? ??? ??? ??? ??? ??? ??? ? ? ?? ??? ? ? ? ?? ? ? ??? ??? ??? ??? ??? ? ?? ?? ?return res; }?? ?
这里的mode的作用就是选择是否支持连续按。这里只讲解mode=0,及不支持连按
TPAD_Get_MaxVal(sample);?
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; } ?
这里sample为3,及计算三次。TPAD_Get_Val();这个函数上面讲过,就是获取计数器的值,只不过这里指的是按键按下后的值;
? ?if(temp>res)res=temp;获取3次数值中最大的,返回;
? ?if(rval>(tpad_default_val+TPAD_GATE_VAL))//大于tpad_default_val+TPAD_GATE_VAL,有效 ?? ?{?? ??? ??? ??? ??? ??? ??? ?? ?? ??? ?if(keyen==0)res=1;?? ??? ?//keyen==0,有效? ?? ??? ?//printf("r:%d\r\n",rval);?? ??? ? ? ? ?? ? ? ??? ??? ??? ??? ??? ? ?? ?? ??? ?keyen=3;?? ??? ??? ??? ?//至少要再过3次之后才能按键有效 ?? ?? ?}? ?? ?if(keyen)keyen--;?? ??? ? ? ?? ??? ??? ??? ??? ??? ??? ? ? ?? ??? ? ? ? ?? ? ? ??? ??? ??? ??? ??? ? ?? ?? ?return res; }?? ?
如果条件成立,则进行返回1,同时也可以选择输出按键按下的时间
?if(keyen)keyen--;? 这句话,个人认为是进行延时
?? ?if(TPAD_Scan(0))?? ?//成功捕获到了一次上升沿(此函数执行时间至少15ms) ?? ??? ?{ ?? ??? ??? ?LED1=!LED1;?? ??? ?//LED1取反 ?? ??? ?} ?? ??? ?t++; ?? ??? ?if(t==15)?? ??? ?? ?? ??? ?{ ?? ??? ??? ?t=0; ?? ??? ??? ?LED0=!LED0;?? ??? ?//LED0取反,提示程序正在运行 ?? ??? ?} ?? ??? ?delay_ms(10); ?? ?} ?} 当if语句得到为1时,则可以令灯翻转,得到0,则跳动到下面语句
?t++; ?? ??? ?if(t==15)?? ??? ?? ?? ??? ?{ ?? ??? ??? ?t=0; ?? ??? ??? ?LED0=!LED0;?? ??? ?//LED0取反,提示程序正在运行 ?? ??? ?} ?? ??? ?delay_ms(10); ?? ?}这语句的目的就是告诉我们程序是正在运行的,发生错误的话是TPAD_Scan(0)错误
|