S3C2440無操作系統(tǒng)實驗(一)——LED部分
小小達摩 2011-6-23
硬件:飛凌FL2440開發(fā)板
軟件:ADS1.2
拿到ARM開發(fā)板,應該從哪兒開始學習呢?困擾了很多初學者,當然也正在困擾我,因為我也是初學者。所以我想與其困惑,還不如先做些自己能做的,將會做的做熟悉了,也許就找到學習的方向了吧。那么從哪兒入手呢,想來想去,那就從點亮一個LED燈作為學習的起點。雖然很多人,不推薦用學習單片機的模式來學習ARM,但是我感覺基礎還是重要的,再說高級的學習暫時也不會呀,呵呵。雖然這樣學習會慢點,但是總比開發(fā)板落厚厚一層灰要劃得來吧(開發(fā)板買了快2年了,拿來玩的時間不超過2個月,也許很多朋友都有這樣的經歷吧)。所以,把它拿出來,開始學習吧。
不管是單片機還是ARM,對其進行控制其實就是對相應的寄存器進行控制,如果能夠對常用寄存器進行熟練的操作,那么基本也就掌握了這種芯片的使用方法。所以就先從寄存器介紹開始。
1、相應寄存器介紹:
LED燈是接在某一個I/O上的,點亮或者熄滅LED燈其實就是對I/O口寄存器的操作。ARM的I/O口寄存器主要包括端口配置寄存器GPXCON、端口數據寄存器GPXDAT、端口上拉電阻使能寄存器GPXUP、MISCELLANEOUS控制寄存器和外部中斷寄存器五種(其中X為芯片的I/O口字母)。在這個試驗中我們只使用前三個寄存器,下面就對這三個寄存器進行簡單的介紹。
舉個例子說明可能更容易理解,比如快遞員要送包裹給張三。當貨物送到以后,如何來確定收貨人就是張三呢,這個時候快遞員就要查看張三的身份證。因為它上面的號碼是唯一的。通過身份證號碼,就可以準確無誤地確定張三的身份。
對于ARM芯片來說,也有這樣的問題,因為它其中的寄存器很多,那么根據什么來區(qū)分寄存器呢?為了能讓ARM找到相應的寄存器,系統(tǒng)給每個寄存器都分配一個固定地址,地址就像一個人的身份證號碼一樣,是唯一的。那么當你使用這個地址的時候,ARM就知道你要操作哪個寄存器了。所以在編程的時候,首先要聲明待操作的寄存器的地址。下表就是我們要使用的三種寄存器地址和位定義:

當快遞員找到張三后,就通知張三有個包裹需要簽收。同樣的道理,ARM芯片找到相應寄存器后,也先要設置端口配置寄存器GPBCON,也就是告訴寄存器做好接收數據或者輸出數據或者響應外部中斷的準備,下表為它的功能描述:

當張三簽收后,就可以得到包裹了。ARM設置完I/O口狀態(tài)后,就準備讀/寫數據了。這個功能可以通過設置數據寄存器GPBDAT來實現。下表即為它的功能描述:

設置完前兩個寄存器后,還要設置上拉電阻使能寄存器GPBUP,顧名思義,它的作用就是告訴ARM這個端口要不要配置上拉電阻。上拉電阻、下拉電阻的作用在于,當IO引腳處于第三態(tài)(即不是高電平,也不是低電平,而是高阻態(tài),相當于沒接芯片)時,它的電平狀態(tài)由上拉電阻、下拉電阻確定。下表為它的功能描述:當為0時,上拉電阻是允許的;反之,則上拉電阻是被禁止的。

