嵌入式软件设计之提高代码可移植性

前面一篇博文嵌入式软件架构设计之分层设计�o大家分享了程序分层设计的一些个人观点。里面有提到接口统一规范的问题,下面这篇博文详细阐述一下关于代码可移植性的问题。代码可移植性非常重要!在这里有的人很纳闷,有人会问:除了汇编语言的移植性很差以外,c语言,c++,java等其他高级语言的移植性不是都很棒的么。毋庸置疑,c语言的移植性是非常好。但本文不是在探讨一段编程语言的移植性问题,要探讨的是:在不同的嵌入式平台上,如何提高开发嵌入式产品的效率问题。换一种表达方式:如何提高嵌入式软件平台之间的可移植性。

在探讨之前先抛出一个问题:如何才能使开发的应用程序运行在不同的硬件平台上?这里所指的应用程序不是指pc端的应用也不是指的android,ios等平台的应用。而是指的是其他嵌入式平台的应用,他们更多的是c语言实现。我们都知道windows平台也好还是ios,android平台也罢,他们的应用是不同的编程语言实现的,但是他们有一个共同的特点:�o应用开发人员提供标准的sdk接口。正是这些标准的接口使得应用开发和硬件以及底层相分离。大大提高了应用程序的可移植性。

目前来看,嵌入式产品若不是android系统,是没有统一的接口规范的。公司在开发设计某款嵌入式产品时,涉及到如下内容:硬件平台选型,软件平台选型,底层软件开发,应用软件开发。底层软件和硬件是紧密联系在一起的,而应用软件可以做到与硬件平台无关。要做到无关就得定义统一的接口规范。让应用程序调用接口规范规定的接口,底层软件根据不同的平台封装不同的程序来实现其接口功能。如此大大提高应用程序不同硬件平台的可移植性。这样做也规范了编程,增加了程序的可读性。

下面以文件操作为例

1.本模块相关宏定义

//本模块相关宏定义以及结构定义如下:
//  错误码定义
#define     FILE_EXIST          1
#define     FILE_NOEXIST        2
#define     MEM_OVERFLOW        3
#define     TOO_MANY_FILES      4
#define     INVALID_HANDLE      5
#define     INVALID_MODE        6
#define     FILE_NOT_OPENED     8
#define     FILE_OPENED         9
#define     END_OVERFLOW        10
#define     TOP_OVERFLOW        11
#define     NO_PERMISSION       12
#define     FS_CORRUPT          13
//以上错误码与标准linux的错误码errno有差异,对于Linux系统的机型,错误码保留linux标准的errno定义。
//  文件打开模式,可使用"或"组合
#ifndef O_RDONLY
#define O_RDONLY     00
#endif
#ifndef O_WRONLY
#define O_WRONLY     01
#endif
#ifndef O_RDWR
#define O_RDWR     02
#endif
#ifndef O_NONBLOCK
#define O_NONBLOCK  04000
#endif
#ifndef O_CREAT
#define O_CREAT   0100
#endif
//  文件定位起点
#ifndef SEEK_SET
#define     SEEK_SET            0
#endif
#ifndef SEEK_CUR
#define     SEEK_CUR            1
#endif
#ifndef SEEK_END
#define     SEEK_END            2
#endif
//  文件信息
typedef struct
{
    char     fid;
    char     attr;
    char     type;
    char     name[17];
    int      length;
} FILE_INFO;

2.接口函数规范说明

2.1 dev_FileOpen

原型:

int dev_FileOpen(const char *FileName, int Mode);

功能:

打开本应用所属的文件

参数:

FileName(输入)

1-16Bytes

文件名,最多可以是16个字符,以‘\x00’结尾,超过16个字符的只取前16个。

Mode(输入)

O_CREAT

0100

文件打开方式,O_CREATE表示创建一个新的文件

O_RDWR

02

文件打开方式,O_RDWR表示打开文件用于读/写,

返回:  

[0,255]

成功,返回句柄号

-1

失败,错误码放在errno

注释:

如果以O_CREATE | O_RDWR的方式打开文件,若文件已存在,则以O_RDWR的方式打开,否则创建该文件。已打开的文件可以重复打开,每次打开后,文件指针移到文件开头。

错误码介绍:

INVALID_MODE       mode不为O_RDWR/O_CREATE

FILE_NOEXIST 文件不存在

FILE_EXIST         当文件存在时以O_CREATE的方式打开 

MEM_OVERFLOW空间不足

TOO_MANY_FILES    文件句柄太多,超过255,无法创建新文件

FILE_OPENED        文件已打开

示例:

打开文件”Demo.bin”, 如果不存在该文件,则创建该文件。

int Ret = 0;

Ret = fileOpen(“Demo.bin”,O_RDWR); // 以读写方式打开该文件

if(Ret < 0) // 该文件不存在,则重新创建

{

     Ret = fileOpen(“Demo.bin”, O_CREAT);

     if(Ret < 0)

{

      return Ret; // 创建失败,则返回错误码

}

}

…………………  // 对这个文件进行其他的读写操作等

 2.2 dev_FileSeek

原型:

int dev_FileSeek(int FileID, int Offset, int Whence);

功能:

对打开的文件指针进行定位

参数:

FileID(输入)

[0,255]

文件句柄号,从打开文件获得的返回句柄

Offset(输入)

Whence指定的位置到要移到的位置的字节数(有符号值)

Whence(输入)

SEEK_SET

0

表示从文件头开始

SEEK_CUR

1

从当前文件指针开始

SEEK_END

2

从文件尾开始。

返回:

>=0

成功返回的数值是文件当前指针相对文件头的位置

-1

失败,参数错误或偏移目标超出文件范围,错误码放在errno中。

注释:

错误码介绍:

INVALID_FILEID无效的文件句柄

FILE_NOT_OPENED   文件未打开

END_OVERFLOW      后移时超出文件长度的偏移

TOP_OVERFLOW    前移时出错

示例:

调用该函数前,必须先确认指定文件已经被正确打开了:

  fd = fileOpen(“filename”, O_RDWR);

1、定位到文件的第2个字节位置:

   Ret = fileSeek(fd, 2, SEEK_SET);

2、定位到文件的倒数第2个字节位置:

   Ret = fileSeek(fd, -2, SEEK_END);

3、定位当前位置的后面第2个字节处:

   Ret = fileSeek(fd, 2, SEEK_CUR);

 2.3 dev_FileRead

原型:

int dev_FileRead(int FileID, void *DataBuf, int Len);

功能:

从文件的当前定位位置开始读取文件数据

参数:

FileID(输入)

[0,255]

文件句柄号,从打开文件获得的返回句柄

DataBuf(输出)

应用指定的用来存放读取数据的缓冲区

Len(输入)

期望读取的数据字节数。

返回:

>=0

读取成功,返回实际读到的字节数。

-1

失败,错误码放在errno

注释:

读取后文件指针位置将移动到读取后的位置

错误码介绍:

FILE_NOT_OPENED文件未打开

INVALID_FILEID无效的文件句柄

示例:

调用该函数之前,先调用fileOpen打开指定的文件:

fd = fileOpen(“Filename”, O_CREAT|O_RDWR);

 

1、从文件起始处读取16字节

Ret = fileSeek(fd, 0, SEEK_SET);

Ret = fileRead(fd, Buff, 16);

 

2、从当前光标位置处开始读取16字节:

Ret = fileSeek(fd, 0, SEEK_CUR);

Ret = fileRead(fd, Buff, 16);

 

3、读取文件的最后16个字节

Ret = fileSeek(fd, -16, SEEK_END);

Ret = fileRead(fd, Buff, 16);

 2.4 dev_FileWrite

原型:

int dev_FileWrite(int FileID, const void *DataBuf, int Len);

功能:

从文件的当前定位位置开始写入数据

参数:

FileID(输入)

[0,255]

文件句柄号,从打开文件获得的返回句柄

DataBuf(输入)

应用指定的存放写入数据的缓冲区

Len(输入)

需要写入数据的字节数。

返回:

>=0

写入成功,返回实际写入的字节数。

-1

失败,错误码放在errno

注释:

写入后文件指针位置将移动到写入后的位置

错误码介绍:

INVALID_FILEID     无效的文件句柄

FILE_NOT_OPENED  文件未打开

MEM_OVERFLOW 空间不足或文件句柄太多

示例:

调用该函数之前,必须先调用fileOpen打开指定的文件:

fd = fileOpen(“filename”, O_RDWR|O_CREAT);

 

1、修改文件的头16个字节:

Ret = fileSeek(fd, 0, SEEK_SET);

Ret = fileWrite(fd, Buff, 16);

 

2、从当前光标处开始写入16字节:

Ret = fileSeek(fd, 0, SEEK_CUR);

Ret = fileWrite(fd, Buff, 16);

 

3、从文件结束处再写入16字节:

Ret = fileSeek(fd, 0, SEEK_END);

Ret = fileWrite(fd, Buff, 16);

 2.5 dev_FileTruncate

原型:

int dev_FileTruncate(int FileID, int Len);

功能:

截短文件

参数:

FileID(输入)

[0,255]

文件句柄号,从打开文件获得的返回句柄

Len(输入)

从文件头保留的文件数据长度。

返回:

0

成功截断。

-1

失败,错误码放在errno中。

注释:

该函数将文件截断为Len长度,原文件中从Len到结尾的内容全被截去。文件指针移至截短后的文件的最后。

错误码介绍:

NO_FILESYS文件系统未建立

INVALID_FILEID    无效的文件句柄

FILE_NOT_OPENED      文件未打开

TOP_OVERFLOW          长度小于0

END_OVERFLOW        长度超出文件长度

示例:

只保留指定文件的前256字节内容:

fd = fileOpen(“filename”, O_RDWR);

 

Ret = fileTruncate(fd, 256);

 2.6 dev_FileClose

原型:

int dev_FileClose(int FileID);

功能:

关闭已打开的文件

参数:

FileID(输入)

[0,255]

文件句柄号,从打开文件获得的返回句柄

返回:

0

成功关闭文件

-1

失败,错误码放在errno中。

注释:

错误码介绍:

INVALID_FILEID无效的文件句柄

示例:

先打开文件:

 fd = fileOpen(“filename”, O_RDWR | O_CREAT);

…………………  // 文件相关处理

关闭文件:

   Ret = fileClose(fd);

 2.7 dev_FileRemove

原型:

int dev_FileRemove(const char *FileName);

功能:

删除文件

参数:

FileName(输入)

1-16Bytes

文件名,最多可以是16个字符,以‘\x00’结尾,超过16个字符的只取前16个。

返回:

0

成功

-1

失败,错误码放在errno中。

注释:

错误码介绍:

FILE_OPENED 文件已打开

FILE_NOEXIST 文件不存在

示例:

删除指定文件:

 Ret = fileRemove(“filename”);

 2.8 dev_FileSize

原型:

int dev_FileSize(const char *FileName); 

参数:

FileName(输入)

1-16Bytes

文件名,最多可以是16个字符,以‘\x00’结尾,超过16个字符的只取前16个。

返回:

>=0

文件的大小

-1

失败,错误码放在errno中。

注释:

错误码介绍:

FILE_NOEXIST 文件不存在

示例:

fileLen = fileSize(“filename”); // 查询文件大小

 2.9 dev_FileExist

原型:

int dev_FileExist(const char *FileName);

功能:

判断文件是否存在

参数:

FileName(输入)

1-16Bytes

文件名,最多可以是16个字符,以‘\x00’结尾,超过16个字符的只取前16个。

返回:

[0,255]

文件序号。

-1

在当前应用中没有指定的文件

注释:

示例:

判断某个文件是否存在,如存在,则获取其大小:

Ret = fileExist(“filename”);

if(Ret >= 0)

{

    fileLen = fileSize(“filename”);

}

 2.10 dev_FileRename

原型:

int dev_FileRename(const char *OldFileName, const char *NewFileName);

功能:

修改属于本应用的数据文件文件名

参数:

OldFileName(输入)

1-16Bytes

原文件名,最多可以是16个字符,以‘\x00’结尾,超过16个字符的只取前16个。

NewFileName(输入)

1-16Bytes

新文件名,最多可以是16个字符,以‘\x00’结尾,超过16个字符的只取前16个。

返回:

0

成功

-1

失败,错误码放在errno中。

注释:

错误码介绍:

FILE_OPENED 文件已打开

FILE_NOEXIST 文件不存在

FILE_EXIST    新文件已经存在

示例:


 2.11 dev_FileFreeSpace

原型:

int dev_FileFreeSpace(void);

功能:

返回文件系统剩余可写的字节数

参数:

返回:

返回文件系统总共剩余的空间大小(字节数)

注释:

示例:

判断文件系统还有多少剩余空间:

FreeLen = fileFreeSpace();

2.12 dev_MntSDCard

原型:

int dev_MntSDCard(const char *TargetDir, int Mode);

功能:

挂载SD卡。

参数:

TargetDir(输

指定的挂载SD目录默认为"/mnt/sdcard/"。若为NULLSD卡自动挂载到"/mnt/sdcard/"目录。

Mode(输

TRUE

挂载SD

FALSE

卸载SD

返回:

0

挂载成功

-1

挂载失败

注释:

应用层不需要关心挂载的设备名和filesystem类型。

挂载SD卡命令:mount -t vfat /dev/sdd1 /mnt/sdcard

 2.13. dev_MntUSBDisk

原型:

int dev_MntUSBDisk(const char *TargetDir, int Mode);

功能:

挂载U盘。

参数:

TargetDir(输

指定的挂载U目录默认为"/mnt/usb/"。若为NULLU盘自动挂载到"/mnt/usb/"目录。

Mode(输

TRUE

挂载U

FALSE

卸载U

返回:

0

挂载成功

-1

挂载失败

注释:

应用层不需要关心挂载的设备名和filesystem类型。

挂载U盘命令:mount -t vfat /dev/sda1 /mnt/usb

统一接口后,凡是对文件的操作,应用程序或者SDK层都只能调用这些接口,这样不管是linux平台还是其他嵌入式操作系统或非操作系统平台,应用程序对文件的操作的接口函数都是一样的了,这样就做到了应用程序不同平台的兼容性。

你可能感兴趣的:(嵌入式,接口规范,平台兼容性)