Linux_kernel汇编驱动06

一、ARM汇编语言(GUN-gcc编译器下)

        1、语句格式

{symbol} {instruction|directive|pseudo-instruction} {@ comment}

symbol:为符号。

在ARM汇编语言中,符号必须从一行的行头开始,并且符号中不能包含空格。

在指令和伪指令中,符号用作地址标号(label);

在一些伪操作中,符号用作变量或常量。

instruction:为指令。

在ARM汇编语言中,指令不能从一行的行头开始。

在一行语句中,指令的前面必须有空格或者符号。

directive:为伪操作。

pseudo-instruction:为伪指令。

comment:为语句的注释。

在ARM汇编语言中,注释以(@)开头。

注释结尾即为一行的结尾。

注释也可以单独占用一行。

注意:

在ARM汇编语言中,各个指令,伪指令以及伪操作的助记符必须全部用大写或小写字母,不可以在一个伪操作助记符中既有大写又有小写字母。

         2、伪指令

                ARM伪指令不属于ARM指令集中的指令。

                定义这些指令可以使ARM汇编程序设计变得更加方便。

                ARM伪指令可以像其它ARM指令一样使用。

                汇编器会自动用一条或多条ARM指令替换ARM伪指令。

                ARM的伪指令包括:ADR、ADRL、LDR、NOP

                1)ADR指令(小范围的地址读取伪指令)

语法格式:

ADR{<cond>}    register,expr

<cond>:指令执行的条件码。(可选)

register:目标寄存器。

expr:基于PC或者寄存器的地址表达式。

①当地址值不是字对齐的,其取值范围为 -255 ~ 255

②当地址值是字对齐的,其取值范围为 -1020 ~ 1020

使用说明:

该指令将基于PC的地址值或基于寄存器的地址值读取到寄存器中。

①在汇编程序处理源程序时,ADR伪指令被编译器替换成一条合适的指令。通常,编译器用一条ADD指令或者SUB指令来实现该伪指令的功能。

②如果不能用一条指令来实现ADR伪指令的功能,编译器将报错,因为ADR伪指令中的地址基于PC或者基于寄存器的,所以ADR读取到的地址为位置无关的地址。当ADR伪指令中的地址是基于PC时,该地址与ADR伪指令必须在同一代码段中。

                2)ADRL(中等范围的地址读取伪指令)

语法格式:

ADRL{cond} register, expr

<cond>:指令执行的条件码。(可选)

register:目标寄存器。

expr:基于PC或者寄存器的地址表达式。

①当地址值不是字对齐时,其取值范围为-64KB~64KB。

②当地址值是字对齐时,其取值范围为-256KB~256KB。

使用说明:

该指令将基于PC或基于寄存器的地址值读取到寄存器中。

ADRL伪指令比ADR伪指令可以 读取更大范围的地址。

ADRL伪指令在汇编时被编译器替换成两条指令。

①在汇编编译器处理源程序时,ADRL伪指令被编译器替换成两条合适的指令,即使一条指 令可以完成该伪指令的功能,编译器也将用两条指令来替换该ADRL伪指令。

②如果不能用两条 指令来实现ADRL伪指令的功能,编译器将报告错误。

                3)LDR(大范围的地址读取伪指令)

语法格式:

LDR{cond} register, =[expr|label-expr]

<cond>:指令执行的条件码。(可选)

register:目标寄存器。

expr:32位的常量。

编译器将根据expr的取值情况,处理LDR伪指令如下:

①当expr表示的地址值没有超过MOV或MVN指令中地址的取值范围(±32M)时,

        编译器用合适的MOV或者MVN指令代替该LDR伪指令。

②当expr表示的地址值超过了MOV或MVN指令中地址的取值范围(±32M)时,

        编译器将该常数放在数据缓冲区中,同时用一条基于PC的LDR指令读取该常数。

label-expr:基于PC的地址表达式或者是外部表达式。

①当label-expr为基于PC的地址表达式时,

        编译器将label-expr表示的数值放在数据缓冲区中,

        同时用一条基于PC的LDR指令读取该数值。

②当label-expr为外部表达式或非当前段的表达式时,

        汇编编译器将在目标文件中插入连接重定位伪操作,

        这样连接器将在连接时生成该地址。

使用说明:

LDR伪指令将一个32位的常数或者一个地址值读取到寄存器中。

①当需要读取到寄存器中的数据超过了MOV及MVN指令可以操作的范围时,可以使用 LDR伪指令将该数据读取到寄存器中。

②将一个基于PC的地址值或者外部的地址值读取到寄存器中。由于这种地址值是在连接时确定的,所以这种代码不是位置无关的。同时,LDR伪指令处的PC值到数据缓冲区中的目标数据所在的地址的偏量要小于4KB。

                4)NOP(空操作伪指令)

语法格式:

NOP

NOP伪指令不影响CPSR中的条件标志位。

使用说明:

NOP伪指令在汇编时,将被替换成ARM中的空操作,用来占用CPU的调度时间。

