逻辑是 让板卡上的两个灯 , 循环的闪烁。
1 首先是 对于 结构体的解释
typedef struct {
unsigned int CON;
unsigned int DAT;
unsigned int PUD;
unsigned int DRV;
}gpx1;
#define GPX1 (* (volatile gpx1 *)0x11000C20 )
这如果 要 对寄存器进行赋值的话, 直接 使用 GPX1.CON= xxxx ,就可以了。
GPX 本事就是 一个 (*P) 的类型了。
移位: 这里有些不理解。直接使用 p->xx , 这样的方式可以吗?
2 然后是 对于 灯循环函数的解释。
while(1)
{
led_on(i%2)
led_off(((i-1)+2)%2)
i++
delay_ms(500)
}
举例:
当i=0 时,
0号灯, 开。
1号灯,关。(第一次,它虽然是关的,但是也把它关掉)
I 加一。
然后是延时。
3 然后是代码
首先是汇编代码:
.text
.global _start
_start:
ldr r0,stacktop /*get stack top pointer*/
mov sp,r0
b main
stacktop: .word stack+4*512
.data
stack: .space 4*512
这里首先是 开辟了一段内存,作为栈。
并且把 sp 指针 指向了 这段栈的 最高处。
5 完整的代码:
//GPL2_0
typedef struct {
unsigned int CON;
unsigned int DAT;
unsigned int PUD;
unsigned int DRV;
}gpl2;
#define GPL2 (* (volatile gpl2 *)0x11000100 )
//GPK1_1
typedef struct {
unsigned int CON;
unsigned int DAT;
unsigned int PUD;
unsigned int DRV;
}gpk1;
#define GPK1 (* (volatile gpk1 *)0x11000060 )
void led_init(void)
{
GPK1.CON = (GPK1.CON & (~(0xf <<4))) | (0x1<<4);
//GPK1.PUD = GPK1.PUD | 0x05;
GPL2.CON = (GPL2.CON & (~(0xf<<0))) | (0x1<<0);
//GPL2.PUD = GPL2.PUD | 0x5;
}
void led_on(int n)
{
switch(n)
{
case 0:
GPK1.DAT = GPK1.DAT|(0x1<<1);
break;
case 1:
GPL2.DAT = GPL2.DAT|(0x1<<0);
break;
}
}
//void led_on(void)
//{
// GPK1.DAT = GPK1.DAT|(0x1<<1);
// GPL2.DAT = GPL2.DAT|(0x1<<0);
//}
void led_off(int n)
{
switch(n)
{
case 0:
GPK1.DAT = GPK1.DAT&(~(0x01<<1));
break;
case 1:
GPL2.DAT = GPL2.DAT&(~(0x01<<0));
break;
}
}
void delay_ms(unsigned int num)
{
int i,j;
for(i=num; i>0;i--)
for(j=1000;j>0;j--)
;
}
int main(void)
{
int i = 0;
led_init ();
while(1)
{
led_on(i%2);
led_off(((i-1)+2)%2);
i++;
delay_ms(500);
}
return 0;
}
然后是makefile 的编写。
TARGET=asm
TARGETC=led
all:
arm-none-linux-gnueabi-gcc -O0 -g -c -o $(TARGETC).o $(TARGETC).c
arm-none-linux-gnueabi-gcc -O0 -g -c -o $(TARGET).o $(TARGET).s
arm-none-linux-gnueabi-gcc -O0 -g -S -o $(TARGETC).s $(TARGETC).c
# arm-none-eabi-ld $(TARGETC).o $(TARGET).o -Ttext 0x40008000 -N -o $(TARGET).elf
arm-none-linux-gnueabi-ld $(TARGETC).o $(TARGET).o -Tmap.lds -o $(TARGET).elf
arm-none-linux-gnueabi-objcopy -O binary -S $(TARGET).elf $(TARGET).bin
# arm-none-linux-gnueabi-objdump -D $(TARGET).elf > $(TARGET).dis
clean:
rm -rf *.o *.elf *.dis *.bin
然后是连接脚本
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x40008000;
. = ALIGN(4);
.text :
{
asm.o(.text)
*(.text)
}
. = ALIGN(4);
.rodata :
{ *(.rodata) }
. = ALIGN(4);
.data :
{ *(.data) }
. = ALIGN(4);
.bss :
{ *(.bss) }
}
疑问: 可不可以这么理解, 就是 在 .data 段里面 就有 栈段了。
4 然后是 代码的编译与烧写。
烧写测试是 正常的。
注意: 关于switch 语句。
switch(n)
{
case 0:
GPK1.DAT = GPK1.DAT&(~(0x01<<1));
case 1:
GPL2.DAT = GPL2.DAT&(~(0x01<<0));
}
如果没有 break 语句的话, 那么 所有的 指令 都会执行, 有点像汇编里面的 标号。