数字电源技术在这几年可以说是炙手可热,而其控制器通常使用由TI设计的C2000芯片。不过绝大多数电力电子的同学一定是对这款MCU又爱又恨,爱的是其强大的处理性能在电力电子控制领域简直是游刃有余,具体可以参考下文。恨的是在C2000平台上开发比较复杂的电力电子系统时非常费时费力,经常是写算法十分钟,查datasheet两小时,好不容易run起来了还要解BUG两小时,相信小伙伴们一定有类似的感受。这篇文章就来探讨为什么会出现这样的问题以及如何解决,希望能解决各位同仁遇到的痛点问题~,相信小伙伴们一定有类似的感受。这篇文章就来探讨为什么会出现这样的问题以及如何解决,希望能解决各位同仁遇到的痛点问题~
数字电源为什么一般用DSP控制,而不能用普通的单片机?
裸机开发实在是太慢
针对裸机与实时操作系统的对比讨论已经很多,而搞电力电子的小伙伴可能不是很熟,所以这里针对电源开发应用再单独讨论讨论。对于一些简单的电源应用,往往只需要一个while大循环再加上中断就能完成。但是随着产品的不断升级和功能复杂化,一个这种“传统”的开发裸机已经逐渐变得愈发臃肿,往往我们会遇到的主要难点是,开发速度非常慢!非常慢!非常慢!其中的原因我想可以展开为以下几点: 一是学习曲线非常陡峭。TI的硬件封装api做得并不好,这就导致C2000平台上出现大部分用户写代码都是直接操作寄存器的奇观,虽说直接操作寄存器显得很高级,代码效率也会比较高,但是代价就是开发速度相当慢。每次做不同的实验的时候都需要重新阅读datasheet,查看每一个寄存器配置成什么值代表什么意思,等到写好了再调试又会花非常多的时间,据我所知,由于C2000架构的相对复杂性,仅仅是一个中断的配置都能难倒不少人。stm32的封装就做的很好,官方的HAL用起来用户省心了不少,那么问题来了,TI什么时候能争点气呢? 二是重复造轮子的情况非常普遍。不同人对于不同功能都有自己的函数实现,导致所有人的代码互相不兼容,各自为阵。以我自己为例,我以前的开发流程是先配置寄存器,也就是导入一个看起来跟自己实现功能差不多的官方例程。每加一个新功能就导入一个例程,然后ctrl c ctrl v。寄存器配置完毕之后写应用层函数,比如说为了实现串口控制,会写串口字符的解析函数。我记得,串口的解析程序和发送程序我都写了好多遍,个中酸爽,相信大家都有所体会。 三是可维护性是一个很大的问题。一般电力电子系统会对于环路计算的实时性要求非常高,所以会放在中断中计算,然后其他任务放在大while中放一个状态机判断。一旦任务多了,任务之间的依赖错综复杂。此时在while大循环中就出现非常复杂的状态机系统,此时系统的可维护性将会变得非常差,出现大家常说的屎山代码,相信包括在我在内的任何人都不愿意看别人写的状态机代码。而且这么复杂的状态机系统如果不经过良好的时序优化,系统的实时性其实也会变得非常糟糕。 四是内存安全得不到有力保障。任何一个处理多个任务的系统都涉及不同任务对共享内存的访问,电力电子控制也不例外。比如有些状态机可能被主循环和ISR同时读写,就可能产生所谓“竞争”的问题。比如主循环正将状态从A修改到B,这时ISR打断主函数并将状态从A修改到C并返回,然后主循环开始执行状态B对应的代码可是此时真正的状态已经是C了,最终导致状态机崩溃。这种问题特别难以调试,一旦出问题就很严重,遇到了可能只能烧香拜佛了。同步和竞争的概念可能对很多电力电子的小伙伴比较陌生,感兴趣的同学可以参见:
如何告别以上这些痛苦呢?让RTOS来拯救你。
实时操作系统
什么是实时操作系统
实时操作系统(Real-time operating system, RTOS),又称即时操作系统,它会按照排序运行、管理系统资源,并为开发应用程序提供一致的基础。实时操作系统与一般的操作系统相比,最大的特色就是“实时性”,如果有一个任务需要执行,实时操作系统会马上(在较短时间内)执行该任务,不会有较长的延时。
以上只是概念,大家可能看得似懂非懂,没关系,咱们掰开了来看RTOS凭啥能解决前面提到的痛点问题?
开发速度快
操作系统是一个统一的平台,也就是下图中的内核部分,上层的组件层同样重要。组件层对物理外设实现了硬件驱动程序,并以API的形式暴露给上层应用层。这样的好处非常明显。首先,用户只需调用简单(可读性好)的函数就能实现想要的功能,再也不用跟底层寄存器打交道了,极大地缩短了开发周期。其次,降低开发难度。系统可以帮程序员省去大量时间思考软件架构,只要保证调度和执行的完整性。甚至不需要特别处理,就可以做到很多事情。免去了,很多意外造成的跑飞。更重要的,统一的平台架构使得用户之间的代码可以共享,从此告别在造轮子这件事情上反复花时间的时代。试想,不久的将来,你可能可以在C2000上运行python程序,这是不是很有意思!
保证系统实时性
实时操作系统天生就是给多任务设计的。对于复杂的电源系统也会涉及到多任务工作。比如各种系统状态判断,状态监控,与上位机或者是FPGA通讯的程序,又或者需要使用到人机交互的处理程序等等。这么多程序中可能还会涉及到大量需要延迟等待的情形,使得任务实现变得异常复杂且难调试。甚至可能需要复杂的状态机设计才有可能完成,这对程序员的素质要求还是非常高的。 而当使用RTOS,效果就四个字,无脑操作。多个任务设定优先级,优先级最高的优先执行,执行完毕之后等待,调度器自动切换到其他任务继续执行。并且调度器能够将任务切片并在不同任务之间进行轮转,避免单一任务过分消耗系统资源,将任务均衡地分配到各个时间片中,使得整个系统的总体运行效率最高。
保证内存安全性
RTOS设计了一整套“同步机制”避免多个任务对内存的“竞争问题”,这些机制包括互斥锁,旗语,临界区段等等,确保多个任务有序对共享内存进行访问。用户只需要调用这些函数即可,完全不需要管其实现细节,简单又好用,再也没有莫名其妙程序跑飞的烦恼!
尴尬的现状
说完这么多优点,但现状却是让人非常尴尬。
缺乏基于C2000的靠谱好用的RTOS
RTOS在嵌入式领域发展的比较好,比如著名的freeRTOS,以及国内自主开发的RT-Thread。但是由于搞嵌入式的人往往很少搞电力电子,这就导致嵌入式的操作系统很少有专门给C2000芯片做移植的,同时设计的时候也没有考虑电力电子应用的特殊性。目前有一些非官方的移植,但是功能有限,年久失修,开发者已经跑路。有兴趣的童鞋可以尝试。
https://github.com/IvanZuy/freertos_c28x
缺乏针对C2000的定制化设备框架
在电力电子领域,外设硬件的操作有自身独特的复杂性。比如PWM不可能仅仅满足于配置周期、占空比这种常规配置。还需要相位、死区、影子装载、外部ADC采样触发等等灵活配置,而这却是目前主流RTOS所空缺的。
TI开发的RTOS
作为C2000的亲妈,TI自然是老早就基于C2000实现了自己的一套RTOS,然而这套工具可谓是非常难用以至于到现在都没有被市场所接受,主要原因是
1、系统开源但不开放。TI-RTOS的C代码使用xdc格式进行分装。xdc格式阅读代码体验较差,比较反人类。首先部分源代码不是直接提供而是通过xdc tool生成的,所以代码的透明性很差。其次经过xdc分装的代码很难追踪,有些符号在代码中的名字和链接之后的名字不一样。这给代码阅读和调试造成了非常大的困难,简直无处着手。由此可见,TI-RTOS经过xdc封装之后,是一个开源但不开放的系统,完全是被CCS控制的,用户可以获得源码但是完全没有办法在OS层面进行改动,只能在ti提供的api之上进行改动。 2、抢占功能有限。为保证实时性,RTOS通常是抢占(preempt)系统,即任务是可以被打断并在不同任务之间轮转的。TI-RTOS缺少在中断中对进行线程上下文切换的机制,这意味着线程内部必须有主动挂起的操作才能抢占,否则将运行到底,不具备真正意义上的多任务抢占的功能。这是TI-RTOS的一个硬伤。 3、维护非常非常差。因为用户有限,TI-RTOS对C2000的支持和维护非常差。笔者在TMS32028379D平台下测试了TI-RTOS的几个例程,发现很多例程(比如swi和task)无法运行直接跑飞。只有hello这一个例程可以正常运行。可见TI-RTOS对C2000根本不像亲儿子,简直就是捡来的孩子。
RT-Thread
RT-Thread诞生于2006年,是一款以开源、中立、社区化发展起来的物联网操作系统。RT-Thread主要采用 C 语言编写,浅显易懂,且具有方便移植的特性(可快速移植到多种主流 MCU 及模组芯片上)。RT-Thread把面向对象的设计方法应用到实时系统设计中,使得代码风格优雅、架构清晰、系统模块化并且可裁剪性非常好。目前已经做到通过wrapper兼容freertos和uC/OS,提供posix接口,同时兼容Arduino。在内核层面提供各种hook方便用户定制。 总之就是傻瓜操作,兼容并包,而且是中国创造。基于此我们选择了基于RT-thread进行开发适配于C2000的RTOS。
目前已实现的效果
兼容控制台使用命令行控制
通过串口,我们可以实现类似命令行的工具,可以非常方便监控系统状态并且操纵外设,启动任务
比如输入如下指令就可以设置pwm占空比、相位、死区等等
pwm probe- probe pwm by name pwm enable - enable pwm channel pwm disable - disable pwm channel pwm get - get pwm channel info pwm set - set pwm channel info pwm phase - set pwm phase pwm dead_time - set pwm dead time
对硬件层做了充分的抽象
以GPIO为例,在此对比了传统的写寄存器的方式和采用封装后的程序调用方式。
// 操作寄存器版本的代码实现 EALLOW; GpioCtrlRegs.GPAPUD.bit.GPIO0 = 0; // Enable pullup on GPIO0 GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 0; GpioCtrlRegs.GPADIR.bit.GPIO0 = 1; GpioDataRegs.GPADAT.bit.GPIO0 = 1; EDIS; // 封装之后的代码实现 #define LED0_PIN GET_PIN(A, 0) rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT); rt_pin_write(LED0_PIN, PIN_HIGH);
从可读性来看,标准化的函数接口看起来一目了然。
再比如ePWM模块,ePWM可谓是C2000的一大法宝,优化设计的驱动也将发挥ePWM的全部实力,为此,我们针对C2000平台定制了专门的设备框架,可以灵活控制周期、占空比、相位、死区等等。比如设置pwm的周期和占空比可以通过如下代码,代码将变得清晰易读,并且可以快速移植。
rt_device_control(&pwm1,PWM_CMD_SET_PERIOD, 10000);//设置周期为10us rt_device_control(&pwm1,PWM_CMD_SET_DUTY, 0.5);//设置占空比为50%
结合Kconfig实现高度灵活配置
也可以通过Kconfig(一种类似图形界面的配置工具)来详细配置每一个寄存器,此时每一个选项都是以文字说明,以交互式的方式直接提示给用户,免去用户查询datasheet的痛苦,非常方便。
抢占式任务调度
我们用一种非常巧妙的机制在C2000中实现了在中断环境下的上下文切换,实现了真正意义上的抢占式多任务调度,解决了TI-RTOS悬而未决的一个重大难题。
几乎无开销的中断响应
熟悉RTOS的小伙伴可能会觉得在电源系统这种实时性要求这么高的场合是否使用RTOS会影响系统中断的延迟。这是由于RTOS的某些关键操作需要关闭全局中断,这可能影响关键中断的响应速度。目前测试结果显示rt-thread关闭全局中断的时间最长不超过1us(测试条件:5个线程,100Hz SYSTICK,CPU 200MHz,TMS320F28379),几乎不会对关键中断(比如PWM)响应造成影响。后续我们会进一步对内核进行改进,使得系统关键操作只关闭部分而非全局中断,确保部分重要中断可以获得**0延迟(与裸机完全相同)**的响应速度。所以这一点大家不必担心,使用我们开发的系统可以做到几乎或者是零延迟!
未来工作
目前已经完成了内核移植(针对C2000的特殊架构定制化了部分内核代码),完成了GPIO、UART、ePWM驱动,未来在持续更新ADC、Timer等外设的驱动。更进一步,我们将会把RTOS进程到Simulink的代码生成工具里面,实现图形化编程和一键代码生成。与Simulink自带的C2000支持包不同,我们的代码生成工具基于RTOS的框架,生成的是人类可以阅读和调试的代码,从样机到产品,代码一步到位,无需手动移植!我们还会针对C2000芯片移植各种自动化工具,比如自动生成cmd文件,自动生成CCS工程文件,让各种重复劳动都成为历史。
全部0条评论
快来发表一下你的评论吧 !