第一個程序,讓你的板子唱國歌(敗筆)

雖然是敗筆,但是文中基本上介紹了一個小應用實現及問題分析的全過程,希望能夠對你有幫助,大膽貼出!畢竟這篇文章寫了好久。。。額,寫文章的人傷不起啊傷不起。。。


學生時代總會有這麼一句笑話:“XXX!去,給我蹲政教處門口唱國歌!”

      一直在思索第一個例子用什麼,才能引起學生的興趣,點亮板子的LED?貌似很無聊啊,你也不能拿着給同學炫耀,毫無吸引力,那麼就索性用這個例子吧,順便也抒發一下我對祖國的熱愛。讓我們的板子唱起國歌來!

      前記:在這篇文章之前,我並沒有做過類似的實驗,所以我們站在同一個起跑線上,千萬不要抱怨別人比你厲害是因爲他比你聰明。如果試驗成功,板子真的這麼唱歌了,像我這麼笨的人都做到了,你沒有理由做不到,這篇文章讓你找回自信。

      首先要貼出來一篇參考文章,是基於C51的唱國歌的例子,做人要實在,我不想把它說成是我原創的,因爲我也沒學過C51,呵呵,但是我會舉一反三。因爲ARM板裸奔跟寫C51的程序差不多,雖然扼殺了它的處理能力(本該是跑系統的),但是要練習對硬件的操控能力和做接口實驗,這些是必須的。下邊是文章內容:

蜂鳴器使用在很多的場合,他一般用來發出報警或者提示的聲音,是一種常用的電子器件,這裏我給大家 簡單的介紹一下用單片機驅動蜂鳴器的方法, 蜂鳴器有二種 1.本身帶有驅動電路, 5v,9v,12v 超電壓使用, 分 聲音沙啞失真。2.象 call 機,喇叭一樣,用軟件驅動。頻率控制音調,時間控制音量大小,第一種蜂鳴器 一般都有一個固定的頻率參數也就是他他發出的聲音是不能變化的,就象食堂用的打卡器一樣,卡一貼近 就發出都的一聲。第 2 種就不同了用單片機驅動第 2 種蜂鳴器後還可以使他演 奏出美妙的音樂,我們只需 要用簡單的程序就可以控制單蜂鳴器所奏的頻率,也就控制了音調。
 c51程序實例: 單片機驅動蜂鳴器演奏中華人民共和國國歌的前 4 節的 c51 程序: 

#include <REG52.h> 
sbit BUZ=P2^6; //蜂鳴器接單片機的p2.6 電路很簡單。
 unsigned int hzs[]={131,147,165,175,196,220,247,262,294,330,349,392,440,494,523,587,659,698,784,880,988,1047,1175,131 9,1397,1568,1760,1976};//標準音調頻率 
char dots[]={8,28,10,12,12,13,28,28,12,28,10,28,8,12,12,12,10,28,28,8,28,5,5,5,5,5,5,8,'#'};//頻率控制數組
 void delay(unsigned int u) //延時 
{ 
while(u--); 
} 
void play_hz(unsigned int u) { 
unsigned int i=u; 
while(i--){
 		BUZ=0; 
BUZ=1;
 delay(18432/u-24); 
} 
} 
void play_sound(int i) {
 if(i<28) 
play_hz(hzs[i-1]); 
else 
delay(500); } 
void main(void) //主程序開始 
{ 
while(1){
 int i=0; 
while(dots[i]!='#') 
play_sound(dots[i++]); 
delay(20000);
 } 
} //播放音樂 
音調與頻率的關係: 
C:261.6256
D:293.6648 
E:329.6276 
F:349.2282 
G:391.9954 
A:440 
B:493.8833 
每個 8 度頻率加倍,如 A 的高 8 度是 880HZ,再高 8 度是 1760HZ。 反之,A 的低 8 度是 220HZ,再低 8 度是 110HZ。

完文章,我們來做ARM下的實現:

我想當你看完文章,應該有些思路了吧,不知道我們的思路一樣不一樣,也不知道這個實驗可否成功,讓我們一起去探索一下》》》》》》

      我的思路是這樣的:蜂鳴器的控制應該和LED的控制是一樣的,根據電路設計,在對應的控制寄存器中的對應控制位寫1(或者0)他就唱,相反,他就不唱。而不同的音調就是通過頻率來控制的,而頻率就是一個動作週期性變化的次數,上邊的程序已經寫得非常到位,簡潔有力,通過while循環和時延控制頻率。理論上我們只用作少量改動就可以使用。而這些改動就要看我們自己的板子的設計了,我的是TQ2440,雖不知你的是不是,但是隻要你可以在你的板子上遷移成功,證明你已經提升了,OK,here we go。


      先看一下TQ2440 的 buzzer相關的電路(在TQ2440底板原理圖裏):



