ahao 发表于 2011-12-9 17:07

单片机边学边玩---带有峰值保持的双15段LED电平显示电路,终于搞定!不喜勿入

本帖最后由 ahao 于 2012-5-3 17:29 编辑

先上图和程序,等有空再编辑。。。。。。。。。。。。。。。。。。

电路图如下,大致原理是:音频信号经过LM358运放的放大、整形、滤波后变成随音频电平变化的脉动直流信号,然后此脉动直流信号进入单片机由单片机AD转换,转换后的结果与设定值做比较使得相对应的LED点亮。图中使用的是高亮LED。



实物图如下。





视频效果:
http://v.youku.com/v_show/id_XMzMwNzAyMTE2.html

或者 http://player.youku.com/player.php/sid/XMzMwNzAyMTE2/v.swf

http://player.youku.com/player.php/sid/XMzMwNzAyMTE2/v.swf

ahao 发表于 2011-12-9 17:08

本帖最后由 ahao 于 2013-4-18 15:17 编辑

应朋友要求现将要用到的STC头文件发上来,解压后将此头文件放在工程所在目录下面即可编辑通过。



c程序如下红字部分。高手请绕道,下面程序只适合新手参考! 我会抽空加上注释。。。。。


#include <intrins.h>                                                               //头文件
#include "STC12C5A60S2.H"                                                   //STC自己的头文件,可以在它的官方网站下载到
#define uchar unsigned char                                                    //两个宏定义
#define uint unsigned int

sbit ledL15=P1^2;sbit ledL14=P1^3;sbit ledL13=P1^4;             //定义左声道15个LED所接的IO口
sbit ledL12=P1^5;sbit ledL11=P1^6;sbit ledL10=P1^7;
sbit ledL9=P3^0;sbit ledL8=P3^1;sbit ledL7=P3^2;
sbit ledL6=P3^3;sbit ledL5=P3^4;sbit ledL4=P3^5;
sbit ledL3=P3^6;sbit ledL2=P3^7;sbit ledL1=P4^5;                  //其中的P4口我是为了学习怎么使用它而特意这样写的


sbit ledR15=P0^0;sbit ledR14=P0^1;sbit ledR13=P0^2;         //定义右声道15个LED所接的IO口
sbit ledR12=P0^3;sbit ledR11=P0^4;sbit ledR10=P0^5;
sbit ledR9=P2^7;sbit ledR8=P2^6;sbit ledR7=P2^5;
sbit ledR6=P2^4;sbit ledR5=P2^3;sbit ledR4=P2^2;
sbit ledR3=P2^1;sbit ledR2=P2^0;sbit ledR1=P4^4;


uint ADC_resultL,ADC_resultR,voL,voR;                                  //定义几个unsigned int型和unsigned char型变量
uchar numL,numR,tt0,tt1,ttL,ttR,biaozhiL,biaozhiR,xuantingL,xuantingR,xialuoL,xialuoR,pfL,pfR;

void delay(uchar z)                                                               //延时程序
{
      uchar x,y;
      for(x=z;x>0;x--)
                for(y=100;y>0;y--);
}

void displayL()                                                                  //左声道显示程序
{
      
      if(pfL==1)
                ledL1=0;
      else
      {
                if(numL>=1)ledL1=0;      else ledL1=1;
      }

      if(pfL==2)
                ledL2=0;
      else
      {
                if(numL>=2)ledL2=0;      else ledL2=1;
      }

      if(pfL==3)
                ledL3=0;
      else
      {
                if(numL>=3)ledL3=0;      else ledL3=1;
      }

      if(pfL==4)
                ledL4=0;
      else
      {
                if(numL>=4)ledL4=0;      else ledL4=1;
      }

      if(pfL==5)
                ledL5=0;
      else
      {
                if(numL>=5)ledL5=0;      else ledL5=1;
      }

      if(pfL==6)
                ledL6=0;
      else
      {
                if(numL>=6)ledL6=0;      else ledL6=1;
      }

      if(pfL==7)
                ledL7=0;
      else
      {
                if(numL>=7)ledL7=0;      else ledL7=1;
      }

      if(pfL==8)
                ledL8=0;
      else
      {
                if(numL>=8)ledL8=0;      else ledL8=1;
      }

      if(pfL==9)
                ledL9=0;
      else
      {
                if(numL>=9)ledL9=0;      else ledL9=1;
      }

      if(pfL==10)
                ledL10=0;
      else
      {
                if(numL>=10)ledL10=0;      else ledL10=1;
      }

      if(pfL==11)
                ledL11=0;
      else
      {
                if(numL>=11)ledL11=0;      else ledL11=1;
      }

      if(pfL==12)
                ledL12=0;
      else
      {
                if(numL>=12)ledL12=0;      else ledL12=1;
      }

      if(pfL==13)
                ledL13=0;
      else
      {
                if(numL>=13)ledL13=0;      else ledL13=1;
      }

      if(pfL==14)
                ledL14=0;
      else
      {
                if(numL>=14)ledL14=0;      else ledL14=1;
      }

      if(pfL==15)
                ledL15=0;
      else
      {
                if(numL>=15)ledL15=0;      else ledL15=1;
      }
}

