1. 程式人生 > >嵌入式硬體及介面開發實踐

嵌入式硬體及介面開發實踐

瞭解嵌入式系統電路設計

時鐘模組

1、如下是時鐘模組結構圖
這裡寫圖片描述
在圖中我們看到XTIPLL是外部晶振,EXTCLK是外部時鐘,他們為時鐘源,2個PLL,他們可以產生需要的高頻時鐘
2、時鐘源的選擇,軟體沒有對MPLLCON暫存器設定,使用外部晶振或外部時鐘為系統時鐘
3、鎖相環PLL模組結構圖
這裡寫圖片描述
從結構圖中我們獲得輸出時鐘頻率的表示為:
這裡寫圖片描述
其中,m=M(分配器M的值)+8,p=P(分配器P的值)+2
4、時鐘控制邏輯決定哪個時鐘源被使用,下圖是電源在上電重啟時的時鐘行為
這裡寫圖片描述
從這個時序圖,我們可以知道在沒有配置PLLCON暫存器時,直接使用FIN為MPLL的FCLK,下圖是正常模式下改變PLL設定時的時序
這裡寫圖片描述


從這個圖可以知道,我們可以通過寫P M L三個分配器的值(在PLL鎖存時間會被自動的插入)改變FCLK,下圖是PLL引數推薦值
這裡寫圖片描述

5、使用分頻係數控制暫存器設定FCLK(用於CPU核),HCLK(用於AHB裝置,如儲存器控制,中斷控制,DMA,LCD控制,USB主模組,),PCLK(用於APB,如看門狗,UART,RTC,GPIO等)三者的比例。
6、系統時鐘設定的步驟
6.1、確定外部輸入晶振頻率,比如,Fin = 12MHz
6.2、確定系統輸出時鐘頻率,比如,FCLk=400MHZ
6.3、對照PLL引數推薦值表,找到合適的MDIV,PDIV.SDIV,設定MPLLCON。
6.4、設定UPLLCON
6.5、確定FCLK,HCLK,PCLK比例係數,設定時鐘分頻係數暫存器CLKDIVN,從而確定當前系統下的FCLK,HCLK,PCLK的具體頻率值

GPIO(通用的輸入輸出埠)

1、2440包含GPA,GPB,GPC,GPD,GPE,GPF,GPG,GPH,GPJ 9組埠
2、GPxCON:選擇引腳工作模式,每兩位控制一個引腳,00(輸入),01(輸出),10(特殊用途),11(保留)
3、GPxDAT:讀寫引腳資料,每一位控制一個引腳,0表示引腳為低電平,1表示引腳為高電平
4、GPxUP:確定是否使用內部上拉電阻,每一位控制一個引腳,0表示使用內部上拉電阻,1表示無內部上拉電阻

UART(通用非同步收發器)

1、下面是模組圖:
這裡寫圖片描述
UART屬於APH裝置,包含控制單元,波特率發生器,傳送器和接收器。
2、傳送或接收的資料構成為1個開始位,5-8個數據位,1個可選的奇偶校驗位和1-2個停止位,由線性控制暫存器ULCONn來設定,如下圖
這裡寫圖片描述


3、波特率描述序列通訊資料傳輸速率,每秒傳輸的二進位制位數,單位是bps,從TxD管腳傳送,從RxD管腳接收。
4、TTL/CMOS邏輯電平轉換為RS-232邏輯電平
5、非同步資料傳輸方式,依靠起始位來實現傳送和接收發的時鐘自同步
6、傳送資料流程,SOC將資料寫入傳送FIFO->傳送移位器->TxD資料線
7、接收資料流程,RxD資料線->接收移位器->SOC從接收FIFO讀取資料
8、串列埠硬體流控制
9、UART初始化
9.1、波特率除數因子暫存器UBRDIVn,設定波特率,計算方法如下圖:
這裡寫圖片描述
9.2、行暫存器ULCONn,設定傳輸格式
9.3、模式控制暫存器UCONn,選擇UART時鐘源,設定UART中斷方式等
9.4、FIFO控制暫存器UFCONn,決定是否使用FIFO
9.5、狀態暫存器UTRSTATn,表明資料是否已經被髮送完畢、是否已經接收到資料
9.6、SOC將資料寫入這個暫存器(UTXHn),SOC讀取這個暫存器(URXHn)

中斷控制

