嵌入式软件架构的六个步骤(一)抽象层

嵌入式软件架构设计:硬件抽象层实践 硬件抽象层(HAL)是嵌入式系统架构设计的核心技术,通过建立统一的硬件操作接口,实现应用逻辑与硬件驱动的解耦。其核心原理遵循依赖倒置原则(DIP),使高层模块不再直接依赖具体硬件实现。这种架构显著提升代码的可移植性和可测试性,支持并行开发和早期验证。在Modbus协议等工业通信场景中,通过抽象UART等硬件接口,可快速适配不同MCU平台。结合Mock对象和持续集成(CI/CD),嵌入式软件也能实现高效的自动化测试流程。合理的抽象层设计已成为提升嵌入式产品可维护性和降低开发成本的关键实践。 阅读详情
AI摘要

本文围绕嵌入式软件架构展开,指出软件架构由产品和业务需求决定。分析了耦合架构将硬件与功能代码耦合带来的移植难、测试难、不易扩展等问题,介绍了隔离架构通过引入抽象层解决这些问题,强调剥离硬件相关代码是嵌入式软件架构关键的第一步。

软件架构这东西,众说纷纭,各有观点。什么是软件架构,我们能在网上找到无数种定义。比如,我们可以这样定义:软件架构是软件系统的基本结构,体现在其组件、组件之间的关系、组件设计与演进的规则,以及体现这些规则的基础设施。怎么定义一般来说,基本上不重要,我们不是在写学术书籍,工程人员嘛,只关心软件架构能解决什么问题。

软件架构不是制定出来的,而是产品和业务需求所决定的,架构师所做的,只是忠于需求,并合理的表达了需求。软件架构也从来都不是一成不变的。在产品或者产品线的整个生命周期中,随着业务和需求的变化,软件架构不断发展和变化,以适应新的需要。软件架构,也不是一个简单的项目问题,而是产品或产品线的技术战略问题。一个良好设计并推广的软件架构,能带来如下好处。

  • 提升软件质量
  • 最大限度地减少不必要的返工
  • 使嵌入式软件在宏观层面建立规划
  • 增强复用性,降低开发成本
  • 便于团队内部的技术培训
  • 使技术积累更加容易

我经常看到的一个常见问题是,新手工程师,由于经历与知识不足,往往看不到项目全貌,很难深刻理解软件架构,他们往往要经过多年的专业训练,才能逐渐建立架构意识。但软件架构真的只是资深工程师和架构师的专利吗?这个也不见得。古人作文,讲究立意为先。今天工程师做项目和产品,也应该先立意。这个意,就是指要有高度。工程师入门能从软件架构的高度出发,看待软件问题,相信对软件的理解,会更加深刻一些。因此,我总结了软件架构的六个步骤,供嵌入式工程师参考。

  • 隔离硬件相关代码,建立抽象层
  • 建立统一的软件基础设施
  • 妥善识别和处理产品数据
  • 功能分层与分解
  • 组件及其接口设计
  • 测试、调试与跨平台开发的支持

需要注意的是,看完这六篇文章,并不足以保证嵌入式工程师学会软件架构。事实上,嵌入式软件架构师是不可培养的。但至少,嵌入式工程师们,可以了解到什么是正确的努力方向,很多时候,选择比努力更加重要。因此,在未来的几篇文章中,我们会一起探讨一下设计嵌入式软件架构,可以采取的六个步骤。

嵌入式软件架构之一 抽象层与硬件隔离

许多新手乃至老手嵌入式工程师,在未了解软件架构之前,把应用层功能和硬件相关的代码,不由自主的搅和在一起写。这种做法非常普遍。比如下面的代码:

void modbus_rtu_write_reply(uint8_t add, uint8_t func_code, uint16_t reg, uint16_t data)
{
    rs485.buff_tx[0] = add;
    rs485.buff_tx[1] = func_code;
    rs485.buff_tx[2] = (uint8_t)(reg >> 8);
    rs485.buff_tx[3] = (uint8_t)(reg);
    rs485.buff_tx[4] = (uint8_t)(data >> 8);
    rs485.buff_tx[5] = (uint8_t)(data);
    
    uint16_t crc16 = mb_crc16(rs485.buff_tx, 6);
    
    rs485.buff_tx[6] = (uint8_t)(crc16);
    rs485.buff_tx[7] = (uint8_t)(crc16 >> 8);
   
    rs485.tx_total = 8;
    rs485.tx_num = 0;

    /* Send data from the uart port. The hardware related program. */
    LL_USART_ClearFlag_TC(USART1);
    LL_USART_EnableIT_TC(USART1);
    USART1->DR = rs485.buff_tx[rs485.tx_num ++];
}

上面的这一段代码,不是一个好例子。从函数LL_USART_ClearFlag_TC开始的一句,也就意味着,这个Modbus的代码,和MCU提供出的固件库耦合在一起写了。著名的SOLID原则中,有个依赖倒置原则,高层模块不应该依赖于底层模块,它们应该共同依赖于抽象。此处的代码,显然违反了这一原则。Modbus作为高层模块,此处对MCU固件库的API进行了依赖。

对于这种将硬件相关的代码与功能耦合在一起的软件架构,在本文中,我们姑且称之为“耦合架构”;而我们要追求的,是将隔离硬件相关的软件架构,我们称之为“隔离架构”。接下来,我们将详细对比,耦合架构和隔离架构各自的特征。

耦合架构的问题

虽然从原则上来说,耦合架构是不对的,但我个人对这种软件写法,还是能理解的。为什么?万事皆有因,存在即合理。一般而言,大部分嵌入式软件工程师,都出自硬件相关的专业(比如电子、自动化等),来自于软件工程和计算机专业的嵌入式工程师不多(他们都去互联网行业了),因此从他们的知识结构和习惯思维出发,一般从硬件视角看待嵌入式系统,而不是站在软件抽象的视角。我个人也是电子工程专业毕业的,对此有感受。但理解归理解,道理归道理,既然已经从事嵌入式软件,哪怕是硬件专业出身的,我也建议他一定抛弃既有思维,学会抽象这一强大的软件思维工具,否则他的职业天花板将非常低。

耦合架构带来的问题,也是显而易见的,那就是,实实在在的难以移植。因为一旦硬件发生变化,比如MCU停产,芯片短缺等等(在当前形势下太过常见),嵌入式软件就要大把修改。如果软件规模较大,尝试移植耦合架构的代码到在新MCU上,是一项艰巨的工作,没人愿意干这事。因此产品开发完成,更新架构并推倒重来,几乎是不可能。别说工程师不愿意,你问问老板答应吗?于是工程师们只能检查所有代码,把与硬件交互的每一行代码改掉,遇到硬件交互方式大不相同的,就更糟心,还要大篇幅的改,边改边骂娘。比如上面的代码,如果换一片芯片,可能要改为以下代码。

void modbus_rtu_write_reply(uint8_t add, uint8_t func_code, uint16_t reg, uint16_t data)
{
    rs485.buff_tx[0] = add;
    rs485.buff_tx[1] = func_code;
    rs485.buff_tx[2] = (uint8_t)(reg >> 8);
    rs485.buff_tx[3] = (uint8_t)(reg);
    rs485.buff_tx[4] = (uint8_t)(data >> 8);
    rs485.buff_tx[5] = (uint8_t)(data);
    
    uint16_t crc16 = mb_crc16(rs485.buff_tx, 6);
    
    rs485.buff_tx[6] = (uint8_t)(crc16);
    rs485.buff_tx[7] = (uint8_t)(crc16 >> 8);
   
    rs485.tx_total = 8;
    rs485.tx_num = 0;

    /* Send data from the uart port. The hardware related program. */
    MCU_NEW_USART_ClearFlag_TC(NEW_USART1);
    MCU_NEW_USART_EnableIT_TC(NEW_USART1);
    NEW_USART1->DR = rs485.buff_tx[rs485.tx_num ++];
}

其次,耦合架构会导致,在开发环境中(如Windows或者Linux,非目标硬件),很难对应用程序进行单元测试。脱离目标硬件,跨平台开发嵌入式程序,是提升开发效率的重要措施。这一点,我会《嵌入式软件架构六部曲之六 测试、调试与跨平台开发的支持》一文,会有所涉及;在后续的《嵌入式程序的单元测试》系列教程中(暂定)进行详细阐述。这样的开发方式,在很中小型团队的嵌入式开发中,还不多见,但掌握了就真的提升很大效率。

对耦合架构来说,应用程序代码直接调用硬件,如果要进行完整的测试工作,就要花费大量工作,因为测试程序也要去操作硬件,才能验证正确与错误。或者,需要工程师在硬件上完成手动测试(实际上现在大家就这么干的,哈哈)。手动测试很繁琐,往往让人烦躁,工程师的主观感受,会影响测试质量。很多时候,为了赶进度,或者规避繁琐的测试工作,软件并没有经过很好的测试,整体系统质量受到影响。另外,手动测试,交付软件可能需要更长的时间。而自动测试,往往只需要一瞬间,清楚明了。这个我们会在后续文章《嵌入式软件的测试驱动开发》中进行阐述。

第三,耦合架构将存在不易扩展的问题。耦合架构,往往是共享数据的,也就是所谓的全局变量满天飞。随着软件系统的扩大,每个新功能的添加,变得更加困难,而且是越来越困难,出现BUG的机会急剧增加。屎山就是这么炼成的。但需要说明的是,数据问题,不是说隔离了硬件,就能完全解决掉。数据问题,是嵌入式软件乃至任何软件的核心问题,它需要在架构六部曲之二和之三中,通过软件基础设施的合理构建,和数据机制的合理制定,共同得到解决。

隔离架构如何解决问题?

到这里,我们架构的第一步,呼之欲出,那就是:将软件架构分离为硬件相关和硬件无关两个部分。这就要引入抽象层这个概念。何为抽象层?抽象层有很多种,比如硬件抽象层(HAL)、设备抽象层(DAL),操作系统抽象层(OSAL),网络抽象层,文件系统抽象层,Flash抽象层(RT-Thread里就有这个)等等。对谁进行抽象,就会建立这个东西的抽象层,无一定之规。本文中的抽象层,特指硬件抽象层,或者设备抽象层,或者二者兼备。具体是谁,取决于产品特性,可参考后续文章《嵌入式软件中的抽象层》。

在硬件相关代码和硬件独立代码之间创建抽象层,这是软件移植的要求,实际上也是依赖倒置原则需求。在这里,我们有必要对依赖倒置原则进行强调:高层模块不应该依赖于底层模块,它们应该共同依赖于抽象。也就是说,应用层代码(硬件无关),不应该依赖于硬件相关的代码(驱动代码),他们应该依赖于抽象层代码。

抽象层的创建,将允许将应用代码从一个微控制器移动到下一个微控制器,或者一套硬件迁移到另一套硬件,应用层代码不必更换。抽象层打破了硬件依赖关系;换句话说,应用程序根本不必知道,也不必关心,当前运行的是什么硬件,应用程序只需要关心抽象层的API是什么样的。新的硬件驱动程序要做的,仅仅是满足接口的要求而已。这意味着如果我们更改硬件,则只会更改硬件相关的模块,而不是整个代码库。

void modbus_rtu_write_reply(uint8_t add, uint8_t func_code, uint16_t reg, uint16_t data)
{
    rs485.buff_tx[0] = add;
    rs485.buff_tx[1] = func_code;
    rs485.buff_tx[2] = (uint8_t)(reg >> 8);
    rs485.buff_tx[3] = (uint8_t)(reg);
    rs485.buff_tx[4] = (uint8_t)(data >> 8);
    rs485.buff_tx[5] = (uint8_t)(data);
    
    uint16_t crc16 = mb_crc16(rs485.buff_tx, 6);
    
    rs485.buff_tx[6] = (uint8_t)(crc16);
    rs485.buff_tx[7] = (uint8_t)(crc16 >> 8);
   
    rs485.tx_total = 8;
    rs485.tx_num = 0;

    /* Send data from the uart port. The hardware related program. */
    hal_uart_send(HAL_UART_ID_1, rs485.buff_tx, rs485.tx_total);

}

void hal_uart_send

硬件相关的代码,应该改为如下的样子。这尚且算不上真正的抽象层,只是抽象层最简陋的替代实现方法,实际工程应用中,抽象层还有很多细节需要阐述。限于篇幅,在本文中,我们不进行探讨,请关注后续的《抽象层》系列文章。

void hal_uart_send(uint8_t uart_id, void *buffer, uint32_t size)
{
    /* Start the uart sending process, the remaning data will be send in UART ISR 
       function. */
    MCU_NEW_USART_ClearFlag_TC(NEW_USART1);
    MCU_NEW_USART_EnableIT_TC(NEW_USART1);
    NEW_USART1->DR = rs485.buff_tx[rs485.tx_num ++];
}

抽象层还可以为嵌入式软件的单元测试提供可能性。有了抽象层,我们可以在Windows或者Linux上创建硬件的替身程序(mock),也可以称为假硬件。我们可以在假硬件上给出输入数据,并通过检查假硬件给出的输出数据会否符合预期,来对软件进行单元测试。在没有硬件的情况,也可以对应用层程序进行开发。很多嵌入式程序员觉得不可能,但这是很多大公司开发软件的方式。过去一个月,我刚在Windows上开发一个STM32单片机运行的软件模块,所有的开发工作,都是使用电脑主机上用单元测试完成的。最终在目标硬件进行实测的时间,仅仅用了两个小时而已。我们公司是做高端医疗设备的,每台设备价值一两千万,整个公司上上下下,只有八个机器供我们几百人的团队进行测试,非常紧张,需要预约。如果所有人都在目标设备上调试,才能完成编程,那成本是不能承受的。

抽象层的建立,还有一个好处。软件不必等着硬件就绪才开始开发,而在硬件可用之前,就开始专注于开发和交付应用程序。这样做的好处是,可以在项目早期就对客户提供试用服务,并根据客户反馈进行功能调整。如今,太多的团队专注于首先准备好硬件,而核心应用程序是事后才想到的。这样并不利于对嵌入式软件进行良好的设计和实现。

那么如何建立抽象层呢?抽象层的建立,涉及到几个关键的因素:抽象的程度、抽象的手段以及抽象的对象。这些问题,非常复杂,非三言两语就能说清,我们会在后续文章《抽象层》系列文章和视频中,结合实际例程中进行详细阐述。

结论

嵌入式软件与其他软件领域都不一样,因为没有一个软件领域,和嵌入式软件一样,会和硬件进行直接交互(请注意此处直接二字)。为了应对可能出现的硬件变化(无论是MCU,PCBA,还是连接PCBA的设备),嵌入式软件架构师应该将硬件相关的代码独立出去,并压缩在一个最小的范围内。否则,一旦使用耦合架构,不对硬件相关代码进行剥离,屎山式的代码,几乎是注定的结局。

一个成功的软件架构,从来不是一蹴而就,通常是通过迭代和演进创建的。这需要技术负责人,或者架构师,主动去推动软件架构的迭代,不断推动软件的优化重构。这就有点像明星的好身材,从来不是天生,都是后天自律的结果。但在嵌入式领域,无论搞什么产品,搞什么复杂的软件架构,剥离硬件相关,是第一步,也是最为关键的一步。连硬件相关代码都剥不干净,软件架构就犹如浮沙筑高台,无从谈起。合抱之木,生于毫末,有志于提升技术水平的工程师们,先从隔离硬件开始吧。我在此先预祝成功!

像明星的好身材,从来不是天生,都是后天自律的结果。但在嵌入式领域,无论搞什么产品,搞什么复杂的软件架构,剥离硬件相关,是第一步,也是最为关键的一步。连硬件相关代码都剥不干净,软件架构就犹如浮沙筑高台,无从谈起。合抱之木,生于毫末,有志于提升技术水平的工程师们,先从隔离硬件开始吧。我在此先预祝成功!

嵌入式软件架构设计-建立抽象层 一般而言,大部分嵌入式软件工程师,都出自硬件相关的专业(比如电子、自动化等),来自于软件工程和计算机专业的嵌入式工程师不多(他们都去互联网行业了),因此从他们的知识结构和习惯思维出发,一般从硬件视角看待嵌入式系统,而不是站在软件抽象的视角。换句话说,应用程序根本不必知道,也不必关心,当前运行的是什么硬件,应用程序只需要关心抽象层的API是什么样的。但理解归理解,道理归道理,既然已经从事嵌入式软件,哪怕是硬件专业出身的,我也建议他一定抛弃既有思维,学会抽象这一强大的软件思维工具,否则他的职业天花板将非常低。 阅读详情

相关推荐

嵌入式软件架构中抽象层设计方法

软件架构这东西,众说纷纭,各有观点。什么是软件架构,我们能在网上找到无数种定义。比如,我们可以这样定义:软件架构是软件系统的基本结构,体现在其组件、组件之间的关系、组件设计与演进的规则,以及体现这些规则的基础设施。怎么定义一般来说,基本上不重要,我们不是在写学术书籍,工程人员嘛,只关心软件架构能解决什么问题。软件架构不是制定出来的,而是产品和业务需求所决定的,架构师所做的,只是忠于需求,并合理的表达了需求。软件架构也从来都不是一成不变的。

嵌入式技术开发 1612

嵌入式软件架构的六个步骤(二)软件基础设施

统一的软件基础设施的前提,就是声明统一的基础数据类型和宏,以克服不同的硬件平台和编译器的差异性。比如下面是我从自己的开源项目EventOS中摘录出来的代码,不见得很完整,只能代表在我在项目里需求。编译器相关的宏定义。使用宏,屏蔽掉编译器的差异,会#else#endif一些常用的数据结构。这些数据结构,与硬件和编译器无关,是在代码中频繁使用,并在多个模块间共享的数据结构,有必要将其提升至基础设施的层面进行支持,以避免各个模块,对同一个数据类型,进行不同的定义带来的数据转换问题。

moderncode的博客 4742

嵌入式软件架构基础设施设计方法

统一的软件基础设施的前提,就是声明统一的基础数据类型和宏,以克服不同的硬件平台和编译器的差异性。比如下面是我从开源项目EventOS中摘录出来的代码,不见得很完整,只能代表在我在项目里需求。编译器相关的宏定义。使用宏,屏蔽掉编译器的差异,会#else#endif一些常用的数据结构。这些数据结构,与硬件和编译器无关,是在代码中频繁使用,并在多个模块间共享的数据结构,有必要将其提升至基础设施的层面进行支持,以避免各个模块,对同一个数据类型,进行不同的定义带来的数据转换问题。

嵌入式技术开发 909

嵌入式软件开发之程序分层(二)

硬件抽象层和驱动层主要就是为功能模块层提供的,实现该项目需要的功能,比如KEY、LED和EEPROM等功能,其中LEY、LED基本调用硬件抽象层的API函数(更复杂的可能通过片外芯片获取/控制等,因此可能也需要使用硬件驱动层),EEPROM调用硬件驱动层的API函数,即使EEPROM芯片更换(AT24C02或W25Q128等),也不影响EEPROM之前编写含的功能代码程序(前提是AT24C02,W25Q128提供的API函数提供的是统一标准)。即使换了MCU,也只需将调用过硬件抽象层的API函数替换即可。

坚持 1570

掌握Struts2框架:必备六个核心包详解

本文还有配套的精品资源,点击获取 简介:Struts2是一个基于MVC设计模式的Java Web应用框架。本文深入分析了构建Struts2应用程序所必需的六个核心包的详细作用和相关知识点。了解这些包及其内部组件是高效利用Struts2框架并掌握其高级特性的关键。 1. Struts2框架概述 Struts2框架的发展历史与特点 Struts2框架作为J...

weixin_35677363的博客 1421

马尔可夫网络和贝叶斯网络的一些总结

马尔可夫网络和贝叶斯网络的一些总结 最近学习了一些关于概率逻辑的知识图谱推理方法,主要就是贝叶斯网络和马尔可夫网络,在此总结一下,内容都来自网络资料,这里加入了一下个人的理解,尽量为大家避雷! 这篇文章是我目前在网络上找到的关于贝叶斯网络最清晰的介绍,大家有兴趣可以去看看 贝叶斯网络中比较重要的两部分就是贝叶斯网络的结构和条件概率表。 贝叶斯网络的结构 下面通过一个例子来介绍: 贝叶斯网的网络结...

qq_43654303的博客 4374

嵌入式软件开发之程序架构(一)

设计思想

坚持 3393

[STM32]HAL STM32F103 软件I2C读写MPU6050(附DMP库移植)

本文介绍了如何在STM32F103C8T6上通过I2C协议与MPU6050传感器通信并实现DMP功能。首先,使用STM32CubeMX配置项目,设置时钟和I2C接口。然后,编写软件I2C协议的读写函数与MPU6050交互。接着,移植DMP库并进行必要修改,获取加速度计和陀螺仪数据,并将其转换为欧拉角显示在OLED上。最后,利用Keil的逻辑分析仪监测I2C时序。

