搜索
bottom↓
回复: 226

AVR普通IO端口实现非接触式电容触摸感应试验成功

[复制链接]

出0入0汤圆

发表于 2008-8-13 11:18:43 | 显示全部楼层 |阅读模式
说到电容非接触摸技术相信大家并不陌生......

    前几天在书城看到匠人的手记.....里面看到这样一个描述用普通IO口加一个电阻实现电容感应的方案...只是大概描述..回来后经过自己分析...由于人体就是导体,通过大地回路形成一个很小的电容.在人体接近感应区域时,人体电容和IO口内部的电容并联,可以通过RC充电方式测量出这个电容,在人体没有接近时,只有IO口内部电容,通过软件处理后可识别是否有人体接近... 由于电容很小,所以电阻要很大才行,现在这里用20M的电阻.......

经过软件仿真.电压5V 电阻20M 测得

电容 = 1P  充电时间大约为   20US
电容 = 5P  充电时间大约为  100US
电容 = 10P 充电时间大约为  200US
电容 = 25P 充电时间大约为 1750US

那就费话少说.....直接上图...上源代码.....

(原文件名:电容感应1.jpg)


(原文件名:电容感应2.JPG)


(原文件名:电容感应3.JPG)


(原文件名:电容感应4.JPG)


(原文件名:电容感应5.JPG)


//引用文件***********************************************************
#include<iom8v.h>
#include<macros.h>

//输出定义***********************************************************
#define       Led1On         PORTD |= (1 << 7)             //输出指示
#define       Led1Off        PORTD &= ~(1 << 7)            //关闭指示

//*******************************************************************
//函数名字; Delay1Ms();
//输入参数; 无  
//输出参数; 无
//功能描述; 延时1 毫秒
//建造日期; 2008年08月09日
//*******************************************************************
void Delay1Ms(void)                                        //毫秒延时
{   
     unsigned int i;
         
     for (i = 0; i < 140; i++);                            //
}

//*******************************************************************
//函数名字; DelayNms(n);
//输入参数; 延时周期参数据
//输出参数; 无
//功能描述; 延时程序
//建造日期; 2008年08月09日
//*******************************************************************
void DelayNms(unsigned int n)                              //延时周期
{
     unsigned int i;
         
     for (i = 0; i < n; i++)
          {
           Delay1Ms();                                         //毫秒延时
          }
}

//*******************************************************************
//函数名字; PortInit();                                          
//输入参数; 无                                       
//输出参数; 无                                                   
//功能描述; 脚位设置                                             
//建造日期;2008年08月09日                                          
//*******************************************************************
void PortInit(void)
{
     PORTB = 0xff;                                         //控制输出
     DDRB  = 0x07;  
         
         PORTC = 0x6f;                                         //上拉输入
         DDRC  = 0x08;
         
         PORTD = 0x00;                                         //控制输出
         DDRD  = 0xff;
}

//*******************************************************************
//函数名字; DischargeOut();
//输入参数; 无
//输出参数; 无
//功能描述; 电容放电
//建造日期; 2008年08月09日
//*******************************************************************
void DischargeOut(void)
{
     PORTC |= (1 << 4);                                    //置高电平
     DDRC  |= (1 << 4);                                    //开始放电

     DelayNms(1);                                          //放电时间
}
  
//*******************************************************************
//函数名字; SurveyRc();
//输入参数; 无
//输出参数; 无
//功能描述; 测量充电时间
//建造日期; 2008年08月09日
//*******************************************************************
unsigned char SurveyRc(void)
{
     unsigned char time = 0;                               //时间计数
     
         DischargeOut();                                       //电容放电
          
         DDRC  &= ~(1 << 4);                                   //输入设置
         PORTC &= ~(1 << 4);                                   //高阻输入
          
         while (PINC & (1 << 4))                               //充电计时
          {
           time++;                                             //计时增加
       asm("nop");                                         //精确10us
                 
           if (time > 250) break;                              //最大限时                  
      }
   
     return time;                                          //返回时间
}

//*******************************************************************
//函数名字; DataAdd(*buffer, size);
//输入参数; 缓冲区首址, 大小
//输出参数; 数据总和
//功能描述; 缓冲区所有数据相加
//建造日期; 2008年08月09日
//*******************************************************************   
unsigned int DataAdd(unsigned char *buffer, unsigned char size)
{
     unsigned int add;
     unsigned char i;
     
     add = 0;                                              //数据清零

     for (i = 0; i < size; i++)
      {
       add += buffer;                                   //数据相加
      }
  
     return add;                                           //返回总和                  
}

//*******************************************************************
//函数名字; DataMax(*buffer, size);
//输入参数; 缓冲区首址, 大小
//输出参数; 数据最大值
//功能描述; 选出缓冲区最大值
//建造日期; 2008年08月09日
//*******************************************************************
unsigned char DataMax(unsigned char *buffer, unsigned char size)
{
     unsigned char max, i;
     
     max = buffer[0];                                      //假设最大
     
     for (i = 1; i < size; i++)
      {
       if (max < buffer) max = buffer;               //对比最大
      }
  
     return max;                                           //最大数据                  
}

//*******************************************************************
//函数名字; DataMin(*buffer, size);
//输入参数; 缓冲区首址, 大小
//输出参数; 数据最小大值
//功能描述; 选出缓冲区最小值
//建造日期; 2008年08月09日
//*******************************************************************
unsigned char DataMin(unsigned char *buffer, unsigned char size)
{
     unsigned char min, i;
     
     min = buffer[0];                                      //假设最小
     
     for (i = 1; i < size; i++)
      {
       if (min > buffer) min = buffer;               //对比最小
      }
  
     return min;                                           //最小数据
}

//*******************************************************************
//函数名字; DataEqually(idend, isor);
//输入参数; 被除数,除数
//输出参数; 平均值
//功能描述; 数据平均
//建造日期; 2008年08月09日
//*******************************************************************
unsigned char DataEqually(unsigned int idend, unsigned char isor)
{
     return (idend / isor);                                //数据平均
}

//*******************************************************************
//函数名字; FilterData();
//输入参数; 无
//输出参数; 平均值
//功能描述; 取样平均滤波
//建造日期; 2008年08月09日
//*******************************************************************
unsigned char FilterData(void)
{
     unsigned char i, max, min, data[10];
     unsigned int sum;
  
     for (i = 0; i < 10; i++)
          {
           data = SurveyRc();                               //收集数据
      }
          
     sum = DataAdd(data, 10);                              //数据相加
     max = DataMax(data, 10);                              //取最大值
     min = DataMin(data, 10);                              //取最小值

     return (DataEqually((sum - max - min), 8));           //取平均值                 
}   
  
//*******************************************************************
//函数名字; main();
//输入参数; 无
//输出参数; 无
//功能描述; 主程序
//建造日期; 2008年08月09日
//*******************************************************************
void main(void)
{   
     unsigned char temp = 0;                               //上电校准
         unsigned char time = 0;
          
         PortInit();                                           //端口设置
         temp = FilterData();                                  //读取误差
                                                  
     while (1)
      {
           time = FilterData();                                //读取时间
          
           if (time > temp)
            {
             Led1On;                                           //输出指示
                }
                 
           else
            {
             Led1Off;                                          //关闭指示
                }
      }   
}

出0入0汤圆

 楼主| 发表于 2008-8-13 11:20:48 | 显示全部楼层
点击此处下载 ourdev_376373.rar(文件大小:257K) (原文件名:电容感应.rar)

出0入0汤圆

 楼主| 发表于 2008-8-13 11:22:05 | 显示全部楼层
单片机AVR M8 使用内部1M RC振荡.

现可以感应到2MM厚的洞洞板,由于没有防止临界状态处理程序.在临界状态LED输出闪烁...

出0入0汤圆

发表于 2008-8-13 11:34:25 | 显示全部楼层
太牛了,不知道可靠性和一致性怎么样

出0入0汤圆

发表于 2008-8-13 11:36:14 | 显示全部楼层
需要考虑感应灵敏度可调才有实际应用价值

出0入4汤圆

发表于 2008-8-13 11:37:46 | 显示全部楼层
一至性好的话,做键盘很有前途的

出0入0汤圆

发表于 2008-8-13 11:38:59 | 显示全部楼层
不错的尝试,我们公司有用到PSOC来做过触摸按键

出0入0汤圆

 楼主| 发表于 2008-8-13 11:40:49 | 显示全部楼层
现在只是试验成功,要想产品上用.还要经过一些防误处理,自动校准处理,灵敏调节处理,临界状态处理.....看看大家在这方面处理的算法有什么好的建议或方法.....请多多指教...

出0入0汤圆

发表于 2008-8-13 11:53:25 | 显示全部楼层
很容易玩坏IO口……而且完全无法抵御蓄意破坏,过脉冲群测试更是做梦都不要想……

出0入0汤圆

 楼主| 发表于 2008-8-13 12:08:28 | 显示全部楼层
楼上的,如果有新的技术出现都不去尝试一下.怎么去发现它的缺点并改进它,这样技术怎么会有进步呢.
现在AVR原产也正在试验这种新技术,PIC产家也在设想在为单片机加入这一功能.....很有可能有些型号的单片机以后就有这种模块功能....很有可能会取代按键开关.....这东东很有前途.......

出0入0汤圆

发表于 2008-8-13 12:11:38 | 显示全部楼层
标记,以后再仔细看。

出0入0汤圆

发表于 2008-8-13 12:14:27 | 显示全部楼层
是的,也许很多IC厂家正在尝试把这个功能加入MCU,但是人家是建立在给IO口内部电路额外增加足够的保护功能的前提之上的

而你的电路……不得不说,绝对是缺点大于优点,实际产品中如果敢这么用,保证三个月后要求退货的顾客踏破你公司的门槛……

但如果你要在自己的电路上也增加足够的保护功能……我可以确定那东西做出来不会比普通薄膜开关便宜的……

出0入0汤圆

发表于 2008-8-13 12:35:31 | 显示全部楼层
标记,谢谢

出0入0汤圆

发表于 2008-8-13 12:38:04 | 显示全部楼层
顶!

出0入0汤圆

发表于 2008-8-13 13:30:40 | 显示全部楼层
不错,偶试试~

出0入0汤圆

发表于 2008-8-13 13:42:22 | 显示全部楼层
atmel和quantum合作,电容触控IC已经量产了,有单键,两键...24键,48键等.用得就是AVR

