精华内容
下载资源
问答
  • #STM32cubeMX HAL库学习记录.md
  • STM32ZET6例程HAL库学习

    2017-07-04 15:19:25
    基于STM32ZET6的HAL开发例程,有UART,ADC,DAC,PWM,EXIT,RTC农历等,可以给HAL库学习参考
  • 本文开发环境: 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()函数溢出问题

    待写

    展开全文
  • STM32 HAL库学习(一):点亮led STM32 HAL库学习(二):按键检测 未完 说明 开发板:野火stm32mini 软件:stm32cubemx,vscode,arm-gcc工具链 想使用mdk或者其他IDE,需要在cubemx里边修改IDE GitHub地址 ...

    目录

    说明

    博客或者代码有问题一定给我留言,我好改改,我虽菜但也不想误导人……
    开发板:野火stm32mini
    软件:stm32cubemx,vscode,arm-gcc工具链,make

    想使用mdk或者其他IDE,需要在cubemx里边修改IDE

    GitHub地址

    展开全文
  • 这一节是设置STM32单片机定时器PWM输出驱动舵机 pwm输出依靠的是定时器通道,stm32除了定时器6,7,都有输出pwm的通道,高级定时器通道数还要多于普通定时器。定时器的核心特质就是两个参数:频率和占空比,这里先...
  • STM32HAL库学习笔记1、新建项目2、3、4、5、 1、新建项目 选择芯片型号以及封装 选择文件目录 选择文件路径以及IDE 此处为所使用的HAL库的路径 2、 3、 4、 5、

    STM32HAL库学习笔记(一)


    新建项目

    在这里插入图片描述

    选择芯片型号以及封装

    在这里插入图片描述
    选择文件目录
    在这里插入图片描述
    选择文件路径以及IDE
    在这里插入图片描述
    此处为所使用的HAL库的路径
    在这里插入图片描述

    不勾时,代码的初始化函数都在main.c上:勾了会分开来

    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 目录HAL库外设设计思想HAL库和Cube MX相结合一、对外设的封装——句柄结构体二、外设初始化初始化结构体初始化的逻辑三、外设使用逻辑通用接口函数初始化函数I/O操作函数控制函数状态参数扩展接口函数总结 ...


    HAL库外设设计思想

    HAL库借鉴面向对象的设计思想,将外设驱动封装为对象。
    在这里插入图片描述
    采用此种开发方式有以下特点:

    • 屏蔽底层硬件:只需了解相关接口函数的功能和参数要求即可
    • 提高开发效率:开发难度较小,开发周期较短,后期的维护升级、以及硬件平台的移植等工作量小
    • 程序执行效率:由于考虑了程序的稳健性、扩充性和可移植性,程序代码比较繁琐和臃肿,执行效率较低

    HAL库和Cube MX相结合

    在这里插入图片描述

    一、对外设的封装——句柄结构体

    围绕着芯片设计的外设多种多样,功能也越来越多,为了能够统一管理这些外设,HAL库设计了统一的外设句柄数据类型PPP_HandleTypeDef(PPP代表外设名称)。如定时器句柄:
    在这里插入图片描述

    /**
      * @brief  TIM Time Base Handle Structure definition
      */
    typedef struct
    {
      TIM_TypeDef                 *Instance;     /*!< Register base address             */
      TIM_Base_InitTypeDef        Init;          /*!< TIM Time Base required parameters */
      HAL_TIM_ActiveChannel       Channel;       /*!< Active channel                    */
      DMA_HandleTypeDef           *hdma[7];      /*!< DMA Handlers array This array is accessed by a @ref DMA_Handle_index */
      HAL_LockTypeDef             Lock;          /*!< Locking object                    */
      __IO HAL_TIM_StateTypeDef   State;         /*!< TIM operation state               */
    } TIM_HandleTypeDef;
    

    保护锁是HAL库提供的一种安全机制,以避免对外设的并发访问。

    二、外设初始化

    由上述的句柄结构体可知,我们需要定义一个外设句柄指针,并向其中填充参数,其中最重要的就是初始化参数,为此HAL库为不同的外设定义了不同的初始化结构体,且相同外设的不同功能也有不同的初始化结构,如定时器,有时基单元初始化结构体、输入初始化结构体和输出初始化结构体等,分别用于输入捕获和输出比较等不同功能。
    初始化这一步骤使用CubeMX配置,可自动生成初始化代码,大大减少了开发难度。如下的初始化函数代码即由CubeMX自动生成的,带有MX前缀。

    初始化结构体

    代码如下(示例):

    /**
      * @brief  TIM Time base Configuration Structure definition
      */
    typedef struct
    {
      uint32_t Prescaler;         
      uint32_t CounterMode;  
      uint32_t Period;           
      uint32_t ClockDivision;    
      uint32_t RepetitionCounter;  
      uint32_t AutoReloadPreload; 
    } TIM_Base_InitTypeDef;
    /**
      * @brief  TIM Input Capture Configuration Structure definition
      */
    typedef struct
    {
      uint32_t  ICPolarity;  
      uint32_t ICSelection;  
      uint32_t ICPrescaler;  
      uint32_t ICFilter;     
    } TIM_IC_InitTypeDef;
    /**
      * @brief  TIM Output Compare Configuration Structure definition
      */
    typedef struct
    {
      uint32_t OCMode;   
      uint32_t Pulse;        
      uint32_t OCPolarity;    
      uint32_t OCNPolarity;   
      uint32_t OCFastMode;   
      uint32_t OCIdleState;   
      uint32_t OCNIdleState;  
    } TIM_OC_InitTypeDef;
    

    初始化的逻辑

    在这里插入图片描述
    如我们在串口笔记中讲到的串口初始化过程,在HAL_PPP_Init()初始化函数中,将句柄结构中的初始化参数存入寄存器,即进行相关参数的传入赋值,然后调用HAL_PPP_MspInit()函数完成具体的时钟、引脚等资源初始化,完成围绕具体MCU的配置;MSP函数调用完成后,回到HAL_PPP_Init()函数调用现场,根据返回值情况进入下一步,最后完成外设初始化。
    以串口为例:
    代码如下(示例):

    UART_HandleTypeDef huart1;
    DMA_HandleTypeDef hdma_usart1_rx;
    DMA_HandleTypeDef hdma_usart1_tx;
    
    void MX_USART1_UART_Init(void)
    {
      huart1.Instance = USART1;
      huart1.Init.BaudRate = 115200;
      huart1.Init.WordLength = UART_WORDLENGTH_8B;
      huart1.Init.StopBits = UART_STOPBITS_1;
      huart1.Init.Parity = UART_PARITY_NONE;
      huart1.Init.Mode = UART_MODE_TX_RX;
      huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
      huart1.Init.OverSampling = UART_OVERSAMPLING_16;
      huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
      huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
      if (HAL_UART_Init(&huart1) != HAL_OK)
      {
        Error_Handler();
      }
    }
    
    void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
    {
      GPIO_InitTypeDef GPIO_InitStruct = {0};
      if(uartHandle->Instance==USART1)
      {
        /* USART1 clock enable */
        __HAL_RCC_USART1_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();
        /**USART1 GPIO Configuration
        PA9     ------> USART1_TX
        PA10     ------> USART1_RX
        */
        GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
        /* USART1 DMA Init */
        /* USART1_RX Init */
        hdma_usart1_rx.Instance = DMA1_Channel5;
        hdma_usart1_rx.Init.Request = DMA_REQUEST_2;
        hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
        hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        hdma_usart1_rx.Init.Mode = DMA_NORMAL;
        hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW;
        if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK)
        {
          Error_Handler();
        }
        __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart1_rx);
    
        /* USART1_TX Init */
        hdma_usart1_tx.Instance = DMA1_Channel4;
        hdma_usart1_tx.Init.Request = DMA_REQUEST_2;
        hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
        hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        hdma_usart1_tx.Init.Mode = DMA_NORMAL;
        hdma_usart1_tx.Init.Priority = DMA_PRIORITY_LOW;
        if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK)
        {
          Error_Handler();
        }
        __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart1_tx);
        /* USART1 interrupt Init */
        HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(USART1_IRQn);
      }
    }
    
    

    三、外设使用逻辑

    通用接口函数

    在这里插入图片描述

    初始化函数

    通过上述的初始化步骤完成调用。

    I/O操作函数

    根据不同的功能使用,设计了三种不同的编程模型:轮询、中断和DMA。
    以后缀区分,入口参数均为外设句柄的指针,其中轮询模式还需要传入超时时间参数。三种不同编程模型的具体实现可参考串口的三篇笔记。

    控制函数

    可以在使用中,动态的调节外设的参数,如中断及时钟。

    状态参数

    可以清除和查询一些标志位,获取外设的运行状态以及出错信息。

    扩展接口函数

    设计扩展接口函数可以兼顾STM32各产品系列的特有功能和扩展功能,兼顾同一个产品系列中不同芯片的特有功能。通过单独定义后续为ex的文件来实现。如stm32fxxx_hal_ppp_ex.hstm32fxxx_hal_ppp_ex.c

    总结

    HAL库外设的使用步骤总结如下:

    1. 定义并填充PPP外设句柄结构体
    2. 如果遵循HAL库规范,通过HAL_MspInit()函数,实现外设底层资源的初始化,包括但不限于GPIO、时钟、DMA、中断等资源的初始化
    3. 调用HAL库的对应外设初始化函数,形如:HAL_PPP_Init()
    4. 初始化完成,开始使用外设
    5. 使用方法具体查看对应外设的HAL库驱动包中的说明:##### How to use this driver#####
    展开全文
  • STM32f103Hal库学习笔记 文章目录STM32f103Hal库学习笔记前言一、串口是什么?二、Hal库串口通信1.中断模式(1)CubeMx配置(2)逻辑介绍(4)代码(3)总结2.DMA模式生成一个适合你的列表创建一个表格设定内容居中...
  • STM32f103Hal库学习笔记–IWDG 文章目录STM32f103Hal库学习笔记--IWDG前言一、看门狗是什么?二、HAL库配置IWDG1.CubeMx配置2.函数相互调用逻辑总结 前言 因为火箭的程序在很多次的发射中出现了运行不正常的情况,...
  • STM32HAL库学习(一) 搭建工程模板

    万次阅读 多人点赞 2018-05-01 22:26:15
    STM32HAL库学习(一) —— 搭建工程模板一.简介 众所周知STM32开发需要用到库函数,除了寄存器版本,还有标准库和HAL库。然而,标准库已经不在更新支持,ST官方推荐使用HAL库。而HAL库开发,比较常见的也是官方...
  • STM32 HAL库学习(一):GPIO and EXIT

    千次阅读 2019-08-26 17:28:31
    STM32 HAL库学习(一):GPIO 1. HAL库 1.1 HAL库初始化 HAL_Init() 是主函数中首要处理的函数,主要用来初始化HAL库。 HAL_Init(); 即用来初始化所有的外围设备,Flash接口和系统定时器,系统中断组,初始化低级别...
  • ADC工作均为非阻塞状态 轮询模式 中断模式 DMA模式 ...HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc);...HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef* hadc);...HAL_StatusTypeDef HAL_A...
  • STM32CubeMX + HAL学习笔记,内容丰富,带源码,使用`STM32CubeMX`代码生成工具,不用关注底层配置的细节。
  • STM32 HAL库学习系列第8篇---回调函数总结

    万次阅读 多人点赞 2018-01-21 14:20:21
    普通函数与回调函数的区别:就是ST将中断封装,给使用者的API,就是标准的中断函数 对普通函数的调用: 调用程序发出对普通函数的调用后,程序执行立即转向被调用函数执行,直到被调用函数执行完毕后,再返回...
  • STM32CubeMX与HAL库学习--基本定时器定时中断背景STM32CubeMX生成初始化代码在MDK-ARM里编辑代码其他 背景 本人小白,最近在学着使用STM32CubeMX与HAL库进行开发,这是学习过程的一些记录。 STM32CubeMX版本:6.3.0 ...
  • STM32 HAL库学习(三):UART

    千次阅读 2019-08-27 11:13:11
    STM32 HAL库学习(三):UART 1 USART 与 UART // USART 是对 UART 的扩展。它除了支持异步传输之外,也支持同步传输。但目前较少应用。 UART(Universal Asynchronous Receiver/Transmitter),即通用异步收发传输...
  • STM32CubeMX与HAL库学习--简单的CAN回环测试前言STM32CubeMX生成初始化代码在MDK-ARM里编辑代码其他 前言 本人小白,最近看了CAN协议与STM32的bxCAN外设的一些资料,简单地做个CAN回环测试练习一下。这只是一个初学...
  • STM32HAL库学习必需软件和硬件 一、软件 1.MDK5(KEIL网站下载) http://www.keil.com/ 2.STM32CUBEMX 3.JAVA https://www.java.com/zh_CN/ 4.STM32 PACK支持文件 ...
  • STM32F030R8Tx HAL例程学习例程源码,包含SPI,UART,PWM,TIM,RTT_VIEW,PWR,KEYSCAN,RTC,SYSTICK,IWDG,FLAH,ADC,UART OTA等例程,且每个例程都与博客对应,欢迎下载
  • 本次测试再次换了一块板子,用的是STM32F103RCT6,并不是我想换哈,hal库无论你用啥板子,函数几乎都没发生改变,底层驱动又是软件默认帮我们设置好的,之所以换板子是因为系列中中低端的大多数板子是没有DAC功能的...
  • STM32 HAL库学习心得

    千次阅读 2019-02-14 22:43:44
    最近一段时间在将一个W25Q存储器芯片的程序从STM32F103Z移植到STM32F030,原程序用的是F1的Hal库,所以030也用Hal库,但是是F0的HAL库。 简单的把程序改下,能编译成功了,但是哪都走不通,连串口都不通! 无奈之...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 8,222
精华内容 3,288
关键字:

hal库学习