Linux学习笔记—驱动篇(三) 按键与汇编

  1. 按键输入实验
    #define GPIOEOUT (*(volatile unsigned int )0xC001E000) // 输出的电平
    #define GPIOEOUTENB (
    (volatile unsigned int )0xC001E004) // 输出使能
    #define GPIOEALTFN0 (
    (volatile unsigned int )0xC001E020) // 对应的焊脚功能选择
    #define GPIOEALTFN1 (
    (volatile unsigned int )0xC001E024) // 对应的焊脚功能选择
    GPIOA28 输入
    #define GPIOAOUTENB (
    (volatile unsigned int )0xC001A004) // 输出使能
    #define GPIOAALTFN0 (
    (volatile unsigned int )0xC001A020) // 低16位 对应的焊脚功能选择
    #define GPIOAALTFN1 (
    (volatile unsigned int )0xC001A024) // 高16位 对应的焊脚功能选择
    #define GPIOAPAD (
    (volatile unsigned int *)0xC001A018) // 读GPIO值
    void delay(int val);

void _start(void)
{

// 灯设置成输出
GPIOEALTFN0 &= ~(3<<(13*2)); //function0--GPIOE13
GPIOEOUTENB |= (1<<13);//GPIOE13--OUTOPUT
// 按键设置成输入
GPIOAALTFN1 &= ~(3<<((28-16)*2)); //function0--GPIOA28
GPIOAOUTENB &= ~(1<<28);//GPIOA28--INPUT	
while(1)
{
	if(! (GPIOAPAD&(0x01<<28))) // 真就表示读到低电平,按下
	{
		delay(0x40000);	//消抖
		if(! (GPIOAPAD&(0x01<<28)))
			GPIOEOUT &= ~(1<<13);//GPIOE13 output 0			
	}
	else	
		GPIOEOUT |= (1<<13);//GPIOE13 output 1		
}
}

void delay(int val)
{
	volatile int i=val;
	while(i--);	
}
  1. 用汇编来写点灯程序
    程序如下

.global _start // 全局变量定义

_start:
	LDR R0, =0XC001E020  	// 将0XC001E020作为地址赋给R0   	
	LDR R1, [R0]			//将R0即地址0XC001E020的内容赋给R1
	BIC R1, R1, #(3<<26)	//R1=R1 & ~(3<<26),因为BIC自动取反
	STR R1,[R0]			//将R1存储到地址0xC001E020

	LDR R0, =0xC001E004  // 设置成输出
	LDR R1, [R0]
	ORR R1, R1, #(1<<13);
	STR R1,[R0]; 

_loop:        // 循环
	LDR R0 , =0xC001E000  // 设置输出值
	LDR R1, [R0]
	BIC R1 , R1,#0X2000   // (1<<13) 与操作,设置为0  灯亮
	STR R1,[R0];

	bl  _delay      // 执行延时函数	
	LDR R1, [R0]
	ORR R1 , R1,#0X2000  // (1<<13) 或操作,设置为1  灯灭
	STR R1,[R0];
	bl  _delay    // 执行延时函数
	b  _loop     // 继续循环

_delay:
	MOV R4, #0x4000000	
d_loop:
	subs R4,R4,#1 
	cmp R4,#0 
	bne d_loop
	mov  pc , lr

注意:
Linux学习笔记—驱动篇(三) 按键与汇编_第1张图片

1)文件的后缀是大写的S,小写的.s编译会不过的(编译环境ubuntu18.04 arm-linux-gcc)
2)mov PC ,LR ---- 相当于子函数调用返回return
3)代码块可以直接用 name: 表示
4)对于_start 函数入口,需要在文件头定义成全局变量
.global _start // 全局变量定义

5)LDR和MOV 的区别
LDR 是ARM指令集中的
.LDR伪指令用于加载32位的立即数或一个地址值到指定寄存器;
若加载的常数未超出 MOV 或 MVN 的范围,则使用 MOV 或 MVN 指令代替该 LDR 伪指令
MOV 是 16位的;
数据传送指令.将 8 位立即数或寄存器(operand2)传送到目标寄存器(Rd)

6)BIC 和 AND 的区别
BIC 会把对应位先取反再相与
位清除指令.将寄存器Rn的值与operand2的值的反码按位作逻辑与操作,结果保存到 Rd 中.指令格式如下:
BIC{cond}{S}Rd,Rn,operand2
BIC R1,R1,#0x0F ;将 R1 的低 4 位清零,其它位不变

AND 直接和对应数相与
逻辑与操作指令.将 operand2 值与寄存器 Rn 的值按位作逻辑与操作,结果保存到Rd 中.指令格式如下:
AND{cond}{S} Rd,Rn,operand2
ANDS R0R0#x01 ;R0=R0&0x01取出最低位数据

7)STR 的作用
STR Rd,addressing 存储字数据 [addressing]←Rd,addressing 索引 STR{cond}
;STR 指令用于将寄存器中的数据保存到内存(存储器地址).

8)BL和B的区别
B 跳转指令.跳转到指定的地址执行程,不能返回,使用于循环操作
BL带链接的跳转指令.指令将下一条指令的地址拷贝到 R14(即 LR)链接寄存器中,然后跳转到指定地址运行程序.可以用mov PC , LR 返回到调用处

你可能感兴趣的:(Linux学习笔记—驱动篇(三) 按键与汇编)