void displayR()                                                                  //右声道显示程序
{
      
      if(pfR==1)
                ledR1=0;
      else
      {
                if(numR>=1)ledR1=0;      else ledR1=1;
      }

      if(pfR==2)
                ledR2=0;
      else
      {
                if(numR>=2)ledR2=0;      else ledR2=1;
      }

      if(pfR==3)
                ledR3=0;
      else
      {
                if(numR>=3)ledR3=0;      else ledR3=1;
      }

      if(pfR==4)
                ledR4=0;
      else
      {
                if(numR>=4)ledR4=0;      else ledR4=1;
      }

      if(pfR==5)
                ledR5=0;
      else
      {
                if(numR>=5)ledR5=0;      else ledR5=1;
      }

      if(pfR==6)
                ledR6=0;
      else
      {
                if(numR>=6)ledR6=0;      else ledR6=1;
      }

      if(pfR==7)
                ledR7=0;
      else
      {
                if(numR>=7)ledR7=0;      else ledR7=1;
      }

      if(pfR==8)
                ledR8=0;
      else
      {
                if(numR>=8)ledR8=0;      else ledR8=1;
      }

      if(pfR==9)
                ledR9=0;
      else
      {
                if(numR>=9)ledR9=0;      else ledR9=1;
      }

      if(pfR==10)
                ledR10=0;
      else
      {
                if(numR>=10)ledR10=0;      else ledR10=1;
      }

      if(pfR==11)
                ledR11=0;
      else
      {
                if(numR>=11)ledR11=0;      else ledR11=1;
      }

      if(pfR==12)
                ledR12=0;
      else
      {
                if(numR>=12)ledR12=0;      else ledR12=1;
      }

      if(pfR==13)
                ledR13=0;
      else
      {
                if(numR>=13)ledR13=0;      else ledR13=1;
      }

      if(pfR==14)
                ledR14=0;
      else
      {
                if(numR>=14)ledR14=0;      else ledR14=1;
      }

      if(pfR==15)
                ledR15=0;
      else
      {
                if(numR>=15)ledR15=0;      else ledR15=1;
      }

}

void init()                                                                        //初始化程序
{
      TMOD=0x11;                           //设定定时器0、1工作方式
      EA=1;                                       //开总中断      
      TH0=0xb1;                                  //高8位装初值 TH0=(65536-20000)/256;
      TL0=0xe0;                                 //低8位装初值 TL0=(65536-20000)%256;
      ET0=1;                                       //开定时器0
      TR0=1;                                       //启动定时器0

      TH1=0xd8;                                 //高8位装初值TH1=(65536-10000)/256
      TL1=0xf0;                                 //低8位装初值TL1=(65536-10000)%256
      ET1=1;                                       //开定时器1
      TR1=1;                                       //启动定时器1
}

//---------------------------------------------------------------------
//以下选择 ADC 转换速率,只能选择其中一种
                        //            SPEED1 SPEED0A/D转换所需时间
#define AD_SPEED   0x60 //0110,0000   1      1   70 个时钟周期转换一次,
                        //CPU工作频率21MHz时 A/D转换速度约 300KHz
