打印
[活动专区]

【赛元95F】——低功耗电容式接近触摸感应水龙头(二)

[复制链接]
1062|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 szkeinew 于 2020-4-10 23:41 编辑

上接【赛元95F】——低功耗电容式接近触摸感应水龙头(一)https://bbs.21ic.com/icview-2939306-1-1.html?fromuser=szkeinew

一,框图及流程图如下:


二,触摸库调试简图:


三,程序部分内容展示:


//主程序
#include "sys_variable.h"


void SYS_INIT(void); //系统初始化
void TOUCH_KEY(void); //触摸键处理
void SOLENOID_VALVE_RESET(void); //电磁阀复位
void SLEEP_FRONT_RESET(void); //休眠前复位
void ADC_CHECK_FUNCTION(void); //ADC检测
void BAT_CHARGE_FUNCTION(void); //电池充电
void SLEEP_FUNCTION(void); //休眠

void main(void){
    SYS_INIT();
    while(1){
        TOUCH_KEY();                     
    }
}               


void SYS_INIT(void){
   //端口复位
   GPIO_INIT(GPIO0,0XEF,0X10,0XDF);
   GPIO_INIT(GPIO1,0XFF,0X00,0XCF);
   GPIO_INIT(GPIO2,0XF2,0X00,0XFF);
   GPIO_INIT(GPIO3,0XFF,0X00,0XFF);
   GPIO_INIT(GPIO4,0XFF,0X00,0XFF);
   GPIO_INIT(GPIO5,0XFF,0X00,0XF7);
   SOLENOID_VALVE_RESET();
   
   ADC_INIT(FADC32);
   ADC_SWITCH(AIN6);//VIN_ADC
   ADC_SWITCH(AIN7);//BAT_ADC
   
   adc_select_chan = AIN6;
   ADC_ADCIF_RESET_FLAG();
   INTERRUPT_INIT(ADC,1);
   ADC_EN(1);
   ADC_SWITCH(adc_select_chan);//VIN_ADC
   ADC_START();
   while(BAT_VOLTAGE() < 1.0F);
   TouchKeyInit();
   
   INTERRUPT_INIT(Timer0,1);
   timer0_timing_value = TIMER0_INIT(TIMER0BITS_16BIT,CONTROLMODE_TIMING,FREQ_SELECT_1DIV,10000);//10ms
   TIMER0_TIMING_SET(timer0_timing_value);
   TIMER0_SWITCH(1);
   LED1_FUN_EN(1);
   LED2_FUN_EN(1);
   while(!sys_reset_finish_flag); //第一次上电要长启动,防止由于上电期触控误工作,令电磁阀开启
   LED1_FUN_EN(0);
   LED2_FUN_EN(0);
   polarity_reset_flag = 1;
   adc_check_time = ADC_CHECK_TIME_VALUE - 3;
   BTM_INIT(BTM_1S);
   //sys_fun_error_flag = 1;
}

void SOLENOID_VALVE_RESET(void){
    INTERRUPT_INIT(INT2,1);
    INT2R |= 0X01; //上升沿触发
    PWR_FUN_EN(1);
    DELAY_MS(50);
    POLARITY_SELECT(1,1);
    DELAY_MS(20);
    POLARITY_SELECT(1,0);
    PWR_FUN_EN(0);
    INTERRUPT_INIT(INT2,0);
    INT2R &= 0XFE; //取消上升沿触发
}

void TOUCH_KEY(void){
    if((SOCAPI_TouchKeyStatus & 0x80) == 0x80){
    SOCAPI_TouchKeyStatus &= 0x7f;
    touch_select_valid_flag = 0;
    if(touch_start_number >= TOUCH_START_NUMBER_VALUE){
        if((TouchKeyScan() & 0x00000080) == 0x00000080){
            touch_select_valid_flag = 1;//((TouchKeyScan() & 0x00000080) >> 7) & 0x01;
        }
        /**************
            触摸有效值是当前与上一次的值同时一样才能判断当次有效,
            这样才能最大地避免误判,提高稳定度。
        **************/
        if(touch_select_valid_flag && touch_select_valid_history_flag){                       
            touch_check_number++;
            if(touch_check_number >= (TOUCH_CHECK_NUMBER_VALUE/4)){
                touch_unselect_count = 0;
            }
            if(touch_check_number >= TOUCH_CHECK_NUMBER_VALUE){
                touch_check_number = TOUCH_CHECK_NUMBER_VALUE;
                /*
                if(~polarity_reset_flag){
                    LED1_FUN_EN((work_timing_time >= WORK_TIMING_TIME_VALUE)?0:1);
                }else{
                    LED1_FUN_EN();
                }*/               
                TIMER0_SWITCH(1);
                if(!work_timeout_flag){
                    polarity_reset_flag = 0;
                    LED1_FUN_EN(1);
                }else{
                    LED1_FUN_EN(0);
                }
                //LED2_FUN_EN(0);                                                
            }                                                         
        }else{
            LED1_FUN_EN(0);                    
            touch_unselect_count++;
            
            if((touch_unselect_count >= (TOUCH_UNSELECT_COUNT_VALUE/4)) ||
                (touch_check_number == TOUCH_CHECK_NUMBER_VALUE)){
                touch_check_number = 0;
            }
            touch_check_number = 0;
            if(touch_unselect_count >= TOUCH_UNSELECT_COUNT_VALUE){
                touch_unselect_count = 0;                        
                if(!polarity_reset_flag){
                    polarity_reset_flag = 1;
                    work_timing_time = 0;
                    while(polarity_reset_flag);
                 }
                work_timeout_flag = 0;
                SLEEP_FRONT_RESET();                     
            }
        }
        touch_select_valid_history_flag =  touch_select_valid_flag;                                 
    }else{
        PWR_FUN_EN(0);
        touch_select_valid_flag = (TouchKeyScan() & 0x00000000);
    }               
    touch_start_number++;
    TouchKeyRestart();
}
}

