S3C2440裸機實戰 之一 建立初始工程
2015-2-3
好幾年沒玩S3C2440,從單片機玩到嵌入式,就記得這個是我入門嵌入式比較早的一款晶片。
S3C2440是帶MMU的,可以上WinCE/Linux這些作業系統,适合做嵌入式開發,做單片機開發的話,總感覺有點浪費,但如果隻是玩玩應該沒問題。
最近業餘時間想把S3C2440拿來當單片機玩玩。
廢話少說,直接入主題。
第一步當然是建立初始工程。
既然是裸機實戰,想玩點什麼特色,看來彙編是不能少的了。ARM9彙編支援的好一點的當然是MDK,IAR的彙編文法和ARM官方的有一些差別,還是算了。
gcc 文法和ARM官方是差不多,不過編譯出來的代碼品質比不過MDK。這裡就選用MDK5.1
單片機的helloworld都是從LED開始,我這裡也不例外,就從Led開始,或者說從GPIO開始。
打開MDK5,如果已經有工程随MDK啟動時打開,就先關閉目前已經打開的工程。
1.建立新工程,要求選擇晶片型号,直接在搜尋框輸入S3C2440A,确認。
MDK會自動加入S3C2440.s啟動檔案,裡面用ARM彙編語言編寫的,對于不太熟悉彙編的童鞋來說,可能看懂還是有點難度。
這裡就先不用去看,直接使用它建立完整工程,否者後面編譯連結通不過。
到後面如果需要加入一些有趣的功能,再回到這個檔案中修改,或者按需要直接重寫。
2.設定
1)Target頁,
IRAM1 Start 0x40000000 Size 0x1000
去掉Use Cross-Module Optimization 代碼前期建議不要開優化
去掉Use mirolib
2) Output頁
勾上Create HEX File
3)User頁
勾上 Run #1
後面填上 fromelf.exe --bin -o @p.bin @p.axf
4)後面 Debug和 Unitiily頁按仿真器類型修改,其他頁預設不用修改。
3.初始工程當然還是先完成main函數,那就先建立個main.c的檔案,編寫基本的main函數。
int main(void)
{
unsigned int a = 0;
unsigned int b = 1;
while(1){
a = b ;
b = a;
}
return 1;
}
編寫完後,當然首先想到的是編譯嘛。沒想到一編譯出來各種各樣奇怪的錯誤。
仔細看資訊,發現不是編譯錯誤,而是在連結階段出的錯誤。
好吧,估計是分散加載設定有問題了。
4.編寫分散加載腳本。
點選options for target,也就是工具欄中的設定鍵,到Linker一欄,去掉Use Memory Layout from Target Dialog,這樣就可以用
Scatter File,也就是分散加載腳本方式。我個人的習慣是喜歡用分散加載腳本,上面點勾選的是簡單的設定方式,隻能支援比較簡單的分散加載要求。
複雜的分散加載,它就做不了了,分散加載腳本是簡單和複雜通吃。
去掉勾後,系統預設給出早工程檔案的同目錄下的同工程名的分散加載腳本名,點選後面的Edit。
用下面的代碼覆寫它,儲存後,重新全部編譯,通過。
ER_ROM1 0x40000000
{
ER_ROM1 0x40000000
{
*.o (RESET, +First)
*(InRoot$$Sections)
* (+RO)
}
RW_RAM1 0x40000800
{
;S3C2440.o (MyStacks)
.ANY (+RW,+ZI) ; * (+RO)
}
HEAP +0 UNINIT
{
S3C2440.o (Heap)
}
STACK 0x40001000 UNINIT
{
S3C2440.o (STACK)
}
}
那是不是到這裡就OK了呢,我也不知道,接下來先仿真下。
2015-2-9
5.仿真測試
一仿真才知道出了大問題,ARM的體系,系統棧是滿遞減方式的,是以棧頂不能超過0x40001000,棧底就更不能超了
計算了下棧容量,應該是0x488,看了map檔案,棧頂到了0x40001488,是以腳本要看一下
ER_ROM1 0x40000000
{
ER_ROM1 0x40000000
{
*.o (RESET, +First)
*(InRoot$$Sections)
* (+RO)
}
RW_RAM1 0x40000800
{
;S3C2440.o (MyStacks)
.ANY (+RW,+ZI) ; * (+RO)
}
HEAP +0 UNINIT
{
S3C2440.o (Heap)
}
STACK 0x40000B00 UNINIT
{
S3C2440.o (STACK)
}
}
這會編譯下,仿真能進main函數了,但是卻老會重新開機,另外發現CPU跑的是自己之前燒寫到nandflash前4KB的程式,說明仿真時看到的代碼其實是
steppingstone的代碼,且在仿真時,指向第一條指令的位置是0x00000000,而不是0x40000000,看來仿真時仿真期沒有讓PC指向0x40000000,是以仿真時跑的是steppingstone的代碼。
在debug一欄中edit initization file中加上以下代碼,這裡隻是想讓PC直到0x40000000,把初始化時鐘,RAM等去掉,以後需要再加。
既然是裸機實戰,那還是盡量讓代碼來做我們需要的功能,腳本替我們做了初始化工作就沒意思了。當然PC的初始化是沒辦法了,隻能通過腳本來設定,畢竟現在在
nandflash前4KB的代碼還不是我們自己的代碼,不能掌控。
FUNC void SetupForStart (void) {
// <o> Program Entry Point
PC = 0x40000000;//0x30000000
}
FUNC void Init (void) {
// Clock Setup
// FCLK = 300 MHz, HCLK = 100 MHz, PCLK = 50 MHz
// _WDWORD(0x4C000000, 0x0FFF0FFF); // LOCKTIME
// _WDWORD(0x4C000014, 0x0000000F); // CLKDIVN
// _WDWORD(0x4C000004, 0x00043011); // MPLLCON
// _WDWORD(0x4C000008, 0x00038021); // UPLLCON
// _WDWORD(0x4C00000C, 0x001FFFF0); // CLKCON
}
// Reset chip with watchdog, because nRST line is routed on hardware in a way
// that it can not be pulled low with ULINK
//_WDWORD(0x40000000, 0xEAFFFFFE); // Load RAM addr 0 with branch to itself
CPSR = 0x000000D3; // Disable interrupts
PC = 0x40000000; //0x30000000 // Position PC to start of RAM
//_WDWORD(0x53000000, 0x00000021); // Enable Watchdog
//g, 0 // Wait for Watchdog to reset chip
//Init(); // Initialize memory
LOAD project.axf INCREMENTAL // Download program
SetupForStart(); // Setup for Running
再次仿真,沒有問題了。
好,到此,S3C2440裸機的第一個工程已經差不多完成了。
在仿照CMSIS編寫了外設頭檔案後,再來操作Led。
這裡是借由MKD提供的彙編啟動代碼來做的,懂彙編的童鞋可以自己寫一個彙編啟動,如果不懂也沒什麼,可以照着ARM7的彙編啟動代碼寫,例如LPC213x/214x系列的彙編啟動代碼。
推薦個連結,對MDK提供的S3C2440彙編啟動代碼的解讀:
http://www.oschina.net/question/565065_115207