出0入0汤圆

发表于 2008-8-13 13:44:09 | 显示全部楼层
不得不说现在这些现成的东西很多了呢。ATMEL,CYPRESS都有,国内好像也有现成产品了。

出0入0汤圆

发表于 2008-8-13 13:46:26 | 显示全部楼层
liuxj 军哥:

atmel和quantum合作,电容触控IC已经量产了,有单键,两键...24键,48键等.用得就是AVR  


应该说是ATMEL把QUANTUM收购了。CYPRESS的CAPSENSE也比较有特色,感兴趣可以玩玩,做开关不错。

出0入0汤圆

发表于 2008-8-13 13:46:46 | 显示全部楼层
学习。

出0入0汤圆

 楼主| 发表于 2008-8-13 18:13:14 | 显示全部楼层
最新改进型电容感应电图:


(原文件名:改进型电容感应.jpg)

出0入0汤圆

 楼主| 发表于 2008-8-13 18:17:50 | 显示全部楼层
最新改进型源源程序代码....加入防误处理....实现按键功能....每按一下,LED输出指示取反一次...

AVR M8 内部1MRC振荡

//引用文件***********************************************************
#include<iom8v.h>
#include<macros.h>

//输出定义***********************************************************
#define       Led1On         PORTD |= (1 << 7)             //输出指示
#define       Led1Off        PORTD &= ~(1 << 7)            //关闭指示

//*******************************************************************
//函数名字; Delay1Ms();
//输入参数; 无  
//输出参数; 无
//功能描述; 延时1 毫秒
//建造日期; 2008年08月09日
//*******************************************************************
void Delay1Ms(void)                                        //毫秒延时
{   
     unsigned int i;
         
     for (i = 0; i < 140; i++);                            //
}

//*******************************************************************
//函数名字; DelayNms(n);
//输入参数; 延时周期参数据
//输出参数; 无
//功能描述; 延时程序
//建造日期; 2008年08月09日
//*******************************************************************
void DelayNms(unsigned int n)                              //延时周期
{
     unsigned int i;
         
     for (i = 0; i < n; i++)
          {
           Delay1Ms();                                         //毫秒延时
          }
}

//*******************************************************************
//函数名字; PortInit();                                          
//输入参数; 无                                       
//输出参数; 无                                                   
//功能描述; 脚位设置                                             
//建造日期;2008年08月09日                                          
//*******************************************************************
void PortInit(void)
{
     PORTB = 0xff;                                         //控制输出
     DDRB  = 0x07;  
         
         PORTC = 0x6f;                                         //上拉输入
         DDRC  = 0x08;
         
         PORTD = 0x00;                                         //控制输出
         DDRD  = 0xff;
}

//*******************************************************************
//函数名字; DischargeOut();
//输入参数; 无
//输出参数; 无
//功能描述; 电容放电
//建造日期; 2008年08月09日
//*******************************************************************
void DischargeOut(void)
{
     PORTC |= (1 << 4);                                    //置高电平
     DDRC  |= (1 << 4);                                    //开始放电

     DelayNms(1);                                          //放电时间
}
  
//*******************************************************************
//函数名字; SurveyRc();
//输入参数; 无
//输出参数; 无
//功能描述; 测量充电时间
//建造日期; 2008年08月09日
//*******************************************************************
unsigned char SurveyRc(void)
{
     unsigned char time = 0;                               //时间计数
     
         DischargeOut();                                       //电容放电
          
         DDRC  &= ~(1 << 4);                                   //输入设置
         PORTC &= ~(1 << 4);                                   //高阻输入
          
         while (PINC & (1 << 4))                               //充电计时
          {
           time++;                                             //计时增加
           asm("nop");                                         //精确10us
                 
           if (time > 250) break;                              //最大限时                  
      }
   
     return time;                                          //返回时间
}

//*******************************************************************
//函数名字; DataAdd(*buffer, size);
//输入参数; 缓冲区首址, 大小
//输出参数; 数据总和
//功能描述; 缓冲区所有数据相加
//建造日期; 2008年08月09日
//*******************************************************************   
unsigned int DataAdd(unsigned char *buffer, unsigned char size)
{
     unsigned int add;
     unsigned char i;
     
     add = 0;                                              //数据清零

     for (i = 0; i < size; i++)
      {
       add += buffer;                                   //数据相加
      }
  
     return add;                                           //返回总和                  
}

//*******************************************************************
//函数名字; DataMax(*buffer, size);
//输入参数; 缓冲区首址, 大小
//输出参数; 数据最大值
//功能描述; 选出缓冲区最大值
//建造日期; 2008年08月09日
//*******************************************************************
unsigned char DataMax(unsigned char *buffer, unsigned char size)
{
     unsigned char max, i;
     
     max = buffer[0];                                      //假设最大
     
     for (i = 1; i < size; i++)
      {
       if (max < buffer) max = buffer;               //对比最大
      }
  
     return max;                                           //最大数据                  
}

