所有由gracepower发布的文章

磁性材料应用入门

因为要做6KW,0.9MHz高频焊接机,必须要接触磁学,目前高频焊接机已经初步完成,故把磁学的基本概念总结一下,一为自己今后复习,二为做开关电源方面的朋友提供参考,理清思路。

磁学因为大家接触的不多,不常用,所以相对比较陌生,往往局限于开关电源行业,并且大部分设计开关电源的以参考别的电路为主,很少有自己设计的,所以往往对其理论理解不深,此为其一。磁学有两套单位,如MKS(米-千克-秒)和CGS(厘米-克-秒)两套单位制度,国内教学一般采用MKS制,但工程实际采用CGS值,并且名词太多,比较混淆,此为其二。磁性材料种类较多,并且参数测量不方便,也少有仪器测量,比如磁导率、感应磁感应强度等,最容易的电感值,一般的万用表也没有,都需要用专用仪器,此为其三。以上这些,制约了磁学的普及推广。

根据实际工程需要,我们一般需要了解这么几个指标
1、电感值,这个建议采用专业仪器直接测量,手持式的价格不贵
2、发热损耗,这个建议用温度枪来测量,价格也不贵,只要保证在工作范围内即可。
3、饱和磁感应强度,是不允许磁饱和的,否则此电感失效导致设备炸机,尤其是开关电源上,本文的核心就是为了获取这个值。
本文基于CGS实用单位制,采用Gs(高斯)和Oe(奥斯特)为单位,讨论的对象是均匀闭合类磁性材料,如磁环,E型变压器。pi为圆周率

1、磁场的产生在实际中是基于闭合的电流,当一个磁环在有外部闭合线圈时,那么这个线圈产生的激励磁动势用mmf(等价于电场的电压概念)表示:
mmf = 0.4π*NI,N指圈数,I是通过线圈的电流,这个是产生磁场的原动力

2、因为内部微小电流产生的磁场会同步于外部,当外部磁场撤销的时候,内部的磁场还存在形成一个同方向的闭合大磁场,需要足够长的时间才能被分子热运动打乱,这

个就取决于不同的材料,可以分为软磁性材料跟硬磁性材料,硬磁性材料当外部电流撤销后,内部的磁场还长期保留,形成了磁铁。软磁性材料一般当外部磁场撤销后内部磁场也马上消失,但这个也需要一个时间,因为这个时间的存在,或者把内部微小磁场与外部磁场同步需要时间,这个就产生了磁滞回路,也就是损耗,一般频率越高,损耗越大。

3、具体磁场的大小也即磁场强度,受制于磁性材料的回路长度:
H = mmf / L(奥斯特)  当为磁环的时候,L = 2πR,R为磁环的半径, 注意此处L单位为cm(厘米)

4、H是磁场的原动力,当碰到不同的材料,他们内部的微型自闭合电流环会与外部的磁场方向一致,增强了其内部的磁场,所以有了磁感应强度B和磁导率μ
B = μ*H(Gs高斯)
1T(特斯拉) = 10000Gs(高斯)

5、真空中的μ= 1,其他的材料一般都大于1

6、综合以上:
B = 0.2μ*N*I/R
只要计算出来这个值不超过材料的饱和磁感应强度值即可

7、μ和Bmax,一般磁性材料厂家可以提供,并且根据材料的分类,我们也可以大概的知道范围:
                硅钢         锰锌          镍锌         铁粉芯           铁硅铝
应用         工频       <1MHz       >1MHz      高频滤波     高频滤波
μ             1.5K       750~15K   15~1500    4~100         26~125
Bmax(T)1.5~1.8  0.3~0.5  0.3~0.5      0.5~1.4           1

居里点      700度     200度       350度        400以上        400以上

8、材料说明
a、硅钢片一般用于工频变压器及电机类设备,工作频率在50Hz附近
b、锰锌因为性价比高,大量应用于当前的开关电源功率变化级上,根据不同的损耗频率,可以细分很多级别,一般的讲,μ越高损耗越大,所以越高频率的u值越低。目前市场上常用的是PC40材料的,导磁率在2500.
c、镍锌跟锰锌形成互补,一般用于较高的频率上,因为大于1MHz,大量应用于EMI器件,抗辐射干扰。镍锌的磁环基本上不导电,这个是最简单的区别锰锌(锰锌磁环导电)的地方。
d、铁粉芯因为性价比高,大量应用于开关电源输出滤波环节,这个往往带有很强的直流分量而交流分量不大,采用铁粉芯的低导磁率可以抗磁饱和。
e、铁硅铝价格较高,但性能是铁粉芯的2倍,功能跟铁粉芯一样,往往替代铁粉芯为了降低发热量的。
 

附件是一个计算磁环电感的工具,只要输入磁环尺寸、磁导率,线圈匝数,就能求出电感量,并且输入电流,可以得到磁感应强度。

有了这个,可以通过测量电感的工具,倒推出磁导率,也可以预计实际工作的磁感应强度的大小,非常好用,建议大家使用。

应用-嵌入式微系统连载之十二

msOS设计的本意是为电子类设备及PLC设计的,一开始想着仿造西门子的S7-300来设计一套基于CAN通讯的分布式PLC,然而当设计了几个硬件模块之后就发现,这个需求定义超出了我的个人能力,自己的综合知识还没有上到这个层面,所以开始退而求其次,仿照普通的PLC,结合华禹工控及俊知机械的需求设计了一款简单的嵌入式PLC,取名为msPLC-Demo。

 


