《电子技术应用》
您所在的位置:首页 > 其他 > 业界动态 > 嵌入式自开发软件的设计与实现

嵌入式自开发软件的设计与实现

2008-05-16
作者:余晓建1,沈永林1,郭 超2

  摘 要: 在基于SH7709S处理器的系统上实现了自开发软件,介绍了自开发软件的原理、方法、组成与结构,分析了与上位机" title="上位机">上位机通信接口设计、在系统烧写" title="烧写">烧写Flash及设置断点等关键技术。
  关键词: 自开发 串口" title="串口">串口 ISP 断点 人机界面 SH7709S处理器


  嵌入式自开发软件有两大特点。一是嵌入,指嵌入在系统内部,占用系统一定的RAM和ROM资源;二是自开发功能,自身具备调试和开发功能。自开发软件也被称为监控程序(monitor program)。实现自开发软件是一种廉价的仿真器替代方案,它让用户更加深入地理解处理器和底层硬件的工作实质。更为重要的是,它嵌入于应用系统内部,提供完全在线式的调试功能。
  自开发软件的配置如图1所示。上位机完成程序的编辑、编译和链接,由自开发软件通过与上位机的通信接口把程序下载到应用系统中,并提供基本的调试手段。自开发软件在功能上可以划分为上位机通信接口、内存操作、寄存器操作、下载及执行用户程序和断点调试几大部分。


1 与上位机通信接口设计
  (1)底层设计
  系统状况和用户命令都要通过与上位机的通信接口互相交换,一般用串行通信接口实现。通常使用最简单的硬件连接和通信协议:只使用发送(TXD)、接收(RXD)和地这三根线,不使用其他握手信号线。PC端可以使用通用的串口通信软件,如超级终端、串口助手;应用系统端的通信软件需要自行编写。
  串口接收数据时,当数据量很大、通信速度很快时,如果处理数据包的时间过长,则无论使用查询或中断方式,都有可能出现接收缓冲寄存器溢出,造成数据丢失。较好的解决方法是以中断方式接收,在内存中开辟一个环形队列缓冲区,把程序分成两部分实现。
  如图2所示,当接收数据中断时,由中断服务程序" title="中断服务程序">中断服务程序迅速地把数据放置到缓冲区内,再重新使接收中断使能,准备接收下一个数据,而主程序则通过查询缓冲区是否为空从中读取读据。这样做可以尽可能地减少关闭接收中断的时间,防止数据丢失。缓冲区的大小与通信速度和处理器的处理速度有关。通信速度越快,处理速度越慢,缓冲区应开辟得越大。反之亦然。在SH7709S系统中,传输速率为38 400bps,处理器运行在64MHz时钟下,缓冲区设置为64B。


  发送也可以使用中断加发送缓冲区的方式,主程序向缓冲区写入数据的同时打开发送中断,发送中断服务程序从缓冲区取得数据并发送,如果缓冲区非空,则保持发送中断使能。但发送的情况和接收很不一样,发送时可以主动控制发送字节的时机,在高传输速率下发送每一个字符的时间比较短。例如,在38 400bps的传输速率下,传输一个字符的时间大约26μs。
  (2)实现printf()函数
  为了便于后续程序的设计,应该实现自己的格式化输出函数printf(),包含字符串输出、字符输出、十进制数输出和十六进制数输出功能。输出十六进制数时,要有8位、4位和2位宽度方式,如果数据的宽度不够,则在前面补0,使得数据对齐,以便于用户观察。printf函数的实现思路是,把所有数据按不同的格式先转换成对应的ASCII码,然后将其串成一个有结束符的字符串,再统一按照字符串输出。要注意一点:当输出回车符\n时,要同时输出一个换行符\r,以符合C语言中的编程习惯。