//*******************************************************************
//函数名字; DataMin(*buffer, size);
//输入参数; 缓冲区首址, 大小
//输出参数; 数据最小大值
//功能描述; 选出缓冲区最小值
//建造日期; 2008年08月09日
//*******************************************************************
unsigned char DataMin(unsigned char *buffer, unsigned char size)
{
     unsigned char min, i;
     
     min = buffer[0];                                      //假设最小
     
     for (i = 1; i < size; i++)
      {
       if (min > buffer) min = buffer;               //对比最小
      }
  
     return min;                                           //最小数据
}

//*******************************************************************
//函数名字; DataEqually(idend, isor);
//输入参数; 被除数,除数
//输出参数; 平均值
//功能描述; 数据平均
//建造日期; 2008年08月09日
//*******************************************************************
unsigned char DataEqually(unsigned int idend, unsigned char isor)
{
     return (idend / isor);                                //数据平均
}

//*******************************************************************
//函数名字; FilterData();
//输入参数; 无
//输出参数; 平均值
//功能描述; 取样平均滤波
//建造日期; 2008年08月09日
//*******************************************************************
unsigned char FilterData(void)
{
     unsigned char i, max, min, data[5];
     unsigned int sum;
  
     for (i = 0; i < 5; i++)
          {
           data = SurveyRc();                               //收集数据
      }
          
     sum = DataAdd(data, 5);                               //数据相加
     max = DataMax(data, 5);                               //取最大值
     min = DataMin(data, 5);                               //取最小值

     return (DataEqually((sum - max - min), 3));           //取平均值                 
}   
  
//*******************************************************************
//函数名字; KeyCheck(adjust);
//输入参数; 校准数据
//输出参数; 有效标置
//功能描述; 检测按键是否有效
//建造日期; 2008年08月09日
//*******************************************************************  
unsigned char KeyCheck(unsigned char adjust)
{
         if (FilterData() > adjust)
          {
           Led1On;                                             //
           return 1;                                           //有效按键
          }
         
         else
          {
           Led1Off;                                            //
           return 0;                                           //无效按键
          }
}
  
//*******************************************************************
//函数名字; KeyState(adjust);
//输入参数; 校准数据
//输出参数; 无
//功能描述; 按键处理
//建造日期; 2008年08月09日
//*******************************************************************
void KeyState(unsigned char adjust)
{
     static unsigned char count = 0;                       //限时记数
         static unsigned char valid = 0;                       //有效标志
         static unsigned char reach = 0;                       //长按标志
         
         if (valid == 1)                                       //是否有效
          {
           if (KeyCheck(adjust))                               //扫描按键
            {
                 if (reach == 0)                                   //长按无效
                  {
                   if (++count > 5)                                //防误处理
                    {
                         reach = 1;                                    //长按置位
                         PORTD ^= (1 << 6);                            //取反输出
                        }
                  }
                }
               
           else
            {
                 valid = 0;                                        //有效清零
                 reach = 0;                                        //长按清零
                 count = 0;                                        //记数清零
                }       
          }
         
         else if (KeyCheck(adjust)) valid = 1;                 //有效置位   
}
   
//*******************************************************************
//函数名字; main();
//输入参数; 无
//输出参数; 无
//功能描述; 主程序
//建造日期; 2008年08月09日
//*******************************************************************
void main(void)
{   
     unsigned char adjust;                                 //上电校准
          
         PortInit();                                           //端口设置
         adjust = FilterData();                                //读取误差
                                                  
     while (1)
      {
           KeyState(adjust);                                   //按键处理
           DelayNms(5);                                        //定时扫描
      }   
}

//*******************************************************************
//函数名字;
//输入参数;
//输出参数;
//功能描述;
//建造日期; 2008年08月09日
//*******************************************************************

出0入0汤圆

 楼主| 发表于 2008-8-13 18:18:41 | 显示全部楼层
点击此处下载 ourdev_377313.rar(文件大小:28K) (原文件名:电容触摸感应.rar)

出0入0汤圆

发表于 2008-8-13 18:30:47 | 显示全部楼层
强,不顶不行。

理论联系实际

出0入0汤圆

发表于 2008-8-13 18:45:51 | 显示全部楼层
还是有价值的

出0入0汤圆

发表于 2008-8-13 18:56:27 | 显示全部楼层
记号

出0入12汤圆

发表于 2008-8-13 19:02:31 | 显示全部楼层
估计这个也是普通单片机实现的吧,有可能是类似em78p156的单片机。

tf745 (原文件名:tt.jpg)

出0入0汤圆

发表于 2008-8-13 20:34:18 | 显示全部楼层
4148一样过不了脉冲群……

用P6KE系列还差不多……

出0入0汤圆

发表于 2008-8-13 20:55:34 | 显示全部楼层
好久没来这逛了,出来透透气

出0入0汤圆

 楼主| 发表于 2008-8-13 21:11:13 | 显示全部楼层
刚刚又对程序进行了优化.....改进电容放电时间.....按键按下.....按键释放....都加入防止处理.....实用性大大增强....

现在就差温度漂移及其它因素导致的漂移.....不知哪位高手有这方面的算法...

出0入0汤圆

发表于 2008-8-13 22:36:09 | 显示全部楼层
顶一下,楼主的触摸距离有多少?以前做过实验,采用1M电阻上拉端口方式,距离1cm左右,干扰比较大,触点和端口不能接太长的线。有时间试试楼主的电路。

出0入0汤圆

发表于 2008-8-13 23:32:50 | 显示全部楼层
做个记号