msOS开发过程中的调试都是用msPLC-Demo的,msPLC-Demo也经历了3个版本,逐渐完善成熟,通过对msPLC-Demo的完善,让我更清楚工业设计的需求,对PLC更加理解,比如我们自己建立系统,可以不需要标准PLC那么复杂,接口部分最后演化为共阳输入光耦隔离,中功率共阴输出直接驱动,可以直驱继电器类驱动器,简单、易用。
基于msPLC Demo,做了不少项目,比如400~1200度的红外激光测温仪的校准台,测温仪与高频感应加热设备的控制器,这两个都需要一套PID算法,于是就开发了一套面向对象的PID库,简单设置一下温度值就搞定了温度控制。机械自动化里面,经常需要控制步进电机,于是基于德国公司高性能的TMC262步进控制器,编写了步进电机驱动库。
  


msOS开发过程中,我就建了一个QQ群:291235815,并且在博客上发表了要做msOS的信息,把需求描述了一下,感兴趣的网友加入了进来,其中最主要的群体是原基于Mcu51的“实用单片机系统”MS3的客户群体,他们就是从MS3入门嵌入式的,所以他们一开始就对msOS产生了很强烈的兴趣,在他们的热情下,加速了msOS的推动。在开发完成msOS之后,庞大的MS3用户群体让我认识到需要把这个群体引导到msOS中,于是把MS3按msOS风格改写,为了区分两个系统,改写后的MS3,命名为msOS-Mcu51,而原来的msOS,命名为msOS-Stm32,加上之后的无OS精简版本msOS-Lite,形成了一个完整的系列。

群内网友积极参与,有些看了msOS代码之后,写了一些读后感,比如“独钓千古愁”写了不少关于“设备”、“抽象”、“面向对象”方面的文章。有些基于msOS系列开发产品后,共享了代码,也有些把msOS移植到msp430、AVR中,也共享了代码。我公司基于msOS开发的产品,部分的共享了代码,比如PID库,步进驱动库等。
为了进一步普及,想做一个低成本的msPLC,但最后发现黑白字库屏的成本下不来,考虑到很多网友都有通用的评估板,于是想着做一个PC机端的界面,虚拟LCD屏,代替真实的LCD屏,并且采用C#来开发,这样形成一套PC上位机的C#开发模版,支持串口等常用功能。因为我没有太多的精力,群内“Claude”恰好会一些C#,于是他负责了这个项目,通过短短几个星期,给出了一个模版,我做了优化,“南方的风”增加了ISP功能后,形成了一个比较不错的上位机C#模版,取名为msMenu,真正实现嵌入式与PC端统一开发的风格,感谢“途客”推荐C#开发环境“SharpDevelop”,非常好用。
  


msOS的开发成功,统一了我公司嵌入式设备的开发,主要的几个负责人,包括硬件负责人都在第一时间都掌握了编程技巧,通过代码相互检查,编码质量显著提高,有效的降低了公司的软件成本。并且硬件人员也可以轻松修改软件,对于相对不是很复杂的软件,硬件人员也可以参考模版完成。
我公司现在的人才招聘,现在都在群内招聘,而通过这样知根知底的招聘方式,有效的降低了公司经营的成本,人员稳定性也大。有一位在华为工作多年的软件高手“蓝色雪狐”看中msOS的价值,加盟我公司,今后由他来负责msOS的后续应用及推广,开发更多的电子设备和自动化设备,比如火花放电加工设备和俊知的机械自动化控制设备,目前都在开发之中,之后把这些应用中的经验融合到msOS中,写成书让更多的人受益。
msOS到今天算是告一段落,因为二代高频机的深入研究需要FPGA技术,自己设计精准控制芯片,所以最近我都在学习FPGA。幸运的是,刚开始学习,就获得我本科大学同学的支持,他是清华研究生,一直在某著名芯片设计公司工作,当我把我的这套思路跟他讲了之后,他非常认同,希望能在FPGA上建立一套通用的编程模版,暂时取名叫msFPGA吧,若开发完成后,争取实现开源共享。
msOS从一开始,就获得我公司众多高手的支持,后来广大网友参与进来,让msOS快速成长,目前QQ群已经接近1000人满员,这个速度还是基本上没有做规模推广下获得的,因为我想着主要靠口碑来传递。

裸奔的双任务系统-嵌入式微系统连载之十一

 2014年春节里的一天上午,msOS QQ群里面讨论的很激烈,“传说中的火枪手”认为msOS-Mcu51已经很好了,对于msOS-Stm32引入uC/OS-II意见比较大。虽然他也用过uC/OS-II,但并不喜欢,有一种不确定感,所以很喜欢裸奔。但他的观点受到群内不少RTOS用户网友的反驳,以“独钓千古愁”为代表,持续了将近半个多小时,我一直在旁围观。
