0%

Linux驱动学习日记(3) C语言实现点灯

C语言运行环境构建

创建start.s
① 设置6ULL处于SVC模式下。

图 1 9种运行模式
  所有的处理器模式都共用一个CPSR物理寄存器,因此CPSR可以在任何模式下被访问。设置CPSR寄存器的bit4-0位10011=0X13。

  读写状态寄存器需要用到MRS和MSR指令。MRS将CPSR寄存器数据读出到通用寄存器,MSR将通用寄存器的值写入到CPSR寄存器。

图 2 处理器模式位

② 设置SP指针
  我们将SP指针指向DDR。512MB的DDR范围是0x80000000 - 0x9FFFFFFF。栈大小可以设置为0x200000 = 2MB,由于SP指针是向下增长的,所以我们将SP指向0x80200000。

③ 跳转到C语言
  使用b指令,跳转到C语言函数,比如main函数。

汇编代码实现如下。

1
2
3
4
5
6
7
8
9
.GLOBAL _start
_start:
MRS R0, CPSR
BIC R0, #0x1F
ORR R0, #0x13
MSR CPSR,R0

LDR SP,=0x80200000
b main

简单C语言实现

main.h

1
2
3
4
5
6
7
8
9
10
11
12
#ifndef _main_h_
#define _main_h_

#define CCM_CCGR1 *((volatile unsigned int*)0x020C406C)

#define SW_MUX_GPIO1_IO03 *((volatile unsigned int *)0X020E0068)
#define SW_PAD_GPIO1_IO03 *((volatile unsigned int *)0X020E02F4)

#define GPIO1_DR *((volatile unsigned int *)0X0209C000)
#define GPIO1_GDIR *((volatile unsigned int *)0X0209C004)

#endif

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include "main.h"
void Clk_En()
{
CCM_CCGR1 = 0xffffffff;
}

void LED_Init()
{
SW_MUX_GPIO1_IO03 = 0x5;
SW_PAD_GPIO1_IO03 = 0x10B0;
GPIO1_GDIR = 0x8;
}

void delay_short(volatile unsigned int n)
{
while(n--);
}

void delay(volatile unsigned int ms)
{
while(ms--)
{
delay_short(0x7ff);
}
}

void Led_Set_Pin(int a)
{
if(a == 0) // 拉低
{
GPIO1_DR &= ~(1 << 3);
}
else if(a == 1) // 拉高
{
GPIO1_DR |= (1 << 3);
}
else
return;
}

int main()
{
Clk_En();
LED_Init();
while(1)
{
delay(500);
Led_Set_Pin(1);
delay(500);
Led_Set_Pin(0);
}
return 0;
}

编写MakeFile

思路:首先将objs定义为最终目标ledc.bin的依赖目标集合,第3行定义了ledc.bin的生成必须要objs中的文件,当你有了objs中的文件后把代码段(.text)放到地址0x87800000后生成ledc.elf,再将ledc.elf转换为bin文件。第9-15行规定了对应每一种文件编译到.o文件的规则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
objs := start.o main.o 

ledc.bin : $(objs)
arm-linux-gnueabihf-ld -Ttext 0x87800000 -o ledc.elf $^
arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@

%.o :%.s
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<

%.o: %.S
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<

%.o: %.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
clean:
rm -rf *.o ledc.bin ledc.elf ledc.dis

链接脚本的编写

下面这段代码规定了可执行代码段从0X87800000开始。.rodata表示只读数据段,跟在.text段的后面,ALIGN(4)表示保证4字节对齐,.data表示已初始化的全局变量,.bss表示未初始化的全局变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SECTIONS{
. = 0X87800000;
.text:
{
start.o
main.o
*(.text)
}

.rodata ALIGN(4) :{*(.rodata*)}
.data ALIGN(4):{*(.data)}
__bss_start = .;
.bss ALIGN(4) : { *(.bss) *(COMMON) }
__bss_end = .;
}

编译完链接脚本后,将MakeFile的第四行改为

arm-linux-gnueabihf-ld -Timx6ul.lds -o ledc.elf $^

运行结果

图 3 运行结果