//#define AD_SPEED   0x40 //0100,0000   1      0   140 个时钟周期转换一次
//#define AD_SPEED   0x20 //0010,0000   0      1   280 个时钟周期转换一次
//#define AD_SPEED   0x00 //0000,0000   0      0   420 个时钟周期转换一次
//---------------------------------------------------------------------
/*---------------------------------------------------------------------
         STC12C5A60S2单片机AD转换函数,直接引用于宏晶科技网站
----------------------------------------------------------------------*/
uint ad_zhuanhuan(uchar channel)
{
    uchar AD_finished=0;            //存储 A/D 转换标志

    ADC_RES = 0;                                        //高八位清零
    ADC_RESL = 0;                                        //低两位清零

    channel &= 0x07;                //0000,0111 清0高5位
    ADC_CONTR = AD_SPEED;
    _nop_();
    ADC_CONTR |= channel;         //选择 A/D 当前通道
    _nop_();
    ADC_CONTR |= 0x80;            //启动 A/D 电源
    delay(1);                     //使输入电压达到稳定
    ADC_CONTR |= 0x08;            //0000,1000 令 ADCS = 1, 启动A/D转换,
    AD_finished = 0;
    while (AD_finished ==0 )      //等待A/D转换结束
    {
      AD_finished = (ADC_CONTR & 0x10); //0001,0000 测试A/D转换结束否
    }
    ADC_CONTR &= 0xE7;            //1111,0111 清 ADC_FLAG 位, 关闭A/D转换,

    return (ADC_RES*256+ADC_RESL);               //返回 A/D 高 8 位转换结果
}



void main()                                                      //主程序
{
      P4SW=0x70;                                           //0x70=0111 0000 ,将P4.4 P4.5 P4.6设置成IO口

      init();
//      ADRJ = AUXR1^2:
//      0: 10 位A/D 转换结果的高8 位放在ADC_RES 寄存器, 低2 位放在ADC_RESL 寄存器
//      1: 10 位A/D 转换结果的最高2 位放在ADC_RES 寄存器的低2 位, 低8 位放在ADC_RESL 寄存器
//      AUXR1 &= ~0x04;               //0000,0100, 令 ADRJ=0
    AUXR1 |= 0x04;                  //0000,0100, 令 ADRJ=1

    ADC_CONTR |= 0x80;            //1000,0000 打开 A/D 转换电源,启动AD转换      
      P1ASF = 0x03;                   //0000,0011, 将 P1.0 P1.1 置成模拟口      

      while(1)
      {

                voL= ADC_resultL*5.0;                                  //左声道电平转换结果。语句中5.0=5*1.0其中*1.0是必不可少
                                                                                 //的,不然C语言作整数处理,没有想要的结果产生.      
                if(voL>2000) numL=14;               
                else if(voL>1635) numL=13;               
                else if(voL>1432) numL=12;      
                else if(voL>1270) numL=11;               
                else if(voL>1130) numL=10;      
                else if(voL>1006) numL=9;               
                else if(voL>895) numL=8;      
                else if(voL>797) numL=7;                              
                else if(voL>709) numL=6;      
                else if(voL>650) numL=5;               
                else if(voL>400) numL=4;               
                else if(voL>262) numL=3;               
                else if(voL>159) numL=2;
                else if(voL>70) numL=1;                                                                                       
                else numL=0;

                if(pfL<=numL)                           //检测如果漂浮物不高于峰值就让漂浮物置于峰值上面,同时 标志位 置0
                {
                        pfL=numL+1;
                        biaozhiL=0;                        //标志位 置0   
                        xuantingL=10;                   //变量xuantingL   改变此值可以设定左漂浮物悬停时间
                }
                else                                       //if(pfL>numL) 如果漂浮物高于峰值就把标志位置1
                {
                        biaozhiL=1;                      //标志位 置1
                }
                displayL();                              //显示左声道具体电平值的段


                voR= ADC_resultR*5.0;             //左声道电平转换结果。原理同上边左声道
                if(voR>2000) numR=14;               
                else if(voR>1635) numR=13;               
                else if(voR>1432) numR=12;      
                else if(voR>1270) numR=11;               
                else if(voR>1130) numR=10;      
                else if(voR>1006) numR=9;               
                else if(voR>895) numR=8;      
                else if(voR>797) numR=7;                              
                else if(voR>709) numR=6;      
                else if(voR>650) numR=5;               
                else if(voR>400) numR=4;               
                else if(voR>262) numR=3;               
                else if(voR>159) numR=2;
                else if(voR>70) numR=1;                                                                                       
                else numR=0;
               
                if(pfR<=numR)
                {
                        pfR=numR+1;
                        biaozhiR=0;
                        xuantingR=10;               
                }
                else
                {
                        biaozhiR=1;
                }
                displayR();                              //显示右声道具体电平值的段
      }
}