2 下载程序及在系统Flash烧写技术
  (1)S文件格式
  上位机完成编译、链接,生成程序文件后,首先要通过串口把程序装载到系统中,自开发软件必须能够识别主机传送来ASCII码的含义,即向哪个地址装载、是否结束、有无误码,这就需要有一个通信协议。这里以SH7709S使用S格式文件为例说明。它是行结构的ASCII码文件,包含了所需要的协议,主要有起始记录(S0)、正文记录(S1、S2、S3)和结束记录(S7、S8、S9)三种。三种记录如下:
  S00E00004D6F6E56657232206D6F74F8
  S113000000090009000900090009DF0B0002D10BF7
  S9030000FC
  图3是S1类型的记录格式,以S1开头;接下来是一个记录长度的字节,长度从该字节后面开始算起,一直到该行结尾;再下来是程序的装载地址,S1、S2和S3的装载地址长度分别是2B、3B、4B,装载程序的地址范围分别是0~64KB、64KB~16MB和16MB~4GB;装载地址后面是ASCII码表示的十六进制机器码,最后一个字节是校验码,等于该记录中的所有内容之和的反码。S格式文件一般以S0记录开始,S0与S1记录类似,所不同的是机器码部分是S格式文件的文件名。S7、S8和S9也与S1记录类似,但没有机器码部分,装载地址表示程序的起始地址,分别为3B、2B、1B。


  通过串口,可以把程序下载到RAM或Flash中。RAM的读写速度较快,可以边从串口下载边装载;Flash的操作相对较复杂,在写入Flash时,一般是先把程序全部下载到RAM中,再集中烧写。
  (2)在系统Flash烧写技术
  Flash存储器由于容量大、价格低、编程方便等优势在嵌入式系统中得到了广泛应用。出于价格、体积等方面的考虑,多数系统只配备单片普通的NAND Flash。由于同一片Flash在写操作时一般不能读,因而很多采用Flash作为程序存储器的系统无法直接实现在系统烧写(ISP)。通过编写程序实现在线烧写Flash,达到“程序烧写程序”目的,这简化了电路设计,降低了开发成本,是目前常用的开发模式。它把烧写Flash的程序与其他程序分开,使用时把它拷贝到RAM中执行。由于大多数嵌入式系统只能执行非可重定位的程序,即只能在链接时指定的地址执行程序,所以这种方法遇到的最大难点就是程序的存储地址和执行地址不一致。这里给出一种非常有效且通行的解决方法。首先,编写独立的烧写Flash程序,使这部分程序不能调用其他程序,避免在写Flash的同时读取Flash;然后把这部分程序编译,并链接到RAM中的指定地址,生成程序文件(如前文中提到的S格式文件);最后用一个在用户主机上运行的小程序把程序文件转换成C语言格式的数组,再在主程序中拷贝或调用。数组定义如下:
  struct rom_data {
  unsigned long start_address;
  unsigned long data_length;
  unsigned char data[4708];
  };
  start address是程序在RAM中执行的入口地址,data length是机器码的字节数,在data中存储程序的机器码。以一个转换后的数组为例:
  const struct rom_data datastruct={
  0x0dff0000,
  0x0000084b,
  0x2f,0x86,0x2f,0x96,0x2f,0xa6,0x2f,0xb6,0x2f,0xc6,
  0x2f,0xd6,0x2f,0xe6,0x4f,0x22,
  ……
  };
  程序从地址0xdff0000开始执行,由0x84b个字节组成。烧写Flash时,由自开发软件把这部分程序搬运到RAM中的对应地址,再跳转到该地址开始执行。只要了解程序文件的结构,程序文件到数组的转换将很容易实现。Renesas公司提供了S文件格式的程序文件到数组转换工具motice_cl,把它设定为IDE的最后一个编译阶段即可方便地生成数组。
3 实现断点
  在程序中设置断点是调试程序的有效方法。通常,断点只能在RAM中实现。SH7709S由于内嵌有UBC(用户断点控制器)单元,所以也可以在ROM中设置断点。
  (1)在RAM中设置断点
  RAM中设置断点的思路是:在断点处用一条软中断指令替换原有指令,当软中断发生时,将所有寄存器的值推入堆栈中。只要在软中断的服务程序显示栈中对应CPU有关寄存器的内容,回到主程序,并允许修改寄存器和内存,就实现了在RAM中设置断点。
  用户设置完断点后,在自开发软件中不要直接替换对应地址的指令,否则观察寄存器或反汇编" title="反汇编">反汇编时,这段程序就被改变了。应该在用户输入运行程序命令后再去替代指令,在软中断服务程序返回前,还要遍历每个断点,将已经替换后的指令恢复,使用户看不到被替换过的痕迹。
  (2)使用UBC单元设置断点
  SH7709S内置两个UBC单元,以方便在ROM或RAM设置断点,协助用户实现自调试功能。它实际上是一个比较中断发生器,在程序运行时,断点寄存器中的地址不停地与程序计数器PC比较,中断发生的条件可以是地址相同,也可以是某一数据的读或写等。发生中断后在中断服务程序中显示寄存器,同时把这个断点清除,屏蔽UBC中断,并返回到主程序,从而实现断点功能。
  实现断点功能后就可以轻松地实现单步调试功能,实际上是在下一条指令前设置断点。
