开始基于S3C2440学习ARM裸机,记录一下第一个ARM汇编程序(比起8086的指令,ARM的指令要难一些,当然ARM指令比起IA32也是稍有逊色,毕竟越是技术更新换代,所需功能越多,复杂度也就越大)
@
@ Time:2019年4月1日23:15:07
@ Author:Apollon_krj
@ 所有的三个LED点灯(GPF4~GPF6):1为灭,0为亮
@ 流水灯
@
@ arm的函数调用与返回,与8086差别还是蛮大的
@ (主要是当前不知道有什么比较类似于x86的call、ret指令,以下指令也是为了一个目的而去搜的)
@ 后者用stack存放函数地址,前者则直接是lr和pc两个寄存器来搞
@ 8086更趋向与软件逻辑,是两步:先call,再ret
@ 而arm则更随心所欲一些,是三步:先保存返回地址,再修改pc指针,执行完恢复pc指针
@ 即call指令相当于“LDR lr, =label_ret”、“LDR lr,=label_call”两条指令
@ 而ret指令相当于“MOV pc,lr”一条指令,也即8086“MOV pc,ss:[sp]”
@ 注意:ARM中,pc寄存器的值,永远是当前执行指令地址+8,而不是当前指令地址
@ 因为:当前执行指令地址为A,A+4指令已经开始被解析,而A+8指令已经开始被读取(即PC)
@
.text
.global _start
_start:
LDR r0, =0x00 @ 0000 0000,亮亮亮
LDR lr, =one_light_delay @ 点灯完成后的地址,即返回地址
LDR pc, =_led_light @ 点灯指令起始地址,等价于 B _led_light
one_light_delay:
LDR lr, =two_light @ 延时完成后的指令地址
B _delay @ 延时指令起始地址
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
two_light:
LDR r0, =0x70 @ 0111 0000,灭灭灭
LDR lr, =two_light_delay
B _led_light
two_light_delay:
LDR lr, =_start @ 死循环
B _delay
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
_led_light:
LDR r1, =0x56000050
LDR r2, =0x1500 @ choose GPF:0001 0101 0000 0000,选中三个GPIO的输入功能
LDR r3, [r1] @ 获取原来的设置
ORR r2, r3 @ 将新配置(GPF4~GPF6 output)增加上去
STR r2, [r1]
LDR r1, =0x56000054
MOV r2, r0 @ set bit:r0
STR r2, [r1]
MOV pc, lr @ 回复函数返回地址,接着执行
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
_delay:
LDR r3, =0x100 @ 延时256 * 256 = 65536个指令周期
loop_ext:
LDR r4, =0x100
loop_int:
sub r4, r4, #1
cmp r4, #0
BNE loop_int @ 内层循环条件判断
sub r3, r3, #1
cmp r3, #0
BNE loop_ext @ 外层循环条件判断
MOV pc, lr @ 循环结束,返回执行
查看一下elf文件部分内容和bin文件的机器码:
可以看出,elf文件的组织形式,有一部分连续的地址存放代码,文件末尾前一部分地址存放符号表,虽然没有看多ELF文件的具体组织形式,但是也和PE文件格式差不了太多。
①用到了五个寄存器,GPFCON、GPFDAT、GPGCON、GPGDAT、WTDOG寄存器(用来控制输出点亮LED、按键输入控制延时(非中断版))
②GPFDAT、GPGDAT数据寄存器,用来存放pins的输入/输出的数据。
③看门狗的控制寄存器:
由于看门狗会在一段时间后复位,使得程序重新运行(流水灯看起来效果可能不连贯,如果延时恰好和复位时间一样,看起来可能没有什么不一样),所以因此需要关闭看门狗的复位功能,或者disable看门狗的定时器。其他bit不再生效。
注:S3C2440看门狗定时器的计算方法为:
t_watchdog = 1/[ PCLK / (Prescaler value + 1) / Division_factor ]
①目录结构:
Krj@VM:/mnt/hgfs/workspace/S3C2440/1stQuarter_BarBoard/03_LED_Nor_Nand_WatchDogTimer_Key$ tree -a
.
├── all.bin
├── all.dis
├── all.elf
├── include
│ └── soc_s3c2440
│ ├── soc_s3c2440_field.h
│ ├── soc_s3c2440_reg.h
│ └── soc_s3c2440_reg_operator.h
├── led.c
├── .led.d
├── led.i
├── led.o
├── Makefile
├── start.o
└── start.S
②Makefile:
INCLUDE := -I ./include/soc_s3c2440/
CFLAGS := $(INCLUDE)
OBJS = start.o led.o
C_SOURCE = led.c
DEP_FILES := $(patsubst %.c,.%.d,$(C_SOURCE))
DEP_FILES := $(wildcard $(DEP_FILES))
#注意,非命令语句不要Tab缩进
ifneq ($(DEP_FILES),)
include $(DEP_FILES)
endif
all:$(OBJS)
#链接两个目标文件到一个.elf文件
#特别注意两个.o文件的顺序,当我把main生成的.o放在前面时其结果是不对的!
@arm-linux-ld -Ttext 0 $^ -o [email protected]
#由目标代码生成机器码的bin文件
@arm-linux-objcopy -O binary -S [email protected] [email protected]
#由目标代码反汇编得出标准汇编指令(非伪汇编指令)
@arm-linux-objdump -D [email protected] > [email protected]
start.o:start.s
@arm-linux-gcc -c -o $@ $<
%.o:%.c
#记录一下预处理结果中对宏的处理,便于观察
@arm-linux-gcc -E $< $(CFLAGS) -o $(patsubst %.c,%.i,$<)
#分别编译,生成目标文件,依赖文件存为隐藏文件
@arm-linux-gcc -c $< $(CFLAGS) -o $@ -MD -MF $(patsubst %.c,.%.d,$<)
.PHONY:clean
clean:
@rm *.bin *.elf *.o *.dis *.i
.PHONY:clean_dep
clean_dep:
@rm -f $(DEP_FILES) && echo "delete depend file success!" || echo "delete depend files failure!"
①汇编程序选择关闭看门狗复位功能和选择Nor启动/Nand启动并设置栈底到高内存地址,设置函数入口地址等:
@
@ Time:2019年4月5日09:40:31
@ Author:Kangruojin
@
.text
.global _start
_start:
@disable watchDog Timer reset function
ldr r0, =0x53000000
ldr r1, =0
str r1, [r0]
@auto choose start-up by Nor flash or Nand flash use Software
@According to the position of hardware switch choose to change
mov r1, #0
ldr r0, [r1] @Backup to save old values
str r1, [r1] @write 0 to addr 0
ldr r2, [r1] @read 0 from addr 0
cmp r1, r2 @compare r1 with r2(Values at different times of addr 0)
ldr sp, =0x40000000+4096 @set stack bottom for start-up by Nor flash
moveq sp, #4096 @if is starp-up by Nand,change stack bottom is 4096
streq r0, [r1] @recover values before address 0 of backup
@led light for C language
bl led_light
_halt:
b _halt
②功能函数(C部分):
/*
* Description:
* 三个按键控制流水灯的延时时间,有增加、减少、回复默认时间三种操作
* start.s中存在关闭看门狗和自动选择Nand启动/Nor启动并设置栈的操作
* Time:2019年4月5日00:18:02
* Author:Kangruojin
* Version:1.2
*/
#include
void delay(volatile int time)
{
while(time--);
}
void init_config()
{
/* 清空GPF0、GPF2、GPF4~GPF6、GPG3的控制信息,同时设置了GPF0、GPF2、GPG3为输入引脚 */
GPFCONr &= ~(GPFCON_GPF0_CONF_BITSf \
| GPFCON_GPF2_CONF_BITSf \
| GPFCON_GPF4_CONF_BITSf \
| GPFCON_GPF5_CONF_BITSf \
| GPFCON_GPF6_CONF_BITSf);
GPGCONr &= ~(GPGCON_GPG3_CONF_BITSf);
/* 配置GPF0、GPF2为输入引脚,GPG3为输入引脚,00;配置GPF4~GPF6为输出引脚 */
GPFCONr |= ((GPFCON_OUTPUT_BITS << 8) \
| (GPFCON_OUTPUT_BITS << 10) \
| (GPFCON_OUTPUT_BITS << 12));
/* 配置GPF0、GPF2、GPG3为输入引脚,上面清空过后,相当于已经配置为输入了,不需要再配一次 */
}
void led_running()
{
#define DEFAULT_DELAY_TIME (25000)
#define DEFAULT_INC_DEALY_TIME (2000)
#define DEFAULT_ADD_DEALY_TIME (2000)
#define DELAY_TIME_MIN (5000)
#define DELAY_TIME_MAX (50000)
/*
* 注意,此处的临时变量是实时更新的,所以防止CPU做优化,加上volatile,防止被不期望的优化
* CPU可能最开始还在取寄存器的值,到后来取了几次发现没变化就不取了,所以被坑了很久
*/
volatile unsigned int gpfTemp=0;
volatile unsigned int gpgTemp=0;
int delay_time = DEFAULT_DELAY_TIME;
unsigned int i = 0;
unsigned int len = 0;
const unsigned char led_info[] = {
0x00, 0x40, 0x60, 0x70, 0x30, 0x10, 0x00, 0x70,
0x00, 0x70, 0x00, 0x70, 0x00, 0x10, 0x30, 0x70,
0x60, 0x40, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70
};
len = sizeof(led_info) / sizeof(led_info[0]);
/* 设置led为流水灯点亮 */
while(1)
{
for(i = 0; i < len; i++)
{
gpfTemp = GPFDATr;
gpgTemp = GPGDATr;
/* 默认是高电平,按下按键为低电平 */
if(_TEST_BIT_IS_ZERO(gpfTemp, 0)){
if(delay_time < DELAY_TIME_MAX){ /* 上限 */
delay_time += DEFAULT_ADD_DEALY_TIME; /* 按键EINT0为增加延时 */
}
}
if(_TEST_BIT_IS_ZERO(gpfTemp, 2)){
if(delay_time > DELAY_TIME_MIN){ /* 下限 */
delay_time -= DEFAULT_INC_DEALY_TIME; /* 按键EINT2为减少延时 */
}
}
if(_TEST_BIT_IS_ZERO(gpgTemp, 3)){
delay_time = DEFAULT_DELAY_TIME; /* 按键EINT11为回复默认延时 */
}
/*
* 要点:不要用或运算直接给这3bit赋值,否则经过几次,这3bit就全被置为1了,不会再改变
* 非要或运算的话,每次需要清空,接着立即去或运算, 但是灯的切换过程,总会有点闪烁不定
* 所以,直接去获取新的状态,然后根据获取的每一位状态设置新的每一位状态即可。
*/
__SET_BIT(GPFDATr, 4, __GET_BIT(led_info[i], 4));
__SET_BIT(GPFDATr, 5, __GET_BIT(led_info[i], 5));
__SET_BIT(GPFDATr, 6, __GET_BIT(led_info[i], 6));
delay(delay_time);
}
}
}
void led_light()
{
init_config();
led_running();
}
③部分寄存器宏定义与字段宏定义:
/* 寄存器定义头文件:soc_s3c2440_reg.h */
#ifndef _SOC_S3C2440_REG_H_
#define _SOC_S3C2440_REG_H_
#define __REG_VALUE(addr) (*((volatile unsigned int *)(addr)))
/* The suffix 'r' means that the macro is a register. */
#define WTCONr __REG_VALUE(0x53000000) /* watch dog register */
#define GPFCONr __REG_VALUE(0x56000050) /* GPF config register */
#define GPFDATr __REG_VALUE(0x56000054) /* GPF data register */
#define GPFUPr __REG_VALUE(0x56000058) /* GPF pins Pull-up constrol register,0 is ebable */
#define GPGCONr __REG_VALUE(0x56000060) /* GPG config register */
#define GPGDATr __REG_VALUE(0x56000064) /* GPG data register */
#define GPGUPr __REG_VALUE(0x56000068) /* GPG pins Pull-up constrol register,0 is enable */
#endif /* _SOC_S3C2440_REG_H_ */
/* 寄存器部分字段定义头文件:soc_s3c2440_field.h */
#ifndef _SOC_S3C2440_FIELD_H_
#define _SOC_S3C2440_FIELD_H_
/* The suffix 'f' means that the macro is the bits of a field in a register. */
/* GPFCON register fields bits */
#define GPFCON_GPF0_CONF_BITSf (0x3 << 0)
#define GPFCON_GPF1_CONF_BITSf (0x3 << 2)
#define GPFCON_GPF2_CONF_BITSf (0x3 << 4)
#define GPFCON_GPF3_CONF_BITSf (0x3 << 6)
#define GPFCON_GPF4_CONF_BITSf (0x3 << 8)
#define GPFCON_GPF5_CONF_BITSf (0x3 << 10)
#define GPFCON_GPF6_CONF_BITSf (0x3 << 12)
#define GPFCON_GPF7_CONF_BITSf (0x3 << 14)
#define GPFCON_INPUT_BITS (0x0)
#define GPFCON_OUTPUT_BITS (0x1)
#define GPFCON_EINT_BITS (0x2) /* EINT0~EINT7 at GPFCON GPF0~GPF7 */
#define GPFCON_REVERSE_BITS (0x3)
/* GPFDAT register fields bits */
#define GPFDAT_GPF0_DATA_BITSf (0x1 << 0)
#define GPFDAT_GPF1_DATA_BITSf (0x1 << 1)
#define GPFDAT_GPF2_DATA_BITSf (0x1 << 2)
#define GPFDAT_GPF3_DATA_BITSf (0x1 << 3)
#define GPFDAT_GPF4_DATA_BITSf (0x1 << 4)
#define GPFDAT_GPF5_DATA_BITSf (0x1 << 5)
#define GPFDAT_GPF6_DATA_BITSf (0x1 << 6)
#define GPFDAT_GPF7_DATA_BITSf (0x1 << 7)
//......GPGCON、GPGDAT略
#endif
④操作宏函数:
#ifndef _SOC_S3C2440_REG_OPERATOR_
#define _SOC_S3C2440_REG_OPERATOR_
#include
#include
#define __GET_BIT(sValue, bitPos) (((sValue) >> (bitPos)) & (0x01))
#define __SET_BIT(sValue, bitPos, setBit) ((setBit) ? ((sValue) |= ((0x01) << (bitPos))) \
: ((sValue) &= ~((0x01) << (bitPos))))
#define _TEST_BIT_IS_ZERO(sValue, bitPos) (__GET_BIT(sValue, bitPos) ? (0) : (1))
#define _TEST_BIT_IS_ONE(sValue, bitPos) (__GET_BIT(sValue, bitPos) ? (1) : (0))
#endif
# 1 "led.c"
# 1 ""
# 1 ""
# 1 "led.c"
# 10 "led.c"
# 1 "./include/soc_s3c2440/soc_s3c2440_reg_operator.h" 1
# 1 "./include/soc_s3c2440/soc_s3c2440_reg.h" 1
# 5 "./include/soc_s3c2440/soc_s3c2440_reg_operator.h" 2
# 1 "./include/soc_s3c2440/soc_s3c2440_field.h" 1
# 6 "./include/soc_s3c2440/soc_s3c2440_reg_operator.h" 2
# 11 "led.c" 2
void delay(volatile int time)
{
while(time--);
}
void init_config()
{
(*((volatile unsigned int *)(0x56000050))) &= ~((0x3 << 0) | (0x3 << 4) | (0x3 << 8) | (0x3 << 10) | (0x3 << 12));
(*((volatile unsigned int *)(0x56000060))) &= ~((0x3 << 6));
(*((volatile unsigned int *)(0x56000050))) |= (((0x1) << 8) | ((0x1) << 10) | ((0x1) << 12));
}
void led_running()
{
# 45 "led.c"
volatile unsigned int gpfTemp=0;
volatile unsigned int gpgTemp=0;
int delay_time = (25000);
unsigned int i = 0;
unsigned int len = 0;
const unsigned char led_info[] = {
0x00, 0x40, 0x60, 0x70, 0x30, 0x10, 0x00, 0x70,
0x00, 0x70, 0x00, 0x70, 0x00, 0x10, 0x30, 0x70,
0x60, 0x40, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70
};
len = sizeof(led_info) / sizeof(led_info[0]);
while(1)
{
for(i = 0; i < len; i++)
{
gpfTemp = (*((volatile unsigned int *)(0x56000054)));
gpgTemp = (*((volatile unsigned int *)(0x56000064)));
if(((((gpfTemp) >> (0)) & (0x01)) ? (0) : (1))){
if(delay_time < (50000)){
delay_time += (2000);
}
}
if(((((gpfTemp) >> (2)) & (0x01)) ? (0) : (1))){
if(delay_time > (5000)){
delay_time -= (2000);
}
}
if(((((gpgTemp) >> (3)) & (0x01)) ? (0) : (1))){
delay_time = (25000);
}
(((((led_info[i]) >> (4)) & (0x01))) ? (((*((volatile unsigned int *)(0x56000054)))) |= ((0x01) << (4))) : (((*((volatile unsigned int *)(0x56000054)))) &= ~((0x01) << (4))));
(((((led_info[i]) >> (5)) & (0x01))) ? (((*((volatile unsigned int *)(0x56000054)))) |= ((0x01) << (5))) : (((*((volatile unsigned int *)(0x56000054)))) &= ~((0x01) << (5))));
(((((led_info[i]) >> (6)) & (0x01))) ? (((*((volatile unsigned int *)(0x56000054)))) |= ((0x01) << (6))) : (((*((volatile unsigned int *)(0x56000054)))) &= ~((0x01) << (6))));
delay(delay_time);
}
}
}
void led_light()
{
init_config();
led_running();
}