通过这次辩论,双方都把各自的观点表达的非常清晰、明了。RTOS有它的优点,但确实存在很明显的缺点,这个无可争议,尤其是大家提出RTOS的几个问题:
1、 带RTOS系统,不便于移植,这个对msOS的跨硬件平台推广是很不利的。
2、 嵌入式工程师,往往有强烈的掌控欲,而理解透彻RTOS有一定的难度,有一种把握不住的感觉而拒绝RTOS。
3、 现实中绝大多数嵌入式项目,只要通过合理的设计,不需要RTOS。
4、 带RTOS的系统,在任务调度时进入临界态,这个时候无法响应中断,不适合应用于实时性要求高的地方。
以上四个原因,让我认识到需要推出一个msOS的无RTOS版本,但还要实现类似RTOS的双任务功能,把业务逻辑与菜单界面分离。在讨论中,“传说中的火枪手”有一句话提醒了我,他提到他自己有一套类似MS3(msOS的前身)的架构,他用了一个中断处理各种消息,而这个时候,我也在想着如何改造uC/OS-II的任务切换软中断,实现双任务,思路完全一致,他的话说明了这个方案是可行的。
后来的一段时间,忙着msOS文档,进一步完善msOS代码,一直没有着手写。直到三月份才开始按照软中断方式,粗粗的写了一个无OS的双任务版本msOS,实现了这个功能,但因为当时对Stm32的中断系统理解不深,所以放到群内让大家一起分析,寻找bug,这个时候,“独钓千古愁”对无OS版本msOS提出了很多建议,并且直接在这个版本基础上修改,完全的丢弃了汇编代码,移植非常容易,其中部分处理方式引用了他跟“传说中的火枪手”交流时提供的代码。我再在“独钓千古愁”提供的版本基础上进一步优化,统一风格,定型后在群内发布,当“传说中的火枪手”看到最终版本,就觉得有他的影子存在。
这个无OS版本的msOS,最终的命名是“Tomsu”起的,他参考软件的命名习惯,因为这是一个无OS简化版本,所以推荐为msOS-Lite版本,Lite的意思是“精简”。可以说,msOS-Lite版本,是整个msOS群大家参与讨论出来的,虽然有几个相对重要的网友积极推动,但其它网友的贡献是不能抹杀的,集体的力量是强大的。我对这个版本非常满意,因为它准确的反应了msOS理念:简单、易用。
无RTOS实现双任务,甚至多任务,本质上讲跟RTOS方法类似的,只是RTOS是把一个main大循环斩成多个具有优先级的小循环,消息触发决定在最高优先级的小循环中执行。因为是小循环,死循环,一直循环下去,所以必须要有触发进入条件和等待资源挂起条件,否则这个小循环一直执行下去,别的小循环就执行不了了。
无RTOS版本多任务,把中断看作是任务,因为中断优先级高于main大循环,建立一个最低优先级的,可以被消息触发的软中断来实现任务,但因为在中断中,所以建立的这个任务不是一个死循环,而是执行完毕就跳出的。而这一点恰好符合msOS的业务逻辑中的消息机制,执行完消息就退出的特点,不需要死循环等待在哪儿。
多个中断,就实现多个任务,尤其是systick系统节拍中断,有很多事务在系统节拍中例行处理,类似任务,虽然这个任务不是严格意义上的,功能不强大,但够我们用了,尤其是对msOS来说,只需要两个任务,恰好合适。
为了跟uC/OS-II兼容,选择了软中断PendSV,把优先级设置为最低。
            


其次,宏定义形式定义软中断PendSV的触发指令,只要EnterInterrupt这个宏指令被调用,就激活PendSV中断。这部分代码“独钓千古愁”和“传说中的火枪手”提供的,写的非常好,原来这部分在汇编中的,移到了C语言中,这样子msOS-Lite就没有了汇编代码。
           


再次,编写PendSV的中断响应代码,考虑到有多个消息源同时抛出消息,但响应中断只有一次,所以在中断响应中必须要把消息队列中的消息处理完,否则会导致后续的消息因为中断此前被占用而不会再次产生中断标记而无法重入的问题。这儿增加了CheckMessage函数来实现。

 

         
最后,改写抛出消息入队列函数,调用EnterInterrupt触发PendSV中断。

 

          
至此,无RTOS的msOS-Lite版本完成,非常感谢msOS QQ群的所有网友的支持与合作

精简uC/OS-II-嵌入式微系统连载之十

 uC/OS-II是最早进入国内的一款开源RTOS,因为代码开源,又有配套的书籍,加上不大的代码量,在嵌入式群体中最为流行。在写“实用单片机系统”第一版之后,就接触了uC/OS-II,虽然大致的明白其工作原理,但一直似懂非懂,尤其有太多的宏定义,严重的干扰了源码的阅读,加上RTOS带来太多的概念,而这些概念都没有实际用过,不知道如何应用,并且听说有很多陷阱,所以心里有些空,把握不住风险,一直都回避RTOS。高频机开发的后期,菜单界面编程的复杂性严重的干扰了业务逻辑,逼迫我设计msOS的时候,考虑把业务逻辑与菜单界面分离开,这必须要引入RTOS,而uC/OS-II因为广为人知又相对简单一些,所以选择了uC/OS-II。