1、CPU和外設之間的資料傳輸控制方式通常包含三種,查詢方式,中斷方式,DMA(Direct Memory access),中斷就是CPU在執行程式時,出現了緊急事件,必須轉去處理它(執行中斷服務程式),並在處理完後再返回的過程
2、中斷過程:請求->仲裁->響應->處理->返回,下圖是arm9中斷處理流程
這裡寫圖片描述
3、中斷控制器支援60箇中斷源
4、如下是s3c2440A中斷過程
這裡寫圖片描述
4.1、中斷源未決暫存器SRCPND,每一個位與一箇中斷源有關,自動置位,指示那個中斷源正在等待服務
4.2、中斷模式暫存器INTMOD,每一個位與一箇中斷源有關,為1時,相應的中斷將在FIQ模式下處理
4.3、中斷遮蔽暫存器INTMSK,每一個位與一箇中斷源有關,為1時,CPU不會服務相應中斷源的中斷請求
4.4、優先順序暫存器PRIORITY
4.5、SUBSRCPND暫存器,S3C2440有15個子中斷,當這些子中斷髮生,且未被遮蔽,他們對應的父中斷將會在SRCPND中被置位
4.6、中斷偏移暫存器

C和組合語言的混合程式設計

1、內嵌的彙編指令用法
1.1、運算元可以是表示式,表示式表示的資訊會被作為無符號數進行操作,表示式不要太複雜。
1.2、內嵌的彙編指令如果包含常量運算元,該指令肯能會被彙編器展開成幾條指令,並且該常量前的符號#可以省略。
1.3、只有指令B能使用C程式中的標號
2、在C程式中使用內嵌的彙編指令的語法格式和注意事項
2.1、語法格式
這裡寫圖片描述
2.2、注意事項
2.2.1、彙編指令段中可以使用C語言的註釋
2.2.2、在彙編指令中“,”用作為分隔符
2.2.3、最好是不要在內嵌的彙編指令中使用物理暫存器,可以使用變數來實現
2.2.4、不要用物理暫存器去引用一個C變數
2.2.5、對於內嵌彙編指令可能會用到的暫存器,沒有必要保護和恢復他們

實驗一

學習使用ARM彙編指令,實現蜂鳴器BEEP鳴叫,四個LED點亮、熄滅實驗。
電路控制,如圖:
這裡寫圖片描述
程式碼(arm彙編版本):

    AREA    beep, CODE, READONLY
    ENTRY
    CODE32
pGPBCON             EQU     0x56000010
pGPBDAT             EQU     0x56000014
pGPBUP              EQU     0x56000018

START   ;配置GPBCON[1:0]=01使引腳GPB0 為輸出工作模式
        LDR R0, =pGPBCON
        LDR R1, [R0]
        LDR R2, =0x15401
        ORR R1, R1, R2
        STR R1, [R0]
         ;配置GPBUP[0]=1使引腳GPB0無內部上拉電阻
        LDR R0, =pGPBUP
        LDR R1, [R0]
        LDR R2, =0x07ff
        ORR R1, R1, R2
        STR R1, [R0]
;light on
beep_on
    LDR R0, =pGPBDAT
    MOV R2, #0x01
    STR R2, [R0]

    MOV R2, #0x10000
    BL  delay

;   b .

;light off
beep_off
    LDR R0, =pGPBDAT
    MOV R2, #0x1e0
    STR R2, [R0]

    MOV R2, #0x100000
    BL  delay

    b   beep_on

delay
    SUB R2, R2, #0x1
    CMP R2, #0x0    
    BNE delay
    MOV PC, LR

    NOP
    END

實驗二

;C - > 彙編程式
通過C語言程式調用匯編子程式字串拷貝函式
程式碼:
心得:
1、設定系統時鐘,

實驗三

彙編程式->C
彙編程式呼叫C程式g()計算5個整數i, 2*i, 3*i, 4*i, 5*i的和
本程式使用5個引數,分別使用暫存器R0儲存第一個引數,R1、R2、R3分別儲存第二、三、四個引數,第五個引數利用堆疊傳送
函式返回值保持在R0中
程式碼(C語言程式部分):

//C程式g()返回5個整數的和
int g(int a, int b, int c, int d ,int e)
{
    return (a + b + c + d + e);
}

程式碼(組合語言部分):

Stack_Size      EQU         0x00000400      ;指定棧的大小
    AREA    STACK, DATA, NOINIT, READWRITE, ALIGN=3     ;初始化一個數據段,初始化為0
Stack_Mem SPACE Stack_Size      ;分配一片記憶體單元用作棧空間
    PRESERVE8       ;宣告8位元組對齊
    AREA    asmCT1, CODE, READONLY  ;申明一個程式碼段
    ENTRY   ;定義程式入口
    CODE32  ;宣告32位arm指令
START
    ;初始化棧
    LDR R0, =Stack_Mem
    ; set its Stack Pointer
    MOV SP, R0
