| 
          【单片机程序】AVRMega8制作的简易示波器(只能检测高低电平) | 
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
            
               单片机端口定义:               
                        
           | 
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
            
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
            
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
             
              注意:本程序按GPL协议发布。 程序如下: 【ports.h】 #define K1 (PINB&BIT(0)) //id减1 #define K2 (PINB&BIT(1)) //id加1 #define K3 (PINB&BIT(2)) //ctrl键,同时按下k1或k2可以加/减25 #define K4 (PINB&BIT(3)) //开始/停止
  #define INPUT (PIND&BIT(2)) //输入的信号 PD2 #define HC595_SHCK1 PORTC|=BIT(0) #define HC595_SHCK0 PORTC&=~BIT(0) #define HC595_STCK1 PORTC|=BIT(1) #define HC595_STCK0 PORTC&=~BIT(1) #define HC595_SD1 PORTC|=BIT(2) #define HC595_SD0 PORTC&=~BIT(2)
  #define LED1_ON PORTB&=~BIT(4) //L1表示端口检测器是否正在工作 #define LED1_OFF PORTB|=BIT(4) #define LED2_ON PORTB&=~BIT(5) //L2表示当前端口是否是高电平 #define LED2_OFF PORTB|=BIT(5)              
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
             
              【eeprom.c】 #include <iom8v.h> #include <macros.h> #include "eeprom.h"
  void EEPROM_Write(unsigned int address, unsigned char Data) {     while (EECR&BIT(EEWE)) //等待上一次写操作结束         ; /*seg8_scan();*/ //如果程序中有数码管,请去掉该注释,避免写入时数码管熄灭或闪烁     EEAR=address; //可以直接对地址寄存器赋int值     EEDR=Data;     EECR|=BIT(EEMWE); //主机写入允许     EECR|=BIT(EEWE); //允许EEPROM }
  void EEPROM_Read(unsigned int address, unsigned char* Data) {     while (EECR&BIT(EEWE)) //等待上一次写操作结束         ; /*seg8_scan();*/ //如果程序中有数码管,请去掉该注释,避免写入时数码管熄灭或闪烁     EEAR=address;     EECR|=BIT(EERE); //启动读操作     *Data=EEDR; }              
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
             
              【eeprom.h】 void EEPROM_Write(unsigned int address, unsigned char Data); void EEPROM_Read(unsigned int address, unsigned char* Data);              
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
             
              【HC595.c】 #include <iom8v.h> #include <macros.h> #include "HC595.h" #include "ports.h"
  void HC595_SerIn(unsigned char Data) {     unsigned char i;     for (i=0;i<8;i++)     {         HC595_SHCK0; //CLOCK_MAX=100MHz         if (Data&BIT(7-i))             HC595_SD1;         else             HC595_SD0;         HC595_SHCK1;     } }
  void HC595_ParOut(void) {     HC595_STCK0;     HC595_STCK1; }              
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
             
              【HC595.h】 void HC595_SerIn(unsigned char Data); void HC595_ParOut(void);              
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
             
              【PortChecker.c】 #include <iom8v.h> #include <macros.h> #include "HC595.h" #include "eeprom.h" #include "ports.h"
  flash unsigned char seg8[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; unsigned int time[256]={0}; unsigned char port_states[32]={0xff};
  int id=0; unsigned int num=0; unsigned char cfg=0x00; unsigned int length=1; unsigned int interval=0;
  void delay250us(void) {     unsigned int i;     for (i=0;i<285;i++); }
  void delay(unsigned int n) {     unsigned int i;     while (n--)         for (i=0;i<1140;i++); }
  //数码管动态扫描 //参数n为扫描次数 void seg8_scan(unsigned char n) {     unsigned char i;     unsigned int f;     unsigned char tmp;     while (n--)     {         f=1000; //每次循环时f必须初始化         for (i=0;i<=2;i++)         {             tmp=~seg8[id%f/(f/10)];             if (i==2)                 tmp|=BIT(7); //小数点             HC595_SerIn(BIT(i));             HC595_SerIn(tmp);             HC595_ParOut();             delay250us();             f/=10;         }
          f=10000;         for (i=3;i<=6;i++)         {             tmp=~seg8[num%f/(f/10)];             if ((cfg&0x03)+0x03==i)                 tmp|=BIT(7);             HC595_SerIn(BIT(i));             HC595_SerIn(tmp);             HC595_ParOut();             delay250us();             f/=10;         }     } }
  //存储数据到EEPROM中 void savedata(void) {     unsigned char i;     for (i=0;i<240;i++) //只存储前240个数据,占用空间480字节     {         EEPROM_Write(i*2,time[i]/256);         EEPROM_Write(i*2+1,time[i]%256);     }
      //写入高低电平标识,占用30字节     for (i=0;i<30;i++)         EEPROM_Write(0x1e0+i,port_states[i]);          //写入长度     if (length>240)         i=240;     else     {         i=length%256;         if (i<1)             i=1;     }     EEPROM_Write(0x1fe,0x3d); //倒数第二位为是否有数据的标识     EEPROM_Write(0x1ff,i); //最后一位表示长度 }
  //停止键的检测 unsigned char stop_working(void) {     if (!K4)     {         while (!K4);         return 0x80;     }     else         return 0x00; }
  //禁用数码管 void disable_seg8(void) {     HC595_SerIn(0x7f); //七个数码管全部显示     HC595_SerIn(0x40); //字符“-”     HC595_ParOut(); }
  //核心函数: //给输入端的高低电平计时 void work(void) {     unsigned int i;     unsigned char low;     disable_seg8();     TIMSK&=~BIT(TOIE0); //禁止定时器0中断     LED1_ON;     LED2_OFF;     for (length=1;length<=256;length++)     {         TCNT1H=0x00;         TCNT1L=0x00;                  if (INPUT)         {             cfg|=BIT(2); //倒数第3位表示当前检测的是什么电平             while (INPUT)             {                 if (stop_working()==0x80)                 {                     cfg|=BIT(3);                     break;                 }             }         }         else         {             cfg&=~BIT(2);             while (!INPUT)             {                 if (stop_working()==0x80)                 {                     cfg|=BIT(3);                     break;                 }             }         }
          //记录时间(us)         low=TCNT1L; //先读取一次低八位         time[length-1]=TCNT1H;         time[length-1]*=256;         time[length-1]+=low;
          //记录电平         if (cfg&BIT(2))             port_states[(length-1)/8]|=BIT((length-1)%8);         else             port_states[(length-1)/8]&=~BIT((length-1)%8);
          if (stop_working()==0x80)             break;         if (cfg&BIT(3))         {             cfg&=~BIT(3);             break;         }     }     LED1_OFF;     length--;     savedata();     TIMSK|=BIT(TOIE0); //允许定时器0中断 }
  //显示时间和电平 void gettime(void) {     num=time[id];     if (num>9999)     {         num/=10;         cfg&=~BIT(1); //小数点位置         cfg|=BIT(0); //65.46ms     }     else     {         cfg&=~BIT(1);         cfg&=~BIT(0); //7.000ms     }          if (port_states[id/8]&BIT(id%8))         LED2_ON;     else         LED2_OFF;     interval=0; //防止再次自动跳变 }
  //按键扫描 void key_scan(void) {     //减1、25     if (!K1)     {         seg8_scan(5); //防止按下按键后数码管熄灭         if (!K1)         {             if (!K3)             {                 if (id>=25)                     id-=25;             }             else             {                 id--;                 if (id<0)                     id=length-1;             }
              gettime();             while (!K1)                 seg8_scan(1);             seg8_scan(3);         }     }     //加1、25     if (!K2)     {         seg8_scan(5);         if (!K2)         {             if (!K3)             {                 if (id+25<=length-1)                     id+=25;             }             else             {                 id++;                 if (id>=length)                     id=0;             }
              gettime();             while (!K2)                 seg8_scan(1);             seg8_scan(3);         }     }
      if (!K4)     {         while (!K4)             seg8_scan(1);         work();         id=1; //完毕后自动显示第2个时间值,通常情况下采集到的第一个和最后一个时间值很不准确         if (length<=1 || length>300)         {             id=0;             length=1;         }         gettime();     } }
  //读取数据 void readdata(void) {     unsigned char i;     unsigned char l,h;     EEPROM_Read(0x1fe,&i); //读标志位     if (i==0x3d)     {         //如果已存储了数据,则读出来         EEPROM_Read(0x1ff,&l); //获取数据长度         length=l;         for (i=0;i<240;i++)         {             EEPROM_Read(i*2,&h);             EEPROM_Read(i*2+1,&l);             time[i]=h*256+l;         }         //读高低电平标识         for (i=0;i<30;i++)             EEPROM_Read(0x1e0+i,&port_states[i]);         if (length>=2)             id=1;         gettime();     } }
  void main(void) {     DDRC=0xff;     PORTC=0xff;     DDRB=0xf0; //PB口低四位为按键     PORTB=0xff;     DDRD=0xf3; //两个外中断口设为输入     PORTD=0xff;
      TCCR1A=0x00;     TCCR1B=0x02; //定时器1设为8分频,也就相当于51单片机接12M晶振     readdata();
      TCNT0=0x06; //定时器0定时32ms     TCCR0=0x05; //定时器0设为1024分频     TIMSK|=BIT(TOIE0); //允许定时器0中断     SEI();
      while (1)     {         seg8_scan(1);         key_scan();     } }
  //自动跳变 #pragma interrupt_handler et0:iv_TIM0_OVF void et0(void) {     if (length>1)     {         interval++;         if (interval>=1000) //定时32s         {             id++;             if (id>=length)                 id=0;             gettime();         }     }     TCNT0=0x06; }              
                        
              
               
             
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
             
              使用时按下K4键开始捕捉或停止捕捉, 完毕后用ISP口连接电路板和电脑,打开AVR Fighter,通过ISP口读取EEPROM数据并保存为bin文件,再用php程序就能生成一张波形图了!              
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
            
              以下就是我获得的我的遥控器发射的电平,我做了一点修改而已               
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
             
              以下为根据bin文件生成波形图的php程序代码: 【portcheck.php】 <?php  /* 根据EEPROM内容生成I/O口波形图像  * 作者:巨大八爪鱼  * 时间:2013年9月30日15:08:06 **/ define("US_PER_PX",50); //每像素表示多少微秒 define("FILENAME","错误的红外发射.bin"); header("Content-type:image/png");
  function BIT($n) {     return 1<<$n; }
  $file=fopen(FILENAME,"rb"); # 打开文件 $time=fread($file,480); # 持续时间 $port_states=fread($file,30); # 高低电平标识 fread($file,1); # 该位始终是0x3d,跳过 $length=ord(fread($file,1)); # 长度
  # 计算图片宽度 $width=0; for ($i=0;$i<$length;$i++) {     $t=ord($time[$i*2])*256+ord($time[$i*2+1]);     $width+=$t; } $width=ceil($width/US_PER_PX);
  $im=imagecreatetruecolor($width,64); $back_color=imagecolorallocate($im,251,252,205); imagefill($im,10,5,$back_color); # 背景颜色
  $color=imagecolorallocate($im,0,64,0); $x=$y=0; for ($i=0;$i<$length;$i++) {     $lasty=$y;     $y=10;     if (ord($port_states[(int)floor($i/8)])&BIT($i%8))         $y=50; # 如果该位是高电平              # 电平发生跳变时加竖线     if ($y!=$lasty && $i>0)         imageline($im,$x,11,$x,49,$color);          # 从第二个电平开始更换颜色     if ($i==1)         $color=imagecolorallocate($im,0,0,128);              $t=ord($time[$i*2])*256+ord($time[$i*2+1]);     $t/=US_PER_PX;     imageline($im,$x,$y,$x+$t,$y,$color); # 绘制水平线     $x+=$t; }
  fclose($file);
  imagepng($im); imagedestroy($im); ?>              
                        
              
               
             
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
             
              1.EEPROM倒数第二位在该版本程序中恒为0x3d,这是版本识别码,将来的版本会改变这个识别码 2.把“18b20单总线信号.bin”烧写回EEPROM,然后打开单片机,就会发现第142、143、144等多个连续的id号是相同的。通常如果电平变化得太快,小于1us,那么就会出现这种情况。所以程序中port_states数组还是很有必要设置的。 3.默认在php绘图的时候每像素表示100us,如果绘出的图形太密,比如像“18b20单总线信号_100ms.png”那样,那么请减小US_PER_PX常量的值,比如设为1us,出来的效果就是“18b20单总线信号_1ms.png”             
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
             
              回复:12楼 简单说一下这个php程序的使用方法,在自己的电脑上安装php开发环境(相关资料见php吧),然后把这个php文件放进去 然后用ISP线连接电路板和电脑,打开AVR Fighter,读取EEPROM并保存为bin文件,把这个bin文件放入php文件所在的文件夹,把这个bin文件命名为“错误的红外发射.bin”,当然文件名可以随便取,别忘了改相应的php程序的第7行的那个FILENAME常量的值。 用浏览器访问这个php页面,就可以得到电平图像了。              
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
            
              以下为我通过这个工具得到的一些波形图: 18b20单总线信号_1ms               
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
            
              回复:15楼 这个图像太宽了,没法直接看,请右键另存到本地再看              
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
            
              18b20单总线信号_5ms:               
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
            
              18b20单总线信号_100ms:               
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
            
              某51单片机程序P0某口的波形2:               
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
            
              以下就是一个典型的遥控器发射的波形:  可以用画图软件量出来,引导码是9ms高电平4.5ms低电平,然后一堆用户码和键码,最后用引导码和短码表示重复按键              
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
             
              顿时感觉自己太触了 这个程序我去年国庆节(2013年10月3日)就做出来了,到现在才正式发布,都怪我没时间 我估计这个电路板一定能给各位单片机开发者带来极大的方便,因为市面上市售的示波器实在是太贵了,几千,,用AVR单片机自己做一个正合适。我计算了一下我做这个的成本也就100块钱上下              
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
            
              回复:21楼 我这个东西缺点就是扫描时间较短,而且还只能检测高低电平,不能检查准确的电压。 下次发布2.0版本的时候我最好弄一个12864液晶来显示电压的变化,显示一个xy坐标系和曲线              
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
            
              红外遥控数据示例2_连按某个键:               
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
            
              自制遥控器信号1:  很显然这个自制的遥控器发射的波形很失败              
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
             
              波形图可以用windows自带的画图软件打开,可以量波形的长度。 比如如果php程序中设置了:define("US_PER_PX",50); //每像素表示多少微秒 那么每个像素就是50us的时间 你用画图软件量某个高电平的宽度为14像素,那么这个高电平持续时间就为0.7毫秒 真的是非常方便!              
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
             
              我争取下一个版本再弄一个按键,可以使用AVR单片机中的各种定时器分频器,这样就可以测量出更长的电平了。              
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
             
              下一个版本我还将再弄一些EEPROM,也可以增加测量的长度,不再是240个单位了。而是几千甚至上万个单位              
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
            
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
            
                        
 | 
|
        
                
          
            
                         一派掌门 二十级              | 
          
            
            
            
              我再简单讲解一下这个电路板的功能。 A处的那个黑色的接口就是ISP接口,用来把EEPROM的内容导入到电脑,以便于生成电平图像。               
                        
 | 
|