OK6410---使用C语言点亮LED灯

参考OK6410---使用汇编语言点亮led灯

通过查阅s3c6410英文手册,有以下一段话:

Address range of internal ROM is from 0x0800_0000 to 0x0BFF_FFFF, but real storage is only 32KB. This region is read-only, and can be mapped to boot image area when internal ROM booting is selected. Address range of internal SRAM is from 0x0C00_0000 to 0x0FFF_FFFF, but real storage is only 4KB.

 大概意思是:IROM的地址范围是0x0800_0000至0x0BFF_FFFF,但实际的空间大小只有32KB。并且这个区域是只读的;当选择从IROM启动设备时,这个区域会映射到启动映射区(即0地址处)。内部SRAM(即stepping stone)地址范围从0x0c00_0000到0x0fff_ffff,但实际只有4KB的存储空间。

 使用C语言的重点就是设置栈区域!!

 从IROM中启动设备,再从SDHC卡中加载LED程序。这段LED程序使用汇编和C语言混合编写的。从《OK6410---点亮led灯》博文中的介绍可知,IROM中的程序会执行如下的操作:

  • 禁止看门狗
  • 初始化TCM
  • 初始化块设备复制功能
  • 初始化栈区
  • 初始化PLL
  • 初始化指令缓存
  • 初始化堆区
  • 复制BL1到Stepping Stone
  • 校验BL1的完整性
  • 跳到Stepping Stone

我们在使用C语言写LED程序时,可以暂时使用IROM中初始化好的栈区域,所以就有下面的代码:

start.S 

.text
.global _start
_start:
	/*异常向量表*/
	b   reset                          /*复位异常,地址0x0000_0000*/
	ldr pc, _undefined_instruction 	   /*未定义指令异常,地址0x0000_0004*/
	ldr pc, _software_interrupt    	   /*软中断异常,地址0x0000_0008*/
	ldr pc, _prefetch_abort        	   /*预取指令异常,地址0x0000_000c*/
	ldr pc, _data_abort            	   /*数据访问异常,地址0x0000_0010*/
	ldr pc, _not_use               	   /*没用到的向量地址,地址0x0000_0014*/
	ldr pc, _irq                   	   /*外部中断异常,地址0x0000_0018*/
	ldr pc, _fiq                   	   /*快速中断异常,地址0x0000_001c*/
	
_undefined_instruction:
	.word undefined_instruction
_software_interrupt:
	.word software_interrupt
_prefetch_abort:
	.word prefetch_abort
_data_abort:
	.word data_abort
_not_use:
	.word not_use
_irq:
	.word irq
_fiq:
	.word fiq
	
/*复位处理程序*/
reset:
	bl main   /*跳转到C语言编写的程序*/
	
/*死循环程序,防止程序跑丢了*/
stop_here:
	b stop_here
	
undefined_instruction:
	/*未定义指令异常处理函数*/
	b undefined_instruction
	
software_interrupt:
	/*软中断异常处理函数*/
	b software_interrupt
	
prefetch_abort:
	/*预取指令异常处理函数*/
	b prefetch_abort
	
data_abort:
	/*数据访问异常处理函数*/
	b data_abort
	
not_use:
	/*未使用的向量地址处理函数*/
	b not_use
	
irq:
	/*中断处理函数*/
	b irq
	
fiq:
	/*快速中断处理函数*/
	b fiq
	
	.end					 /*标识文件结尾,之后的内容无效*/
	

 led.c

void delay(void)
{
	volatile int i = 0x100000;
	while (i--);
}

int main(void){
	int i = 0;
	
	volatile unsigned long *gpmcon = (volatile unsigned long *)0x7F008820;
	volatile unsigned long *gpmdat = (volatile unsigned long *)0x7F008824;
	
	/* gpm0,1,2,3设为输出引脚 */
	*gpmcon = 0x1111;
	
	while(1)
	{
		*gpmdat = i;
		i++;
		if (i == 16)
			i = 0;
		delay();
	}
	
	return 0;
}

当然,由于IROM初始化的栈区域,在一些文档中说明得不是很清晰,所以我们也可以定义自己的栈区域。但是这里需要注意,栈区域只能定义在stepping stone中(即0x0c000000+4*1024)。因为DRAM还没有初始化,不能直接使用,只有stepping stone是不用初始化就可以直接使用的。以后还要注意程序的大小和栈区域增长的大小,两者不要重叠了,以免导致程序运行出错。只需要修改一下start.S文件就好。

.text
.global _start
_start:
	/*异常向量表*/
	b   reset                          /*复位异常,地址0x0000_0000*/
	ldr pc, _undefined_instruction 	   /*未定义指令异常,地址0x0000_0004*/
	ldr pc, _software_interrupt    	   /*软中断异常,地址0x0000_0008*/
	ldr pc, _prefetch_abort        	   /*预取指令异常,地址0x0000_000c*/
	ldr pc, _data_abort            	   /*数据访问异常,地址0x0000_0010*/
	ldr pc, _not_use               	   /*没用到的向量地址,地址0x0000_0014*/
	ldr pc, _irq                   	   /*外部中断异常,地址0x0000_0018*/
	ldr pc, _fiq                   	   /*快速中断异常,地址0x0000_001c*/
	
_undefined_instruction:
	.word undefined_instruction
_software_interrupt:
	.word software_interrupt
_prefetch_abort:
	.word prefetch_abort
_data_abort:
	.word data_abort
_not_use:
	.word not_use
_irq:
	.word irq
_fiq:
	.word fiq
	
/*复位处理程序*/
reset:
	ldr sp, =(0x0c000000+4*1024)   /*增加了这一句,自定义栈区域*/
	bl main
	
/*死循环程序,防止程序跑丢了*/
stop_here:
	b stop_here
	
undefined_instruction:
	/*未定义指令异常处理函数*/
	b undefined_instruction
	
software_interrupt:
	/*软中断异常处理函数*/
	b software_interrupt
	
prefetch_abort:
	/*预取指令异常处理函数*/
	b prefetch_abort
	
data_abort:
	/*数据访问异常处理函数*/
	b data_abort
	
not_use:
	/*未使用的向量地址处理函数*/
	b not_use
	
irq:
	/*中断处理函数*/
	b irq
	
fiq:
	/*快速中断处理函数*/
	b fiq
	
	.end					 /*标识文件结尾,之后的内容无效*/
	

链接脚本文件:

/*链接脚本*/
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS{
	. = 0x0c000000;          /*注意位置计数器的设置,也可以设置为0x00000000*/
	
	. = ALIGN(4);            /*4字节对齐*/
	.text : {
		start.o(.text)       /*首先链接start.o文件*/
		*(.text)
	}
	
	. = ALIGN(4);
	.rodata : {              /*只读数据段*/
		*(.rodata)
	}
	. = ALIGN(4);
	.data : {                /*已初始化全局数据段*/
		*(.data)
	}
	. = ALIGN(4);
	bss_start = .;           /*bss段的开始地址*/
	.bss : {                 /*未初始化全局数据段*/
		*(.bss)
	}
	bss_end = .;             /*bss段的结束地址*/
}

最后是Makefile文件:

#使用的工具链
CROSS_COMPILE = arm-none-eabi-

#编译工具
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump

#编译选项
#ASFLAGS = -gstabs -mcpu=arm1176jzf-s -march=armv6 -mfpu=vfpv2 -o
LDFLAGS = -T linkscript.lds -o

#最终目标为led.bin
ALL:led.bin
	$(OBJDUMP) -S -D led.elf > led.dump
	
led.bin:led.elf
	$(OBJCOPY) -O binary $^ $@

led.elf:start.o led.o
	$(LD) $(LDFLAGS) $@ $^ 
	
%.o:%.S
	$(CC) -g -c -o $@ $^
	
%.o:%.c
	$(CC) -g -c -o $@ $^

.PHONY:clean
clean:
	del *.bin *.o *.elf *.dump

 以上的代码都是位置无关的代码,讨论一下下面的问题。

 讨论1:

将语句bl main改成语句ldr pc,=main会有什么结果?相应的其他文件怎么改才能让LED点亮?

 

你可能感兴趣的:(OK6410)