m0_56841583的博客 2323

嵌入式软件架构的六个步骤(三) 识别和管理系统数据

数据决定设计,是现代嵌入式软件设计的第一原则。Linux的创始人Linus Torvalds在一次演讲中也曾经说过说: "烂程序员关心的是代码,好程序员关心的是数据结构和它们之间的关系。"他在谈到Git时,也曾表达过类似的观点,“好程序员和烂程序员之间的差别,就在于他们认为是代码更重要还是数据结构更重要。”我们上学时,就学过一个公式:程序 = 数据结构 + 算法。如果从嵌入式程序的宏观视角去理解这个公式:所谓的数据结构,就是数据结构与处理机制;而所谓的算法,就是代码逻辑。好的数据结构,总是会简化代码。

moderncode的博客 3158

国产FPGA逆袭!深度评测复旦微RFVU3P5G核心板在相控阵雷达中的表现

本文深度评测了复旦微RFVU3P5G核心板在相控阵雷达中的实战表现。该核心板凭借射频直采架构、高采样率ADC/DAC及丰富的DSP资源,在多通道同步精度、动态范围及系统稳定性等关键指标上表现出色,展现了国产FPGA在高端雷达应用中的硬核实力与供应链自主价值。

b0c1d2的博客 724

【PVCodeNet】《Palm Vein Recognition Network Combining Transformer and CNN》

1]吴凯,沈文忠,贾丁丁,等.融合Transformer和CNN的手掌静脉识别网络[J].计算机工程与应用,2023,59(24):98-109.

bryant_meng 1281

数十种嵌入式 C 语言代码优化的经验和方法_嵌入式代码优化方案

因此,编译器不能将全局变量的值缓存在寄存器中,但这在使用全局变量时便需要额外的(常常是不必要的)读取和存储。尽可能的使用上面的判断方式,这可以在关键循环中减少比较指令的调用,进而减少代码体积并提高代码性能。我们应该尽可能的使用引用值的方式传递结构数据,也就是说使用指针,否则传递的数据会被拷贝到栈中,从而降低程序的性能。另一个我使用过的工具是英特尔的Vtune,它也能很好的检测出程序中运行最慢的部分。根据我的经验,内部或嵌套循环,调用第三方库的方法通常是导致程序运行缓慢的最主要的起因。

2401_84248479的博客 1031

PythonWeb开发框架—Django之工程创建和models的定义和增删改查

文章介绍了Django工程的创建、models的定义和增删改查

weixin_42476668的博客 990

嵌入式系统开发流程

嵌入式系统的开发流程是一个复杂而精细的过程,需要开发人员具备扎实的专业知识和丰富的实践经验。通过严格的需求分析、硬件设计、软件设计、系统集成和优化维护等步骤,可以开发出稳定、可靠、高效的嵌入式系统。

byte轻骑兵的技术小窝 1万+

工业自动化中气动夹爪选型、调试与维护全流程实战解析

在工业自动化领域,气动执行元件是实现精准抓取与搬运的基础。其工作原理是通过压缩空气驱动气缸,转化为直线或旋转运动,从而控制夹爪的开合。这类技术因其结构简单、响应快速、成本低廉及维护方便,在自动化产线中具有极高的技术价值,是实现高效、可靠物料搬运的核心单元。其典型应用场景涵盖电子装配、汽车制造、食品包装等行业的上下料、分拣与定位工序。本文以工业自动化中常见的二指平行气动夹爪(常被工程师代号化为“Gripper-A”)为例,深入剖析其从硬件选型、气动系统集成到程序逻辑联锁的完整工程实践。内容聚焦于夹持力计算中惯

weixin_42629671的博客 366

PSCAD学习笔记(1)Project Settings(重点snapshot设置)