我們看到了只有一條接入的控制線  TOUT0 ,讓我們到S3C2440的針腳中找找它去》》》》》》(在TQ2440_V2核心板原理圖.pdf中,直接ctrl+F搜索一下)



我們看到TOUT0和GPB0是共用一個針腳的,就是可以通過GPIO接口來控制這個針腳輸出高低電平,從而控制Buzzer的聲響和聲調,咱們現在就去S3C2440的datasheet裏探尋一下這個針腳的控制信息》》》》》



圖中我標出了一些信息,這裏有GPBCON,故名思意,CON就是configuration的意思,起到針腳的配置作用,這裏還有GPBCON的地址信息。GPBDAT 就是用來控制針腳輸出的高低電平的。而在最下邊GPB0佔用的只是GPBCON的0位和1位[1:0],而當這兩位是10時,該陣腳就是TOUT0。Ok,基本上明白了吧,我們以上邊的代碼爲基礎,進行簡單更改,下邊是我改後的代碼:

主要邏輯:beep.c

#define GPBCON (*(volatile unsigned long*)0x56000010)
#define GPBDAT (*(volatile unsigned long*)0x56000014)
/* 標準音調頻率 */
unsigned int hzs[]={131,147,165,175,1 96,220,247,262,294,330,349,392,440,494,523,587,659,698,784,880,988,1047,1175,1319,1397,1568,1760,1976};
/*頻率控制數組*/
char dots[]={8,28,10,12,12,13,28,28,12,28,10,28,8,12,12,12,10,28,28,8,28,5,5,5,5,5,5,8,'#'};
		
/*延時*/
void delay(unsigned int u) 
{ 
	while(u--);
}

void play_hz(unsigned int u)
{ 
	unsigned int i=u;
	while(i--){
		
 		GPBDAT &= ~(1);
		GPBDAT &= ~(0);
		
 		delay(18432/u-24);
	}
}

void play_sound(int i)
{
 	if(i<28) 
		play_hz(hzs[i-1]);
	else 
		delay(500);
}

int main(){
	GPBCON = (1<<1);			
	while(1){
 		int i=0; 
		while(dots[i]!='#')
			play_sound(dots[i++]);
		delay(20000);
 }	
	return 0;
}

hard_init.S  — 完成硬件初始化,關閉看門狗、初始化堆棧、調用main等的工作:

/*這個文件是寫裸機程序都要用到的,所以可以保留,以後的裸機程序都可以專注在C語言部分了,不過我沒打算寫多少裸機實驗,因爲我們的目標是Linux*/


@*******************************
@	File:hard_init.S
@ 功能:通過該程序轉入C程序
@
@	author:Jun		2011-7-20
@*******************************

.text
.global	_start
_start:
	ldr	r0,=0x53000000		@	WATCHDOG 寄存器地址
	mov	r1,#0x0
	str	r1,[r0]
	
	ldr	sp,=1024*4				@	設置堆棧,注意不能大於4K,因爲現在可用的內存只有4K,是片內的4K SRAM
	
	bl	main
	
halt_loop:
	b	halt_loop

Makefile   /*Makefile是參照的嵌入式Linux應用開發完全手冊的編譯選項,帶有反編譯的彙編文件,有助於調試和分析*/


beemp.bin:hard_init.S beemp.c
	arm-linux-gcc -g -c -o hard_init.o hard_init.S
	arm-linux-gcc -g -c -o beemp.o beemp.c
	arm-linux-ld -Ttext 0x0000000 -g hard_init.o beemp.o -o beemp_elf
	arm-linux-objcopy -O binary -S beemp_elf beemp.bin
	arm-linux-objdump -D -m arm beemp_elf > beemp.dis
clean:
	rm -f beemp.dis beemp.bin beemp_elf *.o


好的,我們放在/home/jun/arm/ext0/下,然後make。。。。




》》beemp.c:5: error:expected '}' before numeric constant

額,是語法錯誤,數組中的數誤加了一個空格,悲劇,重新編譯:



》》/home/jun/arm/ext0/beemp.c:23:undefined reference to `__aeabi_uidiv'

額,經過一番查詢,發現arm和 arm-linux –gcc 不支持浮點除法,很糾結啊,要自己實現除法,真蛋疼啊。然後用位移實現除法後代碼爲:


#define GPBCON (*(volatile unsigned long*)0x56000010)
#define GPBDAT (*(volatile unsigned long*)0x56000014)
/* 標準音調頻率 */
unsigned int hzs[]={131,147,165,175,196,220,247,262,294,330,349,392,440,494,523,587,659,698,784,880,988,1047,1175,1319,1397,1568,1760,1976};
/*頻率控制數組*/
char dots[]={8,28,10,12,12,13,28,28,12,28,10,28,8,12,12,12,10,28,28,8,28,5,5,5,5,5,5,8,'#'};		
/*延時*/
void delay(unsigned int u) 
{ 
	while(u--);
}
unsigned int div(unsigned int a,unsigned int b)     /*除法的簡單唯一實現,不是精確值*/
{
	b = b >> 1;
	a = a >> b;
	return a;
}

void play_hz(unsigned int u)
{ 
	unsigned int i=u;
	while(i--){
		
 		GPBDAT &= ~(1);
		GPBDAT &= ~(0);
		
 		delay(div(18432,u-24));
	}
}

void play_sound(int i)
{
 	if(i<28) 
		play_hz(hzs[i-1]);
	else 
		delay(500);
}

int main(){
	GPBCON = (1<<1);
			
	while(1){
 		int i=0; 
		while(dots[i]!='#')
			play_sound(dots[i++]);
		delay(20000);
 }	
	return 0;
}


好的,排除了這些錯誤後,再make一下,又杯具了:



報錯:

beemp.o:(.ARM.exidx+0x0): undefined reference to`__aeabi_unwind_cpp_pr0'

beemp.o:(.ARM.exidx+0x10): undefined reference to`__aeabi_unwind_cpp_pr1'

make: *** [beemp.bin] Error 1

這個錯誤是編譯器造成的,原來之前配置的EABI-4.3.3 編譯內核跟給力(編譯busybox也很給力的),編譯裸機程序就杯具了,真坑爹。只有換編譯器了,換編譯器容易,安裝好辦,不過你要把之前安裝的EABI-4.3.3時配置的環境註釋掉,換成新的編譯器的bin目錄,兩個共存的話肯定會有問題了,就像老師提問張三回答問題,班裏卻有兩個張三,不知道是哪一個。這裏附上3.4.5的編譯器(【下載地址】/*115網盤下載,如果鏈接過期請及時留言提醒,我第一時間更新下載鏈接*/),直接解壓到對應位置,修改環境變了即可,具體方法見前邊交叉編譯環境配置的文章。

OK,一番周折後,配好新的交叉編譯工具,我們重新make一下:



好的,順利通過,得到了二進制的.bin文件,這個就是我們要的……驗證奇蹟的時刻到了,我們通過FTP工具把編譯好的文件下載到windows下進行燒寫;(你可以用sjf2440.exe直接裸板燒寫,或者直接用uboot通過DNW下載到nandflash中運行);這裏我通過DNW下載,因爲之前沒有介紹過這個下載過程,這裏順帶的介紹一下:

1、開發板除了接電源線以爲,還要接串口線和USB(接板子的host端,標準USB接口端連電腦)

2、用USB傳輸,還要安裝相應的USB驅動,這個在開發板的光盤中可以找到,驅動是直接的驅動文件,在電腦的硬件管理裏手動安裝就好

3、安裝好後,並連接好了開發板,打開SecureCRT,文件----》快速鏈接----》協議裏選“Serial”,波特率115200,其他默認就好,然後,上電,在焦點在SecureCRT上的情況下點下空格鍵(或者其他按鍵),串口輸出會停在uboot的菜單項,然後就可以進行下載操作了。/*不過前提是你的板子上已經燒有uboot,這裏用的是TQ2440原廠帶的uboot,最好把uboot燒到norflash中,norflash比較穩定,較nandflash位反轉情況會穩定些,所以一般把bootloader放在norflash,把系統、測試程序、root文件系統放在nandflash,具體燒寫uboot過程,用並口通過jtag接口燒寫,也可以用jlink,具體做法在各個板子廠商的板子說明文檔裏應該都有介紹,並且肯定都比我說的專業、詳細。我就不再嗷述了*/


4、 我的串口輸出截圖:



選擇a,Download User Program(eg: uCOS-II or TQ2440_Test)。然後等待傳輸:



然後打開DNW.exe,按圖選擇你剛通過FTP考到的beep.bin,進行傳輸:



選擇後提示下載到nand成功。



然後重啓開發板,從nandflash啓動。。。。。。。。

我的啓動,我勒個去……一直“嘀~~~~”不對,應該是這樣“嘀——”就不間斷,更別提頻率和聲調了。看來只這樣傻帽似的用寄存器控制它是不好辦的,這個不懂電路和硬件太可怕了,看來這TOUT0的控制還是得用PWM。

額,我太單純了,單純的人總會受傷。此實驗以失敗告終。。。期待下次嘗試,就憑着我們的愛國精神,這國歌是必須要唱的。不過好歹還是有些收穫的。希望本文沒讓你失望了(我還是很自信的,哈哈)。



【本文doc文檔及源碼下載地址】


發佈了67 篇原創文章 · 獲贊 1099 · 訪問量 40萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章