嵌入式軟件設計之提高代碼可移植性

前面一篇博文嵌入式軟件架構設計之分層設計給大家分享了程序分層設計的一些個人觀點。裏面有提到接口統一規範的問題,下面這篇博文詳細闡述一下關於代碼可移植性的問題。代碼可移植性非常重要!在這裏有的人很納悶,有人會問:除了彙編語言的移植性很差以外,c語言,c++,java等其他高級語言的移植性不是都很棒的麼。毋庸置疑,c語言的移植性是非常好。但本文不是在探討一段編程語言的移植性問題,要探討的是:在不同的嵌入式平臺上,如何提高開發嵌入式產品的效率問題。換一種表達方式:如何提高嵌入式軟件平臺之間的可移植性。

在探討之前先拋出一個問題:如何才能使開發的應用程序運行在不同的硬件平臺上?這裏所指的應用程序不是指pc端的應用也不是指的android,ios等平臺的應用。而是指的是其他嵌入式平臺的應用,他們更多的是c語言實現。我們都知道windows平臺也好還是ios,android平臺也罷,他們的應用是不同的編程語言實現的,但是他們有一個共同的特點:給應用開發人員提供標準的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平臺還是其他嵌入式操作系統或非操作系統平臺,應用程序對文件的操作的接口函數都是一樣的了,這樣就做到了應用程序不同平臺的兼容性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章