出0入0汤圆

发表于 2008-8-13 23:34:44 | 显示全部楼层
新鲜,顶下

出0入0汤圆

发表于 2008-8-13 23:39:26 | 显示全部楼层
帮楼主改一下程序注释:

---------------
//*******************************************************************
//函数名字; DischargeOut();
//输入参数; 无
//输出参数; 无
//功能描述; 电容放电
//建造日期; 2008年08月09日  
//*******************************************************************
void DischargeOut(void)
{
     PORTC |= (1 << 4);                                    //置高电平
     DDRC  |= (1 << 4);                                    //开始放电   改为:开始充电

     DelayNms(1);                                          //放电时间   改为:充电延时
}
   
//*******************************************************************
//函数名字; SurveyRc();
//输入参数; 无
//输出参数; 无
//功能描述; 测量充电时间
//建造日期; 2008年08月09日  
//*******************************************************************  
unsigned char SurveyRc(void)
{
     unsigned char time = 0;                               //时间计数
      
         DischargeOut();                                       //电容放电   改为:对电容充电
            
         DDRC  &= ~(1 << 4);                                   //输入设置   
         PORTC &= ~(1 << 4);                                   //高阻输入   断开内部上拉电阻
            
         while (PINC & (1 << 4))                               //充电计时   改为:放电计时,判断PC_4从1到0的时间
          {
           time++;                                             //计时增加
           asm("nop");                                         //精确10us  
                  
           if (time > 250) break;                              //最大限时                  
      }
     
     return time;                                          //返回时间  
}

出0入0汤圆

发表于 2008-8-14 08:10:50 | 显示全部楼层
弱弱的问:所谓的人体电容的充电回路是怎样的?
如此高的输入阻抗,EMC恐怕很难过的吧。
有空试试他的抗干扰能力

出0入0汤圆

发表于 2008-8-14 08:37:21 | 显示全部楼层
不錯

出0入0汤圆

发表于 2008-8-14 09:06:58 | 显示全部楼层
不错,学习了

出0入0汤圆

发表于 2008-8-14 09:17:04 | 显示全部楼层
一样做的话,可能用STC好一点,因为STC的I/O脚是自带ESD保护的。

出0入0汤圆

 楼主| 发表于 2008-8-14 09:54:49 | 显示全部楼层
最最新版软件......

对按键按下,释放的时间进行优化,响应时间更快,在试验时没有出现误动作.......在连续2S没有检测到按键,就自动更新校准参数...实现自动跟踪漂移...在宽电压范围内都可正常工作....欢迎大家进行公测试验.....提供改进建议...

下一步要实现灵敏度可调节...


//引用文件***********************************************************
#include<iom8v.h>
#include<macros.h>

//公用变量***********************************************************
     unsigned char follow;                                 //跟踪校准
         
//*******************************************************************
//函数名字; Delay1Ms();
//输入参数; 无  
//输出参数; 无
//功能描述; 延时1 毫秒
//建造日期; 2008年08月14日
//*******************************************************************
void Delay1Ms(void)                                        //毫秒延时
{   
     unsigned int i;
         
     for (i = 0; i < 140; i++);                            //
}

//*******************************************************************
//函数名字; DelayNms(n);
//输入参数; 延时周期参数据
//输出参数; 无
//功能描述; 延时程序
//建造日期; 2008年08月14日
//*******************************************************************
void DelayNms(unsigned int n)                              //延时周期
{
     unsigned int i;
         
     for (i = 0; i < n; i++)
          {
           Delay1Ms();                                         //毫秒延时
          }
}

//*******************************************************************
//函数名字; PortInit();                                          
//输入参数; 无                                       
//输出参数; 无                                                   
//功能描述; 脚位设置                                             
//建造日期;2008年08月14日                                          
//*******************************************************************
void PortInit(void)
{
     PORTB = 0xff;                                         //控制输出
     DDRB  = 0x07;  
         
         PORTC = 0x6f;                                         //上拉输入
         DDRC  = 0x08;
         
         PORTD = 0x00;                                         //控制输出
         DDRD  = 0xff;
}

//*******************************************************************
//函数名字; DischargeOut();
//输入参数; 无
//输出参数; 无
//功能描述; 电容放电
//建造日期; 2008年08月14日
//*******************************************************************
void DischargeOut(void)
{
     PORTC |= (1 << 4);                                    //置高电平
     DDRC  |= (1 << 4);                                    //开始放电

     asm("nop");                                           //放电时间
         asm("nop");                                           //精确 5uS
         asm("nop");
         asm("nop");
         asm("nop");
}
  
//*******************************************************************
//函数名字; SurveyRc();
//输入参数; 无
//输出参数; 无
//功能描述; 测量充电时间
//建造日期; 2008年08月14日
//*******************************************************************
unsigned char SurveyRc(void)
{
     unsigned char time = 0;                               //时间计数
     
         DischargeOut();                                       //电容放电
          
         DDRC  &= ~(1 << 4);                                   //输入设置
         PORTC &= ~(1 << 4);                                   //高阻输入
          
         while (PINC & (1 << 4))                               //充电计时
          {
           time++;                                             //计时增加
           asm("nop");                                         //精确10uS
                 
           if (time > 250) break;                              //最大限时                  
      }
   
     return time;                                          //返回时间
}

