云班课选做

阅读附件中的代码,回答:

  • ucos是如何分层的?
  • HAL都有哪些代码?
  • 分析任务是如何切换的。

一、ucos是如何分层的
共分三层,分别是:上层访问抽象接口层、设备管理核心数据结构层、硬件设备驱动模块层。
(1)上层访问抽象接口层: 一般的抽象层设计会直接在这一层提供5个访问接口API: DeviceOpen、DevGetch、DevPutch、DevControl. DeviceClose,分别用于打开设备、读设备、写设备、设备控制和关闭设备。而在这个设计里面更改了这种定义模式提供两个公用的接口DeviceOpen 和DeviceClose,同时为不同的外设分别提供特定的抽象接口,在移植的时候利
用这些抽象接口的不变性保证应用程序的可移植能力。这样做的优点更适合于有单片机开发经验的工程人员直接调用。

例如UART提供的抽象接口有: v. _MiniPrintf最小格式化字符串函数;UARTSet串口参数设置。暂时没实再格式化字符输入。
IIC提供的抽象接口有: I2CMasterSend IIC 主模式发送;

I2CMasterReceive IIC 主模式接收; I2CSlaverReceive IIC 从模式接收。

其它的还有SPI,外部中断管理。在里只简单介绍下UART和IIC。

(2)设备管理核心数据结构层:这是通用驱动框架的核心,主要用每个设备分配一个设备控制块,通过链表形式进行管理,该链表定义为设备控制块链表DEV_CONTROL_BLOCK* HvlConList。 在这一层, 为系统中的每个硬件设备分配唯一的设备ID。上层应用程序通过将设备ID作为参数传递给DeviceOpen函数实现对相应设备的核心管理数据结构的定位搜索,通过搜索,DeviceOpen函数找到相应设备控制块,申请设备的使用权限,获得相应硬件设备的操作句柄,该句柄指向具体的外设底层操作函数列表,返回该设备句柄;再通过上层抽象接口层提供的接口函数对设备进行访问。

(3)硬件设备驱动模块层:这-一层是硬件设备驱动模块功能的实现层,对各个硬件设备的驱动在相应的硬件设备驱动模块中完成。各个硬件设备驱动模块,原则上需要实现如下几个函数: DevGetch、 DevPut ch、DevControl,分别完成相应设备的读、写、控制,当然,可以根据具体设备的特性,只实现3个驱动函数的其中一部分,例如,如果某设备不支持写操作,那么就不
用实现DevPutch函数。
二、HAL都有哪些代码

  • 背景介绍:
    硬件抽象层技术最初是由Microsoft公司为确保WindowsNT的稳定性和兼容性而提出的。针对过去Windows系列操作系统经常出现的系统死机或崩溃等现象,Microsoft总结发现,程序设计直接与硬件通信,是造成系统不稳定的主要原因。在得出这个结论的基础上,微软公司在WindowsNT上取消了对硬件的直接访问,首先提出了硬件抽象层(Hardware Abstraction Layer,简称HAL)的概念。
  • 概念:
    硬件抽象层就是:“将硬件差别与操作系统其他层相隔离的一薄层软件,它是通过采用使多种不同硬件在操作系统的其他部分看来是同一种虚拟机的做法来实现的。“后来,这种HAL设计思路被一些嵌入式操作系统参考,其系统内核被分成两层,上层称为“内核(Kernel)”,底层则称为“硬件抽象层”。在EOS中,HAL独立于EOS内核;对于操作系统和应用软件而言,HAL是对底层架构的抽象。综合分析HAL层的代码,可以发现这些代码与底层硬件设备是紧密相关的。因此,可以将硬件抽象层定义为所有依赖于底层硬件的软件。即使有些EOS的HAL在物理上是与系统内核紧密联系的,甚至相互交叉的,但是从功能上可以从分层技术的角度去分析它。
  • 作用:
    硬件抽象层是位于操作系统内核与硬件电路之间的接口层,其目的在于将硬件抽象化。它隐藏了特定平台的硬件接口细节,为操作系统提供虚拟硬件平台,使其具有硬件无关性,可在多种平台上进行移植。 从软硬件测试的角度来看,软硬件的测试工作都可分别基于硬件抽象层来完成,使得软硬件测试工作的并行进行成为可能。
    硬件抽象层是一个编程层,允许计算机操作系统在逻辑层而不是硬件层与硬件设备交互。Windows 2000就是支持硬件抽象层的操作系统之一。操作系统核心或者硬件驱动程序都可以调用硬件抽象层。无论哪种情况,调用程序都不用了解硬件的具体设计细节,只需要给出抽象层所需的参数即可。
    官方给出的HAL库的包含结构:
  • stm32f2xx.h主要包含STM32同系列芯片的不同具体型号的定义,是否使用HAL库等的定义,接着,其会根据定义的芯片信号包含具体的芯片型号的头文件:
#if defined(STM32F205xx)
  #include "stm32f205xx.h"
#elif defined(STM32F215xx)
  #include "stm32f215xx.h"
#elif defined(STM32F207xx)
  #include "stm32f207xx.h"
#elif defined(STM32F217xx)
  #include "stm32f217xx.h"
#else
 #error "Please select first the target STM32F2xx device used in your application (in stm32f2xx.h file)"
#endif
  • 紧接着,其会包含stm32f2xx_hal.h:
stm32f2xx_hal.h:stm32f2xx_hal.c/h 主要实现HAL库的初始化、系统滴答相关函数、及CPU的调试模式配置
stm32f2xx_hal_conf.h :该文件是一个用户级别的配置文件,用来实现对HAL库的裁剪,其位于用户文件目录,不要放在库目录中
  • 接下来对于HAL库的源码文件进行一下说明,HAL库文件名均以stm32f2xx_hal开头,后面加上_外设或者模块名(如:stm32f2xx_hal_adc.c):
    库文件:
    stm32f2xx_hal_ppp.c/.h // 主要的外设或者模块的驱动源文件,包含了该外设的通用API
    stm32f2xx_hal_ppp_ex.c/.h // 外围设备或模块驱动程序的扩展文件。这组文件中包含特定型号或者系列的芯片的特殊API。以及如果该特定的芯片内部有不同的实现方式,则该文件中的特殊API将覆盖_ppp中的通用API。
    stm32f2xx_hal.c/.h // 此文件用于HAL初始化,并且包含DBGMCU、重映射和基于systick的时间延迟等相关的API
    其他库文件

用户级别文件:

    stm32f2xx_hal_msp_template.c    // 只有.c没有.h。它包含用户应用程序中使用的外设的MSP初始化和反初始化(主程序和回调函数)。使用者复制到自己目录下使用模板。
    stm32f2xx_hal_conf_template.h   // 用户级别的库配置文件模板。使用者复制到自己目录下使用
    system_stm32f2xx.c              // 此文件主要包含SystemInit()函数,该函数在刚复位及跳到main之前的启动过程中被调用。 **它不在启动时配置系统时钟(与标准库相反)**。 时钟的配置在用户文件中使用HAL API来完成。
    startup_stm32f2xx.s             // 芯片启动文件,主要包含堆栈定义,终端向量表等
    stm32f2xx_it.c/.h               // 中断处理函数的相关实现
    main.c/.h    

三、分析任务是如何切换的


一般函数的生命周期很简单,从开始调用函数起,直到函数返回,即结束。这样一来就完成了这个函数的使命,它也就不再需要了。对于一般的函数就是这样,但是回过头想想,对于一个系统、OS、或者工业控制中的一个控制器重的系统个,函数返回是很轻易很随便的就能返回吗?返回就意味着函数结束,死亡,若是想系统这样一个很大的函数,它的返回就意味着系统结束。因此,对于系统的函数返回有些时候我们不希望它返回,返回时是需要好好设计的,像嵌入式中的控制程序我们也并不需要它返回,直接关机就好了。因此,一个系统往往就是一个很大的循环,不停的扫描,而我们编程的时候对于这个死循环是需要好好设计的。考虑以下一个控制要求,

@.按键控制电机启、停、正转反转,并每秒发送CAN报文报告当前情况。

我们可以有多种方法实现这一要求:

方法一:每次在循环体重扫描当前按键的电平,从而进入对应的控制电机函数,如果所有电平都没有信号则直接进入下一个循环。发送CAN报文就直接用一个定时中断。这样的好处就是编程简单直白,每次循环进入不同的电机控制函数,坏处很明显,一定要等待到下一个循环才能进入其他的电机控制函数,每次循环的时间不好控制,不管你用函数指针还是if/else来判断,每次循环一定要等待电机动作结束才能进入下一个循环。

方法二:改用外部中断来处理按键。仅当按键按下时触发外部中断,从而控制响应的电机进行操作。这样的好处就是循环体简单,可以仅仅就是一个计数器加一,所有控制都等中断来实现。但这样带来的问题也很明显,就是中断嵌套问题。比如当电机正转时按下停止按钮,这时由于是在中断中,停止按钮是否真的能够得到响应?这就涉及到中断嵌套问题,并不见得所有CPU都能支持中断嵌套。

方法三:采用RTOS的思想,加入任务调度系统。每次任务调度系统就是一个小小的循环,对于各个任务进行轮询,当这次轮询发现某任务是已经就绪的优先级最高的任务,则交给CPU处理,所有中断与任务,任务与任务之间有通讯机制可以交换信息。当然实际上并不仅仅只在任务调度器轮询时才进行任务的切换,实际上的操作比这个复杂一些,我的这篇文章就想以uCOS-II为例讨论RTOS的任务调度系统是怎样执行的。

猜你喜欢

转载自www.cnblogs.com/xyejava/p/12108482.html