比如,可能为MOV R0, R0等。

        3、伪操作

                告诉编译器怎么去编译指令,本身不会生成机器码,类似于C语言中的宏定义,伪操作一般都以“.”开头,类似于C语言中的“#”

.global _start               @声明全局标号

.equ DATA, 0x101        @#define DATA        0X101

.space 32                    @开辟32个字节的连续存储空间,并初始化为0

二、汇编驱动外设

        1、驱动LED灯

                1)外观

                        通过外观了解有几盏LED灯?4盏(可控)1盏(常亮)

                2)开发板原理图

                        通过开发板原理图了解LED灯连接的芯片管脚(高【灭】/ 低【亮】)

                3)芯片手册

                        通过芯片手册了解需要驱动哪些寄存器

GPIOB26

GPIOBALTFN1_26[21:20]010xC001B024配置复用功能
GPIOBOUTENB[26]10xC001B004配置输出使能
GPIOBOUT[26]00xC001B000配置输出低电平
GPIOBOUT[26]10xC001B000配置输出高电平
GPIOC11
GPIOCALTFN0_11[23:22]010xC001C024配置复用功能
GPIOCOUTENB[11]10xC001C004配置输出使能
GPIOCOUT[11]00xC001C000配置输出低电平
GPIOCOUT[11]10xC001C000配置输出高电平
GPIOC7
GPIOCALTFN0_7[15:14]010xC001C024配置复用功能
GPIOCOUTENB[7]10xC001C004配置输出使能
GPIOCOUT[7]00xC001C000配置输出低电平
GPIOCOUT[7]10xC001C000配置输出高电平
GPIOC12
GPIOCALTFN0_12[25:24]010xC001C024配置复用功能
GPIOCOUTENB[12]10xC001C004配置输出使能
GPIOCOUT[12]00xC001C000配置输出低电平
GPIOCOUT[12]10xC001C000配置输出高电平
                4)汇编代码
.text
.global _start
.arm

//宏定义GPIOC12寄存器
.equ GPIOCALTFN0, 0xC001C020    //[25:24]   01
.equ GPIOCOUTENB, 0xC001C004    //[12]      1
.equ GPIOCOUT, 0xC001C000       //[12]      0/1

_start:
    //配置GPIOC12管脚的复用功能
    ldr r0, =GPIOCALTFN0//把地址标号放到r0寄存器中
    ldr r1, [r0]//将存储器地址为r0的内容加载到r1寄存器中
    mov r2, #0x03//把立即数0x03传送到r2寄存器中
    bic r1, r1, r2, lsl #24//r1 = r1 位清楚 r2 << 24
    mov r3, #0x01//把立即数0x01传送到r3寄存器中
    orr r1, r1, r3, lsl #24//r1 = r1 | r3 << 24
    str r1, [r0]//将寄存器r1中的内容存储到存储器地址为r0的空间中

    //设置为输出模式
    ldr r0, =GPIOCOUTENB//把地址标号放到r0寄存器中
    ldr r1, [r0]//将存储器地址为r0的内容加载到r1寄存器中
    orr r1, r1, r3, lsl #12//r1 = r1 | r3 << 12
    str r1, [r0]//将寄存器r1中的内容存储到存储器地址为r0的空间中

loop:
    //亮
    ldr r0, =GPIOCOUT//把地址标号放到r0寄存器中
    ldr r1, [r0]//将存储器地址为r0的内容加载到r1寄存器中
    bic r1, r1, r3, lsl #12//r1 = r1 位清除 r3 << 12
    str r1, [r0]//将寄存器r1中的内容存储到存储器地址为r0的空间中

    bl delay//带返回链接的跳转

    //灭
    ldr r0, =GPIOCOUT//把地址标号放到r0寄存器中
    ldr r1, [r0]//将存储器地址为r0的内容加载到r1寄存器中
    orr r1, r1, r3, lsl #12//r1 = r1 | r3 << 12
    str r1, [r0]//将寄存器r1中的内容存储到存储器地址为r0的空间中

    bl delay//带返回链接的跳转
    b loop//跳到loop标号

delay:
    push {r5}
    ldr r5, =0x5000000
delay_loop:
    sub r5, r5, #1
    cmp r5, #0
    bne delay_loop
    pop {r5}
    mov pc, lr

.end
                5)Makefile
TARGET          := bare
BIN             := $(TARGET).bin
LD_ADDR         := 0x48000000
OBJS            := led.o

CROSS_COMPILE   := arm-cortex_a9-linux-gnueabi-

CC              :=$(CROSS_COMPILE)gcc
AS              :=$(CROSS_COMPILE)as
LD              :=$(CROSS_COMPILE)ld
OBJCOPY         :=$(CROSS_COMPILE)objcopy -O binary

CFLAGS += -nostartfiles
CFLAGS += -nostdlib

$(BIN) : $(TARGET)
    $(OBJCOPY) $(TARGET) $(BIN)
$(TARGET) : $(OBJS)
    $(LD) -Ttext=$(LD_ADDR) $^ -o $@

copy :
    cp $(BIN) /tftpboot