//*******************************************************************
//函数名字; DataAdd(*buffer, size);
//输入参数; 缓冲区首址, 大小
//输出参数; 数据总和
//功能描述; 缓冲区所有数据相加
//建造日期; 2008年08月14日
//*******************************************************************   
unsigned int DataAdd(unsigned char *buffer, unsigned char size)
{
     unsigned int add;
     unsigned char i;
     
     add = 0;                                              //数据清零

     for (i = 0; i < size; i++)
      {
       add += buffer;                                   //数据相加
      }
  
     return add;                                           //返回总和                  
}

//*******************************************************************
//函数名字; DataMax(*buffer, size);
//输入参数; 缓冲区首址, 大小
//输出参数; 数据最大值
//功能描述; 选出缓冲区最大值
//建造日期; 2008年08月14日
//*******************************************************************
unsigned char DataMax(unsigned char *buffer, unsigned char size)
{
     unsigned char max, i;
     
     max = buffer[0];                                      //假设最大
     
     for (i = 1; i < size; i++)
      {
       if (max < buffer) max = buffer;               //对比最大
      }
  
     return max;                                           //最大数据                  
}

//*******************************************************************
//函数名字; DataMin(*buffer, size);
//输入参数; 缓冲区首址, 大小
//输出参数; 数据最小大值
//功能描述; 选出缓冲区最小值
//建造日期; 2008年08月14日
//*******************************************************************
unsigned char DataMin(unsigned char *buffer, unsigned char size)
{
     unsigned char min, i;
     
     min = buffer[0];                                      //假设最小
     
     for (i = 1; i < size; i++)
      {
       if (min > buffer) min = buffer;               //对比最小
      }
  
     return min;                                           //最小数据
}

//*******************************************************************
//函数名字; DataEqually(idend, isor);
//输入参数; 被除数,除数
//输出参数; 平均值
//功能描述; 数据平均
//建造日期; 2008年08月14日
//*******************************************************************
unsigned char DataEqually(unsigned int idend, unsigned char isor)
{
     return (idend / isor);                                //数据平均
}

//*******************************************************************
//函数名字; FilterData();
//输入参数; 无
//输出参数; 平均值
//功能描述; 取样平均滤波
//建造日期; 2008年08月14日
//*******************************************************************
unsigned char FilterData(void)
{
     unsigned char i, max, min, data[5];
     unsigned int sum;
  
     for (i = 0; i < 5; i++)
          {
           data = SurveyRc();                               //收集数据
      }
          
     sum = DataAdd(data, 5);                               //数据相加
     max = DataMax(data, 5);                               //取最大值
     min = DataMin(data, 5);                               //取最小值

     return (DataEqually((sum - max - min), 3));           //取平均值                 
}   
  
//*******************************************************************
//函数名字; KeyState();
//输入参数; 无
//输出参数; 无
//功能描述; 按键处理
//建造日期; 2008年08月14日
//*******************************************************************
void KeyState(void)
{
     static unsigned char release = 0;                     //释放记数
     static unsigned char count = 0;                       //按下记数
         static unsigned char valid = 0;                       //有效标志
         static unsigned char reach = 0;                       //长按标志
         static unsigned int  trail = 0;                       //跟踪记数
         
         if (valid == 1)                                       //是否有效
          {
           if (FilterData() > follow)                          //按键按下
            {
                 release = 0;                                      //释放清零
                 
                 if (reach == 0)                                   //长按无效
                  {
                   if (++count > 2)                                //防误处理
                    {
                         reach = 1;                                    //长按置位
                         PORTD ^= (1 << 7);                            //取反输出
                        }
                  }
                }
               
           else if (++release > 2)                             //释放记数
                {
                 valid = 0;                                        //有效清零
                 reach = 0;                                        //长按清零
                 count = 0;                                        //记数清零
                }       
          }
         
         else
          {
           if (FilterData() > follow)
            {
                 trail = 0;                                        //数据清零
             valid = 1;                                        //有效置位   
            }
          
           else
            {
             if (++trail > 400)                                //漂移跟踪
                  {
                   follow = FilterData();                          //更新误差
                   PORTD ^= (1 <<6);                               //跟踪指示
                   trail = 0;                                      //数据清零
                  }
            }
          }   
}
   
//*******************************************************************
//函数名字; main();
//输入参数; 无
//输出参数; 无
//功能描述; 主程序
//建造日期; 2008年08月14日
//*******************************************************************
void main(void)
{              
         PortInit();                                           //端口设置
         follow = FilterData();                                //读取误差
                                                  
     while (1)
      {
           KeyState();                                         //按键处理
           DelayNms(5);                                        //定时扫描
      }   
}

//*******************************************************************
//函数名字;
//输入参数;
//输出参数;
//功能描述;
//建造日期; 2008年08月14日
//*******************************************************************

出0入0汤圆

发表于 2008-8-14 09:56:53 | 显示全部楼层
C8051F系列的某一款 内置了触摸按键接口的 可以查一下

出0入0汤圆

 楼主| 发表于 2008-8-14 10:13:43 | 显示全部楼层
阿莫....应该给裤子了吧........呵呵.....

出0入0汤圆