4 编辑寄存器或内存
  (1)编辑寄存器
  编辑寄存器包括读、写和清零操作。读寄存器并不需要每次都显示当前寄存器的真实值(因为系统被自开发软件控制时,这种显示对用户并无意义),而只需在用户程序被打断后显示打断前的真实寄存器值;写和清零操作也不需要在用户改变寄存器后立即改变CPU的寄存器,否则自开发软件无法正常运行,而只需在用户程序执行前改变。自开发软件应该在内存中建立一个CPU寄存器的映像,用户对寄存器的操作实际上是操作内存中的映像单元。发生断点中断后,在中断服务程序里把入栈的中断前寄存器值写入到对应的内存映像单元,在用户输入运行程序命令后,先把内存映像单元的值写入到CPU的寄存器中再运行程序。
  (2)编辑内存
  编辑内存包括显示、修改和反汇编操作。一般按单字节使用十六进制显示内存的内容,每一行显示16个内存单元,左边要有该段内存的起始地址,在最右边,将这些数据对应的ASCII码显示出来,数值小于0x20的ASCII码控制字符及大于0x7F的非ASCII码字符用“.”表示,如图4所示。
  修改内存应提供按逐个内存单元和按块填充功能。对于总线宽度为32位的系统,内存单元可以是字节、双字和长字,修改过一个单元后,提示符应指向下一个单元,最好能设置一个键码,当发现输入值不当时,可以回退一个单元,如果输入字符不是0~F,则自动退出,此单元无须修改时按回车继续下一单元。填充内存是把一块内存单元修改为同一个值,在观察内存变化时很有用。


  反汇编是将内存的内容以指令形式显示出来。在RISC和CISC机上实现反汇编的难度不同,RISC架构的处理器指令是定长的,如SH7709S每条指令占用两个字节,而CISC架构的处理器上指令占用的字节数从一个到四个不等,读取时还要判断是否读到了正确的指令。反汇编功能需占用较多的存储空间,因为它要建立一个对应关系,在内存中存储一个指令表。一般情况下,指令的设计都有一定规律,设计时要仔细观察,SH7709S的指令集可以分为n、nm和md等类型,找出规律可大大简化程序设计。
5 人机对话界面设计
  用户主机上的串口通信软件,如Hypertrm,只显示接收的字符。为了在用户输入命令的同时把命令字符显示到屏幕上,自开发软件需要把接收到的字符再发送一遍。有些特殊字符要特别处理,如用户键入backspace后,软件应发送退格、空格、退格三个字符才能得到backspace键效果。
  图5是实现人机界面的流程图,当用户输入回车符后,认为一个命令输入完毕。命令中可能会含有一些多余的空格,要先把这些空格剔除并整定成要求的格式。有些命令需要经常执行,如单步、查看内容等。把这些命令设定为需要标记的命令,输入一次后,再输入回车即可重复执行。


  自开发软件可以大大方便调试和开发,它提供了所有基本的调试手段,嵌入于应用系统内部,携带方便,十分灵活,是仿真器的有益补充,甚至可以在一定程度上替代昂贵的仿真器。
  笔者在基于SH7709S处理器的系统上成功地实现了自开发软件,在此基础上阐述了自开发软件的原理和方法,介绍了自开发软件的组成与结构,详细分析了与上位机通信接口设计、在系统烧写Flash和设置断点等关键技术。这些内容也适用于基于不同处理器或单片机的应用及开发系统。
参考文献
1 邵贝贝.单片机嵌入式应用的在线开发方法.北京:清华大学出版社,2004
2 Renesas Co,Ltd.SH7709s hardware manual.2003,9
3 Renesas Co,Ltd.SuperH RISC engine C/C++ Compiler,Assembler,Optimizing Linkage Editor User′s Manual.2003
4 Hitachi Europe LTD.Utility To Convert S-Record DataTo A C Structure.2003

本站内容除特别声明的原创文章之外,转载内容只为传递更多信息,并不代表本网站赞同其观点。转载的所有的文章、图片、音/视频文件等资料的版权归版权所有权人所有。本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如涉及作品内容、版权和其它问题,请及时通过电子邮件或电话通知我们,以便迅速采取适当措施,避免给双方造成不必要的经济损失。联系电话:010-82306118;邮箱:aet@chinaaet.com。