FFT算法在單片機(jī)中的使用&&LCD12864驅(qū)動(dòng)
本次創(chuàng)新基金我是要做一個(gè)簡(jiǎn)易的頻譜儀,核心就是要進(jìn)行一個(gè)FFT運(yùn)算。大家知道,如果采用DSP芯片效果那是相當(dāng)好的。但由于項(xiàng)目資金以及時(shí)間不夠等情況,我采用的是ATMEL公司的AVR單片機(jī),這款單片機(jī)的FLASH存儲(chǔ)和內(nèi)存比51單片機(jī)犀利得多。
由于采用的是12864液晶,也就是一個(gè)橫128點(diǎn)豎64點(diǎn)的一個(gè)點(diǎn)陣,因而采用128點(diǎn)FFT運(yùn)算已然夠了,因?yàn)榧词沟玫皆俣嗟臄?shù)據(jù)也無(wú)法在液晶上可視化顯示出來(lái)。本文是基于128點(diǎn)FFT運(yùn)算。
程序如下:
#include
#include
#include
#define N 128
#define PI 3.141592653589
#define uchar unsigned char
#define uint unsigned int
typedef struct
{
int real;
int img;
}complex;
void initw(); //初始化旋轉(zhuǎn)因子
void bitReverse(); //比特反轉(zhuǎn)
void FFT();
complex x[N];
uchar vis[N];
void delayms(uint ms)
{
uint i,j;
for(i=0;i
{
for(j=0;j<3;j++);
}
}
void FFT()
{
int i,j,k,t,P,B,m;
complex up,down,product;
for (i=0;i<7;i++)
{
B=1<
for (j=0;j
{
t=1<<(6-i);
P=t*j;
for (k=j;k
{
complex product;
product.real=x[k+B].real*cos(2*PI*P/N)+x[k+B].img*sin(2*PI*P/N);
product.img=x[k+B].real*(-1)* sin(2*PI*P/N)+x[k+B].img*cos(2*PI*P/N);
x[k+B].real=x[k].real-product.real;
x[k+B].img=x[k].img-product.img;
x[k].real=x[k].real+product.real;
x[k].img=x[k].img+product.img;
}
}
}
}
void initw() //初始化旋轉(zhuǎn)因子
{
int i;
for (i=0;i
vis[i]=0;
}
void bitReverse() //比特反轉(zhuǎn)
{
int i,j=0;
int k=0;
int q=0;
complex tmp3;
for (i=0;i
{
int tmp=i,tmp2=0,j;
for(j=0;j<7;j++)
tmp2+=((tmp>>j)&1)*(1<<(6-j));
if(vis[i]==0)
{
tmp3=x[i];
x[i]=x[tmp2];
x[tmp2]=tmp3;
vis[i]=1;
vis[tmp2]=1;
}
}
}
void main()
{
uchar ii,y;
float tmp;
for (ii=0;ii<20;ii++)
{
x[ii].real=3;
x[ii].img=0;
}
for (ii=20;ii<128;ii++)
{
x[ii].real=0;
x[ii].img=0;
}
initw();
bitReverse();
FFT();
while(1);
}
上圖是8點(diǎn)FFT運(yùn)算,按照上圖的流程所示,F(xiàn)FT運(yùn)算主要有兩步,一步是比特反轉(zhuǎn),就是右邊不是按照0、1、2、3……這樣順序進(jìn)行計(jì)算的,而左邊是的,兩邊的關(guān)系就是進(jìn)行一個(gè)比特反轉(zhuǎn)??梢钥吹接疫?對(duì)應(yīng)二進(jìn)制為000,左邊對(duì)應(yīng)二進(jìn)制為000,右邊1二進(jìn)制001,左邊4對(duì)應(yīng)二進(jìn)制100,依次下去,可以清楚看到,對(duì)于8位FFT運(yùn)算,對(duì)應(yīng)二進(jìn)制有三位,而左右兩邊的關(guān)系恰巧是按照中間位進(jìn)行了個(gè)反轉(zhuǎn)。
FFT運(yùn)算第二步就是乘以旋轉(zhuǎn)因子,注意的是這里是復(fù)數(shù)運(yùn)算,虛部和實(shí)部都要加入運(yùn)算。乘以旋轉(zhuǎn)因子后對(duì)進(jìn)行加減運(yùn)算得到新的值,依次下去得到最終解。
由于單片機(jī)內(nèi)存的限制,因而對(duì)于傳統(tǒng)的FFT算法,我進(jìn)行了些改進(jìn),原則就是盡量地少使用變量,一個(gè)變量可以重復(fù)的使用是最理想的了,大家可以在程序中看出。個(gè)人意見(jiàn)這是能節(jié)省變量最少的了,如果有好的方法,希望可以告訴我下,我的郵箱是albertvictordu@139.com,謝謝!
下面是12864液晶驅(qū)動(dòng)程序的寫法:
LCD12864液晶,即像素為128*64的顯示液晶。它的每一行橫向一共有128個(gè)可顯示點(diǎn),每一列縱向有64個(gè),這些“點(diǎn)”其實(shí)也都是一個(gè)個(gè)發(fā)光二極管。它可以在一個(gè)16*16的點(diǎn)陣區(qū)域上顯示一個(gè)中文,也可以在一個(gè)8*16的點(diǎn)陣區(qū)域顯示一個(gè)非中文字符,一般稱為半寬字體。即一個(gè)中文字所占顯示面積是一個(gè)非中文字符的兩倍。
關(guān)于驅(qū)動(dòng)函數(shù)的書(shū)寫,是液晶顯示的基礎(chǔ),整個(gè)液晶驅(qū)動(dòng)主要有四個(gè)函數(shù)組成:
1、寫命令函數(shù);
2、寫數(shù)據(jù)函數(shù);
3、讀狀態(tài)函數(shù);
4、讀數(shù)據(jù)函數(shù);
這四個(gè)函數(shù)并不是必須全部寫的,具體要看你實(shí)現(xiàn)的功能,如果只是單純的顯示漢字和字符,寫命令、寫數(shù)據(jù)、讀狀態(tài)這三個(gè)函數(shù)就夠了,如過(guò)你還需要進(jìn)行一些繪圖的操作,那讀數(shù)據(jù)函數(shù)也必須書(shū)寫。
另外關(guān)于讀狀態(tài)函數(shù),其實(shí)也就是用于判忙操作,原則上每次對(duì)控制器進(jìn)行讀寫操作之前,都必須進(jìn)行讀寫檢測(cè),由于單片機(jī)的操作速度慢于液晶控制器的反應(yīng)速度,因此可不進(jìn)行讀寫檢測(cè),或者只進(jìn)行簡(jiǎn)短的延時(shí)即可。因此,讀狀態(tài)函數(shù)也可以不寫,只用簡(jiǎn)短的延時(shí)函數(shù)替換即可。
單片機(jī)用于控制LCD的管腳主要為RS、RW和E管腳,分別的功能是RS為0時(shí),對(duì)應(yīng)單片機(jī)訪問(wèn)的是命令寄存器,為1時(shí)對(duì)應(yīng)數(shù)據(jù)寄存器;RW為1時(shí),對(duì)應(yīng)單片機(jī)操作為讀操作,為0時(shí)對(duì)應(yīng)單片機(jī)為寫操作;E是使能信號(hào)。
讀操作如下圖所示
寫操作如下圖所示
在12864液晶中,開(kāi)發(fā)商將一些基本指令已經(jīng)寫入到命令寄存器中,我們調(diào)用該指令就可以完成相應(yīng)的功能。
LCD初始化
初始化操作如下:
1. 芯片上電;
2. 延時(shí)40ms以上;
3. 復(fù)位操作:RST出現(xiàn)一個(gè)上升沿(RST=1;RST=0;RST=1;);
4. 功能設(shè)定;
5. 延時(shí)100us以上;
6. 再次進(jìn)行功能設(shè)定;
7. 延時(shí)37us;
8. 顯示開(kāi)關(guān)控制;
9. 延時(shí)100us以上;
10. 清除顯示;
11. 延時(shí)10ms以上;
12. 進(jìn)入點(diǎn)設(shè)置;
13. 初始化結(jié)束;
LCD液晶屏初始化過(guò)程如圖所示為:
打點(diǎn)函數(shù)
打點(diǎn)函數(shù)是創(chuàng)建GUI的基礎(chǔ),打點(diǎn)函數(shù)的書(shū)寫分為以下幾個(gè)步驟:
1. 進(jìn)入擴(kuò)展模式
2. 寫入打點(diǎn)地址
3. 讀取該地址的數(shù)據(jù)
4. 修改該地址的數(shù)據(jù)
5. 將修改后的數(shù)據(jù)輸入LCD中
6. 進(jìn)入普通模式
GDRAM地址分布情況,需要注意的是橫縱坐標(biāo)的起始地址都是0x80,還有上下半屏的橫坐標(biāo)是不一樣的,下半屏的橫坐標(biāo)要加上0x08,而縱坐標(biāo)跟對(duì)應(yīng)的上半屏的縱坐標(biāo)是一樣的。GDRAM地址分布圖,如圖所示。
下面的函數(shù)是12864與FFT算法的一個(gè)結(jié)合,里面設(shè)置了一個(gè)門函數(shù),12864上顯示的結(jié)果則是一個(gè)sinc函數(shù),證明結(jié)果是正確的。
#include
#include
#include
#define N 128
#define PI 3.141592653589
#define uchar unsigned char
#define uint unsigned int
#define RS (1<<4)
#define RW (1<<5)
#define EN (1<<6)
//
typedef struct
{
int real;
int img;
}complex;
void initw(); //初始化旋轉(zhuǎn)因子
void bitReverse(); //比特反轉(zhuǎn)
void FFT();
complex x[N];
uchar vis[N];
void delayms(uint ms)
{
uint i,j;
for(i=0;i
{
for(j=0;j<3;j++);
}
}
//此處定義字符串
//寫數(shù)據(jù)
void WriteDataLCM(unsigned char WDLCM) //寫數(shù)據(jù)函數(shù)
{
// ReadStatusLCM(); //檢測(cè)忙
delayms(1);
PORTA|=RS; //RS=1
delayms(1);
PORTA&=~RW; //RW=0
delayms(1);
PORTA|=EN; //EN=1
delayms(1);
PORTB=WDLCM; //輸出數(shù)據(jù)
delayms(1);
PORTA&=~EN; //EN=0
delayms(1);
}
//寫指令
void WriteCommandLCM(unsigned char WCLCM) //寫命令函數(shù)
{
// ReadStatusLCM(); //根據(jù)需要檢測(cè)忙
delayms(1);
PORTA&=~RS; //RS=0
delayms(1);
PORTA&=~RW; //RW=0
delayms(1);
PORTA|=EN; //EN=1
delayms(1);
PORTB=WCLCM; //輸出指令
delayms(1);
PORTA&=~EN; //EN=0
delayms(1);
}
//讀狀態(tài):檢測(cè)忙
void ReadStatusLCM() //讀狀態(tài)函數(shù)
{
uchar temp;
uchar flag = 1;
while(flag==1)
{
PORTB=0xff;
delayms(1);
DDRB=0x00; //端口B改為輸入
delayms(1);
PORTA&=~RS; //RS=0
delayms(1);
PORTA|=RW; //RW=1
delayms(1);
PORTA|=EN; //EN=1
delayms(10);
temp = PINB; //讀端口B
delayms(10);
DDRB=0xff; //端口B改為
delayms(10);
PORTA&=~EN; //EN=0
delayms(1);
if(temp>>7==0)
flag = 0;
}
}
uchar read_data() //讀數(shù)據(jù)函數(shù)
{
uchar lcd_data;
PORTB=0xff;
DDRB=0x00;
PORTA|=RW;
PORTA|=RS;
delayms(10);
PORTA|=EN;
delayms(10);
lcd_data=PINB;
delayms(10);
PORTA&=~EN;
DDRB=0xff;
return(lcd_data) ;
}
void point(uchar x,uchar y) //打點(diǎn)函數(shù),最重要的函數(shù),GUI的基礎(chǔ)
{
uchar x_Dyte,x_byte;
uchar y_Dyte,y_byte;
uchar GDRAM_hbit,GDRAM_lbit;
WriteCommandLCM(0x36);
x_Dyte=x/16;
x_byte=x&0x0f;
y_Dyte=y/32;
y_byte=y&0x1f;
WriteCommandLCM(0x80+y_byte);
WriteCommandLCM(0x80+x_Dyte+8*y_Dyte);
read_data();
GDRAM_hbit=read_data();
GDRAM_lbit=read_data();
delayms(10);
WriteCommandLCM(0x80+y_byte);
WriteCommandLCM(0x80+x_Dyte+8*y_Dyte);
delayms(10);
if(x_byte<8)
{
WriteDataLCM(GDRAM_hbit|(0x01<<(7-x_byte)));
WriteDataLCM(GDRAM_lbit);
}
else
{
WriteDataLCM(GDRAM_hbit);
WriteDataLCM(GDRAM_lbit|(0x01<<(15-x_byte)));
}
WriteCommandLCM(0x30);
}
//LCM初始化
void LCMInit(void)
{
WriteCommandLCM(0x30); //三次顯示模式設(shè)置,不檢測(cè)忙信號(hào)
delayms(10);
WriteCommandLCM(0x30);
delayms(10);
WriteCommandLCM(0x30);
delayms(10);
WriteCommandLCM(0x30); //顯示模式設(shè)置,開(kāi)始要求每次檢測(cè)忙信號(hào)
WriteCommandLCM(0x08); //關(guān)閉顯示
WriteCommandLCM(0x01); //顯示清屏
WriteCommandLCM(0x06); //顯示光標(biāo)移動(dòng)設(shè)置
WriteCommandLCM(0x0C); //顯示開(kāi)及光標(biāo)設(shè)置
}
void clear(uchar dat) //清屏函數(shù)
{
uchar i,j,k;
uchar addr=0x80;
for(i=0;i<2;i++)
{
for(j=0;j<32;j++)
{
for(k=0;k<8;k++)
{
WriteCommandLCM(0x36);
WriteCommandLCM(0x80+j);
WriteCommandLCM(addr+k);
WriteDataLCM(dat);
WriteDataLCM(dat);
}
}
addr=0x88;
}
WriteCommandLCM(0x36);
WriteCommandLCM(0x30);
}
void heng(uchar a)
{
uchar i;
for(i=0;i<127;i++)
point(i,a);
}
void su(uchar a)
{
uchar i;
for(i=0;i<63;i++)
point(a,i);
}
void FFT()
{
int i,j,k,t,P,B,m;
complex up,down,product;
for (i=0;i<7;i++)
{
B=1<
for (j=0;j
{
t=1<<(6-i);
P=t*j;
for (k=j;k
{
complex product;
product.real=x[k+B].real*cos(2*PI*P/N)+x[k+B].img*sin(2*PI*P/N);
product.img=x[k+B].real*(-1)* sin(2*PI*P/N)+x[k+B].img*cos(2*PI*P/N);
x[k+B].real=x[k].real-product.real;
x[k+B].img=x[k].img-product.img;
x[k].real=x[k].real+product.real;
x[k].img=x[k].img+product.img;
}
}
}
}
void initw() //初始化旋轉(zhuǎn)因子
{
int i;
for (i=0;i
vis[i]=0;
}
void bitReverse() //比特反轉(zhuǎn)
{
int i,j=0;
int k=0;
int q=0;
complex tmp3;
for (i=0;i
{
int tmp=i,tmp2=0,j;
for(j=0;j<7;j++)
tmp2+=((tmp>>j)&1)*(1<<(6-j));
if(vis[i]==0)
{
tmp3=x[i];
x[i]=x[tmp2];
x[tmp2]=tmp3;
vis[i]=1;
vis[tmp2]=1;
}
}
}
void xian(uchar x,uchar y)
{
uchar i;
for(i=63;i>=y;i--)
point(x,i);
}
//主函數(shù)
void main(void)
{
uchar ii,y;
float tmp;
//端口初始化
DDRA=0xff;
PORTA=0xff;
DDRB=0xff;
PORTB=0xff;
DDRD=0xff;
PORTD=0x00;
delayms(20);
delayms(20);
LCMInit(); //LCM初始化 //液晶初始化
delayms(100);
clear(0x00);
heng(0);
heng(63);
su(0);
su(127);
for (ii=0;ii<20;ii++)
{
x[ii].real=3;
x[ii].img=0;
}
for (ii=20;ii<128;ii++)
{
x[ii].real=0;
x[ii].img=0;
}
initw();
bitReverse();
FFT();
for(ii=64;ii<128;ii++)
{
tmp=sqrt((x[ii].real*x[ii].real)+(x[ii].img*x[ii].img));
y= 63-(int)tmp;
point(ii-64,y);
xian(ii-64,y);
}
for(ii=0;ii<64;ii++)
{
tmp=sqrt((x[ii].real*x[ii].real)+(x[ii].img*x[ii].img));
y= 63-(int)tmp;
point(ii+64,y);
xian(ii+64,y);
}
while(1);
}
得到的圖片:
MATLAB仿真圖形: