完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
2个回答
|
|
电容触摸按键实验
电容触摸按键的基本原理(原理图层面) 新电容的产生与作用 我们学过模电的同学知道:我们的手其实相当于一块可以存储感应电荷的金属板,当我们的手靠近屏幕时,我们的手与屏幕下的金属板Cx构成了一个平行电容板,这个电容与Cs杂散电容相并联,“并联电容C=C1+C2”。 充放电性能的变化 我们看见“VCC是一定的;电容C越大代表一定电压下,存储的电荷数量越多(Q=CU),反之,充电到U所花费的时间也更多;电阻R的值一定代表着对电荷的阻碍作用是固定的”。 充电到V=Vth,花费时间TA明显小于TB;我们真实情况下的矩形脉冲如下图所示: 脉冲如何被捕获 当电容从0充电到Vth时,就相当于一个上升沿脉冲。其实这个充放电过程很快,我们一般用的RC充放电电路是为了观察现象因此将时间常数做的很大,但是现实的话我们要快速识别必须将时间常数缩小到一定范围内才OK。 原理图层面的连接 STM_ADC其实只是这个引脚功能的其中一个,我们这里并不是用的引脚的ADC功能,以下为该引脚的复用: 我们这里使用的是PA1的TIM5_CH2功能,用于快速捕获TPAD的有效脉冲沿变化。 硬件配置的大致流程
有按键按下。 电容触摸按键的基本原理(软件配置层面)
TPAD_InitConfig()函数 void TPAD_InitConfig() // 读取默认电容充电时间 { u16 RecordChargeTime[10] = {0}; u8 i = 0, temp = 0; uart_init(115200); // 初始化USART1 TIM_CAP_InitConfig(0xFFFF, 72-1); // 初始化TIM5_CH2的输入配置 for(; i<10; i++) { RecordChargeTime = TPAD_GetValue(); // 连续计算10次默认充电时间 } for(i=0; i<9; i++) { if(RecordChargeTime < RecordChargeTime[i+1]) // 冒泡排序(大->小) { temp = RecordChargeTime; RecordChargeTime = RecordChargeTime[i+1]; RecordChargeTime[i+1] = temp; } } for(i=1, temp=0; i<9; i++) { temp += RecordChargeTime; // 去掉MAX与MIN求平均充电时间 } TPAD_DefaultValue = temp/8; // 求出TPAD无动作时的默认充电时间 printf("Default:%dusrn",TPAD_DefaultValue); // 向串口打印未触摸时的电容充电时长 } TPAD_Reset()函数 这里值得注意的是:PA1端口输入输出模式的变化以及TIM_ClearFlag()函数的使用。 PA1端口输入输出模式的变化: 我们会疑问“PA1端口为何要变化输入输出模式?”,在我们上述流程中提及过,我们要对电容充电所造成的上升沿重新计数就必须先将TPAD(PA1)端口置0进行充分的放电。那我们如何放电呢? 在图中,PA1与TPAD通过跳线帽连接在一起,然后为了读取TPAD中产生的上升沿,我们必须将PA1设置为TIM5_CH2的功能,此时PA1为浮空输入模式,但是我们读取完有效的脉冲沿之后,若继续读取必须先进行放电才可以。此时,我们配置PA1为推挽输出模式,放完电后再变成浮空输入模式。 TIM_ClearFlag()库函数的含义: 我们使用库函数的同学会有些疑问:TIM_ClearFlag()函数与 TIM_ClearITPendingBit()函数的区别在哪里?这里我来解说一下。 TIM_ClearFlag()函数是用于“清除标志位的”,比如:当检测到上升沿脉冲(有效的脉冲沿),相应的TIM_IT_CC2标志会出现,其实这些标志位和TIM_ClearITPendingBit ()函数中的中断标志位是一摸一样的,只不过由于未开启中断,事件一旦发生仅仅会使得标志位置1,不会触发中断。 其实如下的程序中我们就是想清除标志位,因此也可以使用TIM_ClearITPendingBit(TIM5, TIM_IT_CC2),这两个函数的功能就是去除相应的标志位,功能相同可以相互转换使用。 TIM_ClearFlag()与TIM_ClearITPendingBit()的函数体对比 TIM_ClearFlag()函数体 void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT) { /* Check the parameters */ assert_param(IS_TIM_ALL_PERIPH(TIMx)); assert_param(IS_TIM_IT(TIM_IT)); /* Clear the IT pending Bit */ TIMx->SR = (uint16_t)~TIM_IT; // 操作SR寄存器 } TIM_ClearITPendingBit()的函数体 void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG) { /* Check the parameters */ assert_param(IS_TIM_ALL_PERIPH(TIMx)); assert_param(IS_TIM_CLEAR_FLAG(TIM_FLAG)); /* Clear the flags */ TIMx->SR = (uint16_t)~TIM_FLAG; // 操作SR寄存器 } 我们从TIM_ClearFlag()与TIM_ClearITPendingBit()的函数体对比发现,完全一摸一样,可以互换使用。 //复位一次 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); //计数器的初始计数值置零 TIM_ClearFlag(TIM5, TIM_IT_CC2); // 清除TIM5_CH2的上升沿标志位 //重新设置GPIOA.1为浮空输入 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //浮空输入 GPIO_Init(GPIOA, &GPIO_InitStructure); } TPAD_GetValue()函数 u16 TPAD_GetValue() // 获取电容充电时间 { TPAD_Reset(); // 复位一次 while(TIM_GetFlagStatus(TIM5, TIM_IT_CC2) == RESET)//不断轮询等待捕获上升沿 { if(TIM_GetCounter(TIM5)>MAX_ARR-500) { return TIM_GetCounter(TIM5);//超时了,直接返回CNT的值 } }; return TIM_GetCapture2(TIM5); } 这里有几行代码特别值得关注: if(TIM_GetCounter(TIM5)>MAX_ARR-500) { return TIM_GetCounter(TIM5);//超时了,直接返回CNT的值 } 这里的提出了一个“检查错误”的概念,我们可以在检查是否有上升沿脉冲触发的同时检测“电容充电时间是否过长”,如果充电时间过长直接return值结束轮询(while(1)),此时我们认为有效脉冲沿持续时间为此时的计数器的值。 TPAD_GetMaxValue()函数 u16 TPAD_GetMaxValue(u8 SampleNumber) // 捕获4次取MAX { u8 temp = TPAD_GetValue(); while(1) { temp = temp>TPAD_GetValue()?temp:TPAD_GetValue(); if(--SampleNumber) break; } return temp; // 求出TPAD无动作时的默认充电时间 } TPAD_Scan() u8 TPAD_Scan() // 扫描TPAD状态且不支持连续按 { u16 TPAD_MaxValue = TPAD_GetMaxValue(3); // 连续采集3次 static u8 PressedFlag = 1; uart_init(115200); // 初始化USART1 printf("Moment:%dusrn",TPAD_MaxValue); // 向串口打印当前充电时长 if((TPAD_MaxValue >= TPAD_Threshold + TPAD_DefaultValue)&&PressedFlag) { PressedFlag = 0; return 1; } else if(TPAD_MaxValue < TPAD_Threshold + TPAD_DefaultValue) { PressedFlag = 1; return 0; } else { return 0; } } 不支持连续按的精髓代码: if((TPAD_MaxValue >= TPAD_Threshold + TPAD_DefaultValue)&&PressedFlag) { PressedFlag = 0; return 1; } else if(TPAD_MaxValue < TPAD_Threshold + TPAD_DefaultValue) { PressedFlag = 1; return 0; } else { return 0; } // 希望大家理解代码执行的逻辑,我在这里就不仔细讲解了。 我们要清楚的一点是:我们前面编写的所有函数都是为了编写TPAD_Scan()做准备,我们真正在main函数中使用的只有TPAD_Scan()函数。我们在编写函数时的好习惯是:注意哪些是辅助函数,哪些是要在main函数中使用得到的函数,函数的功能一定不要过于集中也不要过于分散。 程序示例 Main.c #include "tpad.h" #include "led.h" #include "timer.h" #include "stm32f10x.h" #include "delay.h" #include "usart.h" int main() { extern u16 TPAD_DefaultValue; // TPAD的默认充电时间 u8 TPAD_Status = 0, temp = 0; delay_init(); // systick时钟初始化 LED_InitConfig(); // 初始化LED1&LED0 TPAD_InitConfig(); // 计算TPAD_DefaultValue uart_init(115200); // 初始化USART1 while(1) { TPAD_Status = TPAD_Scan(); // 实时捕获TPAD的动作 if(TPAD_Status == 1) { LED1 = !LED1; // 触摸按键动作触发LED1状态翻转 } temp++; if(temp == 15) { LED0 = !LED0; // LED0作为状态灯使用 } } } |
||
|
||
Led.c
#include "led.h" #include "stm32f10x.h" void LED_InitConfig() { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); // 使能LED1,LED0的时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); // LED0配置为推挽输出模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOE, &GPIO_InitStructure); // LED1配置为推挽输出模式 GPIO_SetBits(GPIOB, GPIO_Pin_5); // LED0初始化为高电平 GPIO_SetBits(GPIOE, GPIO_Pin_5); // LED1初始化为高电平 } Led.h #ifndef _LED_H #define _LED_H #include "sys.h" void LED_InitConfig(); #define LED1 PEout(5) #define LED0 PBout(5) #endif Tpad.c #include "tpad.h" #include "stm32f10x.h" #include "timer.h" #include "delay.h" #include "usart.h" u16 TPAD_DefaultValue = 0; u16 MAX_ARR = 0xFFFF; // 计数器最计数值为0xFFFF u16 TPAD_Threshold = 30; // 有无触摸TPAD的电容充电时间差值为30 void TPAD_InitConfig() // 读取默认电容充电时间 { u16 RecordChargeTime[10] = {0}; u8 i = 0, temp = 0; uart_init(115200); // 初始化USART1 TIM_CAP_InitConfig(0xFFFF, 72-1); // 初始化TIM5_CH2的输入配置 for(; i<10; i++) { RecordChargeTime = TPAD_GetValue(); // 连续计算10次默认充电时间 } for(i=0; i<10; i++) { if(RecordChargeTime < RecordChargeTime[0]) // 冒泡排序(大->小) { temp = RecordChargeTime; RecordChargeTime = RecordChargeTime[0]; RecordChargeTime[0] = temp; } } for(i=1, temp=0; i<9; i++) { temp += RecordChargeTime; // 去掉MAX与MIN求平均充电时间 } TPAD_DefaultValue = temp/8; // 求出TPAD无动作时的默认充电时间 printf("Default:%dusrn",TPAD_DefaultValue); // 向串口打印未触摸时的电容充电时长 } //复位一次 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); // 计数器初始计数值置零 TIM_ClearFlag(TIM5, TIM_IT_CC2); //设置GPIOA.1为浮空输入 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //浮空输入 GPIO_Init(GPIOA, &GPIO_InitStructure); } u16 TPAD_GetValue() // 获取电容充电时间 { TPAD_Reset(); // 复位一次 while(TIM_GetFlagStatus(TIM5, TIM_IT_CC2) == RESET)//等待捕获上升沿 { if(TIM_GetCounter(TIM5)>MAX_ARR-500) { return TIM_GetCounter(TIM5);//超时了,直接返回CNT的值 } }; return TIM_GetCapture2(TIM5); } u16 TPAD_GetMaxValue(u8 SampleNumber) // 捕获4次取MAX { u8 temp = TPAD_GetValue(); while(1) { temp = temp>TPAD_GetValue()?temp:TPAD_GetValue(); if(--SampleNumber) break; } return temp; // 求出TPAD无动作时的默认充电时间 } u8 TPAD_Scan() // 扫描TPAD状态 { u16 TPAD_MaxValue = TPAD_GetMaxValue(3); // 连续采集3次 static u8 PressedFlag = 1; uart_init(115200); // 初始化USART1 printf("Moment:%dusrn",TPAD_MaxValue); // 向串口打印当前充电时长 if((TPAD_MaxValue >= TPAD_Threshold + TPAD_DefaultValue)&&PressedFlag) { PressedFlag = 0; return 1; } else if(TPAD_MaxValue < TPAD_Threshold + TPAD_DefaultValue) { PressedFlag = 1; return 0; } else { return 0; } } Tpad.h #ifndef _TPAD_H #define _TPAD_H #include "sys.h" void TPAD_InitConfig(); // 用于初始化TPAD u16 TPAD_GetValue(); // 捕获一次电容充电时间 u16 TPAD_GetMaxValue(u8 SampleNumber); // 捕获10次电容电容充电时间的MAX u8 TPAD_Scan(); // 用于扫描TPAD是否动作 #endif Timer.c #include "timer.h" #include "stm32f10x.h" #include "sys.h" void TIM_CAP_InitConfig(u16 ARR, u16 PR) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_ICInitTypeDef TIM_ICInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); // 使能TIM5的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA的时钟 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置PA1为浮空输入 TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period = ARR; TIM_TimeBaseInitStructure.TIM_Prescaler = PR; TIM_TimeBaseInit(TIM5, &TIM_TimeBaseInitStructure); // 配置TIM5计数器的属性 TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; TIM_ICInitStructure.TIM_ICFilter = 0; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInit(TIM5, &TIM_ICInitStructure); // 配置TIM5_CH2的输入属性 TIM_Cmd(TIM5, ENABLE); // 使能TIM5 } // 我们不开启捕获中断,但是我们用“识别脉冲沿标志位”来判断事件是否成功发生 Timer.h #ifndef _TIMER_H #define _TIMER_H #include "sys.h" void TIM_CAP_InitConfig(u16 ARR, u16 PR); #endif |
|
|
|
只有小组成员才能发言,加入小组>>
3278 浏览 9 评论
2955 浏览 16 评论
3455 浏览 1 评论
8987 浏览 16 评论
4050 浏览 18 评论
1102浏览 3评论
570浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
568浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2301浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1857浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 01:08 , Processed in 1.364821 second(s), Total 84, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号