发表于 2008-8-14 10:19:20 | 显示全部楼层
楼主是人才,应该给条内裤穿

出0入0汤圆

发表于 2008-8-14 11:46:43 | 显示全部楼层
cy也有的 用过 不错

出0入0汤圆

 楼主| 发表于 2008-8-14 12:16:52 | 显示全部楼层
该程序成功移值到PIC16F84A 上成功运行.....效果更佳.....

http://www.ouravr.com/bbs/bbs_content.jsp?bbs_sn=1400964&bbs_page_no=1&bbs_id=1028


(原文件名:电容触摸感应.jpg)


(原文件名:电容触摸感应1.JPG)


(原文件名:电容触摸感应2.JPG)


(原文件名:电容触摸感应3.JPG)

出0入0汤圆

发表于 2008-8-14 13:10:06 | 显示全部楼层
记号

出0入0汤圆

发表于 2008-8-14 13:26:00 | 显示全部楼层
楼主的代码注释还是错误的,看来楼主还是没有完全弄明白它的原理,采用上拉是检测结电容+人体电容的充电时间,采用下拉是检测结电容+人体电容的放电时间,这样的代码要是给“酷”那就是对初学者的误导。

出0入0汤圆

发表于 2008-8-14 13:39:28 | 显示全部楼层
ti的430也有类似的演示板

出0入0汤圆

 楼主| 发表于 2008-8-14 13:58:27 | 显示全部楼层
原理是检测上拉结电容和人体电容......要是理论分析不对,又怎么做得来出呢........

IO输出高电平,短路电容.....这不是放电吗......那又是什么.......

     PORTC |= (1 << 4);                                    //置高电平
     DDRC  |= (1 << 4);                                    //开始放电

IO高阻输入...电容充电...程短路现象...这时检测高电平....并记录充电时间.....

     DDRC  &= ~(1 << 4);                                   //输入设置
     PORTC &= ~(1 << 4);                                   //高阻输入






(原文件名:仿真模型1.JPG)


(原文件名:仿真模型2.JPG)

出0入0汤圆

发表于 2008-8-14 14:31:16 | 显示全部楼层
感应距离可以到多厚?
比如,5毫米的玻璃板可以吗?

出0入0汤圆

发表于 2008-8-14 14:39:38 | 显示全部楼层
哈哈,关于充电还是放电的问题,其实是一样的,LZ的电容参考点是VCC,而44楼的参考点是GND,还是我问的问题,充电或放电的回路是什么呢?
如果没有回路,怎么充呢?比如象44楼那样说的话,假使GND是接大地的,那么就有了充电回路。LZ的意思是人体是等同接到VCC上的,于是有了充电回路,那么LZ的电路中到底是哪个接的大地呢?如果没用接,而是高度绝缘的,那么就是通过空气的,如果空气够大的湿度的话也可以理解。如果用开关电源供电是一个说法,用变压器又是一个,用初次级隔离骨架作的变压器又是一个。

出0入0汤圆

 楼主| 发表于 2008-8-14 14:44:16 | 显示全部楼层
刚才试了一下,2MM PCB + 5MM 亚克历有机玻璃,可以感应得到...只是灵敏度低了一点点...可以通过加大焊盘的面积来提高灵敏度....

出0入0汤圆

 楼主| 发表于 2008-8-14 15:02:02 | 显示全部楼层
【48楼】 xiaobendan 仲跻东

够细心...对于回路问题.....

1,开关电源供电
   我在刚开始试验时用的是开关电源供电,电路通过在线下载编程器,经过USB 连到电脑....刚开始试的时候..灵敏度比较高...当断开下载线时..灵敏度下降...LED不停闪动.....按键失效....经过分析..是电脑的开关电源通过一个小电容联到了地线....此时不是感应不到人体,只是回路发生了变化..灵敏度变弱了..经过调整软件,加入自动跟踪漂移自动校准后...此问题现以解决...

2,对于次级隔离骨架作的变压器
  昨晚我用可调电源进行了试验....该电源用的是次级隔离骨架作的变压器,进行了测试...可以正常工作.....
  对于这个问题今晚我再用 1:1 的公频变压器试验....看看有什么结果...

出0入0汤圆

 楼主| 发表于 2008-8-14 15:05:00 | 显示全部楼层
所以我说..欢迎大家进行公测试验.....提供改进建议...

出0入0汤圆

发表于 2008-8-14 16:06:40 | 显示全部楼层
至少是一种思路,虽然离实用还有距离,也很支持楼主

出0入0汤圆

发表于 2008-8-14 22:40:13 | 显示全部楼层
看了楼主的仿真电路我也明白为什么楼主能做出来了,完全是歪打正着,居然把代替人体的电容接到VCC上。

请参考人体等效模型:ourdev_379314.pdf(文件大小:86K) (原文件名:armok01123934.pdf)

出0入0汤圆

发表于 2008-8-14 22:46:01 | 显示全部楼层
ivws
用电池供电能正常工作吗?

出0入0汤圆

发表于 2008-8-14 23:10:54 | 显示全部楼层
楼主这个电路应该会受到比较严重的噪声干扰,因为单片机IO做输入后输入阻抗很高,噪声会造成很大的影响
cypress的csr技术也有类似的毛病

出0入0汤圆

发表于 2008-8-15 08:19:23 | 显示全部楼层
54楼的更狠啊,哈哈!不过我的三星NV10的按键是感应的,但是好像是接触上才能感应到的,而且他的外壳是金属的,使用时又是用手拿着的,所以可能是简单的电路作的。
楼主是否测量过这个输入脚上的信号到底是什么样子的?

出0入0汤圆

发表于 2008-8-15 08:30:31 | 显示全部楼层
好。

出0入0汤圆

 楼主| 发表于 2008-8-15 09:04:01 | 显示全部楼层
昨晚上有 1:1 隔离变压器测试....用AVR 方案的可以用...不过漂移较大....有时自激..

用PIC方案的比较稳定...这几天要提高程序自适应算法...加强自动跟踪漂移...

出0入0汤圆

发表于 2008-8-15 23:46:35 | 显示全部楼层
lz很有钻研精神,佩服。。。

出0入0汤圆

发表于 2008-8-16 07:23:46 | 显示全部楼层
看来真的用电池做的话是不行的,不过没用必要这样。因为有些家电就是要用变压器的,反正是非接触的,即使是直连到220上的也没有问题。不过还是要考虑用户的鞋子比较好,鞋底比较厚,还有地板的问题。

出0入0汤圆

发表于 2008-8-16 08:05:27 | 显示全部楼层
所以说没有振荡源的电容非接触摸是不可靠的
其实用2个三极管就可以搞定电容非接触摸,即使用电池供电也一样稳定可靠

出0入0汤圆

发表于 2008-8-16 08:06:47 | 显示全部楼层
piaoguo

出0入0汤圆

发表于 2009-8-5 15:29:16 | 显示全部楼层
AVR-BIN 电子油条,你能给个电路看看吗?

出0入0汤圆

发表于 2009-8-5 16:42:37 | 显示全部楼层
不錯

出0入0汤圆

发表于 2009-8-5 17:10:32 | 显示全部楼层
Mark

出0入0汤圆

发表于 2009-8-5 19:29:25 | 显示全部楼层
Mark

出0入0汤圆

发表于 2009-8-5 19:52:53 | 显示全部楼层
好贴,MARK!!!

出0入0汤圆

发表于 2009-8-5 19:56:10 | 显示全部楼层
不错,学习一下

出0入0汤圆

发表于 2009-8-5 20:51:21 | 显示全部楼层
好贴

出0入0汤圆

发表于 2009-8-5 21:10:09 | 显示全部楼层
MARK!

出0入0汤圆

发表于 2009-8-6 11:05:54 | 显示全部楼层
记号

出0入0汤圆

发表于 2009-8-6 11:17:32 | 显示全部楼层

出0入0汤圆

发表于 2009-8-6 11:32:28 | 显示全部楼层
学习学习,谢谢分享

出0入0汤圆

发表于 2009-8-6 12:02:37 | 显示全部楼层
之前有朋友成功试过,只加两个电阻就可以很稳定做到。并量产。能在凌阳。51中实现

出0入0汤圆

发表于 2009-8-6 13:29:07 | 显示全部楼层
记号

出0入0汤圆

发表于 2009-8-6 13:39:48 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-8-6 15:19:49 | 显示全部楼层
之前听过PIC技术工程师的讲座,PIC单片机也是这样的原理

出0入0汤圆

发表于 2009-9-2 12:27:08 | 显示全部楼层
mark thanks

出0入0汤圆

发表于 2009-9-2 14:36:48 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-9-2 15:04:39 | 显示全部楼层
我也测试过了,确实可行,灵敏度都可以,就是抗干扰不行

出0入0汤圆

发表于 2009-9-2 23:52:10 | 显示全部楼层
是啊, 原理是可行的, 但这个方案, 抗干扰不行, 要提高抗干扰, 得换个方案.

出0入0汤圆

发表于 2009-9-2 23:57:59 | 显示全部楼层
不过, 楼主做了很有益的尝试, 能公布试验结果, 鼓励一下

出0入0汤圆

发表于 2009-10-4 01:31:09 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-10-4 08:14:58 | 显示全部楼层
81楼说一下另外的方案吧

出0入0汤圆

发表于 2009-10-5 14:40:41 | 显示全部楼层
期待有更好的方案

出0入0汤圆

发表于 2009-10-5 15:00:47 | 显示全部楼层
用电池还能感应不?

出0入0汤圆

发表于 2010-4-12 16:12:41 | 显示全部楼层
有动手实践才有说话权,支持LZ,支持动手钻研

出0入0汤圆

发表于 2010-4-30 22:47:38 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-4-30 23:10:20 | 显示全部楼层
标注,等到空点再细看!

出0入0汤圆

发表于 2010-5-6 13:34:08 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-5-9 12:45:58 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-6-17 17:33:33 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-6-17 19:04:51 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-6-17 19:29:09 | 显示全部楼层
做的不错!顶!

出0入0汤圆

发表于 2010-6-17 19:42:02 | 显示全部楼层
记号

出0入0汤圆

发表于 2010-6-17 19:51:14 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-6-17 20:31:26 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-6-17 22:26:27 | 显示全部楼层
mark~~~

出0入0汤圆

发表于 2010-6-19 06:09:16 | 显示全部楼层
mark
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-29 14:55

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表