加入星计划,您可以享受以下权益:

  • 创作内容快速变现
  • 行业影响力扩散
  • 作品版权保护
  • 300W+ 专业用户
  • 1.5W+ 优质创作者
  • 5000+ 长期合作伙伴
立即加入
  • 正文
    • 具体需求
    • C语言中的强符号和弱符号的区别
    • FOC 中封装,用户来实现
  • 推荐器件
  • 相关推荐
  • 电子产业图谱
申请入驻 产业图谱

利用weak关键字来实现FOC 算法库的平台兼容性

01/17 09:03
2452
阅读需 11 分钟
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

在实现FOC电机算法库模块化时,我思考了如何使库的代码在各个平台上都能引入直接编译,实现平台无关性。在一段时间的考虑后,我选择了使用weak关键字。

具体需求

众所周知,FOC的电流采样方式有多种,既可以使用三个ADC进行三电阻采样,也可以使用霍尔电流传感器在相线上进行2路采样。

如果我希望算法库与硬件平台无关,就不能在库中兼容所有硬件平台(也不可能实现),因此我决定让算法库的使用者来实现这部分代码。

学过C++的同学应该很快能想到解决方案,没错,面向对象的高级语言中有一种函数叫做虚函数。

虚函数是面向对象编程中的一个概念,通常与多态相关。在许多面向对象的编程语言中,如C++和Java,都支持虚函数的概念。

虚函数在基类中声明为虚拟的(virtual),并在派生类中进行重写。通过使用虚函数,可以实现运行时多态性,使得程序在运行时能够动态地选择调用哪个版本的函数,而不是在编译时确定。

具体而言,当一个类中的函数被声明为虚函数时,派生类可以通过重写(覆盖)这个函数来提供特定于派生类的实现。然后,通过基类指针或引用调用这个函数时,实际上会调用相应派生类中的函数,而不是基类中的函数。这种动态的函数调用称为运行时多态。

那么在嵌入式 C 语言中,我们如何实现这样的骚操作呢?

C语言中的强符号和弱符号的区别

在C语言中,函数和初始化的全局变量(包括显式初始化为0)被认为是强符号,而未初始化的全局变量则被视为弱符号。

这些符号有一些规则,让我们来看看:

① 如果有两个同名的强符号,那就会出错,编译器会说“这个定义重复了”。

② 你可以有一个强符号和多个弱符号,但是在定义时,系统会选择强符号。

③ 当存在多个相同名字的弱符号时,链接器会选择占用内存空间最大的那个。

在编程中,我们常常碰到一种情况,叫做“符号重复定义”。如果多个目标文件中都定义了一个名为global的全局整数变量并对其进行了初始化,链接这些目标文件时就会出现符号重复定义的错误。

比如:

main.c 文件中

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

led.c 文件中

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

在 MDK 的编译器中,会产生符号重复定义的错误,因为对于 strong 这个变量符号,存在两个强者。

当然由于编译器的差异,在 MDK 中即使我们把 strong 不进行显示初始化,编译器也可以检测出符号重复定义,除非我们使用 extern 来表明这是一个外部符号,或者用 weak 修饰来声明这是一个弱函数。

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

上面这段程序中,"weak"和"weak2"是弱符号,"strong"和"main"是强符号,而"extnum"既非强符号也非弱符号,因为它是一个外部变量的引用。

对于C语言来说,编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号(C++并没有将未初始化的全局符号视为弱符号)。我们也可以通过GCC的"__attribute__((weak))"来定义任何一个强符号为弱符号。

注意,在 MDK 中使用 weak 可以直接使用它定义好的“__weak”即可,可以看后续的案例。

换句话说,就是我们可以定义一个符号,而该符号在链接时可以不解析,注意这里和 C++ 中的虚函数的区别。

我们用函数来做个实验

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

很明显,这样写连编译都无法通过。因为编译器会报错,led_on 符号没有定义。

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

那么,我们声明了一个函数led_on(),属性为weak,但并不定义它,这样,链接器会将此未定义的weak symbol赋值为0,也就是说led_on()并没有真正被调用,试试看,去掉if条件后,它就崩了!

FOC 中封装,用户来实现

这里大家应该突然就明白为什么我要说这个 weak 关键字了吧,没错,这里的弱函数其实也可以叫做虚函数,就是比较务虚,他就是一个占座的,有强者来的时候,就乖乖的让座了。

下面看我代码中的实际例子:

//虚函数,获取相电流,用户应自行实现__weak curr_t get_phase_current(void){    #warning pls define your get_phase_volt function    curr_t c_t = {0};    return c_t;}

这里首先定义一个弱函数符号,让编译器可以编译通过,到任何平台,用户不实现这个函数,他也可以编译通过,只是认为采样电流为 0,同时我们可以使用 warning 的预编译指令提醒用户需要自己实现。

查看编译结果如下:

当用户引入我的 FOC 算法库后,他可以直接编译通过,同时可以自己实现一下从硬件获取电流的函数,只要保证跟我的弱函数一样的符号名和返回值即可。

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

 

推荐器件

更多器件
器件型号 数量 器件厂商 器件描述 数据手册 ECAD模型 风险等级 参考价格 更多信息
XPDV3120R-VF-FA 1 Finisar Corporation Fiber Optic Device,
$64267.43 查看
SN74LVC1T45DBVR 1 Texas Instruments Single-Bit Dual-Supply Bus Transceiver with Configurable Voltage-Level Shifting and 3-State Outputs 6-SOT-23 -40 to 85

ECAD模型

下载ECAD模型
$0.94 查看
ECS-98.3-20-5PXDU-TR 1 ECS International Inc CRYSTAL 9.8304 MHZ 20PF SMD
$2.26 查看

相关推荐

电子产业图谱

多年硬件从业经验,专注分享从研发到供应链,再到精益制造过程中的经验和感悟!