ivws 发表于 2008-8-13 11:18:43

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

说到电容非接触摸技术相信大家并不陌生......

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

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

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

那就费话少说.....直接上图...上源代码.....
http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_376353.jpg
(原文件名:电容感应1.jpg)

http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_376354.JPG
(原文件名:电容感应2.JPG)

http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_376355.JPG
(原文件名:电容感应3.JPG)

http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_376356.JPG
(原文件名:电容感应4.JPG)

http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_376357.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;                                    //假设最大
   
   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;                                    //假设最小
   
   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;
   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;                                          //关闭指示
                }
      }   
}

ivws 发表于 2008-8-13 11:20:48

点击此处下载 ourdev_376373.rar(文件大小:257K) (原文件名:电容感应.rar)

ivws 发表于 2008-8-13 11:22:05

单片机AVR M8 使用内部1M RC振荡.

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

AVR-BIN 发表于 2008-8-13 11:34:25

太牛了,不知道可靠性和一致性怎么样

AVR-BIN 发表于 2008-8-13 11:36:14

需要考虑感应灵敏度可调才有实际应用价值

chendaon 发表于 2008-8-13 11:37:46

一至性好的话,做键盘很有前途的

jclhp 发表于 2008-8-13 11:38:59

不错的尝试,我们公司有用到PSOC来做过触摸按键

ivws 发表于 2008-8-13 11:40:49

现在只是试验成功,要想产品上用.还要经过一些防误处理,自动校准处理,灵敏调节处理,临界状态处理.....看看大家在这方面处理的算法有什么好的建议或方法.....请多多指教...

watercat 发表于 2008-8-13 11:53:25

很容易玩坏IO口……而且完全无法抵御蓄意破坏,过脉冲群测试更是做梦都不要想……

ivws 发表于 2008-8-13 12:08:28

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

ATmega32 发表于 2008-8-13 12:11:38

标记,以后再仔细看。

watercat 发表于 2008-8-13 12:14:27

是的,也许很多IC厂家正在尝试把这个功能加入MCU,但是人家是建立在给IO口内部电路额外增加足够的保护功能的前提之上的

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

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

lionliu 发表于 2008-8-13 12:35:31

标记,谢谢

aduc812 发表于 2008-8-13 12:38:04

顶!

eeyrw 发表于 2008-8-13 13:30:40

不错,偶试试~

liuxj 发表于 2008-8-13 13:42:22

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

lanfeng007 发表于 2008-8-13 13:44:09

不得不说现在这些现成的东西很多了呢。ATMEL,CYPRESS都有,国内好像也有现成产品了。

lanfeng007 发表于 2008-8-13 13:46:26

liuxj 军哥:

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


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

BT.ROBOT 发表于 2008-8-13 13:46:46

学习。

ivws 发表于 2008-8-13 18:13:14

最新改进型电容感应电图:

http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_377293.jpg
(原文件名:改进型电容感应.jpg)

ivws 发表于 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;                                    //假设最大
   
   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;                                    //假设最小
   
   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;
   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日
//*******************************************************************

ivws 发表于 2008-8-13 18:18:41

点击此处下载 ourdev_377313.rar(文件大小:28K) (原文件名:电容触摸感应.rar)

new.ease 发表于 2008-8-13 18:30:47

强,不顶不行。

理论联系实际

TYMCU 发表于 2008-8-13 18:45:51

还是有价值的

lin28 发表于 2008-8-13 18:56:27

记号

kv2004 发表于 2008-8-13 19:02:31

估计这个也是普通单片机实现的吧,有可能是类似em78p156的单片机。
http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_377453.jpg
tf745 (原文件名:tt.jpg)

watercat 发表于 2008-8-13 20:34:18

4148一样过不了脉冲群……

用P6KE系列还差不多……

weiwgml 发表于 2008-8-13 20:55:34

好久没来这逛了,出来透透气

ivws 发表于 2008-8-13 21:11:13

刚刚又对程序进行了优化.....改进电容放电时间.....按键按下.....按键释放....都加入防止处理.....实用性大大增强....

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

xingcn 发表于 2008-8-13 22:36:09

顶一下,楼主的触摸距离有多少?以前做过实验,采用1M电阻上拉端口方式,距离1cm左右,干扰比较大,触点和端口不能接太长的线。有时间试试楼主的电路。

sonic5566 发表于 2008-8-13 23:32:50

做个记号

caiyu 发表于 2008-8-13 23:34:44

新鲜,顶下

xingcn 发表于 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;                                          //返回时间
}

xiaobendan 发表于 2008-8-14 08:10:50

弱弱的问:所谓的人体电容的充电回路是怎样的?
如此高的输入阻抗,EMC恐怕很难过的吧。
有空试试他的抗干扰能力

indi 发表于 2008-8-14 08:37:21

不錯

Notonly 发表于 2008-8-14 09:06:58

不错,学习了

holycat 发表于 2008-8-14 09:17:04

一样做的话,可能用STC好一点,因为STC的I/O脚是自带ESD保护的。

ivws 发表于 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;                                    //假设最大
   
   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;                                    //假设最小
   
   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;
   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 inttrail = 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日
//*******************************************************************

jjldc 发表于 2008-8-14 09:56:53

C8051F系列的某一款 内置了触摸按键接口的 可以查一下

ivws 发表于 2008-8-14 10:13:43

阿莫....应该给裤子了吧........呵呵.....

kevin8 发表于 2008-8-14 10:19:20

楼主是人才,应该给条内裤穿

yaya001 发表于 2008-8-14 11:46:43

cy也有的 用过 不错

ivws 发表于 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

http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_378513.jpg
(原文件名:电容触摸感应.jpg)

http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_378514.JPG
(原文件名:电容触摸感应1.JPG)

http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_378515.JPG
(原文件名:电容触摸感应2.JPG)

http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_378516.JPG
(原文件名:电容触摸感应3.JPG)

HZZCL 发表于 2008-8-14 13:10:06

记号

xingcn 发表于 2008-8-14 13:26:00

楼主的代码注释还是错误的,看来楼主还是没有完全弄明白它的原理,采用上拉是检测结电容+人体电容的充电时间,采用下拉是检测结电容+人体电容的放电时间,这样的代码要是给“酷”那就是对初学者的误导。

zhifeng 发表于 2008-8-14 13:39:28

ti的430也有类似的演示板

ivws 发表于 2008-8-14 13:58:27

原理是检测上拉结电容和人体电容......要是理论分析不对,又怎么做得来出呢........

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

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

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

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





http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_378633.JPG
(原文件名:仿真模型1.JPG)

http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_378634.JPG
(原文件名:仿真模型2.JPG)

xiaobendan 发表于 2008-8-14 14:31:16

感应距离可以到多厚?
比如,5毫米的玻璃板可以吗?

xiaobendan 发表于 2008-8-14 14:39:38

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

ivws 发表于 2008-8-14 14:44:16

刚才试了一下,2MM PCB + 5MM 亚克历有机玻璃,可以感应得到...只是灵敏度低了一点点...可以通过加大焊盘的面积来提高灵敏度....

ivws 发表于 2008-8-14 15:02:02

【48楼】 xiaobendan 仲跻东

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

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

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

ivws 发表于 2008-8-14 15:05:00

所以我说..欢迎大家进行公测试验.....提供改进建议...

thomasdu 发表于 2008-8-14 16:06:40

至少是一种思路,虽然离实用还有距离,也很支持楼主

xingcn 发表于 2008-8-14 22:40:13

看了楼主的仿真电路我也明白为什么楼主能做出来了,完全是歪打正着,居然把代替人体的电容接到VCC上。

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

AVR-BIN 发表于 2008-8-14 22:46:01

ivws
用电池供电能正常工作吗?

twoperson 发表于 2008-8-14 23:10:54

楼主这个电路应该会受到比较严重的噪声干扰,因为单片机IO做输入后输入阻抗很高,噪声会造成很大的影响
cypress的csr技术也有类似的毛病

xiaobendan 发表于 2008-8-15 08:19:23

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

xk2yx 发表于 2008-8-15 08:30:31

好。