这一次正式选用uC/OS-II,必须要深入理解透彻每一个细节,否则因为自己对uC/OS-II的理解不到位,尤其是任务之间的通讯等细节问题引起的缺陷可能让自己的项目失败,这是不可接受的,所以参考书籍仔细的阅读源码,然而一接触这个源码,就让我犯晕,uC/OS-II为了实现可配置、可裁减,运用了大量的宏定义,考虑到各种情况,这严重的干扰了我的阅读,同时也有很多网友向我反应类似的问题,因为要了解uC/OS-II的核心原理,却经常被很多没用的源码干扰,他们迫切需要一份简单、清晰的源码,于是我决定先弄出一份可以清晰阅读的源码来。
第一步,去掉了绝大部分跟内核无关的事件管理功能,比如信号量、互斥型信号量、事件标志组、消息邮箱、内存管理这几个功能,只保留了msOS今后需要用到的时间管理、消息队列功能,这样一来,几乎就剩下内核部分源码,阅读大大简化了,系统基本上没有什么宏定义了,下图为uC/OS-II的头文件,非常简单,只需要定义三个宏定义:任务数、事件数和消息队列数,对于msOS来说,默认就是2、1、1,模式化了,不需要改变。

 

        
第二步,进一步去掉用不上的功能函数,比如时间管理中只保留OSTimeDly函数,消息队列中只保留创建队列、发送消息、等待消息三个必须要用的函数。任务管理中只保留了普通的创建任务函数,其它的删除任务,挂起任务等都删除了,因为msOS中不可能用到删除任务,挂起任务这些函数,放着除了干扰我之外,没有别的作用。这样一来,基本上就没有多少宏定义了,代码较容易看懂了,下图为前两步精简后的uC/OS-II接口函数。

 

  
第三步,因为能够看懂代码,就越觉得msOS不需要uC/OS-II这么多复杂的功能,比如msOS一般来说只需要两个任务即可,uC/OS-II却支持64个任务,因为支持64个任务,需要一个8*8bit的就绪表,为了实现快速查找最高优先级任务,需要一个算法和一个256字节的查找表,虽然这些不是很复杂,但却把很多人搞的稀里糊涂的,比较绕,严重的干扰了内核的阅读理解,所以要降低任务,只需要支持8个即可,对于msOS来说,8个都已经太多了,完全满足了,而实际的项目,一般都是几个任务即可,不建议大家开太多的任务,这样严重影响效率,并且各个任务之间通讯,访问资源等都容易引起很多冲突,理解不准确会导致一系列问题。
下图为msOS双任务查找表,数组中只有4个数据。实际上因为msOS只有两个任务,MenuTask是永恒最低任务存在,只要LogicTask激活,就马上执行LogicTask,处理完后再退到MenuTask,所以只需要识别LogicTask即可,根本不需要算法中的查找表,只是为了保留与uC/OS-II统一,预留扩展8个任务,所以还保留了任务查找表风格。

 

       
第四步,因为只有8个任务,而uC/OS-II默认有两个内部任务:统计任务与空闲任务,所以需要去掉这两个任务,msOS中必须要有业务逻辑与菜单界面两个任务,优先级最低的任务是菜单界面,这样还有6个任务可以供额外使用,6个已经足够了,非特别情况下,不建议用。
第五步,uC/OS-II的任务块和事件块是采用链表结构的,可以动态增删,但这一点对于绝大部分项目来说,没有意义,尤其是对msOS来说,根本就不需要动态的,于是把链表结构改成数组结构,这样非常容易看懂,也节省资源。
第六步,按C#语言风格标准化,跟msOS统一编程风格。
通过以上六步操作操作之后,uC/OS-II非常简单明了,只有os.c、os.h和os_a.asm三个文件,os.c中只有寥寥15个函数,os_a.asm中只有4个汇编函数。考虑到扩展性,还是保留了uC/OS-II的一些影子,其实若再精简下去,可能就只剩下一个内核切换,msOS只需要两个任务即可,完全可以精简到跟uC/OS-II无关了。

面向对象的界面编程-嵌入式微系统连载之九

msOS采用128*64的字库黑白屏,降低硬件设计复杂度,这个屏一行可以显示8个汉字或者16个字母,总共4行。界面开发基于面向对象方式,把界面抽象为几个页面,而页面又由控件组成,以我做的高频机为例,抽象了5个页面,每个页面上都由背景文字、标签和文本组成。

 


上图为工作页面,由一张背景文字(BackText),两个文本(TextBox)和五个标签(Label)组成。背景文字用于显示固定的说明文字,比如频率、功率等,这些文字在这个页面下是固定不能变化的。标签控件用于显示变量值,可以是字符串也可以是数字。文本控件具有标签的功能外,还可以通过按键等修改变量值。以上三种控件构成了最常用的页面。
接触过PC机编程的,对于控件概念是非常容易接受的,若没有接触过,可以通过msOS配套的硬件开发平台msPLC Demo了解界面编程,这样会相对容易很多,此外也可以接触一下C#编程,从跟msOS配套的msMenu源码入手,这个msMenu源码比较简单,在PC机上用C#虚拟了128*64的黑白字库屏。有了msMenu,各类STM32F103的开发板都可以运行msOS,通过串口跟PC机通讯,在msMenu上显示,不需要硬件LCD屏,方便大家学习。msMenu的两个主显示区,就是由两个大的TextBox组成。

 

下图为msMenu

 


   
界面编程基于面向对象设计,首先要建立页面及各个控件的对象类型。下图为页面的对象类型。

 

 

BackTextPointer:背景文字控件加载点,是一个16*4字节的数组,作为页面背景。
ChartPointer:图表控件加载点,高频机项目中用到,是一个特殊控件,一般项目用不到。
LabelPointer:标签控件加载点,一个Form可以支持多个Label,需要支持多控件级联。
TextBoxPointer:文本控件加载点,一个Form可以支持多个TextBox,需要支持多控件级联。
FocusTextBoxDataPointer:文本控件操作选择,设定焦点用于选择那个文本控件用于输入。

 

                    

                             
设计程序的时候,第一步要建立页面Form,如下图:                                           

                                       