void SLEEP_FRONT_RESET(void){
    TK_POWER_EN(0);
    TIMER0_SWITCH(0);
    LED1_FUN_EN(0);      
    touch_check_number = 0;
    touch_unselect_count = 0;
    work_timing_time = 0;
    touch_select_valid_flag = 0;
    touch_start_number = 0;
    polarity_reset_flag = 1;
    if(BAT_CHRG){
        bat_charge_error_flag = 0;
        LED2_FUN_EN(bat_charge_error_flag);
    }
    INTERRUPT_INIT(INT2,0);
    INT2R &= 0XFE;
    WORK_SLEEP_TEST = 1;   
    SLEEP_FUNCTION();
    WORK_SLEEP_TEST = 0;
    if(!sys_fun_error_flag){   
        ADC_CHECK_FUNCTION();
        INTERRUPT_INIT(INT2,1);
        INT2R |= 0X01; //上升沿触发
        SOCAPI_TouchKeyStatus &= 0x7f;
        TK_POWER_EN(1);
        DELAY_MS(2);//待触摸电源稳后才工作        
    }else{
        while(1){                  
            SLEEP_FUNCTION();
            sys_fun_error_led_disp_flag  = ~sys_fun_error_led_disp_flag;
            LED1_FUN_EN(!sys_fun_error_led_disp_flag);
            LED2_FUN_EN(sys_fun_error_led_disp_flag);
            DELAY_MS(5);
            LED1_FUN_EN(0);
            LED2_FUN_EN(0);
        }        
    }   
}

void SLEEP_FUNCTION(void){
    INTERRUPT_INIT(BTM,1);
    BTM_EN(1);
    PCON |= 0x02;
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    BTM_EN(0);
    INTERRUPT_INIT(BTM,0);
}


void ADC_CHECK_FUNCTION(void){
    adc_check_time++;
    if(adc_check_time >= ADC_CHECK_TIME_VALUE){ //一分钟检测一次
        adc_check_time = 0;
        adc_select_chan = AIN6;
        ADC_ADCIF_RESET_FLAG();
        INTERRUPT_INIT(ADC,1);
        ADC_EN(1);
        ADC_SWITCH(adc_select_chan);//VIN_ADC
        ADC_START();
        while(!adc_finish_flag);
        adc_finish_flag = 0;
        BAT_CHARGE_FUNCTION();
    }else if((adc_check_time == 25) || (adc_check_time == 50)){
        TouchKeyInit();
    }
}

void BAT_CHARGE_FUNCTION(void){
    if(BAT_VOLTAGE() < BAT_MIN_VOLTAGE){
        if(POWER_VIN_VOLTAGE() >= POWER_IN_MIN_VOLTAGE){
            //电池电压低于设定最小值并输入电压大于最小值时就充电
            //并且橙色指示灯长亮
            BAT_CHRG_FUN_EN(1);
            LED2_FUN_EN(1);
        }else if(BAT_CHRG){
            //如果电池电压低于设定最小值并无输入电压,则橙色指示灯闪烁指示
            bat_charge_error_flag = ~bat_charge_error_flag;            
            LED2_FUN_EN(bat_charge_error_flag);
            BAT_CHRG_FUN_EN(0);
            adc_check_time = ADC_CHECK_TIME_VALUE - 3; //如果电池出现低电压,则将ADC检测时间缩短到3S
        }
    }else if(BAT_CHRG && (BAT_VOLTAGE() >= BAT_MAX_VOLTAGE)){
        //充电完成后关闭橙色指示灯及充电端口
        BAT_CHRG_FUN_EN(0);
        LED2_FUN_EN(0);
    }
}

//中断
#include "sys_interrupt.h"

#define ARRAY_SUB 5

uint idata ADC_TEMP_ARRAY[ARRAY_SUB] = {0,0,0,0,0};
uchar idata adc_temp_array_number = 0;
bit bittest = 0;


void INTERRUPT_INIT(enum INTERRUPTENUM enummane,bit en){
    switch(enummane){
          case INT0:    en ?(IE |= 0X01):(IE &= 0XFE);
                        break;
          case Timer0:  en ?(IE |= 0X02):(IE &= 0XFD);
                        break;
          case INT1:    en ?(IE |= 0X04):(IE &= 0XFB);
                        break;
          case Timer1:  en ?(IE |= 0X08):(IE &= 0XF7);
                        break;
          case UART:    en ?(IE |= 0X10):(IE &= 0XEF);
                        break;
          case Timer2:  en ?(IE |= 0X20):(IE &= 0XDF);
                        break;
          case ADC:     en ?(IE |= 0X40):(IE &= 0XBF);         
                        break;
                        
          case USCI0:   en ?(IE1 |= 0X01):(IE1 &= 0XFE);
                        break;
          case PWM:     en ?(IE1 |= 0X02):(IE1 &= 0XFD);
                        break;
          case BTM:     en ?(IE1 |= 0X04):(IE1 &= 0XFB);
                        break;
          case INT2:    en ?(IE1 |= 0X08):(IE1 &= 0XF7);
                        break;
          case TK:      en ?(IE1 |= 0X10):(IE1 &= 0XEF);
                        break;
          case CMP:     en ?(IE1 |= 0X20):(IE1 &= 0XDF);
                        break;
          case Timer3:  en ?(IE1 |= 0X40):(IE1 &= 0XBF);
                        break;
          case Timer4:  en ?(IE1 |= 0X80):(IE1 &= 0X7F);
                        break;
                        
          case USCI1:  en ?(IE2 |= 0X01):(IE2 &= 0XFE);
                        break;
          case USCI2:  en ?(IE2 |= 0X02):(IE2 &= 0XFD);
                        break;
    }
    IE |= 0x80;
}

void INTERRUPT_INT0(void) interrupt 0{

}

void INTERRUPT_TIMER0(void) interrupt 1{
     TCON &= 0XDF;//清除中断标志
     TIMER0_TIMING_SET(timer0_timing_value);
     if(sys_reset_finish_flag){
         work_timing_time++;
         if(work_timing_time >= WORK_TIMING_TIME_VALUE){
             if(!work_timeout_flag){
                  work_timing_time = 0;
                  work_timeout_flag = 1;
                  polarity_reset_flag  = 1;                                    
             }else if(!polarity_reset_flag){
                 work_timing_time = WORK_TIMING_TIME_VALUE;
             }
         }else{
              //开启及关闭电磁阀     
              if(work_timing_time < 30){
                  //TK_POWER_EN(0);
                  PWR_FUN_EN(1);
                  if((work_timing_time < 25 ) && (work_timing_time > 15)){
                      if(!polarity_reset_flag){
                           POLARITY_SELECT(0,1);
                      }else{
                           POLARITY_SELECT(1,1);
                      }
                  }
              }else{
                  POLARITY_SELECT(1,0);
                  PWR_FUN_EN(0);                 
                  if(polarity_reset_flag){
                        polarity_reset_flag = 0;                  
                  }
                  if(work_timing_time == 50){
                    //SOCAPI_TouchKeyStatus &= 0x7f;
                    //TK_POWER_EN(1);
                  }
              }
              
          }
      }else if(!sys_reset_finish_flag){
          work_timing_time++;
          if(work_timing_time >= (WORK_TIMING_TIME_VALUE*2)){
              work_timing_time = 0;
              sys_reset_finish_flag = 1;
              TIMER0_SWITCH(0);
           }
      }
}

void INTERRUPT_TIMER2(void) interrupt 5{
     //T2CON &= 0X7F;//清除中断标志
}

void INTERRUPT_ADC(void) interrupt 6{
    uchar idata forx = 0;
    uchar idata fory = 0;
    uint idata temp = 0;
        ADC_ADCIF_RESET_FLAG();
    ADC_TEMP_ARRAY[adc_temp_array_number] = ((ADCVH << 4) |(ADCVL >> 4));
    adc_temp_array_number++;
   
    if(adc_temp_array_number > sizeof(ADC_TEMP_ARRAY[ARRAY_SUB])){
        //排序
        adc_temp_array_number = 0;
        for(forx = 0;forx < (sizeof(ADC_TEMP_ARRAY[ARRAY_SUB])-1);forx++){
            for(fory = 0;fory < (sizeof(ADC_TEMP_ARRAY[ARRAY_SUB])-1);fory++){
                if(ADC_TEMP_ARRAY[fory] > ADC_TEMP_ARRAY[fory + 1]){
                    temp = ADC_TEMP_ARRAY[fory];
                    ADC_TEMP_ARRAY[fory] = ADC_TEMP_ARRAY[fory + 1];
                    ADC_TEMP_ARRAY[fory + 1] = temp;
                }
            }
        }
        
        SET_ADC_VALUE(adc_select_chan,ADC_TEMP_ARRAY[2]);
        if(adc_select_chan != AIN7){
            adc_select_chan = AIN7;
            ADC_SWITCH(adc_select_chan);
            ADC_START();
                     
        }else{
            INTERRUPT_INIT(ADC,0);
            ADC_EN(0);
            adc_finish_flag = 1;            
        }
    }else{
        ADC_START();
    }
}

void INTERRUPT_BTM(void) interrupt  9{
    PCON &= 0XFD;   
}

void INTERRUPT_INT2(void) interrupt 10{
    PWR_FUN_EN(0);
    INTERRUPT_INIT(INT2,0);
    INT2R &= 0XFE;
    INT2F &= 0XFE;
    sys_fun_error_flag = 1;
}
四,编程过程与心得:
      1,现在公司最近开发的两个项目都是用赛元MCU并带有触摸模块,所以在此项目上添加官方触控库比较容易,具体方法请参阅官方相关文件。
      2,在项目前期是打算用官方的低功耗触控库的,但因时间问题在实现官方给出的函数时,出现调用不是好顺利。所以将触控库中的触摸模块寄存器 提取出来,在触摸库检测不到用感应时去关闭触摸模块及其它模块电源,只留Base Time并设置为1S唤醒中断,再一次打触摸库电源。从而实际普通库成低功耗库的方法。
     3,因为接近感应键的感应盘太长,很容易由其它微小的变化引起大的变化,在用触控调试软件来调触控键时,反复几个钟才调好水龙头静态接近感应参数。
     4,当转换到动态测试中发现灵敏度及感应距离短等情况,由于提交项目时间问题,只能等到提交项目后才深入调试。
     5,因SC92F8617这粒MCU资源较丰富,能很容易实现开发中小型项目。其它资源以后再深入了解应用。

余下内容请移步到《【赛元95F】——低功耗电容式接近触摸感应水龙头(三)》...  https://bbs.21ic.com/icview-2940400-1-1.html?fromuser=szkeinew


使用特权

评论回复

相关帖子

沙发
jiekou001| | 2020-4-10 23:53 | 只看该作者
有远吗吗,看看工程

使用特权

评论回复
板凳
jiekou001| | 2020-4-10 23:54 | 只看该作者
我在服务区看到水龙头都是红外感应的。

使用特权

评论回复
评论
o88ne 2020-4-28 21:46 回复TA
哈哈,我也看到过/ 
地板
szkeinew|  楼主 | 2020-4-11 08:53 | 只看该作者
jiekou001 发表于 2020-4-10 23:54
我在服务区看到水龙头都是红外感应的。

现在感应都是红外感应的,曾经去过一间卫浴公司面试,那老板说红外感应的有一个最大的问题就是如果手是黑色的,那感应会失灵。所以我想用这个电容感应来代替红外感应。

使用特权

评论回复
5
鑫越电子| | 2020-4-12 16:54 | 只看该作者
电容触摸的干扰要解决好

使用特权

评论回复
6
szkeinew|  楼主 | 2020-4-12 17:23 | 只看该作者
鑫越电子 发表于 2020-4-12 16:54
电容触摸的干扰要解决好

是要找出好的方法来提高抗干扰能力才行。灵敏度太高误动作次数多,灵敏低了反应慢了。

使用特权

评论回复
7
fyljx666| | 2020-4-21 14:37 | 只看该作者
好资料,谢谢提供学习

使用特权

评论回复
8
fang316| | 2020-4-22 10:43 | 只看该作者
感谢!!!!   <2020.04.22 - 10:43>

使用特权

评论回复
9
cooleaf| | 2020-4-28 19:27 | 只看该作者
赛元整得比较火呀!!

使用特权

评论回复
10
o88ne| | 2020-4-28 21:45 | 只看该作者
红外感应盲点太多了,真想把我家楼的电梯拆了,看看是啥破感应。。。感觉能夹死人

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

5

主题

28

帖子

0

粉丝