实作你的第一个嵌入式系统 第一章读书笔记

/*====================================================
*
* 标题:实作你的第一个嵌入式系统
*
* xue, 2012、05、18 15:45
*
* 说明:读书笔记
*
*===================================================*/
1、嵌入式系统开发流程
 a、在PC上安装开发环境(包含cross-compiler、debugging、tool、editor、ICE驱动程序等)
 b、程序编写
 c、编写makefile
 d、build程序(执行makefile)
 e、利用ICE或其他工具将程序下载到机器上,并测试程序的执行结果
 f、如程序无误,用烧录器将程序烧写到ROM,则以后机器上电,就会执行新的程序
 
2、先别急着写程序,首先要了解嵌入式系统开发环境思想,然后先看CPU厂商提供的
 sample code 和makefile。
 
3、一个完整的嵌入式系统至少包含多少种类的文件,那些文件是程序员必须编写的,那些是由
 工具产生的?
 例:简单的LED亮灭project中包含以下文件
 程序部分(由程序员编写)
  vector.c:中断矢量表
  bot.c:启动程序
  main.c:应用程序主程序
  drv_timer0.c:Timer(定时器)的驱动程序
  drv_LED.c:LED驱动程序
 其他文件(由程序员编写)
  test.mak:makefile
  test.lds:Link Script(提供给Linker的信息,描述程序要被寻址到那个地址去)
  build.bat:执行makefile
 执行build.bat后产生:
  test.elf:build后产生之ELF格式的可执行文件
  test.bin:由ELF格式转换来的binary文件
  test.err:错误信息文件(存储编译及连接时期的输出信息)
  vetor.o,boot.o,main.o,drv_timer0.o,drv_LED.o:.c文件编译后的object文件
  
