嵌入式硬件及接口开发实践

了解嵌入式系统电路设计

时钟模块

1、如下是时钟模块结构图
嵌入式硬件及接口开发实践_第1张图片
在图中我们看到XTIPLL是外部晶振,EXTCLK是外部时钟,他们为时钟源,2个PLL,他们可以产生需要的高频时钟
2、时钟源的选择,软件没有对MPLLCON寄存器设置,使用外部晶振或外部时钟为系统时钟
3、锁相环PLL模块结构图
嵌入式硬件及接口开发实践_第2张图片
从结构图中我们获得输出时钟频率的表示为:
这里写图片描述
其中,m=M(分配器M的值)+8,p=P(分配器P的值)+2
4、时钟控制逻辑决定哪个时钟源被使用,下图是电源在上电重启时的时钟行为
嵌入式硬件及接口开发实践_第3张图片
从这个时序图,我们可以知道在没有配置PLLCON寄存器时,直接使用FIN为MPLL的FCLK,下图是正常模式下改变PLL设置时的时序
嵌入式硬件及接口开发实践_第4张图片
从这个图可以知道,我们可以通过写P M L三个分配器的值(在PLL锁存时间会被自动的插入)改变FCLK,下图是PLL参数推荐值
嵌入式硬件及接口开发实践_第5张图片

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、下面是模块图:
嵌入式硬件及接口开发实践_第6张图片
UART属于APH设备,包含控制单元,波特率发生器,发送器和接收器。
2、发送或接收的数据构成为1个开始位,5-8个数据位,1个可选的奇偶校验位和1-2个停止位,由线性控制寄存器ULCONn来设置,如下图
嵌入式硬件及接口开发实践_第7张图片
3、波特率描述串行通信数据传输速率,每秒传输的二进制位数,单位是bps,从TxD管脚发送,从RxD管脚接收。
4、TTL/CMOS逻辑电平转换为RS-232逻辑电平
5、异步数据传输方式,依靠起始位来实现发送和接收发的时钟自同步
6、发送数据流程,SOC将数据写入发送FIFO->发送移位器->TxD数据线
7、接收数据流程,RxD数据线->接收移位器->SOC从接收FIFO读取数据
8、串口硬件流控制
9、UART初始化
9.1、波特率除数因子寄存器UBRDIVn,设置波特率,计算方法如下图:
嵌入式硬件及接口开发实践_第8张图片
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中断处理流程
嵌入式硬件及接口开发实践_第9张图片
3、中断控制器支持60个中断源
4、如下是s3c2440A中断过程
嵌入式硬件及接口开发实践_第10张图片
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、语法格式
嵌入式硬件及接口开发实践_第11张图片
2.2、注意事项
2.2.1、汇编指令段中可以使用C语言的注释
2.2.2、在汇编指令中“,”用作为分隔符
2.2.3、最好是不要在内嵌的汇编指令中使用物理寄存器,可以使用变量来实现
2.2.4、不要用物理寄存器去引用一个C变量
2.2.5、对于内嵌汇编指令可能会用到的寄存器,没有必要保护和恢复他们

实验一

学习使用ARM汇编指令,实现蜂鸣器BEEP鸣叫,四个LED点亮、熄灭实验。
电路控制,如图:
嵌入式硬件及接口开发实践_第12张图片
代码(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 看成是一个寄存器变量

你可能感兴趣的:(编程,计算机科学,c语言,arm9)