MDK下裸奔2440教程
1、背景
很多人学习嵌入式编程都是从裸机开始的(我个人也是)。这并不是偶然,想要零基础入门嵌入式开发,玩转裸机绝对是最好的入手点。这样至少可以培养以下必备技能:
l CPU体系结构,编程模式及汇编语言、指令结构的知识
l 芯片内部外设,如串口、定时器、LCD、Nandflash等的相关编程知识
l 编程环境(这里主要介绍MDK),调试环境(主要是Jlink)的使用方面的知识
l 使用MDK的软件仿真、Jlink硬件仿真等方法来进一步深入学习的知识和技能
l 把一个自己写的或者移植来的不工作的程序debug到可以工作的技能。
请注意,我这里用了知识和技能两个词来表达我们在这个过程中要学到的东西。知识指的是静态的东西,是别人可以传授给你的(当然你自己也可以主动学习,现在的网络教材很多),而技能是不能被传授的,只能通过自己训练得到。举个课堂上经常给学生讲的例子:就算让刘翔给你当跨栏老师,你就能拿冠军吗?刘翔能教给你的只有跨栏相关的知识,但是你能跑多快这个是你自己要去通过锻炼、练习得到的技能。
2、平台如何选择
2.1、为什么是MDK
现在做2440上的开发,可以选择的大约有4个平台。
1) ADS1.2:老牌的2440开发工具了,现在网上很多流传甚广的学习资料都是基于ADS1.2的。优点就是简单易用、也够用、资料多;缺点是早就不被官方支持了,所以后劲不足,据说最多可以支持多ARM11。
2) MDK:做过C51开发的同学都对Keil uVision不陌生了,MDK的页面和操作继承了KeilC51的风格,可谓简单好用、功能强大。而且目前ARM官方还在不停更新MDK,按照ARM公司的策略,未来ARM的三驾马车之一的Cortex-M系列CPU的开发工具主要就是MDK了。同时,MDK用来开发ARM9的2440也是很好用的。
3) RVDS:这个工具很多人甚至都没听说过,但实际上它才是微软官方主推的ARM开发工具。基于eclipse开发,对很多做过Java的来说可谓爱不释手(而且eclipse有一个最大的好处,就是可以跨平台。既支持windows,又有linux版本)。很可惜本人用微软的VisualStudio太久了,始终习惯不了eclipse,所以RVDS至今没用过。
4) Gnu gcc:对于立志于嵌入式人生的童鞋来说,gnu gcc平台是最终的归宿了,因为linux、uboot等嵌入式领域大牛存在们都是基于gnu平台的。关于gnu平台的裸机开发,韦东山老师的视频和书讲的不错,建议大家可以参考一下。不过,gnu平台总的来说环境搭建较麻烦、不适合新手。(很多人甚至虚拟机装个linux都要折腾个把星期···往往消磨了信息和兴趣,错失了最佳的学习时机。)
鉴于以上分析,推荐大家使用MDK来裸奔2440。因为MDK不但简单好用,而且和Jlink的配合也是天衣无缝,可以让新手不必浪费太多时间的情况下尽快跑起来简单的代码,这样学习才能有兴趣、有乐趣、才不枯燥。而且很多人将来都很可能会使用到Cortex-M系列的CPU(这是ARM未来在单片机领域的主打型号,目前已经占领了大量市场,有很优秀的性价比,值得一用。),届时MDK的使用经验将会对你帮助甚大。
注:IAR之类也很流行的平台,因为本人从未接触过,所以不做评价。
2.2、为什么选择Jlink
这个我想应该不用过多解释,选择Jlink的理由太多了:便宜、好用、功能强大、资料多····而且现在笔记本大多没有并口(即使是台式机也大多都没有并口了),并口Jtag确实已经没有学习的必要的。
2.3、+SDRAM什么意思
代码编译后得到可执行文件(ARM的可执行文件是axf格式,但是官方提供了fromelf工具,可以很方便的转换为bin格式。Axf格式可以直接在SDRAM内运行却不能下载到flash中,而bin格式可以下载到flash中启动运行,也可以直接加载到SDRAM中运行),常用的调试方法有两种:一个是下载到flash中并重启开发板运行,观察并调试;另一种是直接利用Jlink将可执行文件加载到SDRAM中运行并调试。
对于裸机调试,推荐大家使用第二种方法。一是因为新手调试程序往往要反复修改,而SDRAM调试下载启动很快所以很方便了;二是因为Jlink+SDRAM debug程序时可以辅以单步调试、断点、寄存器、地址数据查询等基础调试手段,是用来学习的好方法。
3、开发平台搭建
本教程的实验平台如下:
l 开发板为GEC2440_V1.1
l Jlink V8 + 驱动版本V4.08I
l 串口线:PL2303 USB转串口
l 软件版本:MDK3.5
4、MDK工程建立及设置
说了半天,终于到正题了。我们今天的主题就是用MDK来建立工程,编写代码,并合理设置工程选项,最终在Jlink的配合下将自己编写的代码下载到开发板的SDRAM内运行并调试。程序该怎么写那是另外的话题,我们今天关注的焦点还是工程建立和设置。
4.1、工程建立
首先打开MDK(怎么安装和破解MDK这里就不多说了,网上大把教程),菜单栏Project->New uVision Project,然后是选择器件,这里当然是选择Samsung的S3C2440A了。接下来会弹出对话框提问我们是否要自动添加samsung的2440启动代码,这里选择否。
这个启动代码是samsung官方提供的,标准的启动代码。实际上所有关于2440的bootloader内的start.S(有时叫init.S)都参考了这份官方的启动代码,但是我们这里不选择添加,因为我们后面会自己来写一个启动代码。这个启动代码刚开始很简单(为了便于理解),后面随着课程的进行会逐渐复杂,并最终和官方的启动代码殊途同归。这样就将本来复杂的启动代码,分阶段慢慢的潜移默化的让大家学习、接受。粤嵌教育几年来的实践经验表明,这样的学习方法效果很好。大多数学生在ARM裸机部分学习完毕后,再学习uboot等bootloader时,start.S启动部分代码已经完全理解清晰了。
4.2、源文件等添加
工程建立好后,就要添加源文件了。首先进入到刚才建立好的工程的目录下(我这里是D:\My Documents\ARMProj\2440MDK),手工建立两个文件夹src和cfg。src目录下用来存放工程内的源代码(这里因为是最简单的工程,所以将.S和.c文件放在一起,将来工程复杂起来后,会将.S,.c,.h等文件按照逻辑重新组织以便管理),cfg目录用来存放分散加载文件.scat和调试初始化文件EXT_RAM.ini。参见附图1
进入src目录下,手工建立两个txt文件,并且重命名为start.S和xmain.c。并将附件1中的start.S和xmain.c的内容拷贝进去保存。
进入cfg目录下,手工建立txt文件,重命名为Ext_RAM.ini,并将附件1中Ext_RAM.ini内容copy进去保存留用。
在MDK主页面内左侧ProjectWorkspace->Target1->Source Group 1内右键,添加刚才建立的start.S和xmain.c文件到工程内。
4.3、工程设置
此时源文件已经添加完毕,但是点击编译按钮就会发现报错,这是因为还没有进行工程设置。主要的设置都在Options for Target内,首先点击图标打开这个选项卡,从最上面一行可以看到这个选项内容非常之多,下面我们一项一项来详细介绍如何设置。参见附图2
4.3.1、Device项
选项卡打开时默认是直接到第二项Target的,手动切换到Device项,可以看到就是我们建立工程时选择器件的页面。这里因为我们之前已经选择了2440,因此不需再次设置。
4.3.2、Target项
该项目主要分两部分,上半部分与我们的调试设置无关,可以不管。当然大家若有兴趣可以上网详查或者阅读MDK的帮助文档,这里问了节省篇幅不再赘述。
下半部分很重要。这里主要是设置我们工程的Memory Layout(内存分布),在SDRAM调试程序时,这里的设置如果有分毫偏差调试过程都不能正常进行。详细设置参见附图3。
4.3.3、Output项
该项目设置工程输出文件,这里基本不用设置。不过我一般会将Name of executable项修改为armplat这个名字。这个项目是用来设置将来编译出的axf的名称的,默认是和工程名同名。譬如我们的工程名为test,所以默认编译出的可执行文件名为test.axf。我们这里之所以会改为armplat.axf,是因为这个名字实际上在Ext_RAM.ini文件内有用到,而我们如果每个工程的可执行文件名不同,就需要每个工程都去修改Ext_RAM.ini的内容。为了避免麻烦,于是我把每个工程的可执行文件都命名为armplat,这样Ext_RAM.ini可以不经修改的在各个工程中通用。
4.3.4、Listing项
这个项目下是配置监听项的,这里不用做任何更改。
4.3.5、User项
这个项目是设置项目预编译前,编译前以及编译后的附加任务的。上文提到MDK默认得到的可执行文件是.axf格式的,而我们如果要烧录可执行档到flash内是需要bin文件的。于是乎我们就需要fromelf工具将axf转为bin。所以在这里,我们在Run User Program after Build/Rebuild栏目内打钩第一项,然后输入C:\Keil\ARM\BIN40\fromelf.exe--bin -o ./armplat.bin ./armplat.axf。--bin之前是fromelf工具的路径,注意如果你的Keil不是装在C盘下可能你的路径与我的不同哦,要根据你的电脑上安装目录而调整。--bin开始即是给fromelf的命令行参数了,意思是从armplat.axf得到armplat.bin。(如果你刚才在Output栏设置的名字不是armplat,这里也要相应的改名的)参见附图4。
这里还有一点需要强调,就是选项前面的钩一定要打上。有时候忘记了打钩,编译后就是没有bin文件出来,检查来检查去不知道是哪里出错了。
4.3.6、C/C++项
主要是设置armcc对c和c++文件的处理选项,譬如预定义符号和优化等级等。因为我们是简单工程所以该选项卡内不需设置更改。将来如果工程结构复杂了,会需要在这里设置include目录。这里限于篇幅不多讲了。
4.3.7、Asm项
和C/C++项类似,是armasm汇编器处理选项设置,同样不需做处理。
4.3.8、Linker项
该项目是设置链接器armlink的选项的。这里有一个重要选项,就是勾选上左上角的Use Memory Layout from Target Dialog,其余均不需选择。这个选项勾选的意思就是告诉链接器,链接时按照我们在Target选项设置的存储器结构特征来执行链接。
4.3.9、Debug项
这个选项卡是Debug相关的设置,分为两部分。左边是软件模拟器调试,右边是调试器调试选项。软件模拟器调试是用来学习ARM汇编语言编程,以及分析启动代码等的好工具,非常推荐没有Jlink和开发板在手头的同学使用。但是我们这篇教程主要还是针对Jlink仿真器硬件调试来讲的。
首先勾选右侧Use xxx前的钩,表示我们选择仿真器硬件调试。然后再仿真器下拉框内选择J-link / J-Trace项。Run to main建议大家不勾选,因为这样调试时可以从启动代码开始处进行,方便大家分析学习启动代码的运行流程,和启动代码是如何转入c环境的main函数的问题。
下面一项Initializationfiles是重点了,这里要选择我们刚才建立工程时cfg目录下拷贝的Ext_RAM.ini。这个初始化文件最初是从MDK3.5的安装目录下C:\Keil\ARM\Boards\Samsung\S3C2440\RTX_Blinky目录下copy来的,个人进行了一些小小的改动。这个初始化文件的主要作用就是设置时钟、关看门狗、初始化SDRAM控制器这些在SDRAM上运行代码前必须要做的事情。
4.3.10、Utilities项
这一项主要是设置flash的,如果要使用MDK的烧录flash功能就一定要设置这里了。虽然我们不通过MDK烧录开发板flash,但是这里有一项是一定要设置的。
去掉UpdateTarget before Debugging前的钩。这个选项的意思是在进入调试前先更新目标板,但是我们并没有配置目标板的flash,所以这个选项如果不去掉,在调试时就会弹出错误,大意是在xxxx地址没有找到算法。
5、实验总结
经过了第4步的一系列设置,再次编译程序,没有错误但是有一个警告。armplat.sct(8): warning: L6314W: No section matches pattern*(InRoot$$Sections)。这是因为我们在程序中使用了xmain而不是main,链接器找不到main所以抱怨一下,不必担心这个警告并不会影响程序运行。
编译后,连接Jlink与开发板,开发板上电,点击Debug按钮即可开始调试。建议大家多使用单步调试观察程序运行,这是学习ARM编程、积累自身功力的最好办法。当然也可以直接点击Go全速运行,即可看到开发板上LED1闪烁效果了。
附图1,MDK调试2440工程
附图2,Options forTarget选项卡
附图3,SDRAM调试对应的MemoryLayout设置
附图4,User选项设置fromelf
附件1:调试工程演示源码
1.1 start.S
IMPORTxmain
AREA RESET, CODE, READONLY
ENTRY
B Handler
Handler
B xmain
loop
B loop
END
1.2 xmain.c
#define rGPBCON (*(volatile unsigned int*)0x56000010)
#define rGPBDAT (*(volatile unsigned int *)0x56000014)
#define rGPBUP (*(volatile unsigned int *)0x56000018)
void delay(void)
{
inti;
for(i=0;i<500000;i++);
}
void xmain(void)
{
//GPBCON[11:10]=01
rGPBCON&= ~(0x3<<10);
rGPBCON|=(0x1<<10);
//GPBUP[5]=1
rGPBUP|=(0x1<<5);
rGPBDAT&= ~(1<<5);
while(1)
{
//GPBDAT[5]=1
rGPBDAT|=(0x1<<5);
//DELAY
delay();
//GPBDAT[5]=0
rGPBDAT&=~(0x1<<5);
//DELAY
delay();
}
}
1.3 Ext_RAM.ini
/******************************************************************************/
/* Ext_RAM.INI: External RAM (SDRAM)Initialization File */
/******************************************************************************/
// <<< Use Configuration Wizard inContext Menu >>> //
/******************************************************************************/
/* This file is part of the uVision/ARMdevelopment tools. */
/* Copyright (c) 2005-2008 Keil Software.All rights reserved. */
/* This software may only be used under theterms of a valid, current, */
/* end user licence from KEIL for acompatible version of KEIL software */
/* development tools. Nothing else givesyou the right to use this software. */
/******************************************************************************/
FUNC void SetupForStart (void) {
// <o> Program Entry Point
PC= 0x30000000;
}
FUNC void Init (void) {
_WDWORD(0x4A000008, 0xFFFFFFFF); // Disable All Interrupts
_WDWORD(0x53000000, 0x00000000); // Disable Watchdog Timer
//Clock Setup
// FCLK= 300 MHz, HCLK = 100 MHz, PCLK = 50 MHz @12M cystall external
_WDWORD(0x4C000000, 0x0FFF0FFF); // LOCKTIME
_WDWORD(0x4C000014, 0x0000000F); // CLKDIVN
_WDWORD(0x4C000004, 0x00043011); // MPLLCON s=1,p=1,m=67
_WDWORD(0x4C000008, 0x00038021); // UPLLCON s=1,p=2,m=56, CLK_UPLL=96MHz,so CLKDIVN[3]=1
_WDWORD(0x4C00000C, 0x001FFFF0); // CLKCON enable all peripheral clock
// MemoryController Setup for SDRAM
_WDWORD(0x48000000, 0x22000000); // BWSCON not using UB/LB,WAIT, 32bit data width
_WDWORD(0x4800001C, 0x00018005); // BANKCON6 bank6 setSDRAM type
_WDWORD(0x48000020, 0x00018005); // BANKCON7 bank7 setSDRAM type
_WDWORD(0x48000024, 0x008404F3); // REFRESH
_WDWORD(0x48000028, 0x00000032); // BANKSIZE bank 6/7set to 128M/128M
//_WDWORD(0x48000028, 0x00000030); bank6/7 set to 32M/32M
_WDWORD(0x4800002C, 0x00000020); // MRSRB6
_WDWORD(0x48000030, 0x00000020); // MRSRB7
_WDWORD(0x56000000, 0x000003FF); // GPACON: Enable Address lines for SDRAM
}
// Reset chip with watchdog, because nRSTline 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 toitself,0xEAFFFFFE means B loop. usethis to ressult a watchdog timeout
CPSR = 0x000000D3; // Disable interrupts(irqand fiq),set to svc mode
PC = 0x40000000; // Position PC to start of RAM
_WDWORD(0x53000000, 0x00000021); // Enable Watchdog
g, 0 // Wait forWatchdog to reset chip
Init(); // Initialize memory
LOAD armplat.axf INCREMENTAL //Download program
SetupForStart(); // Setup for Running
//g, xmain // Goto Main
//g, RESET