clean :
    rm -rf $(OBJS) $(TARGET) $(BIN)

        2、按键(button)、蜂鸣器(beep)

.text
.global _start
.arm

//宏
.equ GPIOAOUTENB, 0xC001A004
.equ GPIOAPAD, 0xC001A018
.equ GPIOAALTFN1, 0xC001A024
.equ GPIOA_PULLSEL, 0xC001A058
.equ GPIOA_PULLENB, 0xC001A060

.equ GPIOCOUT, 0xC001C000
.equ GPIOCOUTENB, 0xC001C004
.equ GPIOCALTFN0, 0xC001C020

_start:
    //KEY0 GPIOA28 设置为复用功能0
    ldr r0, =GPIOAALTFN1//把地址标号放到r0寄存器中
    ldr r1, [r0]//将存储器地址为r0的内容加载到r1寄存器中
    mov r2, #0x03//将立即数0x03传输到r2寄存器中
    bic r1, r1, r2, lsl #24//r1 = r1 位清除 (r2 << 24)
    str r1, [r0]//将r1寄存器中的数据存储到存储器地址为r0的空间中

    //KEY0 GPIOA28 设置为输入模式
    ldr r0, =GPIOAOUTENB//把地址标号放到r0寄存器中
    ldr r1, [r0]//将存储器地址为r0的内容加载到r1寄存器中
    mov r3, #0x01//将立即数0x01传输到r3寄存器中
    bic r1, r1, r3, lsl #28//r1 = r1 位清除 (r3 << 28)
    str r1, [r0]//将r1寄存器中的数据存储到存储器地址为r0的空间中

    //KEY0 GPIOA28 设置为上拉输入
    ldr r0, =GPIOA_PULLSEL//把地址标号放到r0寄存器中
    ldr r1, [r0]//将存储器地址为r0的内容加载到r1寄存器中
    orr r1, r1, r3, lsl #28//r1 = r1 | r3 << 28
    str r1, [r0]//将r1寄存器中的数据存储到存储器地址为r0的空间中

    //key0 GPIOA28 上拉输入使能
    ldr r0, =GPIOA_PULLENB//把地址标号放到r0寄存器中
    ldr r1, [r0]//将存储器地址为r0的内容加载到r1寄存器中
    orr r1, r1, r3, lsl #28//r1 = r1 | r3 << 28
    str r1, [r0]//将r1寄存器中的数据存储到存储器地址为r0的空间中

    //beep GPIOC14 设置为复用功能1
    ldr r0, =GPIOCALTFN0//把地址标号放到r0寄存器中
    ldr r1, [r0]//将存储器地址为r0的内容加载到r1寄存器中
    bic r1, r1, r2, lsl #28//r1 = r1 位清除 (r2 << 28)
    orr r1, r1, r3, lsl #28//r1 = r1 | r3 << 28
    str r1, [r0]//将r1寄存器中的数据存储到存储器地址为r0的空间中

    //beep GPIOC14 设置为输出模式
    ldr r0, =GPIOCOUTENB//把地址标号放到r0寄存器中
    ldr r1, [r0]//将存储器地址为r0的内容加载到r1寄存器中
    orr r1, r1, r3, lsl #14//r1 = r1 | r3 << 14
    str r1, [r0]//将r1寄存器中的数据存储到存储器地址为r0的空间中
    bl beep_off

loop:
    ldr r0, =GPIOAPAD//把地址标号放到r0寄存器中
    ldr r1, [r0]//将存储器地址为r0的内容加载到r1寄存器中
    ands r1, r1, r3, lsl #28//r1 = r1 & (r3 << 28)
    bleq beep_on
    blne beep_off
    b loop

beep_off:
    //不响
    ldr r0, =GPIOCOUT//把地址标号放到r0寄存器中
    ldr r1, [r0]//将存储器地址为r0的内容加载到r1寄存器中
    bic r1, r1, r3, lsl #14//r1 = r1 位清除 (r3 << 14)
    str r1, [r0]//将r1寄存器中的数据存储到存储器地址为r0的空间中
    mov pc, lr

beep_on:
    //响
    ldr r0, =GPIOCOUT//把地址标号放到r0寄存器中
    ldr r1, [r0]//将存储器地址为r0的内容加载到r1寄存器中
    orr r1, r1, r3, lsl #14//r1 = r1 | (r3 << 14)
    str r1, [r0]//将r1寄存器中的数据存储到存储器地址为r0的空间中
    mov pc, lr

.end

三、ARM裸板的异常(中断)处理

        1、初始化

                1)设置中断源(产生中断的情况)

                2)设置中断控制器(屏蔽、优先级)

                3)设置CPU总开关(使能中断)

        2、执行程序

        3、产生中断

        4、CPU每执行完一条指令,都会检查有无中断/异常产生

        5、发现有中断/异常产生,开始处理,对于不同的异常,会跳去不同的地址执行程序,在这些地址上,只是一条跳转指令,跳去执行摸个函数。

        6、这些函数的任务:保存现场(各类寄存器);处理异常,调用不同的函数;恢复现场。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值