Chinaunix首页 | 论坛 | 博客
  • 博客访问: 78827
  • 博文数量: 94
  • 博客积分: 1400
  • 博客等级: 上尉
  • 技术积分: 1110
  • 用 户 组: 普通用户
  • 注册时间: 2011-01-07 11:18
文章分类
文章存档

2011年(94)

我的朋友
最近访客

分类:

2011-05-28 10:58:36

模块划分

  模块划分的"划"是规划的意思,意指怎样公道的将一个很大的软件划分为一系列功能独立的部分合作完成系统的需求。C语言作为一种结构化的程序设计语言,在模块的划分上主要依据功能(依功能进行划分在面向对象设计中成为一个错误,牛顿定律碰到了相对论),C语言模块化程序设计需理解如下概念:

  (1) 模块即是一个.c文件和一个.h文件的结合,头文件(.h)中是对于该模块接口的声明;

  (2) 某模块提供给其它模块调用的外部函数及数据需在.h中文件中冠以extern关键字声明;

  (3) 模块内的函数和全局变量需在.c文件开头冠以static关键字声明;

  (4) 永远不要在.h文件中定义变量!定义变量和声明变量的区别在于定义会产生内存分配的操纵,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从其它模块寻找外部函数和变量。如:

/*module1.h*/
int a = 5; /* 在模块1的.h文件中定义int a */

/*module1 .c*/
#include "module1.h" /* 在模块1中包含模块1的.h文件 */

/*module2 .c*/
#include "module1.h" /* 在模块2中包含模块1的.h文件 */

/*module3 .c*/
#include "module1.h" /* 在模块3中包含模块1的.h文件 */

  以上程序的结果是在模块1、2、3中都定义了整型变量a,a在不同的模块中对应不同的地址单元,这个世界上从来不需要这样的程序。正确的做法是:

/*module1.h*/
extern int a; /* 在模块1的.h文件中声明int a */

/*module1 .c*/
#include "module1.h" /* 在模块1中包含模块1的.h文件 */
int a = 5; /* 在模块1的.c文件中定义int a */

/*module2 .c*/
#include "module1.h" /* 在模块2中包含模块1的.h文件 */

/*module3 .c*/
#include "module1.h" /* 在模块3中包含模块1的.h文件 */

  这样假如模块1、2、3操纵a的话,对应的是同一片内存单元。

  一个嵌进式系统通常包括两类模块:

  (1)硬件驱动模块,一种特定硬件对应一个模块;

  (2)软件功能模块,其模块的划分应满足低偶合、高内聚的要求。

  多任务还是单任务

  所谓"单任务系统"是指该系统不能支持多任务并发操纵,宏观串行地执行一个任务。而多任务系统则可以宏观并行(微观上可能串行)地"同时"执行多个任务。

  多任务的并发执行通常依靠于一个多任务操纵系统(OS),多任务OS的核心是系统调度器,它使用任务控制块(TCB)来治理任务调度功能。TCB包括任务确当前状态、优先级、要等待的事件或资源、任务程序码的起始地址、初始堆栈指针等信息。调度器在任务被激活时,要用到这些信息。此外,TCB还被用来存放任务的"上下文"(context)。任务的上下文就是当一个执行中的任务被停止时,所要保存的所有信息。通常,上下文就是计算机当前的状态,也即各个寄存器的内容。当发生任务切换时,当前运行的任务的上下文被存进TCB,并将要被执行的任务的上下文从它的TCB中取出,放进各个寄存器中。

  嵌进式多任务OS的典型例子有Vxworks、ucLinux等。嵌进式OS并非远不可及的神坛之物,我们可以用不到1000行代码实现一个针对80186处理器的功能最简单的OS内核,作者正预备进行此项工作,希看能将心得贡献给大家。

  究竟选择多任务还是单任务方式,依靠于软件的体系是否庞大。例如,尽大多数手机程序都是多任务的,但也有一些小通达的协议栈是单任务的,没有操纵系统,它们的主程序轮流调用各个软件模块的处理程序,模拟多任务环境。
单任务程序典型架构

  (1)从CPU复位时的指定地址开始执行;

  (2)跳转至汇编代码startup处执行;

  (3)跳转至用户主程序main执行,在main中完成:

  a.初试化各硬件设备;

  

后要从romInit()函数开始运行。因此,需要设计一个方案来加载编译工程得到的映象,而且要具备初始化CPU和SDRAM存储空间的功能。

首先,系统从romInit()函数中开始运行,完成CPU和SDRAM初始化,然后跳转到函数romStart()运行。加载第二份bootrom代码到系统的低地址处,然后运行壳代码中的sysInit()函数,并调用自己构造的解压函数usrInit(),将压缩后的工程映象文件解压缩到系统高地址处,然后系统继续运行解压后的bootrom代码。此时,系统开始运行sysInit()函数,然后调用vxWorks系统中的usrInit()函数,开始初始化系统硬件,内核以及其他外围设备。

壳函数usrInit()代码如下:

void usrInit(void)

{

volatile FUNCPTR absEntry;

if(inflate((UCHAR *)ROM_OFFSET(_binArrayStart),

(UCHAR *)(RAM_LOW_ADRS), _binArrayEnd - binArrayStart)!= OK)

return;

absEntry=(FUNCPTR)(RAM_LOW_ADRS);

(absEntry)();

}

其中RAM_LOW_ADRS为系统低地址,是操纵系统运行的起始地址,但bootrom可以利用低地址来实现在系统高地址的运行。宏ROM_OFFSET用于正确定位函数地址,由于在bootrom中运行的代码要求以相对地址方式来寻址,而不能以尽对地址方式来寻址。 _binArrayStart 为压缩后二进制代码的开始符,_binArrayEnd为压缩后二进制代码的结束符。Inflate()是vxWorks软件的解压缩函数,它用于解压缩通过deflate()压缩函数压缩的二进制文件映象。

同时,需要将下述代码添加到编译规则文件rules.vxWorks中的相应部分:

$(CC) -c $(CFLAGS) $(BSP_DIR)/unzip.c -o $(BSP_DIR)/unzip.o

$(LD) $(LDFLAGS) -e sysInit -Ttext $(RAM_LOW_ADRS) \

-o unzip_obj.o sysALib.o $(BSP_DIR)/unzip.o $(LIBS) flex.z.o

其中,unzip.c中包含构造的壳函数usrInit()。SysInit()为解压软件的进口函数。上述语句的功能:第一行完成壳文件的编译,第二、三行完成壳目标代码与第二份bootrom代码的链接。这样一个具有解压功能的壳函数就被链接到第二份bootrom映象中。

以前的系统运行方式与修改后的系统运行方式比较:

通过这两种方式的比较可以看出,以前系统运行方式与修改后的运行方式有下面两点差异:(1)第一份bootrom启动后,前者存在解压缩自身映象的操纵,而后者没有。(2)对于第二份bootrom,前者没有壳代码,而后者有。

23 缩减文件长度

通常第一份bootrom代码只有两个文件,一个是包含CPU和SDRAM初始化文件romInit.s,另外就是包含romStart()函数的bootInit.c文件,另外根据需要还可以添加提供串口轮询显示功能的文件。对于第一份bootrom代码,通常只有10K左釉定这是针对系统修改后的方式),而对于包含壳函数代码的,通过建立工程并编译而天生的第二份bootrom,则比较大通常为570K左右。(留意:这几个数值是通过特定的产品来得出的结论,并不应用于所有产品,但碰到类似的情况可以鉴戒处理。)。而其后面的一部分完全是0,可以考虑往掉这些0,但不能影响软件的功能。经过测试,得出结论:往掉后面的0对系统功能和性能没有任何影响。

通过文件的操纵来实现二份bootrom合并,合并后的大小要求小于或即是512K。假如不采用任何措施,直接将

,那么,中断服务对这样的实时代码执行时间没有直接的影响。



  应用实例
  音频解码器通常作为驱动器应用进行存取。驱动器的作用是从适当的输进设备提取引进的位流,并将经过解码的位流发送到音频输出设备,如下图所显示的那样。它也可以执行其他后处理功能,例如在发送到输出设备之前,对经过解码的音频位流进行低音控制。驱动器通过一个标准接口机制访问MIPS的音频产品,也就是MIPS的软音频接口(Soft Audio Inte***ce,SAI)。这个通用接口可使各种来自MIPS Technologies的音频解码器易于与主应用进行连接。
  MIPS SAI可提供解码器和主程序,或高级控制和运算监控解码器RTOS之间的接口功能和结构。这种接口为访问控制解码器运算和访问状态和差错信息参数提供了机会。该接口可执行三个基本功能:一个是对解码器进行初始化,第二个是阅读帧标题,第三个实际上是进行解码处理。所有的解码器都具有与特定解码器有关的预确认字段数据结构。解码器对所有的帧进行阅读和结构更新。在帧开始或结束时,主程序可访问数据结构,阅读状态或改变控制参数。参考下面的代码实例,它说明了驱动程序的运行情况。MIPS SAI的功能用粗体字表示。
  int main(void)
  {
  decoder_specific_struct dec_ptr
  //Initialize the decoder.
  mips_sai_dec_init(dec_ptr)
  //Open the bitstream input device.
  open_input()
  //Open the audio output device.
  open_output()
  //Allocate buffers for decoder operation.
  allocate_buffers(dec_ptr)
  while(not-end-of-input-bitstream)
  //Read the input bitstream.
  read_input(dec_ptr)
  //Read the frame header for encoded stream parameters.
  mips_sai_dec_readheader(dec_ptr)
  //Set up params for decoder operation.
  setup_params(dec_ptr)
  //Decode the frame
  mips_sai_dec_process(dec_ptr)
  //write the decoder output to the device driver.
  write_output(dec_ptr)
  end-while
  //Close the input and output devices.
  close_input()
  close_output()
  return 0 }
  总之,MIPS处理器可供使用的各种特性为实现消费音频算法提供了一系列的上风。这些上风包括单一的可编程架构、更低的系统本钱和缩短上市时间。这使MIPS处理器成为从电池供电的移动音频播放器到高性能DVD刻录设备等消费电子产品音频应用的一种可行和具有吸引力的解决方案。


(

如何耦合视频放大器F&Q

12V-24V车载电源的制作

实时操纵系统μC/OS-II的改进与应用研究

阅读(369) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~