第二步,定义各种控件和资源。

 

                                                      
第三步,各个页面加载控件,初始化控件参数,关联业务逻辑数据库中的数据。
                      

 

 

第四步,执行MenuTask解析当前页面

 

                                    
msOS采用的菜单界面,非常简单,是一个架构性的菜单界面,很适合用户看懂之后,自己修改、增删,也可以应用于彩屏。菜单界面的细节部分,请参考源码。

系统节拍与软件定时器-嵌入式微系统连载之八

系统节拍是非常重要的一个设备,在早期的MCU51和ARM7芯片中,没有专门的系统节拍,往往由一路硬件定时器来实现其功能,到了Cortex系列,ARM提供了Systick硬件定时器专门用于系统节拍,可见现在的编程对系统节拍的依赖性。
系统节拍的概念比较早的出现在OS中,产生固定间隔的重复中断,用于任务的超时等待或者任务延时多少个节拍周期用。在前后台系统中,也引入了系统节拍,比如msOS-Mcu51版本很早就引入了系统节拍,实现按键扫描、虚拟定时器等功能。此外还有一种基于时间片编程的架构,直接采用系统节拍来处理一些对时间精度要求比较高的需求。常用时间间隔是5mS或者10mS,也就是说每秒钟200次或者100次节拍。随着处理器速度的提高,尤其是在一些控制要求高的地方,可以采用1mS,msOS甚至采用了0.1mS的高频率系统节拍。这个具体的时间间隔跟项目需求有关,可以根据自己的项目灵活配置,但在满足需求的情况下,不建议设置的太高,否则影响处理器效率。
msOS中的很多设备,都需要基于系统节拍来运行,比如按键、软件定时器。这些设备不需要太高的系统时钟,按键只需要每秒钟100次,软件定时器需要每秒钟1000次节拍,但msOS因为面向时间精度要求比较高的行业,所以默认的系统节拍时间间隔设置的很短,达到0.1ms,也就是说一秒钟一万次节拍。所以需要对系统节拍进行分频处理,但因为系统节拍真实的间隔是0.1ms,以STM32工作在72MHz为例,这么短的时间最多只能执行7200个CPU指令,为了防止各个低速设备都挤在同一个系统节拍内运行,超过7200个指令,所以在分频的时候,不要把所有的设备都放在同一个节拍里面,需要把各个设备分散到各个节拍中运行。
Device_systick.c是系统节拍设备,它除了分散节拍直接调用固定的运行在其上的设备外,还提供了注册机制给上层应用程序提供系统节拍,具体如下:

 

 

定时器是一个项目中经常用到的设备,比如动画设计、闹钟、定时工作、超时处理等。然而一个处理器往往自带的硬件定时器是非常有限的,并且功能也比较有限,不能灵活应用,所以需要通过系统节拍虚拟出多路软件定时器。
软件定时器主要定时器服务、启动、停止三个函数组成,默认支持8个软件定时器,以下面代码为例加以说明。

 


FunctionCallback为定时器超时后的回调执行函数,是软件定时器初始化的第三个参数,超时时间到就执行这个函数,第二个参数1000为超时的时间,单位是软件定时器的系统时钟,msOS采用的是1mS,1000也就是一秒钟。第一个参数为回调函数执行的位置,TimerMessageHandle表示在业务逻辑中执行,适合处理代码量大的,处理时间长的函数。TimerSystickHandle表示在系统节拍中直接处理,系统节拍内一般适合代码量少的,处理时间短的函数。软件定时器功能非常有用,它的结构也非常简单,用户看懂之后可以任意扩展其功能完成自己特殊的用途。

注册机制与消息机制-嵌入式微系统连载之七

注册机制在嵌入式编程中很少提到,但回调函数大家经常接触,以软件定时器为例:

 


软件定时器设备通过函数Start把FunctionCallback作为参数传入定时器设备中保存,之后开启定时器,延时1000ms之后,定时器就会直接调用FunctionCallback,所以这个FunctionCallback就叫做回调函数。类似处理器的中断一样。
注册机制类似回调函数性质,只是把概念扩展到变量,上层应用通过注册函数地址或者变量地址到设备中保存,当设备中这个地址对应的函数被激活或者变量值有改变,就调用这个函数或者更改这个变量值,以此达到底层对上层的信息传递。
以ADC设备为例,我们看一下注册机制的应用。首先在ADC设备device_adc.c中定义一个用于注册变量地址的指针数组RegisterPointerBlock,初始值必须要指向一个空变量,否则若指向了一些有用的地址而导致这个地址对应的数据改变,会引起不可预测问题。

 

在设备中引入注册机制,应用层只需要把变量的地址注册到设备中,设备自己可以通过这个地址修改数据,这样解放了应用层,让应用层不需要关心如何获取数据。但是,注册机制也存在一些缺陷,那就是操作的数据,必须要原子操作,否则会导致两个应用层与设备同时修改一个数据的异常存在,出现数据错误,这个是需要避免的。

