面向硬件系统设计的工程组织方式

中山野鬼 发布于 2016/02/08 16:50
阅读 337
收藏 0

(哈,很久没上osc灌水了,这里针对近几个月开发过程中的一个问题,面向硬件系统设计的工程组织方式,进行讨论。本帖所讨论的内容,并不针对单主控处理器之上的纯软应用系统开发,而是针对多样性主控处理器和多样性外部硬件模块子系统的嵌入式、自控系统开发,例如最近所设计的小系统,包括了stm32和atemga328两个系列的mcu,也包括诸如nrf24l01,mpu6050,spi_flash,等等外部模块的设计,同时这些外部模块子系统需要同时能针对stm32f103系列,atmega328p系列的mcu进行控制/驱使。开源的讨论,不仅仅针对纯软应用代码,也包括设计思想和方法的开源,哈,所以有次一喷)

面向硬件的系统设计,并不是新造名词。可以看作是面向模块的设计方法的一种具体展开。和面向对象设计方法并不对立,当然有很大区别,更多是与面向主控处理器的设计方式对立。这里首先举例讨论一下面向硬件设计中的逻辑分层。

我们假设存在一个4位的七段数码管。它有12个脚,其中8个是七段数码管外加小数点,而另外4个是共阳或共阴的位选引脚。假设是共阴的,当7段数码管的引脚拉高,而某个位选引脚拉低,则某位的七段数码管(包括小数点)显示对应的数据。
从系统实现上,我们包含几个层面的内容:
1、应用层面,我们希望给入一个数值,按照10进制或16进制,以特定定点数的方式,显示内容。
2、驱动层面,当确定每位数码管显示的内容后,我们以此驱动不同数码管实现现实。
上述两个层面中,应用层面非常好和具体硬件进行隔离。而驱动层面的设计则融合了两方面的硬件特性:
1、mcu对gpio的控制方式
2、具体显示模块,此处是七段数码管,对显示一个具体数字的方式。
针对驱动层面,很多情况下,我们将mcu/cpu看作主硬件系统,而其他硬件看作外设,也即,如果写论文,则是基于xx平台下的某某硬件模块的驱动实现。
再简单的说,基于传统的操作系统之上的系统设计,就是先确定好一个mcu/cpu,将外部硬件系统看作一个外设,随后各种应用通过操作系统,驱动这些外设,你无需了解外设之间的差异性。此可称作面向主控处理器的设计方式。
这种设计方式,可能会导致同样的外部硬件模块,面向不同的主控处理器,存在不同版本的驱动逻辑代码,而其中最值得关注和需要进行完整测试的控制驱动逻辑又是由外部硬件模块决定且不变的。
当你获取到一个外部模块的测试资料,如果是基于面向主控处理器的设计思维,通常会首先存在不同处理器的子目录,在每个子目录下,存在对应模块的驱动工程,而主要的驱动逻辑代码,每个目录下均有一份,且绝大多数内容相同(毕竟外部模块的驱动逻辑并不随主控处理器的改变而改变)。


从另一种系统分析的角度来看,任何存在独立运行机理的硬件模块,均可看作一个整体系统中的独立子系统,其可被不同主控系统控制/驱使。此时,所谓“驱动”的设计,则并不完全基于某个特定的mcu/cpu。客观的说,外部硬件模块,具有自身独立不变的控制/驱使方式。无论mcu怎么变,51系列,avr系列,或者stm32系列,不会改变4位7段数码管的控制驱使逻辑,他们的差异来自于mcu自身对gpio的驱动方式以及mcu内部定时器(时序)上的控制方式。由此我们可以给出一种系统设计方式:
针对特定硬件模块,将其特有的控制/驱使逻辑,抽象独立化实现。其所依赖的主控处理器的具体操作,由各类具体主控处理器的处理逻辑实现。
这种方式则称为面向硬件的系统设计。其与传统设计思想的最大区别在于,虽然具体的控制/驱使逻辑,需要针对具体的主控处理器mcu/cpu来构造,但将外部硬件模块看作独立子系统,从而将该独立子系统特有的逻辑内容抽象出来进行独立组织。也即,我们将驱动层细分为两个层面:
1、驱动逻辑层,此由外部硬件模块决定,具有不以具体主控处理器改变的特性。
2、驱动执行层,此由具体主控处理器决定,其内部不包含任何外部硬件模块的特性。
此时,存在两种系统工程组织方式。其一和传统思维反过来,具体主控处理器变成了更低层的内容,它服务于各类外部硬件模块。其二,无论是外部硬件模块还是主控处理器,均是硬件模块。而本文的观点,则是采用第二种方式。
上述两种方式在工程文件组织上存在较大区别,其中第一种方式,可以参考目前流行的开源系统arduino,其有如下工程文件组织形式:
/java
    /example
    /hardwares
        /arduino
            /avr
               /cores
               /libraries
    /libraries
        /Servos
        /Stepper




上述组织目录中,通常的实例,应用系统存储在example中,外部硬件模块存储在/libraries中,而与主控系统相关的内容在hardwares/arduino下。如果arduino可以面向stm32系列mcu实现,我想,延续上述思路,应该有如下目录

/java
    /example
    /hardwares
        /arduino
            /avr
               /cores
               /libraries
            /stm32
               /cores
               /libraries
    /libraries
        /Servos
        /Stepper
但此时就存在一个问题,处于/avr/cores/arduino目录下的Arduino.h这个头文件(几乎各个上层模块都需要引用它),就会在stm32/cores下多出一个版本。
而对于本文的观点则有如下组织形式
/apps   //存储具体应用系统

/hardwares
	/ext_devices //此处存储各种外部设备的驱动代码 
		/mpu6050
		/nrf24l01
		/spi_flash
	/virual_mcu
		/memcpy
		/gpio
		/spi
		/i2c
		/uart
		/interrupt
		/timer
		/hw_cfg
   	/mcu
   		/atmega328x
   		/stm32f103
   		/lib  //存放各种库
   		/include //存放库对应头文件
   	/lib  //存放各种库
   	/include //存放库对应头文件




此处,将mcu作为一种特定类别的硬件设备与其他硬件设备平级存储。而其下存在/atmega328x;/stm32f103两个子目录。每个目录为一个独立的工程,用于生成特定的主控处理器内核库,例如atmega328pcores.h stm32f103cores.h,atemga328pcoreslib.a ,stm32f103coreslib.a。这些头文件,库文件,存储在/mcu/include ; /mcu/lib中。
不同的外部硬件模块的控制/驱使模块对应的工程则存储在ext_devices下,例如mpu6050,nrf24l01,spi_flash等子目录。
毕竟每种外部硬件模块总需要通过特定的主控处理器来实现控制与驱使,而不同主控处理器对具体的控制/驱使的执行逻辑存在差异,由此就通过virual_mcu这个目录下的工程进行关联。virual_mcu是一个针对虚拟/抽象的mcu所构建的工程,它不针对任何具体的主控处理器,但包含了具体的,主控处理器所必须提供的硬件资源。如果我们把各个外部硬件模块子系统看作一个个独立的应用系统,那么virual_mcu此时则可看作一个操作系统,用于隔离外部硬件模块的驱动逻辑和驱动执行。
上述文件目录组织方式与开源系统arduino存在明显的差异,这两种工程组织方式,并不存在绝对的好与坏。而本文采用第二种文件目录组织方式,则是面向如下的开发背景:
针对多样性的主控处理器及外部硬件模块进行分模块设计,并联合成整体系统。
由于我们即需要针对诸如mpu6050,nrf24l01,甚至简单的4位7段数码管或者按钮进行独立模块设计,也需要考虑将不同的独立模块能够通过不同的主控处理器方案进行控制/驱使,所以对于/nrf24l01,/STM32f103不存在谁先谁后的设计。他们均被看做硬件模块子系统,无非归成两大类,外部模块和主控处理模块。这些模块最终是通过virual_mcu这个目录下的某个工程进行资源的关联与对接。由此实现尽可能的降低已设计完成的工作内容,并最大化的复用。


工程组织方式最终是为了工程开发服务的。如果你仅仅是个开源爱好者,针对arudino系统及其硬件进行应用级别的开发,当然使用arduino的系统最为方便。但如果你是一个嵌入式系统开发团队,会持续的针对各类主控处理器及外部硬件模块进行多样性,差异化的系统开发,那么你不得不基于整体系统设计目标来考虑具体设计细节。两者最大的区别是,针对arudino系统及其硬件,你不需要考虑硬件资源的重组织,而嵌入式系统,你是直接面向mcu和其他外部硬件模块甚至外部硬件芯片,为了获取更好的系统设计方案,你有时必须重新组织主控处理器的硬件资源。一个极端的例子是,电路设计工程师希望你改用另一组gpio口或者将spi1调整为spi3,这样方便他布线,这种情况针对基于arduino系统的非专业开发几乎不存在(那些堆叠的外部模块扩展板通常已经将硬件资源固化,你所要做的仅仅是调用),而对于专业的嵌入式系统开发团队,则完全有必要考虑是否怎么优化组合mcu的各种接口,中断、存储资源并与外部各类模块子系统进行对接。那么采用第二种文件目录的组织方式,你所需要改动的内容是在/virual_mcu下,而不是在/ext_devices 或 /mcu下。而如果整体系统需要考虑引入freescale的K60系列主控处理器,则你需要扩展的内容是在/mcu和/virual_mcu下,而对应的nrf24l01,spi_flash等处于/ext_devices下的内容则不应当改变。这种面对不同硬件对象的工程文档组织方式,便是一种面向硬件设计思想的具体方式。
最后,讨论一下virtual_mcu下的一个小目录,hw_cfg。
这个目录下,将存在很多头文件。你可以看作它各种具体应用工程的配置表。例如,现在我需要在基于atemga328p的系统上实现一个4位七段数码管进行显示的应用。这需要12个gpio口。而我的同事需要在基于atemga328p的系统上实现一个4行选,5列选的20个按键接收系统。两个应用系统对引脚的占用情况不同,后者需要存在5个输入口,而前者均是输出口。如果将两个应用系统对主控处理器gpio硬件资源的配置放置在一个文件内,最终会因为该文件要支撑的应用系统过多而变得杂乱无章。因此,针对每个具体的应用系统,可以独立在hw_cfg目录下形成一个个资源映射目录和同名文件,这些同名头文件被/virual_mcu其他工程目录的头文件引用,在重编译时,通过改变-I的路径来实现引用文件路径重定向,从而改变编译过程中对各个主处理器硬件资源的重新调整。
加载中
0
翟志军
翟志军

这个主题让我想起:现代 JavaScript 开发硬件 https://ruff.io/zh-cn/docs/getting-started.html


返回顶部
顶部