S3C2440裸机实战 之一 创建初始工程
2015-2-3
好几年没玩S3C2440,从单片机玩到嵌入式,就记得这个是我入门嵌入式比较早的一款芯片。
S3C2440是带MMU的,可以上WinCE/Linux这些操作系统,适合做嵌入式开发,做单片机开发的话,总感觉有点浪费,但如果只是玩玩应该没问题。
最近业余时间想把S3C2440拿来当单片机玩玩。
废话少说,直接入主题。
第一步当然是创建初始工程。
既然是裸机实战,想玩点什么特色,看来汇编是不能少的了。ARM9汇编支持的好一点的当然是MDK,IAR的汇编语法和ARM官方的有一些区别,还是算了。
gcc 语法和ARM官方是差不多,不过编译出来的代码质量比不过MDK。这里就选用MDK5.1
单片机的helloworld都是从LED开始,我这里也不例外,就从Led开始,或者说从GPIO开始。
打开MDK5,如果已经有工程随MDK启动时打开,就先关闭当前已经打开的工程。
1.创建新工程,要求选择芯片型号,直接在搜索框输入S3C2440A,确认。
MDK会自动加入S3C2440.s启动文件,里面用ARM汇编语言编写的,对于不太熟悉汇编的童鞋来说,可能看懂还是有点难度。
这里就先不用去看,直接使用它创建完整工程,否者后面编译链接通不过。
到后面如果需要加入一些有趣的功能,再回到这个文件中修改,或者按需要直接重写。
2.设置
1)Target页,
IRAM1 Start 0x40000000 Size 0x1000
去掉Use Cross-Module Optimization 代码前期建议不要开优化
去掉Use mirolib
2) Output页
勾上Create HEX File
3)User页
勾上 Run #1
后面填上 fromelf.exe --bin -o @p.bin @p.axf
4)后面 Debug和 Unitiily页按仿真器类型修改,其他页默认不用修改。
3.初始工程当然还是先完成main函数,那就先创建个main.c的文件,编写基本的main函数。
int main(void) { unsigned int a = 0; unsigned int b = 1; while(1){ a = b ; b = a; } return 1; }编写完后,当然首先想到的是编译嘛。没想到一编译出来各种各样奇怪的错误。
仔细看信息,发现不是编译错误,而是在链接阶段出的错误。
好吧,估计是分散加载设置有问题了。
4.编写分散加载脚本。
点击options for target,也就是工具栏中的设置键,到Linker一栏,去掉Use Memory Layout from Target Dialog,这样就可以用
Scatter File,也就是分散加载脚本方式。我个人的习惯是喜欢用分散加载脚本,上面点勾选的是简单的设置方式,只能支持比较简单的分散加载要求。
复杂的分散加载,它就做不了了,分散加载脚本是简单和复杂通吃。
去掉勾后,系统默认给出早工程文件的同目录下的同工程名的分散加载脚本名,点击后面的Edit。
用下面的代码覆盖它,保存后,重新全部编译,通过。
ER_ROM1 0x40000000 { ER_ROM1 0x40000000 { *.o (RESET, +First) *(InRoot$$Sections) * (+RO) } RW_RAM1 0x40000800 { ;S3C2440.o (MyStacks) .ANY (+RW,+ZI) ; * (+RO) } HEAP +0 UNINIT { S3C2440.o (Heap) } STACK 0x40001000 UNINIT { S3C2440.o (STACK) } }
那是不是到这里就OK了呢,我也不知道,接下来先仿真下。
2015-2-9
5.仿真测试
一仿真才知道出了大问题,ARM的体系,系统栈是满递减方式的,所以栈顶不能超过0x40001000,栈底就更不能超了
计算了下栈容量,应该是0x488,看了map文件,栈顶到了0x40001488,所以脚本要看一下
ER_ROM1 0x40000000 { ER_ROM1 0x40000000 { *.o (RESET, +First) *(InRoot$$Sections) * (+RO) } RW_RAM1 0x40000800 { ;S3C2440.o (MyStacks) .ANY (+RW,+ZI) ; * (+RO) } HEAP +0 UNINIT { S3C2440.o (Heap) } STACK 0x40000B00 UNINIT { S3C2440.o (STACK) } }
steppingstone的代码,且在仿真时,指向第一条指令的位置是0x00000000,而不是0x40000000,看来仿真时仿真期没有让PC指向0x40000000,所以仿真时跑的是steppingstone的代码。
在debug一栏中edit initization file中加上以下代码,这里只是想让PC直到0x40000000,把初始化时钟,RAM等去掉,以后需要再加。
既然是裸机实战,那还是尽量让代码来做我们需要的功能,脚本替我们做了初始化工作就没意思了。当然PC的初始化是没办法了,只能通过脚本来设置,毕竟现在在
nandflash前4KB的代码还不是我们自己的代码,不能掌控。
FUNC void SetupForStart (void) { // <o> Program Entry Point PC = 0x40000000;//0x30000000 } FUNC void Init (void) { // Clock Setup // FCLK = 300 MHz, HCLK = 100 MHz, PCLK = 50 MHz // _WDWORD(0x4C000000, 0x0FFF0FFF); // LOCKTIME // _WDWORD(0x4C000014, 0x0000000F); // CLKDIVN // _WDWORD(0x4C000004, 0x00043011); // MPLLCON // _WDWORD(0x4C000008, 0x00038021); // UPLLCON // _WDWORD(0x4C00000C, 0x001FFFF0); // CLKCON } // Reset chip with watchdog, because nRST line is routed on hardware in a way // that it can not be pulled low with ULINK //_WDWORD(0x40000000, 0xEAFFFFFE); // Load RAM addr 0 with branch to itself CPSR = 0x000000D3; // Disable interrupts PC = 0x40000000; //0x30000000 // Position PC to start of RAM //_WDWORD(0x53000000, 0x00000021); // Enable Watchdog //g, 0 // Wait for Watchdog to reset chip //Init(); // Initialize memory LOAD project.axf INCREMENTAL // Download program SetupForStart(); // Setup for Running
好,到此,S3C2440裸机的第一个工程已经差不多完成了。
在仿照CMSIS编写了外设头文件后,再来操作Led。
这里是借由MKD提供的汇编启动代码来做的,懂汇编的童鞋可以自己写一个汇编启动,如果不懂也没什么,可以照着ARM7的汇编启动代码写,例如LPC213x/214x系列的汇编启动代码。
推荐个链接,对MDK提供的S3C2440汇编启动代码的解读:
http://www.oschina.net/question/565065_115207