平台:JZ2440开发板——CPU:S3C2440(ARM920)
S3C2440作为一款SOC,一般是用来跑系统(如linux等)居多,这里下降到裸机层面,以最下层的方式熟悉硬件相关的软件开发。学习编程语言的第一个实验就是在黑窗或者其他输出中打印出"hello world",而裸机的"helloworld"则为点亮一个LED灯。
有STC89C51或者STM32等单片机开发经验的都知道,裸板中控制CPU其实就是在做读写寄存器,在此基础上增加我们的业务逻辑,就能让板子按照我们所希望地运行,S3C2440也不例外。
分析
想要控制LED灯亮灭,首先从电路知识可以知道,LED为一种发光二极管,单向导通,只需要在其两端按照方向施加一个电势差(具体视LED定,一般为3.3V即可)就可以将其点亮。因此需要先看下所用板子的原理图,如下:
从原理图可知,板载的LED已经接上3.3V高电势,因此我们只需要控制nLED_x(原理图中,n一般表示低电平有效)连接的引脚输出低电平即可。
同样可以从原题图中找到GPF4/5/6连接着需要控制的三个LED灯。下一步需要去S3C2440的芯片手册查找相应的寄存器信息。查看芯片手册可以知道,控制GPIOF来点亮LED需要控制如下寄存器
其中GPFCON为IO复用功能选择,这里只要设置为output作为输出引脚即可,按照手册可知将相应位设置为01即可。
GPFDATA为数据寄存器,当我们设置为输出引脚时,可以往相应位写入数据即可使其输出高或低电平。
GPFUP为上拉控制寄存器,对应引脚位设置为0即使能上拉,上拉设置后,在没有设置GPFDAT时,引脚会被上拉呈高电平状态,该寄存器reset时被初始化为全0,全部引脚默认打开上拉,板子LED默认熄灭,刚好满足需求,因此可以不用设置它。
程序部分
1.汇编
由于S3C2440为ARM架构,因此使用的是ARM汇编指令集,这里简单地写了下设置寄存器让三个LED点亮,直接写值覆盖的方式很粗糙,后续将用更加常用且安全的方式写寄存器。
.text
.global _start
_start:
/* 将GPF4,5,6设置为输出引脚 */
LDR R0,=0x56000050
LDR R1,=0x1500
STR R1,[R0]
/* 设置GPF4,5,6输出低电平 */
LDR R0,=0x56000054
LDR R1,=0x8f
STR R1,[R0]
halt:
b halt
2.C语言
C语言统一的入口为main函数,但是裸板中,需要一个程序来调用我们编写的main,这个程序称为启动程序。由于C语言函数调用传递参数以及自动变量等需要用到栈内存,因此启动程序还要负责将栈设置好,从而让C语言程序正常运行。
Start.S
--------------------------------------------------
.text
.global _start
_start:
MOV R0,#0
LDR R1,[R0] //保存0地址值
STR R0,[R0] //将0值写入,测试是否为NOR_Flash启动
LDR R2,[R0]
CMP R2,R0
LDR SP,=0x40000000+4096
MOVEQ SP,#4096
STREQ R1,[R0]
bl main
halt:
b halt
led.c
---------------------------------------------------------------------------
int main()
{
volatile unsigned int* pGPFCON = (unsigned int*)0x56000050;
volatile unsigned int* pGPFDAT = (unsigned int*)0x56000054;
//能不影响其他位的寄存器写方式
*pGPFCON &= ~((3<<8) | (3<<10) | (3<<12)); //寄存器对应的控制位清0
*pGPFCON |= ((1<<8) | (1<<10) | (1<<12)); //置位对应的控制位
*pGPFDAT &=~((1<<4) | (1<<5) | (1<<6));
return 0;
}
启动文件中,需要判断是nandflash还是norflash启动方式,是由于S3C2440开发板存在norflash启动方式和nandflash启动方式。查阅S3C2440芯片手册可以看到如下内存映射表,
当norflash方式启动时,默认以norflash的0地址作为内存0地址,而芯片内置SRAM(Boot internal SRAM)地址则为0x40000000+4096。
当nandflash方式启动时,芯片将会将nandflash中前4k数据拷贝到内置SRAM(Boot internal SRAM),并以此为0地址运行,默认可用控制内存块为0-4096。
如果需要使用其他地址内存需要进行相应的初始化,这是后面的内容暂且不谈。norflash只读,启动文件利用这一点尝试写入一个0值到0地址处,并读出与0比较。如果为norflash则写入失败,读出的数据应该不为0,因此将栈地址设置为0x40000000+4096。nandflash可读可写,因此写入0后读出数据也为0,因此将栈设置为4096。
至此,用汇编和C语言分别完成了点灯的操作,很粗糙也很简单但是能够实现功能,效果为板子上三个led都亮起。编译该程序使用交叉编译器,并且使用makefile简化编译操作,避免重复输入,针对C语言的一个粗糙Makefile如下:
all:
arm-linux-gcc -o Start.o -c Start.S
arm-linux-gcc -o led.o -c led.c
arm-linux-ld -o led.elf Start.o led.o
arm-linux-objcopy -O binary -S led.elf led.bin
arm-linux-objdump -D led.elf > led.dis
clean:
rm *.bin *.elf *.dis *.o
其中dis为程序反汇编文件,后续会对反汇编文件做分析,暂且先放在这里。