到此,需要的寄存器就配置完成了,那么接下來介紹下LED在開發(fā)板上的電路原理圖。
2、電路原理圖介紹:
電路如下圖所示,這個電路采用灌電流方式驅動LED,只需加一限流電阻即可。根據基本電路知識可以計算出限流電阻R的值:
R={U(電源)-U(LED端電壓)}/IO口推薦電流
由LED的手冊參數可知,當LED內流過10mA的電流時,兩端壓降約為1.7V,即U(LED壓降)=1.7V;S3C2440普通I/O口推薦電流為4mA。據此就可以計算出R的值為400Ω,一般會留有余量,比如取到470Ω。因為LED燈的亮度由流過的電流決定,FL2440電路板采用10K的電阻,就是為了減弱LED燈的亮度,要不就會很刺眼。所以在設計電路的時候,可以根據實際需要做些調整。
LED的工作原理很簡單,要讓LED發(fā)光,首先要將I/O口設置為輸出模式,然后控制I/O口輸出不同的電平。當輸出為低電平時,LED燈發(fā)光;反之當輸出為高電平時,LED燈熄滅。

3、例程
下面就通過幾個例子來練習下這幾種寄存器的用法。如果不會使用ADS,可以參考下我寫的《ADS使用方法》一文,可以方便快速上手。
實驗一:點亮一個LED燈。
首先要編寫init.s匯編源文件,這段程序的目的是進行一些初始化操作,并跳轉到主函數中繼續(xù)執(zhí)行后面的程序。
Init.s程序:
1 AREA |DATA|,CODE,READONLY
2 ENTRY
3 ldr r13,=0x1000
4 IMPORT led0
5 b led0
6 END
注意:前面必須要有空格,不能頂格寫;前面的數字為了方便分析程序才加的,編寫程序的時候不需要。init.s程序只是在跳轉的目標函數上有區(qū)別,所以以后的程序只需要改變第四句和第五句中的函數名就行了,所以,以后省略對此函數的介紹。
程序分析:
第一句:
AREA |DATA|,CODE,READONLY
這句是用AREA偽指令定義一個段名為|DATA|的代碼段,屬性為只讀。再給段命名的時候要注意,如果是以非字母開頭的話,那么要用“|”將段名括起來。所以本句中的段名取掉“|”也可以正常執(zhí)行。
第二句:
ENTRY也是偽指令,用來指定匯編程序的入口點。
第三句:
ldr r13,=0x1000
LDR指令用于從寄存器中將一個32位的字數據傳送到目的寄存器中。因為2440處理器的內部有4K的RAM,0x1000是放到了4K的最高端,也就是堆棧所對應的地址。
第四句:
IMPORT led0
IMPORT偽指令用于通知標號led0在其它源文件中定義,但要在當前源文件中引用。
第五句:
b led0
B為最簡單的跳轉指令,當遇到B指令,ARM處理器就立即跳轉到給定的目標地址,并從那里繼續(xù)執(zhí)行。
第六句:
END
END偽指令用于通知編譯器已經到了源程序的結尾。
Led0.c主程序
程序控制流程:

將程序加載到開發(fā)板上,就可以看實驗的結果了,其它LED燈的控制和LED0一樣,只是寄存器的設置值不同罷了。
實驗二、流水燈
終于能點亮一個LED燈了,那么它有什么用呢?它的主要用途在于一些狀態(tài)的顯示上,比如可以應用在開發(fā)板的電源指示燈上。但是這樣會很單調,所以接下來我想讓LED燈動起來,也就是4個燈順序點亮,但是每次只有一個燈保持點亮狀態(tài),也就是所謂的流水燈或者跑馬燈,那么怎樣來實現呢?流水燈的原理就是點亮一個LED燈延遲一段時間后熄滅,然后點亮另外一個燈,延遲后熄滅,反復循環(huán),就實現了流水的功能。知道原理了,程序編寫就方便多了。
程序控制流程:

流水燈程序:
/******************************************
程序的目的:流水燈
I/O口與LED的對應關系:
GPB5 LED0
GPB6 LED1
GPB8 LED2
GPB10 LED3
********************************************/
/*---------地址聲明----------*/
#include "2440addr.h"
注釋:在本程序的開頭,引入一個2440addr.h的頭文件,該文件中定義了我們要常用的寄存器的地址,以后的程序中,只需引入即可。引入的方法是將這個頭文件直接放在ADS安裝文件的include中就行了。
/*---------變量聲明---------*/
#define uint unsigned int
/*---------函數聲明----------*/
void delay(uint); //延時函數
#define LED0_ON() (rGPBDAT&=~(1<<5)) //LED0點亮
#define LED1_ON() (rGPBDAT&=~(1<<6)) //LED1點亮
#define LED2_ON() (rGPBDAT&=~(1<<8)) //LED2點亮
#define LED3_ON() (rGPBDAT&=~(1<<10)) //LED3點亮
#define LED0_OFF() (rGPBDAT|=(1<<5)) //LED0熄滅
#define LED1_OFF() (rGPBDAT|=(1<<6)) //LED1熄滅
#define LED2_OFF() (rGPBDAT|=(1<<8)) //LED2熄滅
#define LED3_OFF() (rGPBDAT|=(1<<10)) //LED3熄滅
注釋:這種對LED的控制方式非常方便。
/*-----------主函數----------*/
int LSD_Main(void)
{
rGPBCON=0x1dd7fc; //將GPB5、GPB6、GPB8、GPB10設置為輸出
rGPBUP=0xfff; //禁止GPB端口上拉
rGPBDAT=((1<<5)|(1<<6)|(1<<8)|(1<<10)); //讓LED燈全滅
while(1) //程序進入流水燈死循環(huán)
{
LED0_ON();
delay(30);
LED0_OFF();
LED1_ON();
delay(30);
LED1_OFF();
LED2_ON();
delay(30);
LED2_OFF();
LED3_ON();
delay(30);
LED3_OFF();
delay(30);
}
return 0;
}
/*---------延時函數---------*/
void delay(uint x)
{
uint i,j,k;
for(i=0;i<x;i++)
for(j=0;j<=0xff;j++)
for(k=0;k<=0xff;k++);
}
到現在,流水燈的實驗就做完了。但是這種流水燈太過于單調,我想讓LED燈按照我要求的方式流動,那應該怎么實現呢?那么就是下面要實現的花樣燈實驗的內容了。
實驗三 花樣燈
花樣燈的目的就是讓LED燈可以隨意流動。
/******************************************
程序的目的:花樣燈
I/O口與LED的對應關系:
GPB5 LED0
GPB6 LED1
GPB8 LED2
GPB10 LED3
********************************************/
/*---------地址聲明----------*/
#include "2440addr.h"
/*---------變量聲明---------*/
#define uint unsigned int
/*---------函數聲明----------*/
void Delay(uint); //延時函數
void BoardInit(void);
uint tab[]={
0x29E,0xffe,0xfde,0xfbe,
0xefe,0xbfe,0xefe,0xfbe,
0xfde,0xebe,0xbde,0xffe};
注釋:用數組方式來實現花樣燈。
/*-----------主函數----------*/
int HYD_Main(void)
{
BoardInit(); //板子初始化
注釋:將初始化放在一個函數中,這樣使程序看起來清晰明了。
while(1) //程序進入花樣燈死循環(huán)
{
int i;
for(i=0;i<13;i++)
{
rGPBDAT=tab[i];
Delay(20);
}
}
return 0;
}
/*---------延時函數---------*/
void Delay(uint x)
{
uint i,j,k;
for(i=0;i<x;i++)
for(j=0;j<=0xff;j++)
for(k=0;k<=0xff;k++);
}
/*---------初始化函數-------*/
void BoardInit( )
{
rGPBCON=0x1dd7fc; //將GPB5、GPB6、GPB8、GPB10設置為輸出
rGPBDAT=((1<<5)|(1<<6)|(1<<8)|(1<<10)); //讓LED燈全滅
rGPBUP=0xfff; //禁止GPB端口上拉
}
到此,LED燈的實驗基本就做完了。
PDF格式下載地址:http://wenku.baidu.com/view/15373bdad15abe23482f4dbf.html