相对注册机制,消息机制是大家所熟悉的一种底层向上层传递的方式,msOS采用uC/OS-II,标配为业务逻辑与菜单界面两个任务,业务逻辑为高优先级,支持消息队列,设备发送的消息,都在业务逻辑中处理。
   

 
需要注意的是,msOS是采用的是uC/OS-II,它属于RTOS,业务逻辑与菜单界面虽然是两个独立的任务,但并不是同时运行的,一个时刻只能运行一个任务,业务逻辑的任务优先级高于菜单界面,所以在没有消息的时候,业务逻辑任务(LogicTask)在PendMessageQueue函数中挂起等待消息到来,这个时候退到菜单界面任务(MenuTask)执行,一旦按键设备检测到按键并通过PostMessageToLogicTask发送消息,就会激活业务逻辑任务,让业务逻辑任务抢占菜单界面任务运行。按键设备的运行是基于系统节拍的,它是在中断中运行,优先级比任务高,不会被任务抢占。

面向设备编程-嵌入式微系统连载之六

System结构体封装了整个系统层,让App很容易基于System跨平台,那么System内部该如何组织?

 

 

ARM公司推荐嵌入式开发遵循CMSIS架构,用户应用程序可以调用实时内核(OS)、中间件等,也可以直接调用底层硬件基于CMSIS标准的函数接口,比如ST公司发布的STM32的硬件驱动LIB库,甚至直接访问最底层的寄存器。这种架构编程比较灵活,对于规模不大的嵌入式系统比较适合,但这样的一个架构分层还比较模糊,应用层几乎可以访问所有的系统层资源,比较任意。各种底层接口没有封装,规模一大很容易引起重名、相互调用,若更换处理器芯片,整个系统层都要重新设计,不便于移植。为了解决这些问题,需要引入新的概念:设备,基于设备把底层的硬件驱动函数按功能封装起来,这样可以把系统层内部理清、分层次、模块化、可移植。

 

 

设备这个词,我们是经常接触的,我们知道PC机的组成,就是由中央处理器、内存、硬盘、主板、电源、显示器等几样组成,这是物理上讲的,若从Windows操作系统角度看,我们可以看Windows的设备管理器。一台PC机在Windows XP的眼中由DVD驱动器、IDE控制器、处理器、磁盘驱动器、电池等等组成,每一个设备里面又细分为很多小设备。所以设备是Windows XP管理的基本对象。

 

 

在VS.Net架构的C#编程角度看,系统层System由各种功能子类组成,子类下面又分子类。应用层开发程序,就是面向System各种类开发即可,所以类是C#编程的基础单元。

无论设备还是类,它们都是表征一组具有相对完整功能的集合。比如一个人,由头、四肢、五脏六腑组成,这些部件都是相对完整的功能集,而头呢,又由大脑、眼睛、鼻子、耳朵等组成,它们的功能也是相对完整的。相对于类这个概念来说,嵌入式人员更容易接受设备这个名词,因为有很多实际的对应物存在,比如LCD、按键、存储器、串口、ADC、定时器、IO等等,都是具体的一个设备,它们有非常明确的功能定义,基本上各个项目都需要这些设备组合起来完成,只是不同的项目可能涉及的设备种类不同罢了,但基本上脱离不了这么几种。所以我们可以认为一个系统层System就是由不同的设备组成的,系统层内的中间件也是由设备来支持的,而设备则需要编写设备驱动代码,需要调用各种硬件驱动接口函数来实现,比如ST公司发布的STM32硬件驱动库来完成。

 

   
中低端嵌入式编程,因为资源比较紧张,为了节省资源,代码编写比较紧凑,所以长期以来应用层与系统底层是分不清的,这就导致经常一个人既要懂上层应用,又要懂底层驱动,对开发者的要求较高,这也增加了项目的开发难度。而现在高性能嵌入式芯片的出现,可以不再被资源约束,所以引入设备概念,让嵌入式编程真正意义上解决了一人打天下的尴尬局面,对于应用层开发人员来说,他们虽然不懂底层硬件驱动,但可以很容易理解设备这个功能接口,轻松完成项目需求,而对于底层驱动人员来说,一个个明确独立的设备,更有助于自己清晰的编写底层驱动,适合多人协助开发。所以msOS,可以认为是面向设备开发的架构。
每一个设备,都有一个对应的结构体封装,因为中低端嵌入式系统规模不大,为了方便应用查看,所以直接封装在System的Device里面。

 

 

只有在System的Device中的基础设备才可以被应用层访问、调用,而设备内的其它函数,都需要定义为Static类型,防止被外部调用。
一个设备驱动的代码编写一般的讲要分为五部分,以LCD设备为例:
1、 建立一个按键设备文件device_lcd.c。
2、 在device_lcd.c中编写lcd设备驱动代码,除了外部接口函数之外,其它的都需要定义为Static类型的静态函数,防止被外部函数调用。
3、 在SystemStruct的Device中添加Lcd设备结构体,内部包含接口函数指针。
4、 在Lcd设备初始化函数InitializeLcd中把Lcd设备的接口函数指针与Lcd设备的接口函数关联起来:System.Device.Lcd.DisplayString = LcdDisplayString;
5、 有些需要基于系统节拍运行的设备,比如按键、定时器设备,需要由系统节拍提供系统节拍服务程序:KeySystick100Service();TimerSystick1000Service();
引入设备概念,把系统层分为一个个相对独立的模块,内部的函数都通过Static封装了,只是保留了设备接口与外界联系,这样大大简化了系统的复杂度,调理更加清晰,可以支撑项目做的更大更强。封装之后的设备功能相对完整,独立性强,那么它是如何运转的呢?必须要有一套支撑设备运行的规范。
1、 设备一般分为两类,一类不需要系统节拍支撑它运行的,比如Lcd、IO等设备,它们由上层函数直接调用接口即可。还有一类需要由系统节拍设备支撑其运行的,比如按键和定时器设备,按键是基于每秒100次的按键扫描获取按键值,定时器是基于每秒1000次的系统节拍实现多路虚拟定时器。所以这一类设备,需要由系统节拍设备提供支持,它们的工作基于系统节拍之上运行,系统节拍设备是它们运行的基础。device_systick.c是系统节拍设备,它是相对比较特殊的基础设备。
2、 调用方式分为三种:上层与下层可以直接调用,下层对上层提供了两种方式,一是利用OS的消息机制实现信息上传,二是采用注册机制,实现类似处理器中断的方式,给上层提供信息。

