因为要移植一个微内核到6410的体系上,我手里正好有一块友坚恒天的UT6410BV04的开发板,在这个板子上有D4、D5、D7、D8四个LED灯,经过查看原理图,知道它们分别对应GPM0~GPM3,查看Datasheet,知道GPMCON的物理地址是0x7F008820,GPMDAT的地址是0x7F008824,下面编写程序控制LED灯亮灭,程序有两种写法,一是汇编实现,一是C实现:
一、汇编实现:
# touch led_on.S
# gedit led_on.S
.text
.global _start
_start:
ldr r0, =0x7f008820
mov r1, #0x00000001
str r1, [r0]
ldr r0, =0x7f008824
mov r1, #0x00000000
str r1, [r0]
main_loop:
b main_loop
Makefile的内容:
CROSS_COMPILE := /usr/local/arm/arm-none-linux-gnueabi/bin/arm-none-linux-gnueabi-
CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
led_on.bin : led_on.S
$(CC) -g -c -o led_on.o led_on.S
$(LD) -Ttext 0x0000000 -g led_on.o -o led_on_elf
$(OBJCOPY) -O binary -S led_on_elf led_on.bin
clean:
rm -f led_on.bin led_on_elf *.o
二、C实现
ARM Linux C环境需要crt0.S构建运行环境,诸如SP之类的,因此先建立crt0.S,下面是crt0.S的内容:
.text
.global _start
_start:
ldr r0, =0x53000000
mov r1, #0x0
str r1, [r0]
ldr sp, =1024*4
bl main
halt_loop:
b halt_loop
然后建立C文件:led_on_c.c,其内容为:
#define GPMCON (*(volatile unsigned long *)0x7F008820)
#define GPMDAT (*(volatile unsigned long *)0x7F008824)
int main()
{
GPMCON = 0x00000101;
GPMDAT = 0x00000000;
return 0;
}
下面是Makefile:
CROSS_COMPILE := /usr/local/arm/arm-none-linux-gnueabi/bin/arm-none-linux-gnueabi-
CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
led_on_c.bin : crt0.S led_on_c.c
$(CC) -g -c -o crt0.o crt0.S
$(CC) -g -c -o led_on_c.o led_on_c.c
$(LD) -Ttext 0x0000000 -g crt0.o led_on_c.o -o led_on_c_elf
$(OBJCOPY) -O binary -S led_on_c_elf led_on_c.bin
$(OBJDUMP) -D -m arm led_on_c_elf > led_on_c.dis
clean:
rm -f led_on_c.dis led_on_c.bin led_on_c_elf *.o
然后编译之后,使用dnw,开发板加电,在uboot下使用usb OTG下载程序到开发板内存0xc0008000,然后:
go 0xc0008000启动程序运行,可以看到LED灯根据控制变亮或者灭掉。
问题1 GPIO( 在上一篇文章说过啦)
问题2 值的确定
/ uboot/include/s3c6410.h
/*
* GPIO
*/
#define ELFIN_GPIO_BASE 0x7f008000
#define GPACON_OFFSET 0x00
#define GPADAT_OFFSET 0x04
#define GPAPUD_OFFSET 0x08
#define GPACONSLP_OFFSET 0x0C
#define GPAPUDSLP_OFFSET 0x10
#define GPBCON_OFFSET 0x20
#define GPBDAT_OFFSET 0x04
#define GPBPUD_OFFSET 0x08
#define GPBCONSLP_OFFSET 0x0C
#define GPBPUDSLP_OFFSET 0x30
*****
****
*******
#define GPLPUD_OFFSET 0x81C
#define GPMCON_OFFSET 0x820
#define GPMDAT_OFFSET 0x824
#define GPMPUD_OFFSET 0x828
#define GPNCON_OFFSET 0x830
#define GPNDAT_OFFSET 0x834
#define GPNPUD_OFFSET 0x838
#define GPOCON_OFFSET 0x140
#define GPODAT_OFFSET 0x144
#define GPOPUD_OFFSET 0x148
在核心板图中能找到子上有D4、D5、D7、D8四个LED灯,经过查看原理图,知道它们分别对应GPM0~GPM3
知道GPMCON的物理地址是0x7F008820,GPMDAT的地址是0x7F008824, 上面的头文件给出。
********************************************************************************************************
作者Makefile写的不错:嘿嘿,分析:
objcopy
GNU objcopy 程序拷贝目标文件的内容到另一个文件。objcopy使用GNU BFD 库读写目标文件. 它可以把源目标文件的内容用不同的格式写入另一个目标文件。命令行选项决定objcopy确切的行为。 注意objcopy应该可以在两种格式之间拷贝经过充分连接的文件。 然而,在两种格式之间拷贝可重定位 的目标文件可能不象预期一样工作。objcopy 产生一些临时文件去做转换工作,这些文件过后被删除掉。 objcopy使用BFD库 来做所有的转换工作。它可以访问BFD描述的所有格式,因而能够识别大部分的格式,即便 没有显式地被告知。
objcopy 可以被使用来产生S记录。(.e.g use -O srec) objcopy 可以被用来产生纯二进制文件。 (.e.g use -O binary) 当 objcopy产生纯二进制 文件时,本质上它会产生输入目标文件的一个内存映像。 所有的符号及重定位信息将被丢弃掉。当产生一个S记录或纯二进制文件时,用-S 选项去掉包含调试信息的段是比较有帮助的。在 某些情况下 用-R 选项去掉二进制文件中的不需要的段是有帮助的。
objdump
objdump:用来显示目标文件的信息. 可以通过选项控制显示那些特定信息. objdump一个最大的用处恐怕
# 就是将C代 码反汇编了. 在嵌入式软件开发过程中, 也可以用它查看执行文件或库文件的信息.
问题3
我们先来分析文件 crt0.s
@ 文件 crt0.s
@ 作用:设置堆栈指针
.text
.global _start
_start:
ldr sp, =1024*4
bl main
halt_loop:
b halt_loop
你可能会有疑问,这个汇编文件有什么用?呵呵,这是因为我们的串口通信代码要用 C
编写(用汇编可读性太差了)。可这又和这个 crt0.s 有什么关系呢?这得从 C 语言程序的
编译说起。C 语言程序执行的第一条指令并不在 main 函数里。当生成一个 C 语言程序时
编译器总是在我们的代码前加一段固定的代码--crt0.o,它是编译器自带的一个文件,用来
设置 C 程序的堆栈等,然后调用 main 函数。可惜在我们的裸板上它自带的 crt0.o 的代
码是不能运行的,我们得自己动手写,这就是为什么要有 crt0.s 这个文件。稍后你将看到,
这个 crt0.s 被编译成我们自己的 crt0.o 文件。
问题4 0x53000000
有一行
request_mem_region(res->start, size, pdev->name)
而
在devs.c中
static struct resource s3c_wdt_resource[] = {
[0] = {
.start = S3C24XX_PA_WATCHDOG,
.end = S3C24XX_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_WDT,
.end = IRQ_WDT,
.flags = IORESOURCE_IRQ,
}
}
在arch/arm/mach-s3c2410/include/mach/map.h
#define S3C24XX_PA_WATCHDOG S3C2410_PA_WATCHDOG
在/arch/arm/plat-s3c24xx/include/mach/map.h
#define S3C2410_PA_WATCHDOG (0x53000000)
即上面行代码要申请0x53000000开始的一段size大小的内存地址空间以访问watchdog
从s3c2440/s3c2410手册上获知
watchdog的寄存器地址本来就是从0x53000000开始的一段内存区域
比如WTCON 0x53000000,WTDAwatchdog 0x53000004,WTCNT 0x53000008等,就是说这些io内存的物理地址都是已经被cpu定义好的了,即使不去request_mem_region,watchdog那些寄存器的物理地址也是从这里开始的,即在驱动程序中访问watchdog就必须去访问这部分物理地址(的ioremap之后的虚拟地址)。
难道内核会把0x53000000这个地址空间分配给别的寄存器吗?
不知道request_mem_region用意为何?
s3c2440的地址空间中,从4千8百万到5b00001c这段地址都是已经分配好的了,用于特殊寄存器的寻址,
访问某个地址就是访问一个已定的寄存器。
不知道我的理解哪有问题
&&&&&&&&&&&&&&&&&&&&&&&&&&&答案7777777777777777777777777777777
至于怎样处理的,跟计算机平台,使用 追捕的总线以及页表有关,很复杂的。
看下LDD3下面的 驱动精灵I/O操作内容吧。
寄存器的地 乔登美语址对cpu而言是不变、既定的,但linux并不知道这些,比如他不知道WTCON寄存器的地 盛大文学 涉黄址是0x53000000,所以要访问硬件WTCON的时候驱动程序必须先向linux申请从0x53000000开始的一段 宁波大学地址,用的便是request_mem_region(res->start, size, pdev->name),然后再将申请到的 霍思燕地址控件用ioremap转化成虚拟地址再去访问........当然如果你访问WTCON的 鲜花朵朵时候却向内核申请了比如0x54000000(在0x54000000还未被申请的情况下,应该可以申请成功),这样访问到 何洁门的寄存器便不是WTCON了
恩,似乎明白了,,对于48000000--5b00001c 马克西姆的那些特殊寄存器,在访问前应先按照cpu说明书上的地址在linux中申请同样 索纳塔御翔的地址空间,成为io内存,再去访问----------因为
寄存器的地 乔登美语址对cpu而言是不变、既定的,但linux并不知道这些,比如他不知道WTCON寄存器的地 盛大文学 涉黄址是0x53000000,所以要访问硬件WTCON的时候驱动程序必须先向linux申请从0x53000000开始的一段 宁波大学地址,用的便是request_mem_region(res->start, size, pdev->name),然后再将申请到的 霍思燕地址控件用ioremap转化成虚拟地址再去访问........当然如果你访问WTCON的 鲜花朵朵时候却向内核申请了比如0x54000000(在0x54000000还未被申请的情况下,应该可以申请成功),这样访问到 何洁门的寄存器便不是WTCON了