STM32 PCB触摸按键(RC检测法)

来源:互联网 发布:js获取整个页面的内容 编辑:程序博客网 时间:2024/05/22 11:40

无意中翻出了大学刚毕业时用来来忽悠老板的触摸按键的程序,突然感概白发又多了。做硬件的不容易,做软件的也不容易,做硬件又做软件的更不容易。。。。

回想起来印象也不深刻,感觉纯粹为了好玩,又发现了键盘边有个有三个焊盘的pcb板,心血来潮把就它翻新了一下。

感觉触摸按键比物理按键简单多了,物理按键还要按键(废话),但是触摸按键的可是是一个铜片,铁片,金属片(反正是导体就行了)。如果手头上又没有pcb按钮的,可以自己随便找个废板,在有铜片的地方挖个按钮引条线出来也是可以的,甚至拿一条导线也可以。手按按钮时要在按键上贴个胶纸绝缘,不然,按下的时候电流都被人体吸光了。


要说明一下,程序和硬件都是借鉴STM8,ST有相关例程,是AN几就忘了。st的例程使用的查询电平的方式,而且连充放电的时间也要去等,我用可是高贵的stm32这么宝贵的cpu时间岂能白白浪费掉,所以我就把它改成了中断触发的方式。

好了,先贴个原理图。


原理图就这么的简单。

PA4,是充电引脚。PA5、PA6、PA7是电平的检测引脚。这里用的是三个按键,R12、R13、R14是充电的限流电阻,这三个电阻要在几百K到几M欧之间,视单片机的性能而定吧,电阻越小电流越大充放电时间就越短,有句话说就是可以避免夜长梦多。但是电阻太小了,时间太短单片机就检测不到了。我在这选的是1M欧,充放电时间大概是10us,R15,R16,R17作用不是太大,不能取太大就是了。

所谓的RC法,就是R和C,R就是电阻,C就是电容也就是按键,利用充放电的时间来检测电容的变化。

更多的资料,百度谷歌很多,就不抛砖头了。

我的按键是这样的,注意要贴上胶布绝缘。


好了贴程序


这里是三个按键,硬件资源使用了两个定时器,4个IO口

TIM2用于时间的记录。

TIM3每100us触发一次,Ttimer_cnt 记录的是触发次数

Ttimer_cnt  = 1 现在所有IO都为低电平状态配置所有IO为输入高电平触发中断,并拉高充电的IO口

Ttimer_cnt  = 4到这时所有的IO已经充电完毕,可以记录充电的时间

Ttimer_cnt  = 5现在的所有IO口都处于高电平状态,配置所有IO为输入低电平触发中断,并拉低充电的IO口,放电

Ttimer_cnt  = 8到这时,所有的IO都放电完毕,记录放电时间

Ttimer_cnt  = 9计算按键的充放电时间值,滤波。Ttimer_cnt  = 0;


芯片使用的是stm32F103