4、第一步:弄清楚CPU的规定流程,简单来说,就是CPU reset后,会到哪个地址执行第一条指令?
  回答这个问题前,我们先来看一下一般的嵌入式系统执行的流程
   a、CPU会到特定的地址处获取第一条指令,实现的细节有两种。
    1、CPU重新启动后,将其PC寄存器设为特定的地址,只要user的程序确实存储于这个地址,
    就可以正确被执行。
    2、CPU重新启动后,会将CPU存储的中断矢量表地址的寄存器设为某特定的地址,接着引发
    RESET中断,所以程序员只要把中断矢量表存储于这个地址,经指定RESET中断的处理程序为
    自己写的某个函数(如:boot()),则该函数就可以在开机后被CPU执行。
   b、user程序开始运行后,会对CPU进行初始化
   c、将程序的数据段从存储器(ROM或FLASH)载入到RAM中
   d、初始化CPU后,接着初始化应用程序所用到的硬件设备
   e、初始化各个子系统,如嵌入式操作系统(RTOS),动态存储管理、图形界面系统等
   f、执行应用程序
   
  首先,我们先不管b、c、d、e、f,我们先把目光放到a上面,假设我们的CPU使用手册上规定我们程序的开头必须
  被寻址到0xc0000,它是采用方式2的。即是CPU启动后,会将0xc0000当做存放中断矢量表的地址,并引发RESET中断,
  因此,我们只要把启动程序所在的地址存放在中断矢量表中代表RESET中断的entry即可。
  
 第二步:我们来聊一下中断矢量表
  首先我们来看一下一般程序和中断处理程序(简称ISR)的区别?
   我们都知道一般程序是循序执行的,而中断程序则是可能在任何时间点发生,而且一般情况下(CPU不屏蔽中断),
   当中断发生时,CPU先记录目前的状态,然后去执行中断处理程序,执行完毕后,再回来取回刚才的状态,然后
   返回被中断的地址继续循序执行。
  来看一下中断向量表是咋回事,程序如下:
  /*================================================
   vetor.c
  =================================================*/
  void dummy(void);
  
  //定义其他程序中的functions
  extern void boot(void);
  extern void drv_isr_timer0 (void);
  
  //中断矢量表定义
  const unsigned long vetor[] =
  {
   (unsigned long) boot,  //00  Reset
   (unsigned long) dummy,  //Division by zero
   (unsigned long) dunmy,
   (unsigned long) dunmy,
   (unsigned long) dunmy,
   (unsigned long) dunmy,
   (unsigned long) dunmy,
   (unsigned long) dunmy,
   (unsigned long) dunmy,
   (unsigned long) drv_isr_timer0,  //Timer 0
   (unsigned long) dunmy,
   (unsigned long) dunmy,
   ........
  }
  从上面我们可以看到,一般的,中断矢量表就是一个C数组,当然也可以用汇编来编写,不过意义
  都是一样的,实际上CPU只有存储地址的思想,当然不知道C数组是啥东东,说穿了,所谓的中断矢
  量表就是从某个地址开始,每4个BYTE为一个单位(entry),每一个entry记录了一个函数的地址,就这么简单。
  
  现在,我们来理清一下思路:在上述的中断矢量表中,只有两个不是dummy()的中断矢量,第一个指到
  boot(),CPU启动后会产生RESET中断,而boot()就是RESET中断的ISR程序,所以CPU启动后第一个执行的
  程序就是boot(),另一个不是dummy()的中断矢量则是指向drv_isr_timer0的,当Timer0溢出时,CPU就会引发
  Timer0中断,因此,drv_isr_timer0就会被调用,明白否?
  
 第三步:现在,我们已经聊完了中断矢量表了,接下来就是boot()了。先看一下boot()负责干些什么:
  a、设定某些重要的CPU寄存器,特别是堆栈指针寄存器与状态寄存器。
  b、CPU各部分初始化
  c、系统初始化
  d、调用应用程序的主程序
  e、结束
  例:boot()程序范例
  /*====================================
   boot.c
  ====================================*/
  _interrupt void boot(void)
  {
   //设定SP寄存器
   asm("xld.w  %15,0x2000");
   asm("ld.w  %sp,%15");
   
   //设置CPU状态寄存器
   asm("xld.w  %15,0x200010");
   asm("ld.w  %psr,%15");
   
   //CPU初始化
   _init_pull();
   _init_bbcu();
   _init_ebcu();
   _init_cache();
   _init_int();
   
   //系统初始化
   _init_sys();
   
   //调用主程序
   main();
   
   //一般的嵌入式系统,主程序不会返回boot程序
   exit();
  }
 对于以上的这个程序,我们来解剖一下
  Q1:函数定义前面为何有‘_interrupt’这个东西,是C关键字吗?貌似标准C关键字没这个家伙吧!
  A1:'_interrupt'并非C语言的标准,它是这个Cross_Compiler自定的关键字,写在代表中断处理
  程序的函数定义前面。这个关键字并非只是用来区分一般函数和ISR那么简单,实际上,ISR的
  开头和结尾要做的动作和一般的C语言函数不同,具体不同表现在一般函数开头只需将返回地
  址存放在堆栈中,而ISR函数必须把返回地址(发生中断的地址)以及CPU的状态寄存器存放在堆
  栈中,而且,CPU的所有寄存器的值也要存放到堆栈中,而在结尾的时候,一般函数使用CPU
  的ret指令,而ISR函数首先要从堆栈中返回所有的CPU寄存器的值,其次它使用的指令是iret.
  因此,Compiler会对加上'_interrupt'的函数进行特殊的处理,即产生不同的汇编语言指令。其实,
  对于我们初学者来说,我们只需要记住,写ISR时一应要记住加上这个关键字就行了,至于其中
  的道理,以后等我懂了之后再告诉你,哈哈,我菜鸟一个,等我懂这些东西,不知道要等到猴
  年马月,上面文字说了一大堆,咯里啰嗦,不过没办法,我是照着书上写的..别怪我
  
 第四步:接下来,让我们一起来看一下main(),程序范例如下:
 /*=====================================================
                                                       main.c
 =====================================================*/
 void main(void)
 {
  //设定定时器0,每100ms就timeout一次
  drv_init_timer0(100);
  
  //LED驱动程序初始化
  drv_init_LED();
  
  //启动Timer 0
  drv_start_timer0();
  
  while(1);
 }
 以上程序很简单,在此我们不必过多讨论它了
 
 现在,我们来对这个范例程序的运行流程来归纳一下
 step1:程序的中断矢量表是一个数组,它会被寻址到CPU指定的启动地址,中断矢量表
   Reset ISR的中断矢量指到我们的boot程序中。
 
 step2:CPU启动后,会引发Reset中断,所以boot程序会被执行。
 
 step3:boot程序负责初始化CPU,并把控制权交给应用程序的主程序
 
 step4:主程序初始化它会用到的硬件设备
 
 step5:在本例中,每100ms会产生一次Timer0中断。
 
 第四步:下面,我们来看一下LED驱动程序,范例如下
 /*=================================================
     drv_LED.c
 =================================================*/
 void drv_init_LED(void)
 {
  //设定P10为output PIN
  *(volatile unsigned char *)0x300023 |= 0x02;
  
  //关闭LED
  *(volatile unsigned char *)0x300022 &= 0xfd;
 }
 ...... //以下略吐舌头

 以上程序中,我们需要关注的是volatile这个关键字,这里不再做详细的分析,
 感兴趣的同学可以上网查一下,另外,有些同学对
 *(volatile unsigned char *)0x300023 |= 0x02;这个语句有些不明白,下面我们来讲解一下
 其实相当于
 volatile unsigned char data;      //存储寄存器器的值
 volatile unsigned char *reg;      //指向寄存器所在地址的指针
 reg = (volatile unsigned char *)0x300023;  //设定指针所指向的地址,以及指针指向的地址存放的数据类型
 data = *reg;           //取出0x300023寄存器的值
 
 data = data | 0x02;
 *reg = data;
 现在是不是容易理解了啊,如果你还看不懂的话,那你应该去复习一下C语言了,哈哈
 
5、架设开发环境是第一步,看懂程序是第二步,那接下来呢?我们应该干些什么?
 对,是测试,我们如何测试我们的程序是否正确无误执行?
 利用示波器等等...我不大懂...大笑
 
  
  
  
  
  
  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值