若不勾选,则横坐标为实际时间2-7s,故障显示在第3s发生。图片中时间坐标轴显示为0-5s,故障时刻显示为第1s,即0-1s展示系统稳态,1-5s展示系统发生故障后的暂态响应,如何通过快照文件实现?勾选该项后,使用快照文件启动仿真会将仿真起始时间强制设置为0s,反之不勾选则依据快照文件保存的时间轴继续(即坐标为实际时间)。如果已有的快照文件保存的是0s至5s的仿真内容,则仿真运行时间从5s开始,到10s结束。如果已有的快照文件保存的是0s至2s的仿真内容,则仿真运行时间从2s开始,到7s结束;

gwenh的博客 7343

嵌入式软件架构(摘录)

摘录

prk_ing的博客 254

SPRD Android 14 通过属性控制系统设置显示双栏或者单栏

SPRD Android 14 通过属性控制系统设置显示双栏或者单栏

m0_60898338的博客 1118

软件架构——嵌入式软件抽象层

关注、星标公众号,直达精彩内容来源:狗哥嵌入式https://zhuanlan.zhihu.com/p/600061712软件架构这东西,众说纷纭,各有观点。什么是软件架构,我们能在网上找到无数种定义。比如,我们可以这样定义:软件架构是软件系统的基本结构,体现在其组件、组件之间的关系、组件设计与演进的规则,以及体现这些规则的基础设施。怎么定义一般来说,基本上不重要,我们不是在写学术书籍,...

李肖遥的专栏 697

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

Android硬件抽象层 HAL 概要介绍和学习计划

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.youkuaiyun.com/jiangjunshow也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!                 &

hfyugg的博客 261

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Android硬件抽象层(HAL)概述

http://blog.youkuaiyun.com/luoshengyang/article/details/6567257       Android的硬件抽象层,简单来说,就是对Linux内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节。也就是说,把对硬件的支持分成了两层,一层放在用户空间(User Space),一层放在内核空间(Kernel Space),其中,硬件抽象层运行在用户空间,

单向街的夏天 785

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Android硬件抽象层(HAL)概要介绍和学习计划

Android的硬件抽象层,简单来说,就是对Linux内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节。也就是说,把对硬件的支持分成了两层,一层放在用户空间(User Space),一层放在内核空间(Kernel Space),其中,硬件抽象层运行在用户空间,而Linux内核驱动程序运行在内核空间。为什么要这样安排呢?把硬件抽象层和内核驱动整合在一起放在内核空间不可行吗?从技术实现的角度来看,

qiaoqiangv的专栏 448

HAL——硬件抽象层读书笔记

本章介绍了——HAL(硬件抽象层),建立在linux驱动之上的一套程序库。它并不属于linux内核,而是属于linux内核层之上的应用层。 Google为了满足这些不想开源的linux驱动作者的要求,在android层次结构中的运行库层增加了一个HAL,从而统一硬件的调用接口,解决了GPL版权问题并且针对一些特殊的要求。 在新的HAL架构中核心代码仍然没变,只是增加了新的入口代码,使这些程序可...

weixin_30216561的博客 128

【Android】Android硬件抽象层(HAL)概要介绍和学习计划

把硬件抽象层和内核驱动整合在一起放在内核空间不可行吗?因此,Android才会想到把对硬件的支持分成硬件抽象层和内核驱动层,内核驱动层只提供简单的访问硬件逻辑,例如读写硬件寄存器的通道,至于从硬件中读到了什么值或者写了什么值到硬件中的逻辑,都放在硬件抽象层中去了,这样就可以把商业秘密隐藏起来了。大家想想,Android放在内核空间的驱动程序对硬件的支持是不完整的,把Linux内核移植到别的机器上去时,由于缺乏硬件抽象层的支持,硬件就完全不能用了,这也是为什么说Android是开放系统而不是开源系统的原因。

weixin_41028159的博客 648

Android硬件抽象层(HAL)概要介绍…

http://blog.youkuaiyun.com/luoshengyang/article/details/6567257  Android的硬件抽象层,简单来说,就是对Linux内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节。也就是说,把对硬件的支持分成 了两层,一层放在用户空间(User Space),一层放在内核空间(Kernel Space),其中,硬件抽象层运行在用户空间,而Linux内核

CoderLife9192的专栏 592
上一篇: 当单片机遇上状态机(二) 为什么QP难以入门?
下一篇: 嵌入式软件架构的六个步骤(二)软件基础设施
狗哥嵌入式
博客等级 码龄5年 1444粉丝 5原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

狗哥嵌入式

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值