void timer0() interrupt 1                        //定时器0中断程序
{
      TH0=0xb1;
      TL0=0xe0;
      tt0++;

      if (tt0>=1)                                       //20ms转换一次.
   {
         tt0=0;
         ADC_resultL = ad_zhuanhuan(0); //P1.0 为 A/D 当前通道, 测量结果存ADC_result0
         ADC_resultR = ad_zhuanhuan(1); //P1.1 为 A/D 当前通道, 测量结果存ADC_result7                        
         
   }
}

void timer1() interrupt 3                        //定时器1中断程序
{
                                                             //再装一次初值
      TH1=0xd8;
      TL1=0xf0;
      
      tt1++;
      if(tt1>=1)
      {
                if(biaozhiL)
                {
                        ttL++;
                        if(ttL>7)                        //改变此值可以设定左漂浮物下降速度
                        {
                              if(xuantingL==0)
                                        pfL--;
                              else if(xuantingL>0)
                                        xuantingL--;                              
                              ttL=0;                        
                        }                        
                }
                                          
                if(biaozhiR)
                {
                        ttR++;
                        if(ttR>7)                            //改变此值可以设定右漂浮物下降速度
                        {
                              if(xuantingR==0)
                                        pfR--;
                              else if(xuantingR>0)
                                        xuantingR--;                              
                              ttR=0;                        
                        }                        
                }                  

                tt1=0;
      }
}

yhq8501 发表于 2011-12-9 17:12

沙发,这个要顶

jackd80 发表于 2011-12-9 17:12

我也准备学习DSP,找了一个51单片机的书。

零距离爱你 发表于 2011-12-9 17:14

用高亮的太刺眼了,用柔光的吧

何治华 发表于 2011-12-9 17:23

LZ可以做一个多频段的吗?

huangjingjie 发表于 2011-12-9 18:43

{:2_65:}{:2_65:}做个多段频谱就更好看了。顶版主的贴。

Sayno? 发表于 2011-12-9 21:17

学得好快哦~~~~~

606461 发表于 2011-12-9 21:38

准备学习单片机,还没入门,能学到复,考就好了:lol

lihuotu 发表于 2011-12-9 22:23

多频段的更爽

myelg 发表于 2011-12-10 11:56

正想找套带峰电平玩下,可帮做个吗?到手能用的,因本人不会编程!

wtgq520 发表于 2011-12-10 12:23

丹麦王子 发表于 2011-12-10 12:30

不会瞎折腾的,弄个旧的进口高级卡座。
cd信号接到卡座输入,放盒磁带,打开录音,就可以看到显示了.

RC522 发表于 2011-12-10 15:31

用switch.........case........break    会比if...else 看起来更简洁些吧

RC522 发表于 2011-12-10 15:33

做个多段频谱就更好看了。顶版主的贴。
huangjingjie 发表于 2011-12-9 18:43 http://bbs.hifidiy.net/images/common/back.gif


    想做这样的你就得用到FFT,比这个更难搞一点

ahao 发表于 2011-12-10 16:41

用高亮的太刺眼了,用柔光的吧
零距离爱你 发表于 2011-12-9 17:14 http://bbs.hifidiy.net/images/common/back.gif
这是测试用的。可以换成磨砂的LED就柔了:lol

LZ可以做一个多频段的吗?
何治华 发表于 2011-12-9 17:23 http://bbs.hifidiy.net/images/common/back.gif
做个多段频谱就更好看了。顶版主的贴。
huangjingjie 发表于 2011-12-9 18:43 http://bbs.hifidiy.net/images/common/back.gif
正在学习,将来可能会做出来

用switch.........case........break    会比if...else 看起来更简洁些吧
RC522 发表于 2011-12-10 15:31 http://bbs.hifidiy.net/images/common/back.gif
:handshake 谢谢你的指点。
我当时也考虑用switchcase 了,不过我发现case 是要求等于一个数的,我的愿意是大于等于,所以后来还是用if了。
不知道就这个程序如果用switch的话应该怎么写合适?麻烦给帮写一下:handshake :loveliness:

ifuleu 发表于 2011-12-10 17:01

这儿还是需要用else if语句。用swithch不好办

minyang3011 发表于 2011-12-10 17:46

很不错的,也试试学习学习。

czxcv 发表于 2011-12-10 20:35

我弟弟也在玩单片机,我就没那兴趣。太麻烦了

bear375 发表于 2011-12-10 23:33

页: [1] 2 3 4 5
查看完整版本: 单片机边学边玩---带有峰值保持的双15段LED电平显示电路,终于搞定!不喜勿入