` 本帖最后由 3010203109 于 2013-3-4 22:07 编辑
看到论坛上有很多朋友做了音乐频谱,感觉很炫,于是也模仿着做了一个。在制作过程中,从网上查了很多资料,得到很多帮助。我要非常感谢那些辛勤付出的网友们,是你们无私的付出让我们可以站在更高的起点奔跑。诗经有云:投我以木桃,报之以琼瑶。如果一味的索取,泉源也会枯竭。故将我制作的所有资料传上来,以作为对那些帮助过我的网友的答谢,也希望看到此贴的人能将此一做法传承下去, 为论坛贡献自己的一份力量。
效果图:
视频:
这个作品的制作可以分为三个过程:1、焊接LED点阵;2、设计控制 电路;3、编程
所谓音乐频谱就是将音乐的各个频率分量显示出来,LED点阵的水平轴代表各个频率,竖直轴代表强度。从下面的图中可以看出,该点阵大小为16*30(本来要做16*32,限于万用板大小只能容下30列)。每两列为一组,共15组,由于是阴极接在一起,姑且称之为共阴极组;同理,每一行的阳极接在一起,称为共阳极组,共16组。
点阵接线图:
正面:
(围成圆圈的灯是七彩快闪LED,只要接上 电源,其颜色自动变换。)
背面:
从图中可以看到,所有的阴极线(15根)、阳极线(16根) 都用排针引出来,再用杜邦线就可以和控制板相连。
控制电路图:
单片机用的是ATMEGA324PA(内部晶振,8MHZ),只用到通用IO功能,移植到其他单片机上也很方便。基本控制思路:扫描。每一时刻只让一个共阴极组接地,其它接高电平,从左至右依次选通每个共阴极组。每次选中共阴极组后,需要对其阳极写入数据,由于有16行,需要写入两个字节。假如以上方为高字节,我想让选中的共阴极组下面的9行灯亮,则应往阳极段写入的两个字节为:0x01,0xff。控制电路采用了4个锁存器74HC573,2个ULN2803(八路达林顿管,其实是一个8路反向器,可以承受大电流 ) 。左边两个573用来对阳极写入数据,右边两个573和2803接在一起,用来选通共阴极组。4个573的数据输入端都连到一起,接在单片机的PB口。573还有一个选通端LE,当其为高电平时,可以对其写入数据,低电平时输出锁存。将其作为片选(SS0,SS1,SS2,SS3)依次接单片机PA0,PA1,PA2,PA3。每次只让一个片选为1,就可以对每个573分开写入数据了。
最后说说编程。用了一个声音模块采集声音,声音模块 原理为:麦克接收声音,OUT端输出声音信号波形。OUT端接在单片机的PA7(AD转换端口)上。程序的循环中,先进行32次AD采样,得到32(必须是2的n次方)个采样值,做快速傅里叶变换,得到32个频率的强度,由于是前后对称的,只取其前15个频率的强度,经过幅值调整(调为0~16),在15个共阴极组上依次显示出来。
上程序:
1、
主函数(AD转换,FFT变换)
#define F_CPU 8 //可定义为你所用的晶振频率(单位Mhz)
#include
#include "delay.h"
#include "data.h"
#include "display.h"
#define DATA PORTB
#define SS0 PORTA_Bit0
#define SS1 PORTA_Bit1
#define SS2 PORTA_Bit2
#define SS3 PORTA_Bit3
#define pi 3.141592654
#include//要调入数学(和谐)运算的头文件
//定义复数结构
struct compx
{
float real;
float imag;
};
//定义复数乘法
struct compx EE(struct compx a,struct compx b)
{
struct compx c;
c.real=a.real*b.real-a.imag*b.imag;
c.imag=a.real*b.imag+a.imag*b.real;
return(c);
}
//定义复数求模
float cmplxabs(struct compx a)
{
float b;
b=sqrt(a.real*a.real+a.imag*a.imag);
return(b);
}
//FFT子程序
//入口:复数数组指针和点数。
//出口:原复数数组,取模后为强度,可用于频谱。
void FFT(struct compx *xin,unsigned int N)
{
int f,m,nv2,nm1,i,k,j=1,l;
int le,lei,ip;
struct compx v,w,t;
nv2=N/2;
f=N;
for(m=1;(f=f/2)!=1;m++);
nm1=N-1;
for(i=1;i<=nm1;i++)
{
if(i
{
t=xin[j-1];
xin[j-1]=xin[i-1];
xin[i-1]=t;
}
k=nv2;
while(k
j=j+k;
}
/* FFT*/
for(l=1;l
{
le=(int)pow(2.0,l);
lei=le/2;
v.real=1.0;v.imag=0.0;
w.real=cos(pi/lei);w.imag=-sin(pi/lei);
for(j=1;j<=lei;j++)
{
for(i=j;i<=N;i+=le)
{
ip=i+lei;
t=EE(xin[ip-1],v);
xin[ip-1].real=xin[i-1].real-t.real;
xin[ip-1].imag=xin[i-1].imag-t.imag;
xin[i-1].real=xin[i-1].real+t.real;
xin[i-1].imag=xin[i-1].imag+t.imag;
}
v=EE(v,w);
}
}
}
void ADC_Init()
{
ADCSRA=0xE3;//ADC使能,开始转换,自动触发转换,中断bit3,8分频
ADCSRB=0x00;//自由转换模式
ADMUX=0x47;//基准电压Internal 1.1V Voltage,通道0 4n通道n
}
char s[15][2]={0};
void start()
{
flash2(1);
flash4(1);
flash5(5);
}
void end()
{
scroll(67,word,1);
flash2(1);
while(1)xiaolian(5);
}
char counter=0;
#pragma vector=tiMER1_OVF_vect
__interrupt void TIMER1_OVF_ISR()
{
TCNT1=0;//8.4s
counter++;
if(counter==28)
end();
}
void TIMER1_INIT()//TIMER1 16位定时器
{
TCNT1=0;
TIMSK1=0x01;//T1溢出中断使能
SREG=0x80;//总中断使能
TCCR1A=0x00;
TCCR1B=0x05;//1024分频,开始计数
}
struct compx ss[32];
char w=0,bar[15]={0};
unsigned int t,tL,tH;
void main()
{
PORTA=0x00;
DDRA=0x0f;
PORTB=0x00;
DDRB=0xFF;
ADC_Init();
start();
TIMER1_INIT();
while(1)
{
tL=ADCL;
tH=ADCH<<8;
t=tH+tL;
ss[w].real=(float)t;//采样结果送实部,注意数据类型转换
ss[w].imag=0;
w++;
for(char i=1;i<10;i++)refresh(bar);
if(w==32)
{
w=0;
clear();
FFT(ss,32);
unsigned int sum;
for(char i=1;i<=15;i++)sum+=cmplxabs(ss);
sum/=15;//平均值
for(char i=0;i<=14;i++)
{
char temp=(char)(cmplxabs(ss[i+1])/250);
if(temp>16)temp=16;
if(bar>temp)
bar-=1;
else
{
bar=temp;
}
}
}
}
}
2、
底层驱动函数、动画显示函数、刷新函数
#define F_CPU 8 //可定义为你所用的晶振频率(单位Mhz)#include
#include "delay.h"
#include "data.h"
#define DATA PORTD
#define SS0 PORTA_Bit0
#define SS1 PORTA_Bit1
#define SS2 PORTA_Bit2
#define SS3 PORTA_Bit3
void clear()
{
PORTA&=0XF0;
PORTD=0X00;
PORTA|=0X03;//SS0,SS1
PORTA&=0XF0;
}
void clear_all()//全部清除
{
for(char i=0;i<15;i++)
{
s[0]=0;
s[1]=0;
}
}
void refresh(char *p)//刷新频谱
{
char k=0,tempU,tempD;
k=0;
PORTA&=0XF0;
PORTD=0X00;
PORTA|=0X02;//SS1
for(char i=0;i<=6;i++)
{
tempU=0;tempD=0;
if(p[k]>8)
{
for(char j=0;j
tempD=0xff;
}
else
{
tempU=0;
for(char j=0;j
}
PORTA&=0XF0;
PORTD=0X00;
PORTA|=0X0c;//SS2,SS3
PORTA&=0XF0;
PORTD=(1<
PORTA|=0X01;//SS0
PORTA&=0XF0;
PORTD=tempD;
PORTA|=0X04;//SS2 下8行
PORTA&=0XF0;
PORTD=tempU;
PORTA|=0X08;//SS3 上8行
//delay_ms(500);
k++;
}
delay_us(30);
PORTA&=0XF0;
PORTD=0X00;
PORTA|=0X0c;//SS2,SS3
PORTA&=0XF0;
PORTD=0X00;
PORTA|=0X01;//SS0
for(char i=0;i<=7;i++)
{
tempU=0;tempD=0;
if(p[k]>8)
{
for(char j=0;j
tempD=0xff;
}
else
{
tempU=0;
for(char j=0;j
}
PORTA&=0XF0;
PORTD=0X00;
PORTA|=0X0c;//SS2,SS3
PORTA&=0XF0;
PORTD=(1<
PORTA|=0X02;//SS1
PORTA&=0XF0;
PORTD=tempD;
PORTA|=0X04;//SS2 下8行
delay_us(10);
PORTA&=0XF0;
PORTD=tempU;
PORTA|=0X08;//SS3 上8行
k++;
}
delay_us(30);
}
}
void show(char p[15][2])//显示图片
{
char k=0;
k=0;
PORTA&=0XF0;
PORTD=0X00;
PORTA|=0X02;//SS1
for(char i=0;i<=6;i++)
{
PORTA&=0XF0;
PORTD=0X00;
PORTA|=0X0c;//SS2,SS3
PORTA&=0XF0;
PORTD=(1<
PORTA|=0X01;//SS0
PORTA&=0XF0;
PORTD=p[k][0];
PORTA|=0X08;//SS3 上8行
PORTA&=0XF0;
PORTD=p[k][1];
PORTA|=0X04;//SS2 下8行
k++;
}
PORTA&=0XF0;
PORTD=0X00;
PORTA|=0X0c;//SS2,SS3
PORTA&=0XF0;
PORTD=0X00;
PORTA|=0X01;//SS0
for(char i=0;i<=7;i++)
{
PORTA&=0XF0;
PORTD=0X00;
PORTA|=0X0c;//SS2,SS3
PORTA&=0XF0;
PORTD=(1<
PORTA|=0X02;//SS1
PORTA&=0XF0;
PORTD=p[k][0];
PORTA|=0X08;//SS3 上8行
PORTA&=0XF0;
PORTD=p[k][1];
PORTA|=0X04;//SS2 下8行
k++;
}
}
}
void clear_dot(char x,char y)//x:0~14 y:0~15清除点
{
if(y>=8)
{
s[x][0]&=~(1<<(y-8));
}
else
{
s[x][1]&=~(1<
}
}
void set_dot(char x,char y)//x:0~14 y:0~15显示点
{
if(y>=8)
{
s[x][0]|=1<<(y-8);
}
else
{
s[x][1]|=1<
}
}
void set_dot(char x,char y,char q[15][2])//x:0~14 y:0~15
{
if(y>=8)
{
q[x][0]|=1<<(y-8);
}
else
{
q[x][1]|=1<
}
}
void rise(char p[15][2],char n)//n:1~15 图像上移n行
{
unsigned int temp;
char q[15][2]={0};
for(char j=0;j<15;j++)
{
temp=(int)p[j][0]*256+p[j][1];
temp<<=n;
q[j][0]=temp/256;
q[j][1]=temp%256;
}
for(long i=0;i<300;i++)show(q);
clear();
}
void flash1(int t)//列右移
{
for(int v=0;v
{
clear_all();
s[0][0]=0xff;
s[0][1]=0xff;
for(char j=0;j<15;j++)
{
s[(j+1)%15][0]=s[j][0];
s[(j+1)%15][1]=s[j][1];
s[j][0]=0x00;
s[j][1]=0x00;
for(long i=0;i<400;i++)show(s);
}
}
}
void flash2(long t)//缩小的圈
{
signed char x=0,y=15;
for(int v=0;v
{
clear_all();
x=0;y=15;
for(char i=0;i<15;i++)
{
while(x<15-i)
{
set_dot(x,y);
x++;
for(int i=0;i<100;i++)show(s);
}
x--;
while(y>=i)
{
set_dot(x,y);
y--;
for(int i=0;i<100;i++)show(s);
}
y++;
while(x>=i)
{
set_dot(x,y);
x--;
for(int i=0;i<100;i++)show(s);
}
x++;
while(y<15-i)
{
set_dot(x,y);
y++;
for(int i=0;i<100;i++)show(s);
}
y--;
}
}
}
void xiaolian(int t)//笑脸
{
for(int v=0;v
{
for(long i=0;i<5000;i++)show(xiaolian1);
for(long i=0;i<2000;i++)show(xiaolian2);
}
//for(long i=0;i<5000;i++)show(xiaolian1);
}
void flash3(long t)//“RUN”垂直落下
{
for(int v=0;v
{
for(signed char i=15;i>=0;i--)rise(run,i);
for(long i=0;i<20000;i++)show(run);
}
}
void flash4(long t)//“RUN”笔画书写
{
int dd=200;
for(int v=0;v
{
clear_all();
for(signed char i=15;i>=1;i--)
{
set_dot(0,i);
for(int i=0;i
}
for(signed char i=1;i<=3;i++)
{
set_dot(i,15);
for(int i=0;i
}
for(signed char i=14;i>=9;i--)
{
set_dot(3,i);
for(int i=0;i
}
for(signed char i=3;i>=1;i--)
{
set_dot(i,9);
for(int i=0;i
}
for(signed char i=8;i>=7;i--)
{
set_dot(1,i);
for(int i=0;i
}
for(signed char i=7;i>=5;i--)
{
set_dot(2,i);
for(int i=0;i
}
for(signed char i=5;i>=1;i--)
{
set_dot(3,i);
for(int i=0;i
}
for(int i=0;i<2000;i++)show(s);
for(signed char i=15;i>=2;i--)
{
set_dot(5,i);
for(int i=0;i
}
for(signed char i=6;i<=7;i++)
{
set_dot(i,1);set_dot(i,2);
for(int i=0;i
}
for(signed char i=2;i<=15;i++)
{
set_dot(8,i);
for(int i=0;i
}
for(int i=0;i<2000;i++)show(s);
for(signed char i=15;i>=1;i--)
{
set_dot(10,i);
for(int i=0;i
}
for(signed char i=13;i>=11;i--)
{
set_dot(11,i);
for(int i=0;i
}
for(signed char i=10;i>=7;i--)
{
set_dot(12,i);
for(int i=0;i
}
for(signed char i=6;i>=3;i--)
{
set_dot(13,i);
for(int i=0;i
}
for(signed char i=1;i<=15;i++)
{
set_dot(14,i);
for(int i=0;i
}
for(int i=0;i<2000;i++)show(s);
}
}
void scroll(char l,char p[][2],char t)//滚屏
{
for(char j=0;j
{
for(int i=0;i<3200;i++)show(p);
p+=2;
}
}
void flash5(long t)//“RUN”闪烁
{
for(int v=0;v
{
for(int i=0;i<1000;i++)show(run);
clear();
delay_ms(50);
}
}
3、
存放需要显示的汉字字模
char word[][2]=
{
0x08,0x08,0x0b,0x30,0x08,0xc2,
0xff,0xff,0x08,0xc0,0x2b,0x30,
0x18,0x08,
0x00,0x00,
0x02,0x03,0xfa,0x7c,
0xaa,0x0c,0xab,0xfe,0xaa,0x63,
0xfa,0x63,0x02,0x01,0x00,0x01,//求是
0x00,0x00,
0x04,0x00,0x1b,0xff,0xe4,0x03,
0x33,0xc3,0x08,0x07,0x0f,0xf8,
0x00,0x02,0xff,0xff,
0x00,0x00,
0x32,0x48,0x2a,0x52,0xe7,0xff,
0x2a,0x50,0x32,0x48,0x0f,0xff,
0x12,0x00,0x23,0xff,0x42,0x00,//创新
0x00,0x00,0x00,0x00,0x00,0x00,
0x21,0x04,0xff,0xff,0x00,0x04,
0x3f,0xfa,0x65,0x12,0xa5,0x11,
0x3d,0xf1,
0x00,0x00,
0x00,0x01,0x08,0x08,
0x0b,0x30,0x08,0xc2,0xff,0xff,
0x08,0xc0,0x2b,0x30,0x18,0x08,//追求
0x00,0x00,
0x00,0x08,0x0f,0xc8,0x09,0x48,
0xf9,0x7f,0x49,0x48,0x4f,0xc8,
0x00,0x00,
0x02,0x0c,0x12,0x38,0xff,0xfc,
0x12,0x44,0x02,0x04,0x1f,0xfa,
0x10,0x92,0xff,0xe2,0x52,0x19,//卓越
0x00,0x00,0x00,0x00,0x00,0x00,
0x24,0x10,0x24,0x22,0x7f,0xff,
0x84,0x88,0x7f,0xf0,0x24,0x3e,
0x14,0xc3,
0x00,0x00,
0x08,0x00,0x3f,0xff,
0xc0,0x00,0x17,0xff,0x28,0x00,
0x20,0x00,0x20,0x02,0x3f,0xff,//我们
0x00,0x00,
0x02,0x03,0xfa,0x7c,
0xaa,0x0c,0xab,0xfe,0xaa,0x63,
0xfa,0x63,0x02,0x01,0x00,0x01,//是
0x00,0x00,
0xff,0xfe,0x83,0x80,0x82,0xe0,
0xfe,0x3e,0x00,0x00,0xff,0xfc,
0x00,0x06,0x00,0x06,0xff,0xfc,
0x00,0x00,0xff,0xfe,0x38,0x00,
0x07,0x80,0x00,0x78,0xff,0xfe,//RUN
0x00,0x00,0x00,0x00,
0x7f,0xfe,0x44,0x12,0x44,0x62,
0x44,0x8a,0x7f,0xfe,0x44,0x02,
0x7f,0xfe,0x00,0x00,0x7f,0xfe,
0x44,0x82,0x7b,0x0c,0x00,0x30,
0x7f,0xc0,0x00,0x30,0x00,0x0e,//团队
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
};
4、头文件
(1)
#ifndef __IAR_DELAY_H#define __IAR_DELAY_H
#include
#define delay_us(x) __delay_cycles ((unsigned long)(x * F_CPU))
#define delay_ms(x) __delay_cycles ((unsigned long)(x * F_CPU*1000UL))
#define delay_s(x) __delay_cycles ((unsigned long)(x * F_CPU*1000000UL))
#endif
(2)
#ifndef __IAR_DATA_H
#define __IAR_DATA_H
extern char s[15][2];
extern char run[15][2];
extern char tuandui[15][2];
extern char chuanxin1[15][2];
extern char chuanxin2[18][2];
extern char qiushi[15][2];
extern char women[15][2];
extern char zhuiqiu[15][2];
extern char zhuoyue[15][2];
extern char xiaolian1[15][2];
extern char xiaolian2[15][2];
extern char word[][2];
#endif
(3)
#ifndef __IAR_DISSPLAY_H
#define __IAR_DISPLAY_H
extern void refresh(char *p);
extern void clear_all();
extern void clear();
extern void show(char p[15][2]);
extern void refresh1(char *p);
extern void clear_dot(char x,char y,char q[15][2]);//x:0~14 y:0~15
extern void set_dot(char x,char y,char q[15][2]);//x:0~14 y:0~15
extern void clear_dot(char x,char y);//x:0~14 y:0~15
extern void set_dot(char x,char y);//x:0~14 y:0~15
extern void rise(char p[15][2],char n);//n:1~15
extern void flash1(int t);
extern void flash2(long t);
extern void xiaolian(int t);
extern void flash3(long t);
extern void flash4(long t);
extern void scroll(char l,char p[][2],char t);
extern void flash5(long t);
#endif
`
|