新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > ARM無痛苦起步(S3C44B0X)

        ARM無痛苦起步(S3C44B0X)

        作者: 時間:2016-11-20 來源:網絡 收藏
        arm(44b0x)無痛苦起步

          這兩個星期在看arm的資料,在朋友的幫助下弄明白了是怎么回事后,我覺得我原來看的入門資料總有些說的不詳細的地方。所以決定寫一個文檔,總結開始arm開發的入門知識,給后來的朋友參考,也希望得到高手指正我認識上的錯誤。我的開發板是s3c44b0x的,2m NOR FLASH在bank0,8m sdram在bank6.
          首先看看我們要解決的問題。有些ARM芯片有內嵌的RAM 和FALSH.這樣可以直接在片內運行程序,44B0X片內只有幾K CACHE,ROM和RAM都是外接的芯片。我們的程序是要寫入FLASH中保存,但執行時是拷到SDRAM中執行的(如在ROM中執行速度會較慢)。要做到這一點需要把程序做成兩個分程序:一個是實現你的系統功能的主程序,如果你用嵌入式系統,那就是UCOS和UCLINUX之類的程序,這個程序的代碼保存在FLASH中,但執行時會拷到RAM中再執行;一個是引導程序,直接在FLASH中執行,負責把初始化芯片和外設,并把主程序從FLASH中拷到RAM中,然后跳到主程序去執行,對應的概念是UBOOT等常見的引導程序,這個程序會被寫入0X0開始的地址,開機后自動執行。
          那么我們需要解決以下幾個問題:
          1.如何編譯和調試主程序
          2.如何使中斷跳到RAM中的中斷服務程序執行
          3.如何把引導程序和主程序寫入FLASH中.
        以下我們來解決這幾個問題:
          1 開始在仿真器中寫代碼和調試
          由于主程序會被拷貝到RAM中執行,則我們應該在編譯時就把程序定位到RAM中。這里先要說說幾個ADS的參數的意義,在ADS的ARM LINKER頁有RO,RW兩個參數,此外還有一個ZI沒有在頁中給出,RO是只讀代碼的起始地址,由這個地址開始存放編譯出來的程序指令;RW是程序的讀寫段的開始,即你程序中的數據存放的開始地址,ZI緊跟在RW區后,ZI區存放的是需要在程序運行時初始化為0的數據。
        了解這幾個鏈接參數的意義后我們可以設置這幾個參數了:對于我的44B0X板8M SDRAM在0XC00_0000.因此在開發時把ADS中的RO BASE的地址指定為0XC00_0000;置于RW,在程序完成前可以預先估計一下程序的體積有多大,需要用到的數據區有多大,避免數據區太小或代碼區覆蓋掉前面的數據區就是了,我用了0XC10_0000,1M的代碼空間,其他作數據區。這樣,我們編譯出來的程序代碼就是在0XC00_0000中,可以直接由仿真器寫入RAM中運行仿真運行。此外,在linker-〉layout頁有個object symbol和section的選項,要求你填入映像文件最開始的object文件名和段名,這兩個參數在仿真時不填寫也不會影響運行,因為仿真器會自動修改pc指針,但要建立能燒寫的映像文件,則一定要填寫好,具體填寫什么后面分析程序時再講。
          2中斷問題
          和所有單片機一樣,ARM復位后從地址0X0開始執行,而0X0后是一串默認的中斷向量表。對51這樣的芯片,我們會直接在這個中斷向量表中填入跳轉語句,讓它跳到指定的ISR處理中斷事件。由于我們的主程序是在RAM中執行的,編譯時又和引導程序分開,不可能預先知道我們寫的ISR具體地址,而預留的中斷向量表只夠每個中斷一個跳轉指令,因此我們需要做二次跳轉。在內存中建立一個中斷向量表,每個中斷對應一個字,存放ISR的地址。爾后,對每個中斷寫一段短的代碼,把ISR地址取出,填入PC。而0X0后面中斷向量的跳轉指令,則是跳到這小段程序中。
          3燒寫flash,ADX中似乎有個寫入flash的選項,我自己沒有具體用過。但聽說用jtag寫flash會比較慢。由于nor flash或nand flash都是可以編程燒寫的,即我們可以寫個程序擦寫flash,問題是如何讀取編譯出來的映像文件。這個也不用擔心,adx中有個菜單把文件內容寫入指定的地址中,把影響文件指定到一個ram地址,然后就用燒寫程序把ram的內容拷入rom中就是了。我們有個boot程序,一個主程序要映射到rom中.假設我把0xc20_0000開始的2m地址作rom的映像,則把boot程序導入0xc20_0000,boot的程序非常短,在0xc20_1000開始放主程序。然后把0xc20_0000到0xc40_0000的內容全部拷入rom中(當然在導入文件前這些ram應該先被清空或寫入ff.)。
          讓我們來看看相關的代碼,具體認識一下該怎么處理前面說的這些問題,還有另外的一些問題。這里使用的代碼是在44b0x的application note的第三章中拿出來的,這個文件在網上應該很容易找到。
          程序的入口在44binit.s匯編文件中,其中一個Init 段是整個程序的入口:
        AREA Init,CODE,READONLY
        ENTRY
        b ResetHandler ;for debug
        b HandlerUndef ;handlerUndef
        b HandlerSWI ;SWI interrupt handler
        b HandlerPabort ;handlerPAbort
        b HandlerDabort ;handlerDAbort
        b . ;handlerReserved
        b HandlerIRQ
        關鍵字ENTRY告訴編譯器保留這段代碼。從代碼看INIT段就是要寫入0X0地址的原始中斷向量,因此把這個文件編譯生成的44BINIT.O和INTT填入上面提到的LAYOUT頁對應項中。這樣編譯器會把該段代碼編譯到0X0地址。(仿真時你可以試試別填這兩個項目,看看ADX中的反匯編代碼入口被放到哪里)。
        這段代碼里除了reset句外,有每句都有一個HandlerXXX的標號,這就是前面提到的中斷處理程序的入口,它是由前面的一個宏來定義的:
        MACRO
        $HandlerLabel HANDLER $HandleLabel
        $HandlerLabel
        sub sp,sp,#4 ;decrement sp(to store jump address)
        stmfd sp!,{r0}
        USH the work register to stack(lr doest push because it return to original address)
        ldr r0,=$HandleLabel;load the address of HandleXXX to r0
        ldr r0,[r0] ;load the contents(service routine start address) of HandleXXX
        str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack
        ldmfd sp!,{r0,pc} OP the work register and pc(jump to ISR)
        MEND
          我自己沒有寫過宏,所以還是看編譯出來的代碼比較直接:
          HandlerSWI
          0x0c000198:  e24dd004  ..M.  SUB   r13,r13,#4
          0x0c00019c:  e92d0001  ..-.  STMFD  r13!,{r0}
          0x0c0001a0:  e59f0458  X...  LDR   r0,0xc000600
          0x0c0001a4:  e5900000  ....  LDR   r0,[r0,#0]
          0x0c0001a8:  e58d0004  ....  STR   r0,[r13,#4]
          0x0c0001ac:  e8bd8001  ....  LDMFD  r13!,{r0,pc}
        這是ads輸出的匯編代碼,就是剛才的宏對應swi的一個實例,其中有兩句
          LDR   r0,0xc000600
          LDR   r0,[r0,#0]
        是把0x0c000600的內容載入r0,再把r0地址的ram單元載入r0.去看看0xc000600的內容,是0X0c7fff08,這是我設定的內存中的中斷向量表地址之一,中斷向量表的起始地址是0X0c7fff00,因此0X0c7fff08存放的剛好就是swi的isr地址。后面程序就跳到對應的ISR去了。(這段宏程序由于我不熟悉arm的匯編,只看過它怎么執行,實在我不知道中斷向量表地址是如何被放入0x0c000600等地址的。希望有高手能再詳細解釋一下具體的編寫,編譯方法和原理。)
        在c程序中,我們需要給每個中斷向量定義一個宏:
          #define pISR_SWI (*(unsigned *)(_ISR_STARTADDRESS+0x08))
        _ISR_STARTADDRESS是起始地址0X0c7fff00,假設ISR是以下函數:
        void __irq SWI_UserIsr(void){……………}
        則在系統初始化時用pISR_EINT0=(unsigned)SWI_UserIsr;這樣的語句把ISR的地址填入中斷向量表中,對所有中斷作同樣的處理,然后開中斷,系統就能經過上面的宏把跳到ISR執行。
          44binit.s中還有幾段值得留意的代碼:以下的代碼把rw段的數據拷入ram中,并初始化zi段,即把該段清零:
        LDR  r0,=|Image$$RO$$Limit|
        LDR  r1,=|Image$$RW$$Base|
        LDR  r2,=|Image$$ZI$$Base| 
        CMP  r0,r1        
          BEQ  %F1
        0   CMP  r1,r3
          LDRCC r2,[r0],#4
          STRCC r2,[r1],#4
          BCC  %B0
        1   LDR  r1,=|Image$$ZI$$Limit|
          MOV  r2,#0
        2   CMP  r3,r1
          STRCC r2,[r3],#4
          BCC  %B2
        來看反匯編的代碼:
          0x0c000ae0:  e59f0194  ....  LDR   r0,0xc000c7c
          0x0c000ae4:  e59f1194  ....  LDR   r1,0xc000c80
          0x0c000ae8:  e59f3194  .1..  LDR   r3,0xc000c84
        0xc000c7c,開始的三個字的內容是:    
          0x0c000c7c:  0c000e10  ....  DCD  201330192
          0x0c000c80:  0c200000  .. .  DCD  203423744
          0x0c000c84:  0c200000  .. .  DCD  203423744
          0x0c000c88:  0c200004  .. .  DCD  203423748
        這些反匯編的代碼是一個點led的程序的,可以看出我的小程序代碼到0x0c000e10就結束了,0x0c200000是我指定的數據區起始地址。這段程序把|Image$$RO$$Limit| 開始的,長|Image$$ZI$$Base| -|Image$$RW$$Base| 的數據區拷到|Image$$RW$$Base|的對應單元,就是0x0c200000開始的一段ram中。后面還有|Image$$ZI$$Limit|,在我的代碼中是0x0c000c88,內容是0x0c200004.這其實表明我的小程序并沒有rw區,只有一個初始為0的變量。
          另外還有一段初始化ram控制器的代碼:
          ;****************************************************
          ;*  Set memory control registers          
          ;****************************************************
          ldr    r0,=SMRDATA
          ldmia  r0,{r1-r13}
          ldr    r0,=0x01c80000 ;BWSCON Address
          stmia  r0,{r1-r13},
        由于44b0x要求13個控制寄存器要一次完成填入,所以先把參數設定在SMRDATA的地址中,一次載入通用寄存器,在一次填入RAM控制寄存器中。4510的書上介紹調試前需要用SEMEM命令設置這些寄存器,但我自己沒有那么做也可以跑的很好,也許是默認已經用了最保守的配置的原因吧!
          其余的代碼解釋比較清晰了,最后摘出我的LED程序和這個小程序的BOOT程序以及燒寫程序。這幾個程序的project都包括了44binit.s, option.s, memcfg.s,option.h,44b.h幾個從app note中抄來的文件,這里只列了我自己寫的主要c代碼。其他這些文件我除了把ram和rom的對應配置改了一下外,都沒有改動。我的引導程序編譯出來是3k,led程序也是3k,因此我把他們分別定位在rom的0x0和0x2000處,一共寫了8k。
        LED程序中的44BINTT.S程序功能和LOAD中的44BINTT.S是重復的,主要是我懶得去修改這些匯編,由著他們占用一點時間吧!
          load程序負責把從0x20000處開始的4k程序(即led程序)拷到ram 0xc000000中,run函數把pc指到0x0c000000,開始執行led程序:
        void (*Run)(void) = (void (*)(void))RAM_ADDR;
        void Main(void)
        {  INT32U k ;
          INT32U *pulSource = (INT32U*)0x2000,;
          INT32U *pulDest = (INT32U*)0x0c000000;
          rSYSCFG=CACHECFG;
          PortInit();  
          for(k=0;k<2000>     *pulDest++ = *pulSource++;  
          Run();  
        }
        led程序把兩個通用io上連的led作不斷的亮和滅:
        void Main(void)
        {  INT32U k ;
          //INT16U *ptr;
          rSYSCFG=CACHECFG;
          PortInit();  
          while(1)
          {
          LedDisp(0);
          for(k=0;k       LedDisp(3);  
          for(k=0;k     }
        }
        最后是燒寫的程序,詳細的代碼網上高手們寫了不少,我只是給出最簡單的實現。燒寫時當程序執行到清理完0X0C30_0000到0X0C30_4000的RAM后,讓程序中斷下來,通過LOAD MEMORY FORM FILE命令把LOAD.BIN導入0X0C30_0000,LED.BIN導入0X0C30_2000中,繼續運行程序直到一個LED亮起,燒寫就完成了。拔去仿真器后再上電,可以看到兩個LED同時亮滅。
        #i nclude "option.h"
        #i nclude "44b.h"
        #i nclude "def.h"
        //#i nclude "romdef.h"
        //#i nclude "stdio.h"
        //#i nclude "stdlib.h"

        本文引用地址:http://www.104case.com/article/201611/318874.htm

        #define FLASH_START_ADDR  0X0000
        #define FLASH_ADDR_UNLOCK1 0X5555
        #define FLASH_ADDR_UNLOCK2 0X2AAA
        #define FLASH_DATA_UNLOCK1 0XAAAA
        #define FLASH_DATA_UNLOCK2 0X5555
        #define FLASH_DATA_WRITE  0XA0A0
        #define FLASH_ERASE     0X8080
        #define FLASH_ERASE_SECTOR 0X3030
        #define FLASH_ERASE_BLOCK  0X5050
        #define FLASH_ERASE_CHIP  0X1010
        #define FLASH_SID_QUERY   0X9090
        #define FLASH_CFI_QUERY   0X9898
        #define FLASH_SID_EXIT   0XF0F0
        #define FLASH_OP_TIMEOUT  0Xffff

        #define LED_PORTC10   (1 #define LED_PORTC11    (1 #define RAM_ADDR     0xc000000
        void (*Run)(void) = (void (*)(void))RAM_ADDR;
        void infoFlash(void);
        int wait_flash_ready ( INT16U *address, INT16U data );
        int writeFlash(INT16U *Address,INT16U Data);
        int eraseSector(INT16U* SectorAddr);
        int eraseChip(void);

        void PortInit(void);
        void LedDisp(int LedStatus);

        //*****************************************
        //    FLASH WIRTING
        //*****************************************
        void Main(void)
        {  INT32U k ;
          INT16U *pdist,*psrc;
          rSYSCFG=CACHECFG;
          PortInit();  
          //infoFlash();
          eraseChip();
          psrc="/blog/(INT16U" *)0xc300000;
          for(k=0;k<0x4000>   *psrc++=0x0; //clear ram
          psrc="/blog/(INT16U" *)0xc300000;
          pdist=(INT16U *)0x0;
          for(k=0;k<0x4000 k ram to>     writeFlash(pdist++,*psrc++);
          while(1)
          {
          LedDisp(0);
          for(k=0;k       LedDisp(2);  
          for(k=0;k     }
        }


        //*****************************************
        //    init the port
        //*****************************************
        void PortInit(void)
        {

           rPDATC = 0xffff;    //All IO is high
          rPCONC = 0x0f55ff54;  
          rPUPC = 0x3000;    //PULL UP RESISTOR should be enabled to I/O
        }

        //*****************************************
        //    light led
        //*****************************************
        void LedDisp(int LedStatus)
        {
          if((LedStatus&0x01)==0x01)
          rPDATC &= (~LED_PORTC10);  //LED ON
          else
          rPDATC |= LED_PORTC10;    //LED OFF
          
          if((LedStatus&0x02)==0x02)
          rPDATC &=(~LED_PORTC11);  //LED ON
          else
          rPDATC |=LED_PORTC11;    //LED OFF
        }

        //*****************************************
        //    show the flash soft id
        //*****************************************
        void infoFlash()
        {
          int i,j;
          INT16U *pFlash;
          *((volatile INT16U *)FLASH_START_ADDR)=FLASH_SID_EXIT;
          *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK1)=FLASH_DATA_UNLOCK1;
          *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK2)=FLASH_DATA_UNLOCK2;
          *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK1)=FLASH_SID_QUERY;
          for(i=0;i   pFlash=FLASH_START_ADDR;
          i=0;j=0;
          i=(INT16U)*pFlash++;
          j=(INT16U)*pFlash;  
        }
        //*****************************************
        //    erase hold flash
        //*****************************************
        int eraseChip()
        {
          *((volatile INT16U *)FLASH_START_ADDR)=FLASH_SID_EXIT;
          *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK1)=FLASH_DATA_UNLOCK1;
          *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK2)=FLASH_DATA_UNLOCK2;
          *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK1)=FLASH_ERASE;
          *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK1)=FLASH_DATA_UNLOCK1;
          *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK2)=FLASH_DATA_UNLOCK2;
          *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK1)=FLASH_ERASE_CHIP;
          if( wait_flash_ready((INT16U *)FLASH_START_ADDR,0xffff) )
          return 1;
          else return 0;
        }

        //*****************************************
        //    write one falsh word( 16bit)
        //*****************************************
        int writeFlash(INT16U *Address,INT16U Data)
        {  *((volatile INT16U *)FLASH_START_ADDR)=FLASH_SID_EXIT;
          *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK1)=FLASH_DATA_UNLOCK1;
          *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK2)=FLASH_DATA_UNLOCK2;
          *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK1)=FLASH_DATA_WRITE;
          *Address=Data;
          if(wait_flash_ready(Address,Data))
          return 1;
          else return 0;
        }

        //*****************************************
        //    wait for operation finish
        //*****************************************
        int wait_flash_ready ( INT16U *address, INT16U data )
        {
          INT32U tmp;
          INT16U *p;
          tmp =0xff;
          p=address;
          while(((*p)&0x8080)!=(data&0x8080))
          {tmp--;
           if (tmp==0x0)
             return 1; // timeout
          }
          return 0;
        }



        關鍵詞: ARMS3C44B0

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 梓潼县| 九龙县| 碌曲县| 嘉荫县| 抚松县| 清远市| 宁城县| 龙江县| 马关县| 贞丰县| 台北市| 高阳县| 伊通| 涞水县| 吕梁市| 澎湖县| 怀安县| 南投市| 彰化市| 耿马| 中江县| 依安县| 蒲城县| 枣阳市| 萝北县| 灵川县| 长治市| 永寿县| 平和县| 湟源县| 繁峙县| 潍坊市| 温宿县| 海林市| 内乡县| 南部县| 西安市| 阳城县| 长兴县| 永安市| 渑池县|