打印
[微控制器/MCU]

给ARM初学者的一些建议

[复制链接]
2667|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
blackeye99|  楼主 | 2010-10-9 16:55 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
阅读这个文档的人应当首先阅读ADS1.2的帮助文档及相关内容。这个文档不会对编译器及连接器做出具体的说明,在 需要的时候会指出具体内容在相关资料的章节。同时阅读这个文档的人需要了解ARM指令集和一些ARM汇编的基本内容以及C和C 的相关编程内容。同时还需要了解ARM的流水线结构及一些基本的编程知识。同时为了方便查阅英文文档,所有的相关术语都使用英文原文 字串4
第一章 STARTUP
1 ARM的启动
一般的嵌入式 系统在主程序执行之前都需要执行一些初始化的过程以创造嵌入式程序运行的环境,尤其是一些高级的嵌入式系统,由于核心芯片使用内存映射、内存保护等机制以 及编程使用高级语言C,C 甚至JAVA语言,都需要先创建一个适合程序运行的硬件环境,然后初始化或者配置或者剪裁run-time library, 这些工作都必须在主程序运行前完成,所以一个startup程序或者程序组对于一个嵌入式系统来说是非常重要的。要编写startup程序,需要对编译 器、链接器和汇编器的细节有一定的了解,同时对ARM芯片硬件本身的地址分配以及memory mapping机制也需要有一些了解。
2 .ARM 程序的工作过程
首先由各种source file经过编译产生object文件,然后object文件经过链接生成Image文件,然后通过ICE的方法,根据描述文件的指定下载到目标板上的固 态存储器指定地址当中,比如flash,EEPROM, ROM等等。在程序执行之前,根据某些描述文件,将需要读写数据的部分读出放入动态存储器比如RAM当中,然后程序从ROM开始执行。或者有时为了提高程 序的运行速度,也可以将所有的程序(有一些root的部分除外,以后会提及)通过一个描述文件放入指定的RAM当中,然后程序从RAM开始执行,但是这样 会耗费大量的动态存储器,所以大部分程序会取折中的方法,将需要快速运行的部分和要读写的部分放入RAM中(一般读固态存储器的过程和动态存储器的过程是 一样的,但是写就不同了,所以读写的部分一定要放到RAM中),而只读的部分和对速度要求不是那么高的部分放入固态存储器。同时ARM结构的异常向量表规 定放在地址为0x00000000开始的地址空间上,而一般的CPU为了提高异常相应速度,会将这个向量段remap到其他的RAM当中,所以在描述文件 当中必须精确指定异常向量跳转程序的地址到remap的地方。在application程序执行前,还需要由一些文件描述application程序执行 的环境。比如系统工作时钟,总线频率。现在一般嵌入式编程语言为C,C 等。假如在使用它们的时候使用的runtime-library,那么在程序执行前还需要为这些库函数初始化heap。然后ARM可能工作在不同的模式, 还需要为不同的工作模式设置stack。这样,描述链接地址的文件,以及在application运行前所有的初始化程序就是startup程序组 字串5
3. STARTUP分类
这样,将startup程序所完成的功能分类。一类是链接地址描述,一类是各种初始化的程序。根据不同的应用,描述文件和初始化程序的内容以及结构和复杂程度都会不同。但是基本上,它们都必须实现以下功能。 3.1 描述文件实现功能
描述文件可以是链接命令行上简单的几个字符,也可以是一个非常复杂的文件,但是它必须完成如下功能:
; 指定程序下载的地址
; 指定程序执行的地址
3.2 初始化程序实现的功能
初始化程序根据不同的应用,其结构和复杂度也不同,但是它必须完成如下基本功能:
; 异常向量初始化
; 其他硬件环境初始化
; 内存环境初始化

相关帖子

沙发
blackeye99|  楼主 | 2010-10-9 16:56 | 只看该作者
一、不要去管thumb指令,这个东西可以暂时放一放。也就是说,基本上看到thumb这个字眼就可以跳过去。写书的人追求全面,不代表我们一开始学习的时候就要面面俱到。
二、不要去死记硬背ARM指令。知道怎么回事就可以了,用的时候去查书。你经常用到的指令也就那么几条,当你学到一定境界的时候再回过头来看它。但是要知道一些面上的知识,比如条件执行,寻址方式等等。
三、弄清楚ARM的寄存器组织,知道37个寄存器是怎么回事,哪些是公用的,哪些是私有的。
四、学一下异常处理,最少要把IRQ和FIQ产生的原因、处理的办法及返回形式弄懂。
五、接触到具体芯片时,没有必要把所有的功能一下子全搞清楚。可以先跑跑GPIO,哪怕是跑个流水灯。后面可以跑跑串口什么的,建议先用汇编玩,主要是为了摸清楚最底层的东西。
六、操作系统?还是先放一放吧,这个东东比较麻烦。如果你想搞清楚它是怎么回事的话,一些基础知识还是要补一下的。比如操作系统原理、编译原理等等。用UC/OSII比用ucLinux要好一些,因为它更简单。当然了,如果你只是想玩一玩,模仿就足够了。没必要去弄清楚细节方面的东西,但是如果你想进入更高的层次,基础的东西还是回避不了的。
以上纯粹是一家之言,每个人都有自己的看法。

他人建议:

我认为初学ARM 首先用C编程,不要去管汇编;找简单的ARM7入门,不要一开始就选ARM9;用KEIL这个编译环境,本人觉得ADS这个编译环境不适合入门。ARM7最好选择LPC(推介用214X系列或者是22系列,更低级的系列纯粹就是高级点的单片机)系列的入门,他比44B0好入门多了
如果要选择操作系统,首先选择UC/OS-2

使用特权

评论回复
板凳
blackeye99|  楼主 | 2010-10-9 16:57 | 只看该作者
keil for arm中如何实现中断操作

在arm目录里搜带"__irq"的c代码
在arm目录里搜带"__irq"的c代码,然后打开相应的project看看。如:
D:\Keil\ARM\RV30\Boards\Samsung\S3C44001\Hello\

要点:
1.在启动文件里设置中断向量,比如s3c44b0x.s里有一段:
                 IF       VIM_SETUP <> 0

                 MACRO
$IRQ_Vector      IRQ_Vec $Num, $HandlerName
$IRQ_Vector      IF       (VIM_CFG:AND:(1:SHL:$Num)) <> 0
                 IMPORT   $HandlerName
                 LDR PC, =$HandlerName
                 ELSE
                 B        .
                 ENDIF
                 MEND

                 IRQ_Vec 25, HandlerEINT0
                 IRQ_Vec 24, HandlerEINT1
                 IRQ_Vec 23, HandlerEINT2
                 IRQ_Vec 22, HandlerEINT3
                 IRQ_Vec 21, HandlerINT4567
                 IRQ_Vec 20, HandlerTICK
                 B        .
                 B        .
                 IRQ_Vec 19, HandlerZDMA0
                 IRQ_Vec 18, HandlerZDMA1
                 IRQ_Vec 17, HandlerBDMA0
                 IRQ_Vec 16, HandlerBDMA1
                 IRQ_Vec 15, HandlerWDT
                 IRQ_Vec 14, HandlerUERR01
                 B        .
                 B        .
                 IRQ_Vec 13, HandlerTIMER0
                 IRQ_Vec 12, HandlerTIMER1
                 IRQ_Vec 11, HandlerTIMER2
                 IRQ_Vec 10, HandlerTIMER3
                 IRQ_Vec 9,   HandlerTIMER4
                 IRQ_Vec 8,   HandlerTIMER5
                 .................

                 ENDIF

如果要用HandlerTIMER5,将VIM_SETUP设为1,VIM_CFG中与TIMER5相关的bit置1。这样在中断向量表中在TIMER5中断位置会生成一句:
      LDR PC, =HandlerTIMER5
其中HandlerTIMER5为中断服务程序的函数地址

2。在C程序中为中断5写服务程序:
__irq void HandlerTIMER5 (void) {            /* Timer 5 Interrupt Handler */

   timeval++;                                 /* Increment Time Tick   */
   pIC->I_ISPC = INT_TIMER5;                  /* Clear Interrupt Flag */
}

函数定义前加上__irq,函数名与启动文件中的一致。

3。在scatter文件中将中断向量表放到0地址,这个不用解释了吧?

4。如果你调试时程序加载到ram,最后程序是烧到flash中的,如果arm不支持remap(如s3c44b0x),那么用仿真器在ram中程序时发生中断,会到flash中找中断向量表,因此flash中的向量表要与ram程序的中断向量表一致。解决至少有两种,这里介绍中断调用比软件快的一种。启动文件仍然如上,因此只要保证HandlerTIMER5函数的地址在flash中运行与仿真器加载到ram中运行一样。步骤:
a.给中断服务程序的程序段起个名字,加上#pragma arm section编译指令:
#pragma arm section code = "ISR_TIMER5"
__irq void HandlerTIMER5(void)
{
     if(RefreshBatt!=0)RefreshBatt--;
     rI_ISPC |= (1<<8);
}
#pragma arm section code //表示恢复原来的程序段名
b.两个运行版本本给ISR_TIMER5段分配相同的地址,如:
ISR_SECT   0x0C1F0000
{
        isr.o (ISR_TIMER5, +first)
}
这样就保证了HandlerTIMER5在两个运行版本中的地址是一样的,中断向量表当然也是一样的了。

如果有多个中断服务程序,最老实的办法是每个服务程序命名一个段,然后在scatter文件中分配地址。如果你能确认编译参数和链接循序是一样的,产生代码位置也是一样,分配到同一个段中也行,不过总有点隐患。

另外一个方案里改启动文件,思路是中断服务地址在程序运行时存到一个地址中,而中断服务程序先跳到一个取地址的小代码里。这样的好处是中断服务程序的地址随意,但增加了调用中断的时间。因为不仅要多跳转一次,而且取地址要用到寄存器又得压栈。因此个人倾向于前一种方案。

使用特权

评论回复
地板
6019赵文| | 2010-10-9 19:02 | 只看该作者
写启动文件还真是一门技术啊,我现在arm用了好长时间了,但还是不会写自己的启动文件

使用特权

评论回复
5
huzixian| | 2010-10-9 20:10 | 只看该作者
很中肯很有用啊,但是觉得还是得从C入手,了解芯片最好的办法就是先从它的启动代码看起

使用特权

评论回复
6
yulri| | 2010-10-12 16:29 | 只看该作者
追踪……

使用特权

评论回复
7
0331631| | 2010-10-21 21:51 | 只看该作者
很全面知道自己要怎么学习了

使用特权

评论回复
8
huzixian| | 2010-10-24 12:14 | 只看该作者
要走的学习之路还很长

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

73

主题

103

帖子

1

粉丝