软件架构-嵌入式微系统连载之五

嵌入式微系统架构是C#与CMSIS架构的复合体,如下图所示:

 


 

基于C#标准,分为应用层App与系统层System两层。采用两个大结构体AppStruct和SystemStruct把它们各自封装起来。

 


Appstruct里封装了两个结构体:Data和Menu。两者都是应用层的数据类型。
Data是应用层业务逻辑所涉及的传感器、驱动器等全局变量、过程变量、存储参数、设置参数等数据,Data结构体类似数据库概念,只是嵌入式系统数据一般不大,用一个结构体实现即可。
Menu是应用层需要的各种菜单显示页面,一个页面下可以加载多种控件,比如背景文字(BackText)、表(Chart)、标签(Label)和文本(TextBox)四种。目前版本msOS因为主要针对工控,支持本地支持黑白字库屏,所以只需要这四种控件即可,菜单结构相对简单,今后需要支持黑白点阵屏甚至是彩屏,需要支持的控件就更多了。

 

AppStruct可以认为是应用层App的数据库,里面包含了业务逻辑的数据库和菜单界面显示的各个页面。基于这种结构体封装的好处在于把全局变量统一到数据库中管理,尤其是业务逻辑中的全局变量都放到Data区中统一管理,这充分的利用了全局变量的使用便利性,又有效的避免全局变量满天飞,解决了长期以来引起极大争议的全局变量问题。

应用层分为三块,除了数据库意思的AppStruct外,还有业务逻辑(Logic)及菜单界面(Menu),两者分别跟工业自动化控制系统的可编程控制器(PLC)和组态屏(HMI)对应。在工业自动化中,PLC内部有业务逻辑参数存储单元,HMI内部有菜单页面存储单元,现在等价于把PLC和HMI的存储单元合并在AppStruct中来管理。
业务逻辑(Logic)响应各种传感器过来的信息,通过给定的算法或操作流程,及时指挥驱动器工作,所以需要设定在较高优先级的任务中。菜单界面(Menu)显示业务逻辑处理过程中需要显示的各种参数,只需要满足眼睛的刷新频率即可,所以刷新速度相对较慢,一般放在最低优先级任务中处理。需要注意的是,菜单界面不包括按键部分,按键产生按键消息,归入业务逻辑中,菜单界面只是显示各种参数,而不会去修改参数,这个保证了数据库只被业务逻辑一个任务修改,避免了一个数据同时被多个任务修改产生错误的问题。

 


SystemStruct封装了整个底层系统,抽象成一个面向应用的接口芯片。
Initialize为初始化函数,在系统应用前,必须要先初始化。
Device结构体中封装了各类设备,比如LCD、ADC、USART、IO、Timer等。
OS中封装了uC/OS-II的各种常用接口函数。
Gui中封装了界面处理接口。
AppStruct和SystemStruct都是一个自定义的结构体类型,需要用这两个类型定义一个对象。

 


定义应用层结构体。
定义系统层结构体。需要注意的是,因为采用结构体模拟类功能,System在定义的时候就指定了System的Initialize初始化函数,这样系统开始就可以使用System.Initialize功能,便于形式上的完美。
一个System结构体,封装了整个系统层,中间件接口及设备接口,都在System中体现,等价于一个面向于应用层的处理器,App运行于系统层之上,App只需要调用System中的接口即可,简单、易用,这样设计做到了跟实际的芯片无关性,比较容易跨平台,当需要更换芯片的时候,只需要保证System接口一致,应用层无需修改代码。

嵌入式微系统msOS的诞生-嵌入式微系统连载之四

为了解决多人协作,多种需求产品的开发,并且还要长期维护,必须要把这些产品的共性提取出来。
1、 不需要低功耗设计。
2、 传感器类和驱动器类属于单一功能的设备,传统前后台架构的MS3即可。
3、 电源类及控制类设备都属于功能复杂的,实时性要求高,带有屏幕显示,外扩多路传感器或者驱动器的设备,这两类可以统一为一类,是设计的重点,需要建立全新的平台。
那么这个新平台应该做成什么样子,脑子里还是没有概念的,只是知道在高频机设计中,传统的状态机或者函数指针方式的菜单界面编程方式是要改进了。我虽然在手机公司做过近两年的手机驱动开发,但此后一直做硬件方面的工作,后来创业经营公司,所以嵌入式软件水平一直停留在MS3这个层次没有提高,对于这个新平台的设计,首先需要向软件专家请教,尤其是过去这么多年,软件开发也有极大的变化。

 