//.h文件typedef struct {uint32_t Pin;//引脚uint32_t staus;uint32_t even;uint32_t Realse;uint32_t check_cnt;uint32_t MeasRejected;uint32_t RejectionCounter;uint32_t Up_Cnt;uint32_t Down_Cnt;    uint32_t Temp_Cnt;uint32_t AcqLoopIndex;uint32_t Shifter;uint32_t MaxMeasurement;uint32_t MinMeasurement;uint32_t Measurement;uint32_t FinalMeasurementValue;uint32_t CumulatedMeasurement;uint32_t ReadMeasurement;//检测结果uint32_t Eline;}Tkey_Action;//.c文件#define KEYNUMS 3//IO方向设置//    IO口设置76543210       IO口设置76543210#define ACQ_IN(x)  {GPIOA->CRL&= ~(0xF << (uint32_t)(x));GPIOA->CRL|= (0x4 << (uint32_t)(x));} //配置为输入高阻#define ACQ_OUT(x) {GPIOA->CRL&= ~(0xF << (uint32_t)(x));GPIOA->CRL|= (0x8 << (uint32_t)(x));} //配置为推挽输出//IO口操作#define OUT_OUT PAout(4) //充放电引脚#define SAMPLING_SHIFTER_LOOP_START (1)#define SAMPLING_SHIFTER_NB_LOOPS (8)#define MAX_MEAS_COEFF (0x150) //检测结果误差最大值自己调一个合适的值 > 255#define MIN_MEAS_COEFF (0xBC)//检测结果误差允许最小值自己调一个合适的值 < 255#define MAX_REJECTED_MEASUREMENTS (5)Tkey_Action Tkey[KEYNUMS];uint32_t Tkey_cnt;uint32_t Ttimer_cnt;uint32_t Cnt_Dat;//extern unsigned int cnt;void Tkey_Init(void);void TkeyTestFunc(void){//Tkey_Init();while(1){//delay_ms(250);//printf("Get KEY0 %d,KEY1 %d,KEY2 %d\r\n",Tkey[0].ReadMeasurement,Tkey[1].ReadMeasurement,Tkey[2].ReadMeasurement);if(Tkey[0].ReadMeasurement > 1700)//测量值,跟实际pcb按键电容的大小有关{if(Tkey[0].Realse  == 0){printf("KEY0 Press\r\n");}Tkey[0].Realse = 1;//按键按下}else{if(Tkey[0].Realse  == 1){printf("KEY0 Realse\r\n");}Tkey[0].Realse = 0;//按键释放}if(Tkey[1].ReadMeasurement > 2300)//测量值,跟实际pcb按键电容的大小有关{if(Tkey[1].Realse  == 0){printf("KEY1 Press\r\n");}Tkey[1].Realse = 1;}else{if(Tkey[1].Realse  == 1){printf("KEY1 Realse\r\n");}Tkey[1].Realse = 0;}if(Tkey[2].ReadMeasurement > 2400)//测量值,跟实际pcb按键电容的大小有关{if(Tkey[2].Realse  == 0){printf("KEY2 Press\r\n");}Tkey[2].Realse = 1;}else{if(Tkey[2].Realse  == 1){printf("KEY2 Realse\r\n");}Tkey[2].Realse = 0;}}}void Tkey_Exti_Init(void)//按键的外部中断检测{GPIO_InitTypeDef GPIO_InitStructure;EXTI_InitTypeDef EXTI_InitStructure;NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);    //复用时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_Pin_5);    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_Pin_6);    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_Pin_7);EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //设置为中断请求EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //设置中断触发方式为下降沿触发EXTI_InitStructure.EXTI_LineCmd = ENABLE;                                          //外部中断使能        EXTI_InitStructure.EXTI_Line = EXTI_Line5; //选择中断线路1EXTI_Init(&EXTI_InitStructure);    EXTI_InitStructure.EXTI_Line = EXTI_Line6; //选择中断线路1EXTI_Init(&EXTI_InitStructure);    EXTI_InitStructure.EXTI_Line = EXTI_Line7; //选择中断线路1EXTI_Init(&EXTI_InitStructure);NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;    NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;NVIC_Init(&NVIC_InitStructure);}void Tkey_Timer_Init(void)//相关定时器初始化{TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;/* TIM2 and TIM3 clocks enable */RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3, ENABLE);/* 1 bit for pre-emption priority, 3 bits for subpriority */NVIC_SetPriorityGrouping(6); NVIC_DisableIRQ(TIM2_IRQn);/* TIM2 configuration -------------------------------------------------------*/TIM_DeInit(TIM2);TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);TIM_TimeBaseStructure.TIM_Prescaler = 0x01;    /* TIM2CLK = 72 MHz */TIM_TimeBaseStructure.TIM_Period = 0xFFFF;TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);//TIM_GetCounter(TIM2);    TIM2->ARR = 0xFFFF;    /* Enable the TIM Counter */    TIM2->CR1 |= ((uint16_t)0x0001);  /* Clear the IT pending Bit */    TIM2->SR = ((uint16_t)(~TIM_IT_Update));  /* Enable TIM2 update interrupt */  /* Enable the TIM3 Interrupt */NVIC_SetPriority(TIM3_IRQn, 0x01); /* 0x00 = 0x01 << 3 | (0x00 & 0x7*/NVIC_EnableIRQ(TIM3_IRQn);/* TIM3 configuration -------------------------------------------------------*/TIM_DeInit(TIM3);/* TIM3 used for timing, the timing period depends on the sample rate */TIM_TimeBaseStructure.TIM_Prescaler = 0x00;    /* TIM2CLK = 72 MHz */TIM_TimeBaseStructure.TIM_Period = 7199; //0.10msTIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);//TIM_GetCounter(TIM2); /* Relaod ARR register */    TIM3->ARR = 7199;    /* Enable the TIM Counter */    TIM3->CR1 |= ((uint16_t)0x0001);  /* Clear the IT pending Bit */    TIM3->SR = ((uint16_t)(~TIM_IT_Update));   TIM3->DIER |= ((1 << 6) | (1 << 0));}void Tkey_ParInit(Tkey_Action *Tkey_Std,uint32_t GPIOnums){//按键数据结构初始化    Tkey_Std->Shifter = 1;Tkey_Std->AcqLoopIndex = 0;Tkey_Std->MeasRejected = 0;Tkey_Std->RejectionCounter = 0;Tkey_Std->staus = 0;Tkey_Std->Pin = GPIOnums;//引脚Tkey_Std->Eline = (uint32_t)(1 << GPIOnums);//中断资源}void Tkey_Init(void){    uint32_t i;GPIO_InitTypeDef Tkey_Struct;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);Tkey_Struct.GPIO_Pin = (GPIO_Pin_4);//充放电IOTkey_Struct.GPIO_Speed = GPIO_Speed_50MHz;Tkey_Struct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOA,&Tkey_Struct);GPIO_ResetBits(GPIOA,GPIO_Pin_4);    for(i=0;i<KEYNUMS;i++){        Tkey_ParInit(&Tkey[i],i+5);//初始化按键数据参数,按键端口为GPIO5/6/7    }    Tkey_Exti_Init();//设置硬件参数    Tkey_Timer_Init();//启动动定时器Tkey_cnt = 0;}__forceinline void Tkey_upCheck(void){    uint32_t i;    uint32_t ExitLine = 0;        for(i=0;i<KEYNUMS;i++){        Tkey[i].Up_Cnt = Tkey[i].Temp_Cnt;////记录时间        PAout(Tkey[i].Pin) = 1;//输出高电平,开始充电        ACQ_OUT(Tkey[i].Pin);//设置为输出状态        ExitLine |= Tkey[i].Eline;    }    EXTI->IMR &= ~(ExitLine);//失能中断}__forceinline void Tkey_Vih(Tkey_Action *Tkey_Std)//等待到达上升沿设置{ACQ_IN(Tkey_Std->Pin);//按键检测端口设置为输入高阻态EXTI->FTSR &= ~(Tkey_Std->Eline);EXTI->RTSR |= (Tkey_Std->Eline);//设置为上升沿触发EXTI->IMR |= (Tkey_Std->Eline);//使能中断}__forceinline void Tkey_downCheck(void)//记录放电时间{    uint32_t i;    uint32_t ExitLine = 0;        for(i=0;i<KEYNUMS;i++){        Tkey[i].Up_Cnt = Tkey[i].Temp_Cnt;////记录时间        PAout(Tkey[i].Pin) = 0;//输出低电平,放电        ACQ_OUT(Tkey[i].Pin);//输出低电平,放电        ExitLine |= Tkey[i].Eline;    }       EXTI->IMR &= ~(ExitLine);//失能中断}__forceinline void Tkey_Vil(Tkey_Action *Tkey_Std)//等待到达下降沿设置{ACQ_IN(Tkey_Std->Pin);//配置为高阻态,等待放电完成EXTI->FTSR |= (Tkey_Std->Eline);EXTI->RTSR &= ~(Tkey_Std->Eline);//设置为下降沿触发EXTI->IMR |= (Tkey_Std->Eline);//使能中断}__forceinline void Tkey_Vil_All(void){    uint32_t i;    uint32_t ExitLine = 0;    for(i=0;i<KEYNUMS;i++){        ExitLine |= Tkey[i].Eline;    }    for(i=0;i<KEYNUMS;i++){        ACQ_IN(Tkey[i].Pin);//所有检测端口配置为输入    }EXTI->FTSR |= (ExitLine);EXTI->RTSR &= ~(ExitLine);//所有检测端口设置为下降沿触发EXTI->IMR |= (ExitLine);//使能中断    }__forceinline void Tkey_Vih_All(void){uint32_t i;    uint32_t ExitLine = 0;    for(i=0;i<KEYNUMS;i++){        ExitLine |= Tkey[i].Eline;    }    for(i=0;i<KEYNUMS;i++){        ACQ_IN(Tkey[i].Pin);//所有检测端口配置为输入    }        EXTI->FTSR &= ~(ExitLine);EXTI->RTSR |= (ExitLine);//所有检测端口设置为上升沿触发EXTI->IMR |= (ExitLine);//使能中断    }void Tkey_TDeal(Tkey_Action *Tkey_Std){unsigned int temp;Tkey_Std->Measurement = (Tkey_Std->Up_Cnt + Tkey_Std->Down_Cnt);//充放电的总时间Tkey_Std->CumulatedMeasurement += Tkey_Std->Measurement;//统计时间值if(Tkey_Std->Shifter == 1)//第一次检测,计算出参考值{temp = Tkey_Std->Measurement * MAX_MEAS_COEFF;Tkey_Std->MaxMeasurement = (temp >> 8) + 8;temp = Tkey_Std->Measurement * MIN_MEAS_COEFF;Tkey_Std->MinMeasurement = (temp >> 8) + 8;}else//非第一次检测判断值是否在范围内{if ((Tkey_Std->Measurement < Tkey_Std->MinMeasurement) || (Tkey_Std->Measurement > Tkey_Std->MaxMeasurement))//超出可接收范围{Tkey_Std->MeasRejected++;Tkey_Std->RejectionCounter++;Tkey_Std->Shifter = 8;////break; // Out from 'for SamplingShifter' loop !!!} }Tkey_Std->Shifter ++;//检测次数++if(Tkey_Std->Shifter > 8)//检测达八次{if(Tkey_Std->MeasRejected && (Tkey_Std->RejectionCounter <= MAX_REJECTED_MEASUREMENTS))//非法的次数大于可接受范围{Tkey_Std->Shifter = 1;}else//重新开始{if (Tkey_Std->MeasRejected == 0)//{Tkey_Std->FinalMeasurementValue += Tkey_Std->CumulatedMeasurement;Tkey_Std->Shifter = 1;Tkey_Std->AcqLoopIndex ++;}else // RejectionCounter > MAX_REJECTED_MEASUREMENTS //{//break; // Out from 'for AcqLoopIndex' loop !!!Tkey_Std->AcqLoopIndex = 3;Tkey_Std->FinalMeasurementValue = 0;}}Tkey_Std->MeasRejected = 0;    Tkey_Std->CumulatedMeasurement = 0;}if(Tkey_Std->AcqLoopIndex > 2)//一轮的检测完成{if (Tkey_Std->RejectionCounter <= MAX_REJECTED_MEASUREMENTS){Tkey_Std->ReadMeasurement = (Tkey_Std->FinalMeasurementValue >> 3); /* Division by SAMPLING_SHIFTER_NB_LOOPS */}elseTkey_Std->ReadMeasurement = 0;//结果非法Tkey_Std->AcqLoopIndex = 0;Tkey_Std->MinMeasurement = 0;   Tkey_Std->MaxMeasurement = 0;   Tkey_Std->RejectionCounter = 0;Tkey_Std->Shifter = 1;Tkey_Std->staus = 1;Tkey_Std->FinalMeasurementValue = 0;}}void TIM2_IRQHandler(void){    /* Relaod output compare */    TIM2->ARR = 0xFFFF;    /* Clear TIM2 update interrupt */    TIM2->SR = ((uint16_t)(~TIM_IT_Update));}void TIM3_IRQHandler(void){    /* Relaod output compare */    TIM3->ARR = 7199;if(Ttimer_cnt == 1){/*配置IO输入,上升沿触发,使能中断;*/Tkey_Vih_All();TIM2->CNT = 0;//重置计数器OUT_OUT = 1;//充电口输出高电平}else if(Ttimer_cnt == 4){/*记录下CNT计数值;设置IO口输出模式,使电容屏充满电;        到这个时候,理论上所有的端口都已经充满电并触发中断了*/        Tkey_upCheck();}else if(Ttimer_cnt == 5){/*配置IO输入,下降沿触发,使能中断;*/Tkey_Vil_All();        TIM2->CNT = 0;//重置计数器OUT_OUT = 0;//充电口输出低电平}else if(Ttimer_cnt == 8){        /*记录下CNT计数值;设置IO口输出模式,使电容按键放光电;*/Tkey_downCheck();                }else if(Ttimer_cnt == 9)    {        unsigned int i;        for(i=0;i<KEYNUMS;i++){//统计结果            Tkey_TDeal(&Tkey[i]);        }       Ttimer_cnt = 0;//一个周期的检测结束    }Ttimer_cnt ++;  /* Clear TIM2 update interrupt */    TIM3->SR = ((uint16_t)(~TIM_IT_Update));}__forceinline void Tkey_RecordTime(Tkey_Action *Tkey_Std){    if(EXTI->PR  & (Tkey_Std->Eline)){EXTI->IMR &= ~(Tkey_Std->Eline);//失能中断Tkey_Std->Temp_Cnt = TIM2->CNT;EXTI->PR |= (Tkey_Std->Eline);}}void EXTI9_5_IRQHandler(void){/*记录下中断触发时的计数器计数;*/uint32_t i;    for(i=0;i<KEYNUMS;i++){Tkey_RecordTime(&Tkey[i]);}}


(博文爱转不转,但请注明出处吓)

0 0
原创粉丝点击