99久久全国免费观看_国产一区二区三区四区五区VM_久久www人成免费看片中文_国产高清在线a视频大全_深夜福利www_日韩一级成人av

嵌入式開發中常用的軟件工程方法有哪些?

在嵌入式開發里,有哪些常用的軟件工程方法呢?

一、面向對象編程(OOP)

盡管 C 語言并非面向對象編程語言,但借助一些編程技巧,也能實現面向對象編程(OOP)的核心特性,如封裝、繼承和多態。

1. 封裝

封裝是把數據和操作數據的函數捆綁在一起,對外部隱藏內部實現細節。在嵌入式 C 語言中,可通過結構體和函數指針來實現封裝。

#include// 定義一個LED結構體typedefstruct {    int pin;    void (*turnOn)(struct LED*);    void (*turnOff)(struct LED*);} LED;// 實現LED開啟函數voidledTurnOn(LED* led){    printf("LED on pin %d is turned on.\n", led->pin);}// 實現LED關閉函數voidledTurnOff(LED* led){    printf("LED on pin %d is turned off.\n", led->pin);}// 初始化LEDvoidledInit(LED* led, int pin){    led->pin = pin;    led->turnOn = ledTurnOn;    led->turnOff = ledTurnOff;}intmain(void){    LED myLed;    ledInit(&myLed, 13);    myLed.turnOn(&myLed);    myLed.turnOff(&myLed);    return0;}

LED結構體封裝了pin數據和turnOnturnOff函數指針。ledInit函數用于初始化LED結構體,把具體的函數賦值給函數指針。外部代碼僅能通過這些函數指針來操作LED,而無需了解內部實現細節。

2. 繼承

繼承是指一個對象直接使用另一對象的屬性和方法。在嵌入式 C 語言中,可通過結構體嵌套實現繼承。

#include// 定義一個基類結構體typedefstruct {    int id;    void (*printInfo)(struct Base*);} Base;// 實現基類的打印信息函數voidbasePrintInfo(Base* base){    printf("Base ID: %d\n", base->id);}// 定義一個派生類結構體typedefstruct {    Base base; // 繼承Base結構體    char* name;} Derived;// 實現派生類的打印信息函數voidderivedPrintInfo(Derived* derived){    basePrintInfo(&derived->base);    printf("Derived Name: %s\n", derived->name);}// 初始化基類voidbaseInit(Base* base, int id){    base->id = id;    base->printInfo = basePrintInfo;}// 初始化派生類voidderivedInit(Derived* derived, int id, char* name){    baseInit(&derived->base, id);    derived->name = name;    derived->base.printInfo = (void (*)(Base*))derivedPrintInfo;}intmain(void){    Derived myDerived;    derivedInit(&myDerived, 1, "Derived Object");    myDerived.base.printInfo((Base*)&myDerived);    return0;}

Derived結構體嵌套了Base結構體,從而繼承了Base結構體的屬性和方法。derivedInit函數在初始化Derived結構體時,會調用baseInit函數初始化基類部分,并且把printInfo函數指針指向derivedPrintInfo函數。

3. 多態

多態是指不同對象對同一消息做出不同響應。在嵌入式 C 語言中,可通過函數指針實現多態。

#include// 定義一個基類結構體typedefstruct {    void (*operation)(struct Base*);} Base;// 定義一個派生類1結構體typedefstruct {    Base base;} Derived1;// 定義一個派生類2結構體typedefstruct {    Base base;} Derived2;// 派生類1的操作函數voidderived1Operation(Base* base){    printf("Derived1 operation.\n");}// 派生類2的操作函數voidderived2Operation(Base* base){    printf("Derived2 operation.\n");}// 初始化派生類1voidderived1Init(Derived1* derived1){    derived1->base.operation = derived1Operation;}// 初始化派生類2voidderived2Init(Derived2* derived2){    derived2->base.operation = derived2Operation;}// 執行操作voidperformOperation(Base* base){    base->operation(base);}intmain(void){    Derived1 myDerived1;    Derived2 myDerived2;    derived1Init(&myDerived1);    derived2Init(&myDerived2);    performOperation((Base*)&myDerived1);    performOperation((Base*)&myDerived2);    return0;}

Base結構體包含一個函數指針operationDerived1Derived2結構體繼承了Base結構體,并分別實現了自己的operation函數。performOperation函數接收一個Base指針,依據具體對象調用相應的operation函數,從而實現了多態。

二、測試驅動開發(TDD)

測試驅動開發(Test-Driven Development,TDD)是一種軟件開發方法論,它強調在編寫實際功能代碼之前先編寫測試代碼。

TDD 的核心流程遵循 “紅 - 綠 - 重構” 循環,下面將詳細介紹其原理、流程、優勢、局限性以及示例。

TDD 基于 “測試先行” 的理念,開發者首先明確需求并將其轉化為具體的測試用例。

由于此時還未編寫實現代碼,測試用例必然會失敗(呈現 “紅色” 狀態)。

接著,開發者編寫最少的代碼使測試用例通過(達到 “綠色” 狀態)。

最后,對代碼進行重構,在不改變代碼外部行為的前提下優化其內部結構,提高代碼的可讀性、可維護性和可擴展性。

實踐:使用Unity測試框架。

Unity 是一個輕量級的測試框架,它使用 C 語言實現, 代碼本身很小 。其代碼中大多數是宏定義,所以實際編譯后的代碼會更小, 比較適合在嵌入式測試應用

Unity的使用之前也有簡單分享過:

首先,把Unity源碼目錄下的unity.c、unity.h、unity_internals.h三個文件復制至我們的工程目錄下,并把unity.c添加到我們的keil工程中,然后添加文件路徑:

我們打開unity_internals.h文件,發現其有包含一個頭文件unity_config.h

這個文件是配置文件,我們與平臺相關的特性放在這個文件中。而這個文件Unity源碼中并未提供,所以需要我們自己建立,我這邊新建的unity_config.h文件的內容如下:

主要在這里面放了硬件相關的頭文件包含以及兩個必要的宏定義。第一個宏定義用于重定向輸出至串口,第二個宏定義就是我們的串口初始化。

unity_internals.h中我們發現unity_config.h文件被條件編譯屏蔽掉了,我們需要定義宏把它打開:

最后在我們的main.c中包含頭文件unity.h即可使用unity測試框架。在unity_internals.h中有很多可修改的配置,比如在不同的平臺中,整數的長度是不一樣的,在 Unity 中,允許開發者設置整數的長度。如果沒有設置, Unity 指定的默認值是 32 位。我們的STM32就是32位的,所以我們不需要修改。

下面開始編寫測試用例。 在 Unity 中,每個測試用例是一個函數, 該函數沒有參數和返回值。下面我們來測試一個閏年判斷函數:

在測試函數中用到 TEST_ASSERT_TRUE 和 TEST_ASSERT_FALSE ,  是 Unity 實現的兩個斷言, 用于判斷 布爾型表達式的值為真或為假。這些測試框架一般都是用斷言來進行測試的,包括以上分享的幾個框架都是如此。本例中只用到了兩個斷言,在 Unity 中還有很多斷言,如下是部分斷言列舉:

Unity 默認需要實現用例初始化函數 setUp 和用例清理函數 tearDown,這兩個函數均沒有參數和返回值。 在閏年判斷函數的測試用例中,由于不需要初始化和清理操作,實現的兩個函數如下:

在編寫了測試用例后, 接下來就可以在 main 函數中運行測試用例。在 Unity 中,使用宏 RUN_TEST 運行測試用例,參數為要運行的測試用例的函數名稱。主函數如下:

UNITY_BEGIN函數就是UNITY初始化函數,我們的串口初始化也是在這里面被調用的:

RUN_TEST函數用于運行我們的測試用例。UNITY_END函數就是返回我們的測試結果。最終,運行得到如下結果:

假如,我們把測試閏年的測試函數里的2000改為2001:

那么輸出結果就變為:

可以從結果看出沒有通過的用例相關的代碼所在行。在進行這樣子的測試之前,我們當然得要明白我們的功能函數的功能及其預期輸出,我們才能去進行測試用例的設計及進行測試。

相關書籍:《測試驅動的嵌入式C語言開發》

三、防御性編程

防御性編程是一種編程范式,旨在通過預見代碼中可能出現的錯誤、異常輸入或未定義行為,提前設計保護措施,確保程序在非預期情況下仍能穩定運行、優雅降級或清晰報錯,而非崩潰或產生不可控后果。

1. 核心原則

  • 不信任任何輸入:假設所有輸入(包括函數參數、用戶輸入、外部接口數據等)都是不可信的,必須經過驗證。
  • 最小化潛在危害:通過隔離風險代碼限制作用域等方式,避免局部錯誤擴散至整個系統。
  • 清晰的錯誤反饋:錯誤發生時,提供明確的錯誤信息日志,便于調試定位。
  • 優雅降級:無法處理錯誤時,讓系統進入安全狀態

2. 最佳實踐

(1) 參數校驗

函數入口參數合法性檢查:

floatsafe_sqrt(float x){    if (x < 0) {        return NAN;  // 或觸發錯誤處理    }    returnsqrt(x);}

(2)斷言(Assert)

驗證 “不可能發生” 的假設 ,輔助調試:

#includevoidmemcpy_safe(void* dst, size_t dst_size, constvoid* src, size_t src_size){    assert(dst != NULL && src != NULL);  // 指針非空    assert(dst_size >= src_size);       // 目標空間足夠    // 實際復制邏輯}

(3)錯誤碼與錯誤處理

函數通過返回值或輸出參數傳遞錯誤狀態:

enum error_code {    SUCCESS = 0,    ERR_INVALID_PARAM,    ERR_OUT_OF_MEM};enum error_code init_device(struct device* dev){    if (dev == NULL) return ERR_INVALID_PARAM;    if (allocate_memory(dev) != 0) return ERR_OUT_OF_MEM;    return SUCCESS;}

(4)防御未定義行為

確保整數除法不溢出:

intdivide_safe(int a, int b){    if (b == 0) return INT_MAX;  // 或觸發錯誤    if (a == INT_MIN && b == -1) return INT_MAX;  // 處理-2147483648 / -1溢出    return a / b;}

防止空指針解引用:

voidprint_string(constchar* str){    if (str == NULL) {        printf("(null)\n");        return;    }    printf("%s\n", str);}

3.防御性測試優缺點

四、敏捷開發

敏捷開發強調快速迭代、客戶反饋和團隊協作。在嵌入式開發中,可將項目拆分成多個小的迭代周期,每個周期都有可運行的版本。

1.迭代開發與持續集成

  • 短周期迭代:將項目劃分為多個短周期迭代,每個迭代通常為 1 - 4 周。在每個迭代中,團隊完成一定數量的用戶故事,交付可運行的產品增量。短周期迭代有助于快速驗證需求和設計,及時調整項目方向。
  • 持續集成:建立自動化的持續集成環境,每次代碼提交后自動進行編譯、測試和集成。在嵌入式開發中,還需進行硬件集成測試,確保軟件和硬件的兼容性。持續集成能及時發現代碼沖突和缺陷,提高代碼質量。
  • 迭代評審與回顧:每個迭代結束后,進行迭代評審會議,向客戶和相關利益者展示迭代成果,收集反饋意見。同時,召開迭代回顧會議,團隊成員總結本次迭代的經驗教訓,提出改進措施,應用于下一次迭代。

五、瀑布模型

瀑布模型是一種傳統的軟件開發模型,按照線性順序依次進行需求分析、設計、編碼、測試、維護等階段,如同瀑布流水一樣,每個階段完成后才進入下一階段。

瀑布模型作為一種經典的軟件開發方法,在嵌入式開發等眾多領域具有顯著的優勢。

1.階段明確、順序清晰

瀑布模型的各個階段界限分明,從需求分析、設計、編碼、測試到維護,按照固定的線性順序依次推進。

這種清晰的階段劃分使得項目流程易于理解和管理,每個階段都有明確的輸入和輸出,便于項目團隊成員明確各自的職責和任務。

2.強調文檔化

該模型非常重視文檔的編寫和管理,在每個階段都會產生相應的文檔,如需求規格說明書、設計文檔、測試報告等。

這些文檔不僅是項目各個階段的重要成果,也是項目團隊成員之間溝通的重要工具,同時還為項目的維護和升級提供了有力的支持。

3.易于控制和管理

由于瀑布模型的線性順序和明確的階段劃分,項目管理者可以很容易地對項目進行監控和控制。

每個階段都有明確的里程碑和交付物,管理者可以根據這些里程碑來評估項目的進度和質量,及時發現問題并采取相應的措施進行調整。

例如,在編碼階段結束后,可以通過代碼審查和單元測試來評估代碼的質量,如果發現問題,可以及時反饋給開發人員進行修改。

已上就是本次的分享,如果覺得文章有幫助,麻煩幫忙轉發,謝謝!

聲明:本內容為作者獨立觀點,不代表電子星球立場。未經允許不得轉載。授權事宜與稿件投訴,請聯系:editor@netbroad.com
覺得內容不錯的朋友,別忘了一鍵三連哦!
贊 2
收藏 2
關注 28
成為作者 賺取收益
全部留言
0/200
成為第一個和作者交流的人吧
主站蜘蛛池模板: 欧美日韩免费观看视频 | 亚洲精品久久久中文字幕 | 四虎娱乐 | 久久久综合亚洲91久久98 | 视频一区视频二区国产精品 | 国产又大又硬又粗 | 玩弄人妻奶水无码av在线 | 国产麻豆一区二区三区在线观看 | 亚洲一区二区三区四区五区不卡 | wwwwwww黄| yw193can未满十8网站 | 欧美精品乱码久久久久久 | 精品久久久久久久久久ntr影视 | 国产视频精品自拍 | 77777日本少妇久7黄绝片 | 国产二级一片内射视频播放 | 小黄文纯肉污到你湿 | 久久不射2019中文字幕 | 国产精品白丝jk黑袜喷水视频 | 女人被男人桶爽视频网站 | 久久艹av| 日本xxxx裸体bbbb | 一本大道久久精品 | 99999色| 免费无码成人AV片在线在线播放 | 少妇69xx| 欧美国产精品日韩在线 | 东北露脸46熟妇ⅩⅩXX | 国产日韩欧美一区二区 | 国产尺码和欧洲尺码表2022 | 亚欧日韩在线 | 男人操女人国产 | 色婷婷综合久久久久中国一区二区 | 亚洲丰满少妇xxxxx高潮对白 | 最好看的中文视频最好的中文 | 色国产精品一区在线观看 | 二区三区福利 | 成人在线播放网站 | 精品亚洲一区二区 | 亚洲国产精品无码专区影院 | 亚洲精品无码久久久久久久 |