ivws 发表于 2008-8-15 09:04:01

昨晚上有 1:1 隔离变压器测试....用AVR 方案的可以用...不过漂移较大....有时自激..

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

jemmy 发表于 2008-8-15 23:46:35

lz很有钻研精神,佩服。。。

xiaobendan 发表于 2008-8-16 07:23:46

看来真的用电池做的话是不行的,不过没用必要这样。因为有些家电就是要用变压器的,反正是非接触的,即使是直连到220上的也没有问题。不过还是要考虑用户的鞋子比较好,鞋底比较厚,还有地板的问题。

AVR-BIN 发表于 2008-8-16 08:05:27

所以说没有振荡源的电容非接触摸是不可靠的
其实用2个三极管就可以搞定电容非接触摸,即使用电池供电也一样稳定可靠

hjzz 发表于 2008-8-16 08:06:47

piaoguo

xoxo 发表于 2009-8-5 15:29:16

AVR-BIN 电子油条,你能给个电路看看吗?

yzlyear 发表于 2009-8-5 16:42:37

不錯

freezing 发表于 2009-8-5 17:10:32

Mark

dds007 发表于 2009-8-5 19:29:25

Mark

plc_avr 发表于 2009-8-5 19:52:53

好贴,MARK!!!

stevencao 发表于 2009-8-5 19:56:10

不错,学习一下

qzzz 发表于 2009-8-5 20:51:21

好贴

avr-stm32 发表于 2009-8-5 21:10:09

MARK!

lichangan 发表于 2009-8-6 11:05:54

记号

CCAO_75 发表于 2009-8-6 11:17:32

wxfxldz 发表于 2009-8-6 11:32:28

学习学习,谢谢分享

sunki_avr 发表于 2009-8-6 12:02:37

之前有朋友成功试过,只加两个电阻就可以很稳定做到。并量产。能在凌阳。51中实现

anxiangbo 发表于 2009-8-6 13:29:07

记号

zhouq3132 发表于 2009-8-6 13:39:48

mark

tiger1125 发表于 2009-8-6 15:19:49

之前听过PIC技术工程师的讲座,PIC单片机也是这样的原理

Ayumi 发表于 2009-9-2 12:27:08

mark thanks

meeting 发表于 2009-9-2 14:36:48

mark

FREEXP 发表于 2009-9-2 15:04:39

我也测试过了,确实可行,灵敏度都可以,就是抗干扰不行

airwill 发表于 2009-9-2 23:52:10

是啊, 原理是可行的, 但这个方案, 抗干扰不行, 要提高抗干扰, 得换个方案.

airwill 发表于 2009-9-2 23:57:59

不过, 楼主做了很有益的尝试, 能公布试验结果, 鼓励一下

keaiduo 发表于 2009-10-4 01:31:09

mark

xiaobendan 发表于 2009-10-4 08:14:58

81楼说一下另外的方案吧

lv998127 发表于 2009-10-5 14:40:41

期待有更好的方案

nhlijiaming 发表于 2009-10-5 15:00:47

用电池还能感应不?

zxycba 发表于 2010-4-12 16:12:41

有动手实践才有说话权,支持LZ,支持动手钻研

feng741 发表于 2010-4-30 22:47:38

mark

lv998127 发表于 2010-4-30 23:10:20

标注,等到空点再细看!

gpzdc986 发表于 2010-5-6 13:34:08

mark

aohuahua 发表于 2010-5-9 12:45:58

mark

lin2266286 发表于 2010-6-17 17:33:33

mark

chenruichao 发表于 2010-6-17 19:04:51

mark

kouxiangtang 发表于 2010-6-17 19:29:09

做的不错!顶!

liliuqun 发表于 2010-6-17 19:42:02

记号

avrlover 发表于 2010-6-17 19:51:14

mark

tangwei039 发表于 2010-6-17 20:31:26

mark

super373 发表于 2010-6-17 22:26:27

mark~~~

pj5350 发表于 2010-6-19 06:09:16

mark
页: [1] 2 3
查看完整版本: AVR普通IO端口实现非接触式电容触摸感应试验成功