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

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

利用weak關(guān)鍵字來(lái)實(shí)現(xiàn)FOC 算法庫(kù)的平臺(tái)兼容性

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

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

具體需求

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

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

學(xué)過(guò)C++的同學(xué)應(yīng)該很快能想到解決方案,沒(méi)錯(cuò),面向?qū)ο蟮母呒?jí)語(yǔ)言中有一種函數(shù)叫做虛函數(shù)。

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

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

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

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

C語(yǔ)言中的強(qiáng)符號(hào)和弱符號(hào)的區(qū)別

在C語(yǔ)言中,函數(shù)和初始化的全局變量(包括顯式初始化為0)被認(rèn)為是強(qiáng)符號(hào),而未初始化的全局變量則被視為弱符號(hào)。

這些符號(hào)有一些規(guī)則,讓我們來(lái)看看:

① 如果有兩個(gè)同名的強(qiáng)符號(hào),那就會(huì)出錯(cuò),編譯器會(huì)說(shuō)“這個(gè)定義重復(fù)了”。

② 你可以有一個(gè)強(qiáng)符號(hào)和多個(gè)弱符號(hào),但是在定義時(shí),系統(tǒng)會(huì)選擇強(qiáng)符號(hào)。

③ 當(dāng)存在多個(gè)相同名字的弱符號(hào)時(shí),鏈接器會(huì)選擇占用內(nèi)存空間最大的那個(gè)。

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

比如:

main.c 文件中

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

led.c 文件中

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

在 MDK 的編譯器中,會(huì)產(chǎn)生符號(hào)重復(fù)定義的錯(cuò)誤,因?yàn)閷?duì)于 strong 這個(gè)變量符號(hào),存在兩個(gè)強(qiáng)者。

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

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

上面這段程序中,"weak"和"weak2"是弱符號(hào),"strong"和"main"是強(qiáng)符號(hào),而"extnum"既非強(qiáng)符號(hào)也非弱符號(hào),因?yàn)樗且粋€(gè)外部變量的引用。

對(duì)于C語(yǔ)言來(lái)說(shuō),編譯器默認(rèn)函數(shù)和初始化了的全局變量為強(qiáng)符號(hào),未初始化的全局變量為弱符號(hào)(C++并沒(méi)有將未初始化的全局符號(hào)視為弱符號(hào))。我們也可以通過(guò)GCC的"__attribute__((weak))"來(lái)定義任何一個(gè)強(qiáng)符號(hào)為弱符號(hào)。

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

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

我們用函數(shù)來(lái)做個(gè)實(shí)驗(yàn)

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

很明顯,這樣寫連編譯都無(wú)法通過(guò)。因?yàn)榫幾g器會(huì)報(bào)錯(cuò),led_on 符號(hào)沒(méi)有定義。

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

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

FOC 中封裝,用戶來(lái)實(shí)現(xiàn)

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

下面看我代碼中的實(shí)際例子:

//虛函數(shù),獲取相電流,用戶應(yīng)自行實(shí)現(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;}

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

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

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

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); }

 

推薦器件

更多器件
器件型號(hào) 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊(cè) ECAD模型 風(fēng)險(xiǎn)等級(jí) 參考價(jià)格 更多信息
ABS10-32.768KHZ-9-T 1 Abracon Corporation CRYSTAL 32.7680KHZ 9PF SMD

ECAD模型

下載ECAD模型
$0.85 查看
FOD420SD 1 onsemi Random Phase Snubberless Triac Driver, 1000-REEL
$2.69 查看
AT27C256R-70PU 1 Atmel Corporation OTP ROM, 32KX8, 70ns, CMOS, PDIP28, 0.600 INCH, GREEN, PLASTIC, MS-011AB, DIP-28

ECAD模型

下載ECAD模型
$2.54 查看

相關(guān)推薦

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

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