;   MUL SL, SP, #Stack_Size

    BL call_g
    B stop

call_g
    IMPORT g
    STR LR, [sp, #-4]!;儲存返回地址
    ;set parameter passed to function
    MOV R0, #01     ;if i = 1
    ADD R1, R0, R0  ;R1 = 2i
    ADD R2, R0, R1  ;R2 = 3i
    ADD R3, R1, R2  ;R3 = 5i
    ;先移動棧指標,然後將R3壓入堆疊
    STR R3, [SP, #-4]!
    ADD R3, R1, R1   ;R3 = 4i
    BL g    ;呼叫C程式
    NOP
    ADD sp, sp, #4;調整資料棧指標,準備返回
    LDR PC, [SP], #4;返回

stop
    b stop
    END

心得:
1、偽操作SPACE的用法,它的功能是分配一塊記憶體單元,並用0初始化
2、偽操作EQU的用法,語法格式為name EQU expr type,它的功能是為數字常量(32位地址或32位常數),基於暫存器的地址值,程式中的標號(基於PC的值)定義一個字元名稱(命名規範?)。其中type指示expr的資料型別時CODE16,CODE32,DATA
3、偽操作AREA的用法,語法格式為 AREA sectionname {,attr,attr}…。它的功能是定義個程式碼段或資料段,輕重attr是段的屬性,各個屬性用,隔開,比如屬性CODE(程式碼段),DATA(表示資料段),COMMON(一個通用的段,不包含程式碼和資料),COMDEF(一個通用的段,可以包含程式碼和資料),READONLY,READWRITE,ALIGN=expression,NOINIT,ASSOC等。
4、CODE32,CODE12告訴彙編編譯器後面指令的型別,ENTRY指定程式的入口點,END告訴編譯器已經到了源程式結尾,
5、偽操作PRESERVE8指示當前程式碼資料棧是8位元組對齊
6、偽操作IMPORT的用法,它的語法格式是IMPORT symbol[WEAK],它的功能是告訴編譯器符號symbol不是在本原始檔定義的,而是在其他原始檔中定義的。
7,子程式引數傳遞和返回值的規則:引數數量不超過4個時,使用R0-R3傳遞,否則,可以使用棧(sp)傳遞。
8、棧的生長方向是向低地址增長,棧記憶體放的值一般是指標型別,這樣一來就通過棧間接操作記憶體單元。比如
如果棧指標拿到的地址是0x30800000,sp-4拿到的地址就是0x307ffffc,這時如果記憶體單元0x307ffffc儲存的資料為,0x3000000C,那麼執行指令STR LR, [sp, #-4]!後,連結暫存器LR=,0x3000000C,並且更新了sp,此時sp=0x307ffffc

實驗四

在終端執行uart_test程式,PC端通過超級終端向串列埠傳送一行字元(直到敲入回車鍵結束),通過串列埠0傳送到開發板,終端接收串列埠資料後,儲存在陣列中,再傳回到PC端,通過超級終端回顯。
程式碼:

//串列埠傳送一個字元=======
void Uart_SendByte(int data)
{
    if(whichUart==0)
    {
        if(data=='\n')
        {
            while(!(rUTRSTAT0 & 0x2));
            Delay(10);                 //because the slow response of hyper_terminal 
            WrUTXH0('\r');
        }
        while(!(rUTRSTAT0 & 0x2));   //Wait until THR is empty.
        Delay(10);
        WrUTXH0(data);//把字元送到傳送緩衝暫存器
    }
    else if(whichUart==1)
    {
        if(data=='\n')
        {
            while(!(rUTRSTAT1 & 0x2));
            Delay(10);                 //because the slow response of hyper_terminal 
            rUTXH1 = '\r';
        }
        while(!(rUTRSTAT1 & 0x2));   //Wait until THR is empty.
        Delay(10);
        rUTXH1 = data;
    }   
    else if(whichUart==2)
    {
        if(data=='\n')
        {
            while(!(rUTRSTAT2 & 0x2));
            Delay(10);                 //because the slow response of hyper_terminal 
            rUTXH2 = '\r';
        }
        while(!(rUTRSTAT2 & 0x2));   //Wait until THR is empty.
        Delay(10);
        rUTXH2 = data;
    }       
}               

//串列埠接收一個字元=====================================================
char Uart_Getch(void)
{
    if(whichUart==0)
    {       
        while(!(rUTRSTAT0 & 0x1)); //Receive data ready
        return RdURXH0();
    }
    else if(whichUart==1)
    {       
        while(!(rUTRSTAT1 & 0x1)); //Receive data ready
        return RdURXH1();
    }
    else if(whichUart==2)
    {
        while(!(rUTRSTAT2 & 0x1)); //Receive data ready
        return RdURXH2();
    }
    return 0;
}
/*********************************************************************************************
* name:     uart0_test
* func:     uart test function
* para:     none
* ret:      none
* modify:
* comment:      
*********************************************************************************************/
void uart0_test()
{
    char cInput[256];
    UINT8T ucInNo=0;
    char c;
//
    Uart_Init( 0,115200 );
    Uart_Select( 0 ); // 使用串列埠0

    Uart_Printf("\n UART0 Communication Test Example\n");   
    Uart_Printf(" Please input words, then press Enter:\n");

    while(1)
    {
        c=Uart_Getch();
        Uart_Printf("%c",c);
        if(c!='\r')          //enter key
            cInput[ucInNo++]=c;
        else
        {
            cInput[ucInNo]='\0';
            break;
        }
    }
    Delay(1000);    

    Uart_Printf("\n The words that you input are: \n %s\n",cInput);     
    Uart_Printf(" end.\n"); 
}

心得:
1、串列埠0一次接收一個字元,直到接收到回車鍵‘\r‘結束。串列埠0一次傳送一個字元,直到遇到回車鍵’\r’結束。
2、串列埠初始化實質是依次配置行控制暫存器,控制暫存器,波特率分頻係數暫存器,FIFO控制暫存器,MODEL控制暫存器。
3、傳送緩衝暫存器為空時自動設定TX/RX狀態暫存器的相應位為1,所以檢測TX/RX狀態暫存器的相應位,如果為1,則SOC能把資料位寫到傳送緩衝暫存器。接收緩衝暫存器包含有效資料時自動設定TX/RX狀態暫存器的相應位為1,所以檢測TX/RX狀態暫存器的相應位,如果為1,則SOC能從接收緩衝器讀到資料

實驗五

(1)實現單按鍵中斷處理程式。
(2)實現六按鍵中斷處理程式。
(3)實現按鍵控制LED點亮/熄滅實驗。
eg:
K1 - 點亮LED1,其他3個LED熄滅
K2 - 點亮LED2,其他3個LED熄滅
K3 - 點亮LED3,其他3個LED熄滅
K4 - 點亮LED4,其他3個LED熄滅
K5 - 點亮4個LED
K6 - 全熄滅4個LED
程式碼:


//掃描6按鍵所接GPIO口,觀察GPIOx電平值,鍵按下:低電平; 鍵擡起:高電平
//返回當前按下鍵所對應的鍵值
U8 Key_Scan( void )
{
    Delay( 80 ) ;

    if(      (rGPGDAT&(1<< 0)) == 0 )    // K1按下
        return 1 ;
    else if( (rGPGDAT&(1<< 3)) == 0 )    // K2按下
        return 2;
    else if( (rGPGDAT&(1<< 5)) == 0 )     // K3按下
        return 3 ;
    else if( (rGPGDAT&(1<< 6)) == 0 )    // K4按下
        return 4 ;
    else if( (rGPGDAT&(1<< 7)) == 0 )     // K5按下
        return 5 ;
    else if( (rGPGDAT&(1<< 11)) == 0 )    // K6按下
        return 6 ;
    else
        return 0xff;      
}

 // 按鍵中斷處理程式
 void __irq Key_ISR(void)
{
    U8 key;
    U32 r;
    //Uart_Printf("\nKey_ISR+!\n");
    EnterCritical(&r); // 進入臨界區
    if(rINTPND==BIT_EINT8_23) {   //判斷INTPEND暫存器中是否為EINT8_23觸發中斷,如果EINT8_23觸發中斷,則INTPEND暫存器中對應bit位被置一
        ClearPending(BIT_EINT8_23);  //清空BIT_EINT8_23位
        // 繼續比較EINTPEND暫存器,確定外面中斷源
        if(rEINTPEND&(1<<8)) {   //EINT8觸發中斷
            Uart_Printf("eint8\n");
            rEINTPEND |= 1<< 8;  // 清空EINTPEND暫存器中EINT8對應的位
        }
        if(rEINTPEND&(1<<11)) {    //EINT11觸發中斷
            Uart_Printf("eint11\n");        
            rEINTPEND |= 1<< 11;
        }
        if(rEINTPEND&(1<<13)) {    //EINT13觸發中斷
            Uart_Printf("eint13\n");
            rEINTPEND |= 1<< 13;
        }
        if(rEINTPEND&(1<<14)) {     //EINT14觸發中斷
            Uart_Printf("eint14\n");        
            rEINTPEND |= 1<< 14;
        }
        if(rEINTPEND&(1<<15)) {     //EINT15觸發中斷
            Uart_Printf("eint15\n");
            rEINTPEND |= 1<< 15;
        }
        if(rEINTPEND&(1<<19)) {      //EINT19觸發中斷
            Uart_Printf("eint19\n");        
            rEINTPEND |= 1<< 19;
        }

    }
    key=Key_Scan(); //掃描GPGx埠,返回按鍵鍵值
    if( key != 0xff )
        Uart_Printf( "Interrupt occur... K%d is pressed!\n", key) ;

    ExitCritical(&r);  // 出臨界區
    //Uart_Printf("\nKey_ISR-!\n");
}

void KeyScan_Test(void)
{
    Uart_Printf("\nKey Scan Test, press ESC key to exit !\n");  

    // 配置GPGCON,設定6按鍵對應的GPGx管腳功能為外部中斷引腳EINT
    rGPGCON = rGPGCON & (~((3<<0)|(3<<6))) | ((2<<0)|(2<<6)) ;     //GPG0,11 set EINT
    rGPGCON = rGPGCON & (~((3<<10)|(3<<12))) | ((2<<10)|(2<<12)) ;     //GPG5,6 set EINT
    rGPGCON = rGPGCON & (~((3<<14)|(3<<22))) | ((2<<14)|(2<<22)) ;     //GPG7,11 set EINT

    // 設定中斷觸發方式
    rEXTINT1 &= ~(7<<0);
    rEXTINT1 |= (2<<0);      //set eint8 falling edge int

    rEXTINT1 &= ~(7<<12);
    rEXTINT1 |= (2<<12);    //set eint11 falling edge int

    rEXTINT1 &= ~(7<<20);
    rEXTINT1 |= (2<<20);    //set eint13 falling edge int

    rEXTINT1 &= ~(7<<24);
    rEXTINT1 |= (2<<24);    //set eint14 falling edge int

    rEXTINT1 &= ~(7<<28);
    rEXTINT1 |= (2<<28);    //set eint15 falling edge int

    rEXTINT2 &= ~(7<<12);
    rEXTINT2 |= (2<<12);    //set eint19 falling edge int

    // 將按鍵中斷處理程式註冊,入口地址對應EINT8_23中斷IRQ
    pISR_EINT8_23 = (U32)Key_ISR;

    rEINTPEND = 0xFFFFFF;     //清空 EINTPEND中斷請求
    rSRCPND = BIT_EINT8_23; //to clear the previous pending states in SRCPND
    rINTPND = BIT_EINT8_23;  //  to clear the previous pending states in INTPND

    rEINTMASK=~( (1<<8)|(1<<11)|(1<<13)|(1<<14)|(1<<15)|(1<<19) ); //清空六個外部中斷對應的中斷遮蔽位
    rINTMSK=~(BIT_EINT8_23); // 清空BIT_EINT8_23對應的中斷遮蔽位


    Uart_Printf("\nPlease press the Key to test !\n");
    Uart_Printf("\nrINTMSK=0x%x\n",rINTMSK);
    while( Uart_GetKey() != ESC_KEY ) ;  // 無限迴圈,直到使用者鍵入ESC鍵,退出。但此時可被中斷打斷
    Uart_Printf("\nExit Int test !\n");
    rEINTMASK=0xFFFFFF; // 重新設定EINTMASK遮蔽位
    rINTMSK=BIT_ALLMSK; // 重新設定INTMSK遮蔽位
}

心得:
1、設定6按鍵對應的GPGx管腳功能為外部中斷引腳EINT
2、設定中斷觸發方式,
3、將按鍵中斷處理程式註冊,入口地址對應EINT8_23中斷IRQ
4、#define rSRCPND ((volatile unsigned )0x4a000000) //Interrupt request status
4.1、上述表示式拆開來分析,首先(volatile unsigned *) 0x4a000000的意思是把0x4a000000強制轉換成volatile unsigned 型別的指標,暫記為p,那麼就是#define A *p,即A為P指標指向位置的內容了。這裡就是通過記憶體定址訪問到暫存器A,可以讀/寫操作。
4.2、unsigned 型別指標,意思是說讀寫這個地址時,要寫進unsigned 的資料,讀出也是unsigned。
4.3、volatile變數可變 允許除了程式之外的比如硬體來修改他的內容
4.4、訪問該資料任何時候都會直接訪問該地址處內容,即通過cache提高訪問速度的優化被取消
4.5、簡而言之,我們把rSRCPND 看成是一個暫存器變數