精华内容
下载资源
问答
  • HAL

    千次阅读 2011-03-20 21:24:00
     HAL,Hardware Abstraction Layer,硬件抽象层 Windows NT的一个目标是使操作系统可跨平台移植。理想情况下,当一种新机器问世时,他应该可以仅仅使用新机器的编译器来重新编译这个操作系统,就让他首次运行。...

      HAL,Hardware Abstraction Layer,硬件抽象层 Windows NT的一个目标是使操作系统可跨平台移植。理想情况下,当一种新机器问世时,他应该可以仅仅使用新机器的编译器来重新编译这个操作系统,就让他首次运行。但是,现实中并不能这样做。虽然上层的操作系统能够完全移植(因为它们的处理大多是内部数据结构),但底层处理的是设备寄存器、中断、DMA和其他的硬件特性,这些都是因机器而不同的。即使大部分底层代码是用C语言编写的,它也不能仅仅从X86上拿出来放到Alpha上,然后重新编译、重新启动,因为X86和Alpha之间存在许多小的硬件差别,它们和不同的指令集相关并且不能被编译器隐藏。

      微软认识到了这一点并尝试做一个很小的底层,以隐藏不同机器间的差异,这一层被称为硬件抽象层HAL。

      HAL的作用是将操作系统的其余部分表示为抽象的硬件设备,特别是去除了真正硬件所富含的瑕疵和特质。这些设备表现为操作系统的其它部分和设备可以使用的独立于机器的服务的形式(函数调用和宏)。通过使用HAL服务和间接硬件殉职,当移植到新的硬件上时,驱动程序和核心只需做很少的改动。移植HAL本市是直接的,因为所有的机器相关代码都集中在一个地方,并且移植的目标是充分定义的,即实现所有的HAL服务。

      选择HAL中的服务是和主板上的芯片相关的,因为这些芯片从一个机器到另一个机器的变化是具有可预见限度的。换句话说,设计它是为了隐藏不同厂商主板之间的差别,而不是X86和Alpha之间的差别。HAL服务包括对设备寄存器的访问、总线独立的设备寻址、中断处理和复位、DMA传输、定时器和实时时钟的控制、底层的自旋锁(Spin Lock)和多处理机同步、BIOS接口以及CMOS配置内存。HAL没有提供对特殊I/O设备(如键盘、鼠标、硬盘和内存管理单元)的抽象或服务。

      举一个例子来说明硬件抽象层的功能。考虑内存映射I/O和I/O端口的对比。一些机器具有前者,一些机器具有后者。驱动程序该怎样编写?是否使用内存映射呢?强制选择会使驱动程序无法移植到另一种实现方式的机器上,为此,硬件抽象层专为驱动程序的编写者提供了三个读设备寄存器的函数和另外三个写寄存器的函数:

      uc=READ_PORT_UCHAR(port); WRITE_PORT_UCHAR(port, uc)

      us=READ_PORT_USHORT(port); WRITE_PORT_USHORT(port, us)

      ul=READ_PORT_ULONG(port); WRITE_PORT_LONG(port, ul)

      这些函数分别读写无符号8位、16位、32位的证书到特定的端口。由HAL决定是否需要内存映射I/O,这样,一个驱动程序可以不被修改而在具有不同设备寄存器实现的机器间移植。

      驱动程序常由于各种原因而访问特定的I/O设备。在这个硬件层上,一个设备的某个总线上会有一个或多个地址。由于现代计算机常有多种总线(PCI、PCI-E、SCSI、USB等),很可能两个或更多设备具有相同的总线地址,因此需要通过某种方式来区分它们。HAL提供了一个服务,该服务通过将总线相连的设备地址映射到系统范围内的逻辑地址来识别设备。这样,驱动程序就不需要知道哪条总线上有哪个设备了。这些逻辑地址与操作系统为用户程序提供的指向文件和其他系统资源的句柄是类似的。这种机制也使总线结构的属性和寻址方式对于高层不可见。

      中断也存在类似的问题——它们也是总线相关的。同样,在这里,HAL为系统范围内的中断提供命名服务,并允许驱动程序以可移植的方法将中断服务例程和中断联系起来而不用知道哪个中断向量对应于哪条总线。此外,中断请求级别管理也在HAL处理。

      HAL提供的另一项服务是以一种设备独立的方式设置并管理DMA传输。系统范围内的DMA引擎与特定I/O卡上的DMA引擎都可以操作。对设备的访问是通过其逻辑地址进行的。HAL还实现了软件的分散、聚集(scatter/gather)(对非连续的物理存储块进行写或读)。

      此外,HAL还以一种可移植的方式管理时钟与定时器。时间记录以100ns为单位(起始于1601年1月1日),这样就比以2s为单位(起始于1980年1月1日)的MS-DOS事件记录精确得多,HAL还为许多发生于17.18.19世纪的计算机相关事件的记录提供了致贺词。这种时间服务将驱动程序从始终运行的实际频率中分离出来。

      内核组件(Kernel Component)有时需要在非常低的层次上同步,特别是为了避免多处理机系统中的竞争状态。HAL提供了一些原子方法来管理这种同步,如自旋锁——一个CPU仅仅等待一个由其他CPU占用的系统资源被释放,尤其是在资源只被几条机器指令所占用的情况下。

      最后,当系统启动以后,HAL与BIOS进行对话,并检查CMOS配置内存(如果有的话),以查明该系统包含了哪些总线和I/O设备,以及他们是如何配置的。之后这个信息会被存入注册表,这样,其他系统组件就能够查询它,而不必了解BIOS或配置内存如何工作。

      由于HAL高度依赖于机器,它必须与其所装入的系统完全匹配,因此,Windows 的安装光盘上提供了许多种HAL。系统安装时,选择一种合适的HAL并以hal.dll为名复制到硬盘上的系统目录/windows/system32下。之后所有的启动都使用该版本的HAL,删除这个文件将导致系统无法启动。

      尽管HAL已经相当高效,但对于多媒体应用而言,它的速度可能还不够快。为此,微软公司另外提供了一个名为DirectX的软件包,它用附加的过程增强了HAL,并允许用户对硬件进行更直接的访问。DirectX在此不给予讨论。

    展开全文
  • 【STM32】HAL库 STM32CubeMX教程四---UART串口通信详解

    万次阅读 多人点赞 2019-08-11 08:57:51
    今天我们学习STM32CubeMX串口的操作,以及HAL库串口的配置,我们会详细的讲解各个模块的使用和具体功能,并且基于HAL库实现Printf函数功能重定向,UART中断接收,本系列教程将HAL库与STM32CubeMX结合在一起讲解,使...

    前言: 

    今天我们学习STM32CubeMX串口的操作,以及HAL库串口的配置,我们会详细的讲解各个模块的使用和具体功能,并且基于HAL库实现Printf函数功能重定向,UART中断接收,本系列教程将HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用

     

    所用工具

    1、芯片: STM32F407ZET6

    2、STM32CubeMx软件

    3、IDE: MDK-Keil软件

    4、STM32F1xx/STM32F4xxHAL库 

    5、串口: 使用USART1 PA9,PA10

    知识概括:

    通过本篇博客您将学到:

    STM32CubeMX创建串口例程

    HAL库UATR函数库

    重定义printf函数

    HAL库,UART中断接收

    HAL库UATR接收与发送例程

    工程创建

     

    1设置RCC

    • 设置高速外部时钟HSE 选择外部时钟源

    2设置串口

    • 1点击USATR1   
    • 2设置MODE为异步通信(Asynchronous)       
    • 3基础参数:波特率为115200 Bits/s。传输数据长度为8 Bit。奇偶检验无,停止位1      接收和发送都使能 
    • 4GPIO引脚设置 USART1_RX/USART_TX
    • 5 NVIC Settings 一栏使能接收中断

    3设置时钟

    我的是  外部晶振为8MHz 

    • 1选择外部时钟HSE 8MHz   
    • 2PLL锁相环倍频72倍
    • 3系统时钟来源选择为PLL
    • 4设置APB1分频器为 /2

    32的时钟树框图  如果不懂的话请看《【STM32】系统时钟RCC详解(超详细,超全面)》

     

    4项目文件设置

    • 1 设置项目名称
    • 2 设置存储路径
    • 3 选择所用IDE

    5创建工程文件

    然后点击GENERATE CODE  创建工程

    配置下载工具

    新建的工程所有配置都是默认的  我们需要自行选择下载模式,勾选上下载后复位运行

    HAL库UART函数库介绍

     

      UART结构体定义

    UART_HandleTypeDef huart1;

    UART的名称定义,这个结构体中存放了UART所有用到的功能,后面的别名就是我们所用的uart串口的别名,默认为huart1

    可以自行修改

    1、串口发送/接收函数

    • HAL_UART_Transmit();串口发送数据,使用超时管理机制 
    • HAL_UART_Receive();串口接收数据,使用超时管理机制
    • HAL_UART_Transmit_IT();串口中断模式发送  
    • HAL_UART_Receive_IT();串口中断模式接收
    • HAL_UART_Transmit_DMA();串口DMA模式发送
    • HAL_UART_Transmit_DMA();串口DMA模式接收

    这几个函数的参数基本都是一样的,我们挑两个讲解一下

    串口发送数据:

    HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)

    功能:串口发送指定长度的数据。如果超时没发送完成,则不再发送,返回超时标志(HAL_TIMEOUT)。

    参数:

    • UART_HandleTypeDef *huart      UATR的别名    如 :   UART_HandleTypeDef huart1;   别名就是huart1  
    • *pData      需要发送的数据 
    • Size    发送的字节数
    • Timeout   最大发送时间,发送数据超过该时间退出发送   
    举例:   HAL_UART_Transmit(&huart1, (uint8_t *)ZZX, 3, 0xffff);   //串口发送三个字节数据,最大传输时间0xffff

    中断接收数据:

    HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

    功能:串口中断接收,以中断方式接收指定长度数据。
    大致过程是,设置数据存放位置,接收数据长度,然后使能串口接收中断。接收到数据时,会触发串口中断。
    再然后,串口中断函数处理,直到接收到指定长度数据,而后关闭中断,进入中断接收回调函数,不再触发接收中断。(只触发一次中断)

    参数:

    • UART_HandleTypeDef *huart      UATR的别名    如 :   UART_HandleTypeDef huart1;   别名就是huart1  
    • *pData      接收到的数据存放地址
    • Size    接收的字节数
    举例:    HAL_UART_Receive_IT(&huart1,(uint8_t *)&value,1);   //中断接收一个字符,存储到value中

    2、串口中断函数

     

    • HAL_UART_IRQHandler(UART_HandleTypeDef *huart);  //串口中断处理函数
    • HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);  //串口发送中断回调函数
    • HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);  //串口发送一半中断回调函数(用的较少)
    • HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);  //串口接收中断回调函数
    • HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//串口接收一半回调函数(用的较少)
    • HAL_UART_ErrorCallback();串口接收错误函数

    串口接收中断回调函数:

    HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);  

    功能:HAL库的中断进行完之后,并不会直接退出,而是会进入中断回调函数中,用户可以在其中设置代码,

               串口中断接收完成之后,会进入该函数,该函数为空函数,用户需自行修改,

    参数:

    • UART_HandleTypeDef *huart      UATR的别名    如 :   UART_HandleTypeDef huart1;   别名就是huart1  
    举例:   HAL_UART_RxCpltCallback(&huart1){           //用户设定的代码               }

    串口中断处理函数

    HAL_UART_IRQHandler(UART_HandleTypeDef *huart);  

    功能:对接收到的数据进行判断和处理  判断是发送中断还是接收中断,然后进行数据的发送和接收,在中断服务函数中使用

     

    如果接收数据,则会进行接收中断处理函数

     /* UART in mode Receiver ---------------------------------------------------*/
      if((tmp_flag != RESET) && (tmp_it_source != RESET))
      { 
        UART_Receive_IT(huart);
      }
    

    如果发送数据,则会进行发送中断处理函数

      /* UART in mode Transmitter ------------------------------------------------*/
      if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
      {
        UART_Transmit_IT(huart);
        return;
      }

    3串口查询函数

      HAL_UART_GetState();  判断UART的接收是否结束,或者发送数据是否忙碌

      举例:     

    while(HAL_UART_GetState(&huart4) == HAL_UART_STATE_BUSY_TX)   //检测UART发送结束

     

    USART接收与发送

     

    重新定义printf函数

    • 在 stm32f4xx_hal.c中包含#include <stdio.h>
    #include "stm32f4xx_hal.h"
    #include <stdio.h>
    extern UART_HandleTypeDef huart1;   //声明串口
    • 在 stm32f4xx_hal.c 中重写fget和fput函数
    • /**
        * 函数功能: 重定向c库函数printf到DEBUG_USARTx
        * 输入参数: 无
        * 返 回 值: 无
        * 说    明:无
        */
      int fputc(int ch, FILE *f)
      {
        HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
        return ch;
      }
      
      /**
        * 函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx
        * 输入参数: 无
        * 返 回 值: 无
        * 说    明:无
        */
      int fgetc(FILE *f)
      {
        uint8_t ch = 0;
        HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
        return ch;
      }
      
      在main.c中添加
     #define RXBUFFERSIZE  256
    char RxBuffer[RXBUFFERSIZE]; 
    
      while (1)
      {
        /* USER CODE END WHILE */
    			printf("Z小旋测试\n");
    			HAL_Delay(1000);
        /* USER CODE BEGIN 3 */
      }

    之后便可以使用Printf函数和Scanf,getchar函数

    UART接收中断

    因为中断接收函数只能触发一次接收中断,所以我们需要在中断回调函数中再调用一次中断接收函数

    具体流程:

    1、初始化串口

    2、在main中第一次调用接收中断函数

    3、进入接收中断,接收完数据  进入中断回调函数

    4、修改HAL_UART_RxCpltCallback中断回调函数,处理接收的数据,

    5  回调函数中要调用一次HAL_UART_Receive_IT函数,使得程序可以重新触发接收中断

    函数流程图:

    HAL_UART_Receive_IT(中断接收函数   ->  USART2_IRQHandler(void)(中断服务函数)    ->    HAL_UART_IRQHandler(UART_HandleTypeDef *huart)(中断处理函数)    ->    UART_Receive_IT(UART_HandleTypeDef *huart) (接收函数)   ->    HAL_UART_RxCpltCallback(huart);(中断回调函数)

    HAL_UART_RxCpltCallback函数就是用户要重写在main.c里的回调函数。

    代码实现:

        并在main.c中添加下列定义:

    #include <string.h>
    
    #define RXBUFFERSIZE  256     //最大接收字节数
    char RxBuffer[RXBUFFERSIZE];   //接收数据
    uint8_t aRxBuffer;			//接收中断缓冲
    uint8_t Uart1_Rx_Cnt = 0;		//接收缓冲计数
    

    在main()主函数中,调用一次接收中断函数

    /* USER CODE BEGIN 2 */
    	HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);
    /* USER CODE END 2 */
    

    在main.c下方添加中断回调函数

    /* USER CODE BEGIN 4 */
    
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
      /* Prevent unused argument(s) compilation warning */
      UNUSED(huart);
      /* NOTE: This function Should not be modified, when the callback is needed,
               the HAL_UART_TxCpltCallback could be implemented in the user file
       */
     
    	if(Uart1_Rx_Cnt >= 255)  //溢出判断
    	{
    		Uart1_Rx_Cnt = 0;
    		memset(RxBuffer,0x00,sizeof(RxBuffer));
    		HAL_UART_Transmit(&huart1, (uint8_t *)"数据溢出", 10,0xFFFF); 	
            
    	}
    	else
    	{
    		RxBuffer[Uart1_Rx_Cnt++] = aRxBuffer;   //接收数据转存
    	
    		if((RxBuffer[Uart1_Rx_Cnt-1] == 0x0A)&&(RxBuffer[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位
    		{
    			HAL_UART_Transmit(&huart1, (uint8_t *)&RxBuffer, Uart1_Rx_Cnt,0xFFFF); //将收到的信息发送出去
                while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束
    			Uart1_Rx_Cnt = 0;
    			memset(RxBuffer,0x00,sizeof(RxBuffer)); //清空数组
    		}
    	}
    	
    	HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);   //再开启接收中断
    }
    /* USER CODE END 4 */
    

    发送数据被正常返回

     

     

    展开全文
  • STM32 之二 HAL库详解 及 手动移植

    万次阅读 多人点赞 2017-02-15 18:41:23
    2018.1.19 更新其中的错误部分 ...STM32CubeMX就是以HAL库为基础的,且目前仅支持HAL库及LL库!首先看一下,官方给出的HAL库的包含结构: stm32f2xx.h主要包含STM32同系列芯片的不同具体型号的定义,是否
    • 2018.1.19 更新其中的错误部分

    HAL库结构

      说到 STM32 的 HAL 库,就不得不提 STM32CubeMX,其作为一个可视化的配置工具,对于开发者来说,确实大大节省了开发时间。STM32CubeMX 就是以 HAL 库为基础的,且目前仅支持 HAL 库及 LL 库!首先看一下,官方给出的 HAL 库的包含结构:

    SouthEast
    • 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.hstm32f2xx_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						//
    

      根据HAL库的命名规则,其API可以分为以下三大类:

    • 初始化/反初始化函数: HAL_PPP_Init(), HAL_PPP_DeInit()
    • IO 操作函数: HAL_PPP_Read(), HAL_PPP_Write(),HAL_PPP_Transmit(), HAL_PPP_Receive()
    • 控制函数: HAL_PPP_Set (), HAL_PPP_Get ().
    • 状态和错误: HAL_PPP_GetState (), HAL_PPP_GetError ().

    注意:

    1. 目前 LL 库是和 HAL 库捆绑发布的,所以在 HAL 库源码中,还有一些名为 stm32f2xx_ll_ppp 的源码文件,这些文件就是新增的LL库文件。
    2. 使用 CubeMX 生产项目时,可以选择LL库

    HAL 库最大的特点就是对底层进行了抽象。在此结构下,用户代码的处理主要分为三部分:

    • 处理外设句柄(实现用户功能)
    • 处理MSP
    • 处理各种回调函数

    外设句柄定义

      用户代码的第一大部分:对于外设句柄的处理。 HAL库在结构上,对每个外设抽象成了一个称为ppp_HandleTypeDef的结构体,其中ppp就是每个外设的名字。*所有的函数都是工作在ppp_HandleTypeDef指针之下。
      1. 多实例支持:每个外设/模块实例都有自己的句柄。 因此,实例资源是独立的
      2. 外围进程相互通信:该句柄用于管理进程例程之间的共享数据资源。
    下面,以 ADC 为例

    /** 
     * @brief  ADC handle Structure definition
     */ 
    typedef struct
    {
    	ADC_TypeDef                   *Instance;                   /*!< Register base address */
    	ADC_InitTypeDef               Init;                        /*!< ADC required parameters */
      __IO uint32_t                 NbrOfCurrentConversionRank;  /*!< ADC number of current conversion rank */
    	DMA_HandleTypeDef             *DMA_Handle;                 /*!< Pointer DMA Handler */
    	HAL_LockTypeDef               Lock;                        /*!< ADC locking object */
    	__IO uint32_t                 State;                       /*!< ADC communication state */
    	__IO uint32_t                 ErrorCode;                   /*!< ADC Error code */
    }ADC_HandleTypeDef;
    

      从上面的定义可以看出,ADC_HandleTypeDef中包含了ADC可能出现的所有定义,对于用户想要使用ADC只要定义一个ADC_HandleTypeDef的变量,给每个变量赋好值,对应的外设就抽象完了。接下来就是具体使用了。
      当然,对于那些共享型外设或者说系统外设来说,他们不需要进行以上这样的抽象,这些部分与原来的标准外设库函数基本一样。 例如以下外设:
      - GPIO
      - SYSTICK
      - NVIC
      - RCC
      - FLASH
    以 GPIO 为例,对于 HAL_GPIO_Init() 函数,其只需要 GPIO 地址以及其初始化参数即可。

    三种编程方式

      HAL库对所有的函数模型也进行了统一。在HAL库中,支持三种编程模式:轮询模式、中断模式、DMA模式(如果外设支持)。其分别对应如下三种类型的函数(以ADC为例):

    HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc);
    HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef* hadc);
    
    HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc);
    HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef* hadc);
    
    HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);
    HAL_StatusTypeDef HAL_ADC_Stop_DMA(ADC_HandleTypeDef* hadc);
    

      其中,带_IT的表示工作在中断模式下;带_DMA的工作在DMA模式下(注意:DMA模式下也是开中断的);什么都没带的就是轮询模式(没有开启中断的)。至于使用者使用何种方式,就看自己的选择了。
      此外,新的HAL库架构下统一采用宏的形式对各种中断等进行配置(原来标准外设库一般都是各种函数)。针对每种外设主要由以下宏:

    • __HAL_PPP_ENABLE_IT(__HANDLE__, __INTERRUPT__): 使能一个指定的外设中断
    • __HAL_PPP_DISABLE_IT(__HANDLE__, __INTERRUPT__):失能一个指定的外设中断
    • __HAL_PPP_GET_IT (__HANDLE__, __ INTERRUPT __):获得一个指定的外设中断状态
    • __HAL_PPP_CLEAR_IT (__HANDLE__, __ INTERRUPT __):清除一个指定的外设的中断状态
    • __HAL_PPP_GET_FLAG (__HANDLE__, __FLAG__):获取一个指定的外设的标志状态
    • __HAL_PPP_CLEAR_FLAG (__HANDLE__, __FLAG__):清除一个指定的外设的标志状态
    • __HAL_PPP_ENABLE(__HANDLE__) :使能外设
    • __HAL_PPP_DISABLE(__HANDLE__) :失能外设
    • __HAL_PPP_XXXX (__HANDLE__, __PARAM__) :指定外设的宏定义
    • __HAL_PPP_GET_ IT_SOURCE (__HANDLE__, __ INTERRUPT __):检查中断源

    三大回调函数

      在 HAL 库的源码中,到处可见一些以__weak开头的函数,而且这些函数,有些已经被实现了,比如:

    __weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    {
    	/*Configure the SysTick to have interrupt in 1ms time basis*/
    	HAL_SYSTICK_Config(SystemCoreClock/1000U);
    	/*Configure the SysTick IRQ priority */
    	HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority ,0U);
    	/* Return function status */
    	return HAL_OK;
    }
    

    有些则没有被实现,例如:

    __weak void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
    {
      /* Prevent unused argument(s) compilation warning */
      UNUSED(hspi);
      /* NOTE : This function should not be modified, when the callback is needed,the HAL_SPI_TxCpltCallback should be implemented in the user file
      */
    }
    

    所有带有__weak关键字的函数表示,就可以由用户自己来实现。如果出现了同名函数,且不带__weak关键字,那么连接器就会采用外部实现的同名函数。通常来说,HAL库负责整个处理和MCU外设的处理逻辑,并将必要部分以回调函数的形式给出到用户,用户只需要在对应的回调函数中做修改即可。 HAL 库包含如下三种用户级别回调函数(PPP为外设名):
      1. 外设系统级初始化/解除初始化回调函数(用户代码的第二大部分:对于MSP的处理):HAL_PPP_MspInit()HAL_PPP_MspDeInit** 例如:__weak void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)。在HAL_PPP_Init() 函数中被调用,用来初始化底层相关的设备(GPIOs, clock, DMA, interrupt)

      2. 处理完成回调函数:HAL_PPP_ProcessCpltCallback*(Process指具体某种处理,如UART的Tx),例如:__weak void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)。当外设或者DMA工作完成后时,触发中断,该回调函数会在外设中断处理函数或者DMA的中断处理函数中被调用

      3. 错误处理回调函数:HAL_PPP_ErrorCallback例如:__weak void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)**。当外设或者DMA出现错误时,触发终端,该回调函数会在外设中断处理函数或者DMA的中断处理函数中被调用

    1. 绝大多数用户代码均在以上三大回调函数中实现。
    2. HAL库结构中,在每次初始化前(尤其是在多次调用初始化前),先调用对应的反初始化(DeInit)函数是非常有必要的。某些外设多次初始化时不调用返回会导致初始化失败。
    3. 完成回调函数有多中,例如串口的完成回调函数有HAL_UART_TxCpltCallback 和 HAL_UART_TxHalfCpltCallback
    4. (用户代码的第三大部分:对于上面第二点和第三点的各种回调函数的处理)
    5. 在实际使用中,发现HAL仍有不少问题,例如在使用USB时,其库配置存在问题

    HAL库移植使用

    基本步骤

    1. 复制stm32f2xx_hal_msp_template.c,参照该模板,依次实现用到的外设的HAL_PPP_MspInit()HAL_PPP_MspDeInit
    2. 复制stm32f2xx_hal_conf_template.h,用户可以在此文件中自由裁剪,配置HAL库。
    3. 在使用HAL库时,必须先调用函数:HAL_StatusTypeDef HAL_Init(void)(该函数在stm32f2xx_hal.c中定义,也就意味着第一点中,必须首先实现HAL_MspInit(void)HAL_MspDeInit(void)
    4. HAL库与STD库不同,HAL库使用RCC中的函数来配置系统时钟,用户需要单独写时钟配置函数(STD库默认在system_stm32f2xx.c中)
    5. 关于中断,HAL提供了中断处理函数,只需要调用HAL提供的中断处理函数。用户自己的代码,不建议先写到中断中,而应该写到HAL提供的回调函数中。
    6. 对于每一个外设,HAL都提供了回调函数,回调函数用来实现用户自己的代码。整个调用结构由HAL库自己完成。例如:Uart中,HAL提供了void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);函数,用户只需要触发中断后,用户只需要调用该函数即可,同时,自己的代码写在对应的回调函数中即可!如下:
    void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
    void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
    void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);
    void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);
    

    使用了哪种就用哪个回调函数即可!

    基本结构

      综上所述,使用HAL库编写程序(针对某个外设)的基本结构(以串口为例)如下:

    1. 配置外设句柄 例如,建立UartConfig.c,在其中定义串口句柄 UART_HandleTypeDef huart;,接着使用初始化句柄(HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart))
    2. 编写Msp 例如,建立UartMsp.c,在其中实现void HAL_UART_MspInit(UART_HandleTypeDef* huart) 和 void HAL_UART_MspDeInit(UART_HandleTypeDef* huart)
    3. 实现对应的回调函数 例如,建立UartCallBack.c,在其中实现上文所说明的三大回调函数中的完成回调函数错误回调函数

    至此,HAL库的总体结构就介绍完了!具体的每个文件的详细说明,官方源码注释很详细!

    参考文档

    • ST - Description of STM32F4 HAL and LL drivers.pdf
    • ST - en.stm32_embedded_software_offering.pdf
    展开全文
  • 本文开发环境: MCU型号:STM32F051R8T6 IDE环境: MDK 5.25 ...使用HAL_Delay()延时函数实现LED灯闪烁 Systick 定时器 HAL_Delay()延时函数 while (1) { /* USER CODE END WHI...

    本文开发环境:

    • MCU型号:STM32F051R8T6
    • IDE环境: MDK 5.25
    • 代码生成工具:STM32CubeMx 5.0.1
    • HAL库版本:v1.9.0(STM32Cube MCU Package for STM32F0 Series)

    本文内容:

    1. Systick 定时器延时原理
    2. 使用HAL_Delay()实现LED灯闪烁
    3. 修改HAL_Delay()的延时单位
    4. 使用HA_Delay()需要注意的情况
    5. 关于HAL_Delay()函数溢出问题的讨论

    Systick 定时器延时原理

    Systick(滴答时钟)是一个24位,向下计数的定时器,当倒计时完成后,定时器可以产生一个中断,所以,当频率一定,计数个数一定时,这个中断就会以一定的时间间隔发生,如果每个中断发送后调用的中断函数中给一个变量累加,这样我们就可以获得一个与时间相关的变量。有关于滴答时钟相关知识,官方手册和网上已经有非常多的篇幅介绍讲解,这里不再赘述。

    HAL_Delay()延时函数的使用

    如果你使用STM32CubeMx来生成一个工程,那么使用Systick来延时是非常方便的,你只需要调用HAL库的一个虚函数,它的原型如下:

    __weak void HAL_Delay(__IO uint32_t Delay)
    

    可以看到,HAL_Delay()是一个虚函数,这表明用户可以在其它的位置重定义,如果这样,新的函数将会取代它,编译过程中也不会出现重定义的错误。该函数只有一个32位的参数,明显的,这个形参指定了延时的时间,它的单位是毫秒(ms)。关于这个函数的使用,是简单的,下面的例子中,我们在拉高和拉低LED1引脚的程序间插入了延时500ms的语句,编译下载后,你就可以发现LED1灯在以1s的频率闪烁。

        while (1)
        {
            /* USER CODE END WHILE */
    
            /* USER CODE BEGIN 3 */
            HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
    		HAL_Delay(500);
            HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
    		HAL_Delay(500);
        }
        /* USER CODE END 3 */
    

    至此我们已经很好的在实际项目中运用到这个函数。如果你对这个函数还有其它的兴趣,我们可以进一步讨论和这个函数的其它方面。

    HAL_Delay()函数延时单位的调整

    虽然毫秒级的延时可能是应用最广泛的,但这个函数没有被命名为HAL_DelayMs()也可能正是考虑到用户会修改延时数的单位。改变延时的单位有几种方法,最简单,在HAL_Delay()函数内把形参乘一个系数,那么这个单位就会相应的这个系数的倍数。但是由于这种方式并没有改变系统的滴答时钟的中断频率,所以并不能影响到系统的开销。

    这里介绍一种通过降低Systick中断频率来修改延时单位的方法,这种方式也节约了系统的开销。具体操作以下行数的形参改为比1000跟小的值,比如1,这样SysTick 1s 中断一次,而延时函数的单位,和最小单位,也成了1s。

      /*Configure the SysTick to have interrupt in 1ms time basis*/
      HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000U);
    

    这里再简单介绍它的原理,首先来看Systick的中断函数:

    void SysTick_Handler(void)
    {
    	/* USER CODE BEGIN SysTick_IRQn 0 */
    	
    	/* USER CODE END SysTick_IRQn 0 */
    	HAL_IncTick();
    	 /* USER CODE BEGIN SysTick_IRQn 1 */
    	
    	* USER CODE END SysTick_IRQn 1 */
    }
    
    __weak void HAL_IncTick(void)
    {
    	uwTick++;
    }
    

    Systick定时器每中断一次,就调用一次HAL_incTick()函数,来对变量uwTick累加。接着我们来看HAL_Delay()函数的定义:

    __weak void HAL_Delay(__IO uint32_t Delay)
    {
    		uint32_t tickstart = HAL_GetTick();
    		uint32_t wait = Delay;
    		
    		/* Add a period to guarantee minimum wait */
    		if (wait < HAL_MAX_DELAY)
    		{
    			 wait++;
    		}
    		
    		while((HAL_GetTick() - tickstart) < wait)
    		{
    		}
    }
    

    当程序进入延时函数时,就调用HAL_GetTick()获取当前uwTick的值。接着判断wait的值,若不大于可以延时的最大值,则wait自加1,最后不断的获取HAl_GetTick的值,直到这个值和初始值的差不小于等待的时间。所以,当我们的Delay形参越大,wait也越大,也就需要更长的时间来调出while循环。
    综上所述,我们只要改变中断的频率,就可以修改延时单位的效果。
    在main函数中,程序会调用SystemClock_Config()函数来配置系统时钟,当然也包括Systick,最后是通过虚函数HAL_InitTick来配置的,它的形参用来配置中断的优先级。

    __weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    {
      /*Configure the SysTick to have interrupt in 1ms time basis*/
      HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000U);
    
      /*Configure the SysTick IRQ priority */
      HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority ,0U);
    
       /* Return function status */
      return HAL_OK;
    }
    

    我们重点关注以下这个函数:

      /*Configure the SysTick to have interrupt in 1ms time basis*/
    HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000U);
    

    通过CubeMx的注释,表明这个函数决定了SysTick为1ms中断一次,如果形参我们不除以1000,这个函数则会1s中断一次,那么我们HAL_Delay()的延时单位将会是1s。这是因为这个值形参就是SysTick的倒计时个数,如果把SysTick的频率值,作为SysTick倒计时个数,那么单位肯定是1。可以具体数字来协助理解,比如SysTick的频率是10000Hz,意味着它1s减去10000个数,若把10000作为到倒计时数,他们需要则是1s钟的时间。

    注意:由于这段代码存在用户代码区,即/*CODE BEGIN *//*CODE END */之间,当你在使用CubeMx对这个工程生成代码的时候,它会恢复成默认代码。

    HAL_Delay()函数的注意事项

    特别注意,在中断中使用 HAL_Delay() 很容易造成程序异常,原因是 HAL_Delay() 使用 滴答定时器的中断,如果在高于滴答定时器中断的中断函数中使用这个函数,程序将会锁死在 HAL_delay() 中,原因是,滴答定时器无法别调用, HAL_delay() 就无法跳出函数内部的 while 循环。

    HAL_Delay()函数溢出问题

    待写

    展开全文
  • 2018.1.19 HAL库详解见STM32之HAL库详解 及 手动移植 STM32 Embedded Software   工作以来一直使用ST的STM32系列芯片,ST为开发者提供了非常方便的开发库。到目前为止,有标准外设库(STD库)、HAL库、LL库 三...
  • HAL_HAL常用库函数

    2020-09-07 11:57:41
    HAL常用库函数 HAL_Init(); //初始化HAL库 Stm32_Clock_Init(360,25,2,8); //设置时钟,180Mhz __HAL_RCC_GPIOB_CLK_ENABLE(); //开启GPIOB时钟 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET); //PB1置1 常用...
  • HAL架构222

    2016-10-11 10:58:25
    HAL
  • STM32 之 HAL

    万次阅读 多人点赞 2019-05-12 11:27:08
    一般大多数都会选用标准库和HAL库,而极少部分人会通过直接配置寄存器进行开发。网上关于标准库、HAL库的描述相信是数不胜数。可是一个对于很多刚入门的朋友还是没法很直观的去真正了解这些不同开发发方式彼此之间的...
  • Android HAL

    千次阅读 2017-09-05 17:17:52
    Hardware Abstraction Layer HAL 的组成 HAL modules HAL devices 编译HAL 模块 HAL Types Binderized HALs Passthrough HALs
  • 【STM32】HAL库 STM32CubeMX教程六----定时器中断

    万次阅读 多人点赞 2019-08-13 09:10:04
    是我们学习STM32最频繁使用到的外设之一,所以一定要掌握好,这节我们讲解定时器中断,本系列教程将对应外设原理,HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用 所用工具: 1、芯片:...
  • Camera HAL 3 & HAL 1

    2019-10-28 12:00:00
    和你一起终身学习,这里是程序员 Android本篇文章主要介绍Android开发中的部分知识点,通过阅读本篇文章,您将收获以下内容:一、HAL 3 简介一、HAL 3...
  • HAL 类型

    2018-02-24 23:09:27
    作为此变化的一部分,运行 Android 8.0 的设备必须支持绑定式或直通式 HAL: 绑定式 HAL。 以 HAL 接口定义语言 (HIDL) 表示的 HAL。这些 HAL 取代了早期 Android 版本中使用的传统 HAL 和旧版 HAL。在绑定式 ...
  • HAL_UART_Transmit_IT: 无法连续使用。例如HAL_UART_Transmit_IT(...);HAL_UART_Transmit_IT(...);,一般都是只有第二条发出去了,至于为什么也不想细查。 总之HAL_UART_Transmit_IT在连续使用时要在后面加上...
  • HAL库函数说明

    2019-01-18 16:34:51
    适合新手入手STM32,看懂HAL库,学会Cubemax,基本32就可以用起来了。节省不少开发时间。
  • HAL库函数手册

    2017-03-05 16:53:35
    HAL库函数手册(英文版)
  • 关于HAL库中HAL_XX_Init 与HAL_XX_MspInit的问题 情况: 本人在用L4系列的板子做一个ADC实验的时候,使用了HAL库。由于使能ADC端口的模拟输入,需要对GPIO口进行设置以及GPIO和ADC的时钟进行使能。 这时候我不想调用...
  • HAL库函数手册.pdf

    2021-01-08 10:47:41
    HAL库函数手册.pdf
  • HAL

    千次阅读 2018-11-02 11:21:40
    参考文档 ---------------------  作者:ZCShouCSDN  来源:CSDN  ...版权声明:转载文章,转载请附上博文链接...  说到STM32的HAL库,就不得不提STM32CubeMX,其作为一个可视化的配置工具,对于开发者来说,确实...
  • Fingerprint HAL

    2018-05-19 21:32:06
    概要 如果一个设备有fingerprint sensor,那... android系统使用Fingerprint HAL层和vendor-specific library和fingerprint sensor。 为了实现fingerprint HAL, 你必须在vendor-specific library里实现fingerpri...
  • 今天我们会详细的带你学习STM32CubeMX配置外部中断,并且讲解HAL库的GPIO的各种函数,带你学习不一样的STM32 如果还没有配置过工程,请参看上一篇博客《STM32CubeMX教程二--基本使用(新建工程点亮LED灯)》 那么话...
  • 我查看HAL库文件发现的,我查了一下资料,叫半双工模式,就是用一个引脚完成发送和接收。 MX配置 选择单线,半双工,其他配置和普通串口一致。 使用 实验中发现和普通串口没什么区别。 HAL_UART_Transmit_DMA(&...
  • STM32 HAL 库延时函数 HAL_Delay 解析

    千次阅读 2020-04-12 18:59:51
    HAL 库有提供延时函数,只不过它只能实现简单的毫秒级别延时,没有实现 us 级别延时。 下面我们列出HAL 库实现延时相关的函数。首先是功能配置函数: //调用 HAL_SYSTICK_Config 函数配置每隔 1ms 中断一次 __weak ...
  • 《Android HAL 之路》 HAL 简介

    千次阅读 2016-04-20 23:01:31
    HAL层概述名称: HAL, Hardware Abstracting Layer,中文名字:硬件抽象层。 作用:对Linux内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节。向上衔接Android Runtime和Framework,向下衔接驱动程序 产生...
  • 本文章所阐述的内容仅对一下列表中最新日期之前的HAL库有效 2020-08-09:文章发表 HAL_BUSY HAL库中对于硬件的状态共有以下分级 HAL_OK: 0x00 HAL_ERROR: 0x01 HAL_BUSY: 0x02 HAL_TIMEOUT: 0x03 对于硬件IIC...
  • HAL库详解

    2020-09-03 21:26:26
    1.HAL库总纲详解 2.HAL库程序结构及初始化流程 3.简单GPIO初始化及使用 4.HAL库的基本函数
  • HAL库教程6:串口数据接收

    万次阅读 多人点赞 2019-04-09 22:50:01
      与阻塞式发送函数HAL_UART_Transmit配套,有个阻塞式的接收函数,HAL_UART_Receive,但此函数不常用,串口接收通常使用中断函数HAL_UART_Receive_IT。HAL库的串口中断比较复杂,主要流程如下:   USART1_...
  • Android底层技术:HAL驱动开发

    万人学习 2015-09-22 16:12:26
    本课程提供开发者学习Android底层的HAL(硬件抽象层)的开发方法和技术。HAL所在的位置是介于Android系统服务与Linux内核之间,HAL Driver是以library形式出现,给HAL Stub调用,供Android System架构者调用。而HAL ...
  • STM32 HALHAL_Delay配置为微秒级

    千次阅读 2019-11-30 15:50:38
    参考博客:...// HAL_RCC_GetHCLKFreq()/1000 1ms中断一次,即HAL_Delay函数延时基准为1ms // HAL_RCC_GetHCLKFreq()/100000 10us中断一次,即HAL_Delay函数延时基准为10us // HAL_RCC_Ge...
  • HAL库常用函数使用介绍--HAL_GPIO

    千次阅读 2020-08-26 11:14:14
    HAL_GPIO_Init //初始化我们需要用到的引脚的工作模式,包括具体引脚的工作速度、是否复用模式、上下拉等等参数。 void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) HAL_GPIO_DeInit //将...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 25,982
精华内容 10,392
关键字:

hal