幸好我公司的底子是做手机的,主流是MTK手机方案,此外基于Wince做了一款工业手持机,所以有各类专业软件人员,对C、JAVA、C#都非常熟悉。在众多软件人员中,特别要感谢的是苏鹏,他擅长Linux,之后是负责MTK手机开发平台的维护,也需要开发JAVA应用,关键那个时候他也恰好参与了基于MS3的红外温度传感器的开发,所以对嵌入式有一定的概念,对大型软件编程又很擅长,所以当我提出我的需求的时候,他很清楚我想要干什么。
苏鹏认为MS3是一个很好的东西,简单、易用,不能轻易抛弃,所以第一步在MS3上重构,引入当前主流的面向对象菜单界面编程思想,这个重构花了将近一个月,因为MS3是前后台的,只有一个大循环,基于消息机制,很多事件都是在大循环中处理,菜单界面放在大循环中解析的时候,因为菜单界面显示属于是低速事件,会严重影响高速的事件,让MS3中的消息机制失效,所以无法完美的实现面向对象菜单界面编程,只是形式上的实现了一些功能,没有实际使用这个代码,但这也为后来的真正实现面向对象菜单界面编程打下了基础,并且也认识到MS3这种只有一个大循环的架构无法实现真正的面向对象菜单界面编程,必须要引入抢占式多任务操作系统,把菜单界面放在最低优先级的任务中,其他的消息事件处理(消息事件处理,也叫业务逻辑处理,后续用业务逻辑表述)放在一个高的优先级中,这样最少需要两个任务,所以接下来的事情就是选择RTOS,并且深入理解它。

 

首先考虑的是uC/OS-II,因为它的资料最多,用户群体广泛,并且之前也接触过一点,虽然没有深入,但感情上首选它。此外有同事推荐了FreeRTOS和国内的RT-Thread,FreeRTOS跟uC/OS-II类似,但知名度太低,资料及客户群体都很少,虽然它是免费的,还是放弃了。RT-Thread编程风格是Linux的,我不喜欢Linux风格,感觉不好看,不够优雅,其次知名度也远不如uC/OS-II,并且可靠性、稳定性如何也值得怀疑,它带的GUI适合彩屏,相对复杂,也不适合黑白屏的工业场合使用,所以也放弃了。
选定uC/OS-II后,必须要深入理解它的每一个细节,首先碰到的就是uC/OS-II有太多的宏定义,因为要可裁剪、可配置,但实际上有大量的功能是用不到的,所以我就从精简入手,把在新平台中可能用到的函数保留下来,其它的一律去掉,这样就没有了烦人的宏定义,有很多网友也抱怨这些宏定义,严重的干扰了uC/OS-II的代码阅读。
通过精简uC/OS-II,深刻掌握了它的原理,并且这个时候新平台的需求也越来越清晰,绝大部分的需求只要两个任务即可,一个为菜单界面任务,一个为业务逻辑任务,根本不需要64个任务,所以对uC/OS-II大做修改重构,去掉了空闲任务和统计任务,把菜单界面解析安排在最低优先级上,业务逻辑放在高优先级上,这样只需要两个任务即可。

为了考虑今后任务的扩展性,还是保留了任务表,只是精简为支持8个任务。为了降低OS的内存占用,进一步精简OS内核,把原来基于链表结构的任务块改成数组结构。这样一个非常简单的uC/OS-II就出炉了,仅仅两个文件即可。
精简、重构后的内核只是保留了uC/OS-II的任务切换功能而已,而所有的RTOS都有这个功能,并且都是类似的,所以已经脱离了uC/OS-II,只是这个内核开始源自uC/OS-II,风格一样,所以还保留其名,但本质上已经不属于uC/OS-II了,所以也不存在版权问题,若想进一步避开,也可以参考其它的RTOS精简或者直接用其它的RTOS。若只需要用两个任务,新平台还提供了一种软中断的方式实现双任务,完全不需要RTOS。
引入RTOS实现了多任务之后,新平台的架构有些浮现,接下来要做的是确定软件架构,而这个必须要从现代成熟的软件架构中寻找。在跟同事交流后,他们都提到了面向对象设计,但这个需要采用C++,而嵌入式中C++却不流行,我也不会,所以不可能选择C++。后来想到用常规的C语言写成类似C++的面向对象风格,但参考了网上的代码之后,觉得把C语言弄的更复杂了,很别扭,变成了四不像。之后又了解了JAVA,但感觉这个风格也不是很适合,直到有一位负责C#的同事侯德平,他建议采用C#,并且给我演示C#的好处的时候,让我眼睛一亮,这就是我想要的东西。

 

  

 

1、 优雅的编程风格,简单易用的长命名命名规范很容易被开发者接受,抛弃复杂的匈牙利命名法。
2、 System这个命名空间概念,可以很好的封装整个系统层,把应用层独立出来,这样可以提高代码的复用性和稳定性。
3、 C#是面向对象,但比C++简单很多,完全可以利用C语言中的结构体模拟命名空间和类,把C语言写成C#风格。
4、 微软的编程环境,特别适合工业行业,无论PC机还是WINCE嵌入式设备,C#都可以通用。这样嵌入式端用新平台开发之后,本身就是C#风格的,很容易掌握PC端的C#编程。
到了这儿,整个框架基本成型,但是系统层如何进一步细分呢?这时苏鹏建议参考ARM的CMSIS标准,如下图:

 


为了提高可移植性,在硬件驱动层与OS、GUI等中间件层引入了设备层,至此整个软件架构的框架基本建立完成,如下图: