加入星計劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴散
  • 作品版權(quán)保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 具體需求
    • C語言中的強符號和弱符號的區(qū)別
    • FOC 中封裝,用戶來實現(xiàn)
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

利用weak關(guān)鍵字來實現(xiàn)FOC 算法庫的平臺兼容性

01/17 09:03
2451
閱讀需 11 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

在實現(xiàn)FOC電機算法庫模塊化時,我思考了如何使庫的代碼在各個平臺上都能引入直接編譯,實現(xiàn)平臺無關(guān)性。在一段時間的考慮后,我選擇了使用weak關(guān)鍵字。

具體需求

眾所周知,F(xiàn)OC的電流采樣方式有多種,既可以使用三個ADC進行三電阻采樣,也可以使用霍爾電流傳感器在相線上進行2路采樣。

如果我希望算法庫與硬件平臺無關(guān),就不能在庫中兼容所有硬件平臺(也不可能實現(xiàn)),因此我決定讓算法庫的使用者來實現(xiàn)這部分代碼。

學過C++的同學應(yīng)該很快能想到解決方案,沒錯,面向?qū)ο蟮母呒壵Z言中有一種函數(shù)叫做虛函數(shù)。

虛函數(shù)是面向?qū)ο缶幊讨械囊粋€概念,通常與多態(tài)相關(guān)。在許多面向?qū)ο蟮木幊陶Z言中,如C++和Java,都支持虛函數(shù)的概念。

虛函數(shù)在基類中聲明為虛擬的(virtual),并在派生類中進行重寫。通過使用虛函數(shù),可以實現(xiàn)運行時多態(tài)性,使得程序在運行時能夠動態(tài)地選擇調(diào)用哪個版本的函數(shù),而不是在編譯時確定。

具體而言,當一個類中的函數(shù)被聲明為虛函數(shù)時,派生類可以通過重寫(覆蓋)這個函數(shù)來提供特定于派生類的實現(xiàn)。然后,通過基類指針或引用調(diào)用這個函數(shù)時,實際上會調(diào)用相應(yīng)派生類中的函數(shù),而不是基類中的函數(shù)。這種動態(tài)的函數(shù)調(diào)用稱為運行時多態(tài)。

那么在嵌入式 C 語言中,我們?nèi)绾螌崿F(xiàn)這樣的騷操作呢?

C語言中的強符號和弱符號的區(qū)別

在C語言中,函數(shù)和初始化的全局變量(包括顯式初始化為0)被認為是強符號,而未初始化的全局變量則被視為弱符號。

這些符號有一些規(guī)則,讓我們來看看:

① 如果有兩個同名的強符號,那就會出錯,編譯器會說“這個定義重復(fù)了”。

② 你可以有一個強符號和多個弱符號,但是在定義時,系統(tǒng)會選擇強符號。

③ 當存在多個相同名字的弱符號時,鏈接器會選擇占用內(nèi)存空間最大的那個。

在編程中,我們常常碰到一種情況,叫做“符號重復(fù)定義”。如果多個目標文件中都定義了一個名為global的全局整數(shù)變量并對其進行了初始化,鏈接這些目標文件時就會出現(xiàn)符號重復(fù)定義的錯誤。

比如:

main.c 文件中

int strong = 1;  
int main()  {      return 0;  }

led.c 文件中

int strong = 0;  
int led_on()  {      return 0;  }

在 MDK 的編譯器中,會產(chǎn)生符號重復(fù)定義的錯誤,因為對于 strong 這個變量符號,存在兩個強者。

當然由于編譯器的差異,在 MDK 中即使我們把 strong 不進行顯示初始化,編譯器也可以檢測出符號重復(fù)定義,除非我們使用 extern 來表明這是一個外部符號,或者用 weak 修飾來聲明這是一個弱函數(shù)。

extern int extnum;  int weak1;  int strong = 1;  int __attribute__((weak)) weak2 = 2;    int main()  {      return 0;  } 

上面這段程序中,"weak"和"weak2"是弱符號,"strong"和"main"是強符號,而"extnum"既非強符號也非弱符號,因為它是一個外部變量的引用。

對于C語言來說,編譯器默認函數(shù)和初始化了的全局變量為強符號,未初始化的全局變量為弱符號(C++并沒有將未初始化的全局符號視為弱符號)。我們也可以通過GCC的"__attribute__((weak))"來定義任何一個強符號為弱符號。

注意,在 MDK 中使用 weak 可以直接使用它定義好的“__weak”即可,可以看后續(xù)的案例。

換句話說,就是我們可以定義一個符號,而該符號在鏈接時可以不解析,注意這里和 C++ 中的虛函數(shù)的區(qū)別。

我們用函數(shù)來做個實驗

int main(void)  {      led_on();     return 0;  } 

很明顯,這樣寫連編譯都無法通過。因為編譯器會報錯,led_on 符號沒有定義。

__weak void led_on(); 
int main(void)  {      if (f)      f();      return 0;  }

那么,我們聲明了一個函數(shù)led_on(),屬性為weak,但并不定義它,這樣,鏈接器會將此未定義的weak symbol賦值為0,也就是說led_on()并沒有真正被調(diào)用,試試看,去掉if條件后,它就崩了!

FOC 中封裝,用戶來實現(xiàn)

這里大家應(yīng)該突然就明白為什么我要說這個 weak 關(guān)鍵字了吧,沒錯,這里的弱函數(shù)其實也可以叫做虛函數(shù),就是比較務(wù)虛,他就是一個占座的,有強者來的時候,就乖乖的讓座了。

下面看我代碼中的實際例子:

//虛函數(shù),獲取相電流,用戶應(yīng)自行實現(xiàn)__weak curr_t get_phase_current(void){    #warning pls define your get_phase_volt function    curr_t c_t = {0};    return c_t;}

這里首先定義一個弱函數(shù)符號,讓編譯器可以編譯通過,到任何平臺,用戶不實現(xiàn)這個函數(shù),他也可以編譯通過,只是認為采樣電流為 0,同時我們可以使用 warning 的預(yù)編譯指令提醒用戶需要自己實現(xiàn)。

查看編譯結(jié)果如下:

當用戶引入我的 FOC 算法庫后,他可以直接編譯通過,同時可以自己實現(xiàn)一下從硬件獲取電流的函數(shù),只要保證跟我的弱函數(shù)一樣的符號名和返回值即可。

curr_t get_phase_current(void){    s32 C1, C2, temp32 = 0;    curr_t Local_Stator_Currents;
    adcData[2] = HAL_ADCEx_InjectedGetValue(&adcHandle, ADC_INJECTED_RANK_4);    adcData[3] = HAL_ADCEx_InjectedGetValue(&adcHandle, ADC_INJECTED_RANK_3);

    temp32 = _CRT_A_1_75MR1;    switch(m_Sector)    {    case 1:     //BC相電流    case 6:        C1 = (s16)(ADC->JDOR4) - m_ADCOffsetB;        C2 = (s16)(ADC->JDOR3) - m_ADCOffsetC;        C1 = (C1*temp32)>>10;        C2 = (C2*temp32)>>10;        Local_Stator_Currents.C1 = C1+C2;        Local_Stator_Currents.C2 = -C1;        break;    case 2:     //AC相電流    case 3:        C1 = (s16)(ADC->JDOR4) - m_ADCOffsetA;        C2 = (s16)(ADC->JDOR3) - m_ADCOffsetC;        C1 = (C1*temp32)>>10;        C2 = (C2*temp32)>>10;        Local_Stator_Currents.C1 = -C1;        Local_Stator_Currents.C2 = C1+C2;        break;    case 4:     //AB相電流    case 5:        C1 = (s16)(ADC->JDOR4) - m_ADCOffsetA;        C2 = (s16)(ADC->JDOR3) - m_ADCOffsetB;        C1 = (C1*temp32)>>10;        C2 = (C2*temp32)>>10;        Local_Stator_Currents.C1 = -C1;        Local_Stator_Currents.C2 = -C2;        break;    default:        break;    }    Local_Stator_Currents.C1 = Local_Stator_Currents.C1;    Local_Stator_Currents.C2 = Local_Stator_Currents.C2;
    return(Local_Stator_Currents); }

 

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風險等級 參考價格 更多信息
RSE-32.768-12.5-H14-TR-10PPM 1 Raltron Electronics Corporation Parallel - Fundamental Quartz Crystal, 0.032768MHz Nom,
暫無數(shù)據(jù) 查看
NX2012SA-32.768K-STD-MUB-1 1 Nihon Dempa Kogyo Co Ltd Parallel - Fundamental Quartz Crystal, 0.032768MHz Nom, ROHS COMPLIANT PACKAGE-2
$7.18 查看
PLR135/T5P 1 Everlight Electronics Co Ltd Receiver, 16Mbps, Panel Mount, ROHS COMPLIANT PACKAGE-3
$2.4 查看

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

多年硬件從業(yè)經(jīng)驗,專注分享從研發(fā)到供應(yīng)鏈,再到精益制造過程中的經(jīng)驗和感悟!