当前位置: 首页 > news >正文

STM32 HAL库函数入门指南:从原理到实践

1 STM32 HAL库概述

STM32 HAL(Hardware Abstraction Layer)库是ST公司专门为STM32系列微控制器开发的一套硬件抽象层函数库。它的核心设计理念是在应用层与硬件层之间建立一个抽象层,这个抽象层屏蔽了底层硬件的具体实现细节,为开发者提供了一套统一的、标准化的应用程序接口(API)。这种设计极大地提高了代码的可移植性和重用性,使得开发者能够更加专注于应用功能的实现,而不必过多关注硬件细节。

1.1 HAL库架构设计

HAL库的架构设计采用了模块化的思想,将整个库函数分为多个功能模块。每个模块都对应着STM32微控器的一个或多个外设,如GPIO、UART、SPI、I2C等。这种模块化的设计使得代码结构清晰,便于管理和维护。在每个模块中,又将功能细分为初始化配置、控制操作、状态查询等不同类别的函数,形成了一个层次分明的函数调用体系。

1.2 错误处理

在错误处理方面,HAL库实现了完善的错误检测和处理机制。每个HAL函数都会返回执行状态,通过HAL_StatusTypeDef枚举类型来表示函数执行的结果,包括HAL_OK、HAL_ERROR、HAL_BUSY和HAL_TIMEOUT等状态。这种机制使得开发者能够及时发现和处理程序运行中的异常情况,提高了程序的可靠性和稳定性。

1.3 驱动模板

HAL库还提供了丰富的外设驱动模板和示例程序。这些模板和示例涵盖了绝大多数常用的应用场景,开发者可以基于这些模板快速开发自己的应用程序。每个外设驱动都包含了完整的初始化代码、中断处理函数和基本的操作函数,为开发者提供了可靠的参考。

1.4 中断处理

在中断处理方面,HAL库采用了统一的中断处理框架。它定义了标准的中断回调函数接口,开发者只需要实现相应的回调函数,就可以处理各种中断事件。这种设计大大简化了中断处理的编程工作,同时也保证了中断处理代码的规范性和可维护性。

1.5 HAL库优势

为了提高程序的执行效率,HAL库在设计时充分考虑了性能优化问题。它提供了多种操作模式,如轮询模式、中断模式和DMA模式,开发者可以根据实际需求选择合适的操作模式。同时,HAL库也支持低功耗模式的配置和管理,有助于开发低功耗应用。

在使用HAL库时,需要注意的是,所有的外设操作都需要通过相应的句柄(Handle)来进行。句柄是一个包含外设配置信息和状态信息的数据结构,它在外设初始化时创建,在后续的操作中用于标识和控制特定的外设实例。这种基于句柄的设计方式,既保证了代码的可重入性,也便于多外设的并行操作。

HAL库还提供了强大的调试支持。通过设置适当的调试级别,开发者可以获取详细的运行时信息,这对于问题定位和性能优化非常有帮助。HAL库还集成了断言机制,可以在开发阶段及时发现和定位程序中的逻辑错误。

2 HAL库使用步骤

使用HAL库开发程序通常遵循以下步骤:需要配置时钟系统。这包括设置系统时钟源、配置PLL倍频系统以及设置各个总线的分频系数。这些配置通常在SystemClock_Config()函数中完成。初始化外设使用的GPIO引脚。每个外设都需要特定的GPIO引脚配置,包括引脚的工作模式、上下拉状态等。配置并初始化具体的外设模块。这包括设置外设的工作模式、中断优先级等参数。

2.1 工程初始化阶段

在使用HAL库开发STM32项目时,第一步是建立基础工程框架。这需要包含必要的HAL库头文件,其中最基本的是"stm32f4xx_hal.h"(以STM32F4系列为例)。同时,需要在项目中添加相应的HAL库源文件,这些文件通常位于ST官方提供的固件包中。

2.2 系统初始化

在main函数的开始,必须首先调用HAL_Init()函数来初始化HAL库。这个函数会完成以下几个重要任务:设置系统滴答定时器(SysTick)、初始化默认的HAL库状态、配置NVIC中断分组等。紧接着需要配置系统时钟,这通常通过调用SystemClock_Config()函数实现。该函数负责设置PLL、AHB、APB1、APB2等时钟参数,确保系统以正确的频率运行。典型的初始化代码结构如下:

int main(void)
{HAL_Init();                    //HAL库初始化SystemClock_Config();          //系统时钟配置/* 用户代码开始 */while (1){}
}

2.3 外设时钟使能

外设时钟使能是使用任何外设前的必要步骤。在STM32中,所有外设都需要先使能其时钟才能正常工作。HAL库提供了统一的宏定义来完成这个任务,例如:

__HAL_RCC_GPIOA_CLK_ENABLE();     //使能GPIOA时钟
__HAL_RCC_USART1_CLK_ENABLE();    //使能USART1时钟
__HAL_RCC_DMA1_CLK_ENABLE();      //使能DMA1时钟

2.4 外设初始化配置

HAL库采用句柄(Handle)的方式管理每个外设,因此需要先定义相应的句柄结构体,然后进行参数配置。以GPIO为例,配置过程包括:定义GPIO初始化结构体、设置引脚参数(模式、上下拉、速度等)、调用初始化函数。示例代码如下:

GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

2.5 中断配置

如果外设需要使用中断,则需要配置NVIC并编写中断处理函数。HAL库提供了统一的中断回调函数机制,用户只需要实现相应的回调函数即可。例如:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{if(GPIO_Pin == GPIO_PIN_0){//中断处理代码}
}

2.6 错误处理

几乎所有的HAL库函数都会返回执行状态(HAL_OK、HAL_ERROR等),建议在关键操作后都进行状态检查:

if(HAL_UART_Init(&huart1) != HAL_OK)
{Error_Handler();
}

2.7 外设功能使用

HAL库为每个外设提供了完整的操作函数集,包括数据收发、状态查询、参数修改等。这些函数都遵循统一的命名规范:HAL_PPP_Function(),其中PPP代表具体的外设名称。例如:

//UART发送数据
HAL_UART_Transmit(&huart1, TxData, sizeof(TxData), HAL_MAX_DELAY);//ADC开始转换
HAL_ADC_Start(&hadc1);//定时器启动
HAL_TIM_Base_Start_IT(&htim2);

2.8 使用总结

初始化完成后,就可以在主循环中实现具体的应用功能。值得注意的是,HAL库的大多数函数都提供了阻塞和非阻塞两种版本,可以根据应用需求选择合适的方式。UART传输既可以使用阻塞式的HAL_UART_Transmit(),也可以使用非阻塞的HAL_UART_Transmit_IT()或HAL_UART_Transmit_DMA()。

在开发过程中,建议充分利用HAL库提供的DEBUG功能。可以通过配置assert_param宏来启用参数检查,这对于调试程序非常有帮助。同时,建议养成良好的错误处理习惯,合理使用HAL_Delay()函数进行延时,避免使用空循环延时。

3 GPIO的HAL库函数

GPIO(通用输入输出接口)是STM32微控制器最基础也是最常用的外设之一。HAL库为GPIO操作提供了一套完整的函数库,这些函数不仅简化了GPIO的配置和控制过程,还提供了多种工作模式的灵活配置选项。

3.1 了解GPIO结构

在使用GPIO之前,首先需要了解GPIO的基本结构。STM32的每个GPIO引脚都可以配置为不同的工作模式,包括输入模式、输出模式、复用功能模式和模拟模式每个引脚还可以配置上拉、下拉或者浮空状态,并且可以设置不同的输出速度等级。HAL库通过GPIO_InitTypeDef结构体来管理这些配置参数。

GPIO的配置过程主要包含以下几个关键步骤:

  • 使能GPIO时钟
  • 定义GPIO初始化结构体
  • 配置GPIO参数
  • 调用初始化函数

3.2 使能GPIO时钟

必须使能对应GPIO端口的时钟。这是因为STM32采用了时钟门控技术来降低功耗,只有使能了时钟的外设才能正常工作。时钟使能可以通过__HAL_RCC_GPIOx_CLK_ENABLE()宏函数来实现,其中x表示具体的GPIO端口(A、B、C等)

3.3 定义GPIO初始化结构体

接下来是GPIO初始化结构体的配置。GPIO_InitTypeDef结构体包含了以下重要参数:

  • Pin:指定要配置的引脚,可以同时配置多个引脚
  • Mode:设置引脚的工作模式,如输入、输出、中断等
  • Pull:配置引脚的上拉/下拉状态
  • Speed:设置引脚的输出速度
  • Alternate:当使用复用功能时,指定具体的复用功能编号

在实际的GPIO操作中,HAL库提供了一系列函数来实现不同的控制需求。HAL_GPIO_Init()函数用于初始化GPIO引脚,它会根据初始化结构体中的配置参数来设置相应的寄存器。对于输出操作,HAL_GPIO_WritePin()函数可以设置引脚的输出状态,HAL_GPIO_TogglePin()函数可以翻转引脚的状态。而对于输入操作,HAL_GPIO_ReadPin()函数可以读取引脚的当前电平状态。

3.4 中断应用

GPIO可以配置为外部中断源。通过将Mode参数设置为GPIO_MODE_IT_RISING(上升沿触发)、GPIO_MODE_IT_FALLING(下降沿触发)或GPIO_MODE_IT_RISING_FALLING(双边沿触发),可以实现对引脚电平变化的中断检测。当配置为中断模式时,还需要配置中断优先级并使能中断。HAL库提供了HAL_GPIO_EXTI_IRQHandler()函数来处理GPIO外部中断,并通过HAL_GPIO_EXTI_Callback()回调函数来实现用户的具体中断服务程序。

对于需要快速响应的应用,HAL库还提供了一些直接操作GPIO寄存器的宏。比如__HAL_GPIO_SET_PIN()和__HAL_GPIO_RESET_PIN()可以直接设置或清除引脚状态,这些操作比调用标准的HAL函数更快。但使用这些宏时需要格外小心,因为它们会直接操作硬件寄存器。

在实际应用中,一个典型的GPIO配置示例如下:

void GPIO_LED_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};// 使能GPIOA时钟__HAL_RCC_GPIOA_CLK_ENABLE();// LED引脚配置GPIO_InitStruct.Pin = GPIO_PIN_5;                  // 选择PA5引脚GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;        // 推挽输出模式GPIO_InitStruct.Pull = GPIO_NOPULL;                // 无上拉下拉GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;       // 低速模式// 初始化GPIOHAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

在进行GPIO配置时,还需要注意一些特殊情况的处理。例如,当GPIO引脚被配置为复用功能时,除了常规的GPIO配置外,还需要正确设置复用功能编号。同时,某些引脚可能有默认的复用功能(如调试端口),在使用这些引脚时需要特别注意是否会影响系统的其他功能

3.5 GPIO锁定功能

HAL库还提供了GPIO锁定功能,通过HAL_GPIO_LockPin()函数可以锁定引脚的配置,防止配置被意外修改。这在一些需要高可靠性的应用中特别有用。但需要注意的是,一旦引脚被锁定,在系统复位之前将无法修改其配置。

4 HAL库中断配置与处理

中断系统是STM32单片机的核心功能之一,它允许微控制器及时响应外部事件和内部状态变化。在HAL库中,中断的配置和处理采用了统一的框架,使得中断处理变得更加规范和简洁。

中断源可以分为外部中断和内部中断两大类。外部中断主要来自GPIO引脚的电平变化,而内部中断则包括定时器中断、ADC转换完成中断、UART接收发送中断等。无论是哪种中断,其配置过程都遵循相似的步骤。

使用中断时需要:

  1. 配置NVIC中断控制器
  2. 设置中断优先级
  3. 编写中断服务函数

4.1 外部中断配置

在STM32中,任何GPIO引脚都可以配置为外部中断源。配置过程主要包括以下步骤:

// 第一步:GPIO初始化结构体配置
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();    //使能GPIO时钟GPIO_InitStruct.Pin = GPIO_PIN_0;             //选择PA0引脚
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;   //上升沿触发中断
GPIO_InitStruct.Pull = GPIO_PULLDOWN;         //下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; //高速模式HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);// 第二步:配置NVIC
HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);    //设置中断优先级
HAL_NVIC_EnableIRQ(EXTI0_IRQn);            //使能中断线

对于中断处理,HAL库采用了分层的方式首先是中断服务函数(ISR),这是在启动文件中定义的一级中断处理函数。然后是HAL库的中断处理函数,它会进行必要的状态检查和清除中断标志。最后是用户的回调函数,这是实际进行业务处理的地方。以外部中断为例:

// 中断服务函数(在启动文件中)
void EXTI0_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}// 用户回调函数(在用户代码中实现)
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{if(GPIO_Pin == GPIO_PIN_0){// 在这里添加中断处理代码HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);    //翻转LED}
}

4.2 内部中断配置

内部中断的配置也遵循类似的模式。以定时器中断为例,配置过程如下:

// 定时器初始化配置
TIM_HandleTypeDef htim2;htim2.Instance = TIM2;
htim2.Init.Prescaler = 7199;                  //预分频值
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;  //向上计数模式
htim2.Init.Period = 9999;                     //周期值
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{Error_Handler();
}// 启动定时器中断
HAL_TIM_Base_Start_IT(&htim2);// 配置NVIC
HAL_NVIC_SetPriority(TIM2_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);

对应的中断处理函数:

void TIM2_IRQHandler(void)
{HAL_TIM_IRQHandler(&htim2);
}void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM2){// 定时器中断处理代码}
}

4.3 中断优先级配置

在使用中断时,需要特别注意中断优先级的配置。STM32使用抢占优先级和子优先级的组合来管理中断优先级。HAL库在初始化时会设置默认的优先级分组(通常是4位抢占优先级,0位子优先级)。可以通过HAL_NVIC_SetPriorityGrouping()函数修改分组方式:

// 配置中断优先级分组
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);  //4位抢占优先级,0位子优先级

在中断处理中,还需要注意以下几点:

  • 中断处理函数应该尽量简短,避免在中断中执行耗时操作。如果需要处理复杂任务,建议设置标志位,在主循环中处理。
  • 避免在中断中使用printf等耗时的函数,这可能会导致其他中断得不到及时响应。
  • 合理使用中断标志位和状态检查,确保中断处理的可靠性:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart->Instance == USART1){if(__HAL_UART_GET_FLAG(huart, UART_FLAG_RXNE)){// 接收到新数据__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_RXNE);}}
}
  •  在使用DMA时,要注意配置相应的DMA中断:
// DMA中断配置
HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);// DMA中断回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart->Instance == USART1){HAL_UART_Receive_DMA(huart, RxBuffer, RXBUFFERSIZE);}
}

5 定时器的HAL库函数

STM32微控制器的定时器系统是一个功能强大的模块,它包含了多种类型的定时器,可以满足不同应用场景的需求。根据功能复杂度,STM32的定时器可以分为三类:基本定时器(Basic Timer)、通用定时器(General-Purpose Timer)和高级定时器(Advanced Timer)。HAL库为这些定时器提供了统一的操作接口,使得开发者能够方便地实现各种定时功能。

5.1 基本结构

基本定时器是最简单的定时器类型,主要用于基本的定时功能和触发DAC转换它只包含一个16位或32位向上计数器、预分频器和重装载寄存器。通用定时器在基本定时器的基础上增加了捕获/比较通道,可以用于PWM生成、输入捕获等功能。而高级定时器则具有最完整的功能,除了包含通用定时器的所有特性外,还支持互补输出、死区控制、断路控制等高级功能,特别适合于电机控制等应用。

5.2 工作原理

在使用定时器之前,首先需要了解定时器的基本工作原理。定时器的时基单元包含了预分频器(Prescaler)和计数器(Counter)。预分频器用于对输入时钟进行分频,从而降低计数频率;计数器则根据配置的方向(向上、向下或双向)进行计数,当计数值达到设定的自动重装载值(ARR)时,会产生更新事件,计数器重新开始计数。定时器的时间计算公式如下:

定时时间 = (预分频值 + 1) * (重装载值 + 1) / 定时器时钟频率

5.3 配置步骤

HAL库通过TIM_HandleTypeDef结构体来管理定时器的配置和状态。定时器的基本配置过程包括以下步骤:首先使能定时器时钟,然后配置定时器的基本参数,包括预分频值、计数模式、重装载值等。如果需要使用中断功能,还需要配置NVIC并使能相应的中断。

定时器配置步骤:

  1. 使能定时器时钟
  2. 配置定时器基本参数
  3. 配置中断(如需要)
  4. 启动定时器

关键函数:

  • HAL_TIM_Base_Init():基本定时器初始化
  • HAL_TIM_PWM_Init():PWM模式初始化
  • HAL_TIM_Base_Start_IT():启动定时器中断

以下是一个基本定时器配置的示例:

void Timer_Init(void)
{TIM_HandleTypeDef htim2;// 使能TIM2时钟__HAL_RCC_TIM2_CLK_ENABLE();// 基本配置htim2.Instance = TIM2;htim2.Init.Prescaler = 7199;                // 预分频值htim2.Init.CounterMode = TIM_COUNTERMODE_UP;// 向上计数模式htim2.Init.Period = 9999;                   // 重装载值htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;// 初始化定时器HAL_TIM_Base_Init(&htim2);// 启动定时器HAL_TIM_Base_Start_IT(&htim2);// 配置NVICHAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);HAL_NVIC_EnableIRQ(TIM2_IRQn);
}// 定时器中断服务函数
void TIM2_IRQHandler(void)
{HAL_TIM_IRQHandler(&htim2);
}// 定时器中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM2){// 在这里添加定时器中断处理代码}
}

5.5 PWM应用

对于PWM应用,HAL库提供了专门的PWM配置和控制函数。PWM配置需要设置定时器的基本参数,并配置输出通道的参数,包括PWM模式、极性、输出状态等。以下是PWM配置的示例:

void PWM_Init(void)
{TIM_HandleTypeDef htim3;TIM_OC_InitTypeDef sConfigOC = {0};// 配置定时器基本参数htim3.Instance = TIM3;htim3.Init.Prescaler = 71;htim3.Init.Period = 999;htim3.Init.CounterMode = TIM_COUNTERMODE_UP;HAL_TIM_PWM_Init(&htim3);// 配置PWM通道sConfigOC.OCMode = TIM_OCMODE_PWM1;sConfigOC.Pulse = 500;  // 设置占空比为50%sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);// 启动PWM输出HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
}

定时器的输入捕获功能用于测量外部信号的周期、脉宽等参数。配置输入捕获时,需要设置捕获通道的触发边沿、滤波器、预分频等参数。HAL库提供了完整的输入捕获函数集,包括配置函数和捕获回调函数。

5.6 精确时序控制

对于需要精确时序控制的应用,定时器还可以配置为主从模式,实现多个定时器的同步运行。通过设置触发源和从模式,可以实现定时器之间的级联控制,这在复杂的定时控制场景中特别有用。

在使用定时器时,需要特别注意以下几点:

  1. 时钟配置要准确,确保定时器的时钟源和频率符合要求
  2. 中断优先级的合理设置,避免中断优先级冲突
  3. 在中断服务程序中避免执行耗时操作
  4. PWM应用中注意死区时间的设置(使用高级定时器时)
  5. 定时器溢出时间的计算要考虑时钟频率的实际值

6  UART通信的HAL库函数

UART(Universal Asynchronous Receiver/Transmitter)是STM32中最常用的串行通信接口之一,它实现了异步串行通信,广泛应用于设备间的数据传输和调试。在HAL库中,UART的配置和使用都有统一的接口函数。

6.1 配置步骤

首先,我们来看UART的基本初始化配置。在使用UART前,需要先使能相关的时钟并配置对应的GPIO引脚。

  • 配置GPIO引脚
  • 配置UART参数
  • 使能UART
  • 配置中断(如需要)

典型的初始化代码如下:

// 定义UART句柄
UART_HandleTypeDef huart1;void UART1_Init(void)
{// 第一步:使能时钟__HAL_RCC_USART1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();// 第二步:配置GPIOGPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;  // TX:PA9, RX:PA10GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;        // 复用推挽输出GPIO_InitStruct.Pull = GPIO_PULLUP;            // 上拉GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;  // 高速GPIO_InitStruct.Alternate = GPIO_AF7_USART1;   // 复用为USART1HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);// 第三步:配置UART参数huart1.Instance = USART1;huart1.Init.BaudRate = 115200;                 // 波特率huart1.Init.WordLength = UART_WORDLENGTH_8B;   // 8位数据位huart1.Init.StopBits = UART_STOPBITS_1;        // 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;if (HAL_UART_Init(&huart1) != HAL_OK){Error_Handler();}
}

UART通信支持多种数据传输模式,包括轮询模式、中断模式和DMA模式。让我们分别来看这些模式的使用方法。

6.2 轮询模式

轮询模式是最简单的传输方式,适用于数据量小、实时性要求不高的场合:

// 发送数据(阻塞式)
uint8_t TxData[] = "Hello World\r\n";
HAL_UART_Transmit(&huart1, TxData, sizeof(TxData), HAL_MAX_DELAY);// 接收数据(阻塞式)
uint8_t RxData[20];
HAL_UART_Receive(&huart1, RxData, sizeof(RxData), HAL_MAX_DELAY);

6.3 中断模式

中断模式适用于需要及时响应但数据量不大的场合。使用中断模式需要配置NVIC并实现相应的回调函数:

// 配置UART中断
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);// 启动中断接收
HAL_UART_Receive_IT(&huart1, RxData, 1);  // 每次接收1个字节// 中断服务函数
void USART1_IRQHandler(void)
{HAL_UART_IRQHandler(&huart1);
}// 接收完成回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart->Instance == USART1){// 处理接收到的数据// 重新启动接收HAL_UART_Receive_IT(&huart1, RxData, 1);}
}

6.4 DMA模式

DMA模式最适合大量数据的传输,它可以在不占用CPU的情况下完成数据传输:

// DMA配置
DMA_HandleTypeDef hdma_usart1_rx;
DMA_HandleTypeDef hdma_usart1_tx;void UART_DMA_Init(void)
{// 使能DMA时钟__HAL_RCC_DMA2_CLK_ENABLE();// 配置DMA参数(以发送DMA为例)hdma_usart1_tx.Instance = DMA2_Stream7;hdma_usart1_tx.Init.Channel = DMA_CHANNEL_4;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;hdma_usart1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;HAL_DMA_Init(&hdma_usart1_tx);// 关联DMA与UART__HAL_LINKDMA(&huart1, hdmatx, hdma_usart1_tx);// 配置DMA中断HAL_NVIC_SetPriority(DMA2_Stream7_IRQn, 0, 0);HAL_NVIC_EnableIRQ(DMA2_Stream7_IRQn);
}// 使用DMA发送数据
uint8_t TxBuffer[] = "DMA Test\r\n";
HAL_UART_Transmit_DMA(&huart1, TxBuffer, sizeof(TxBuffer));// DMA传输完成回调
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{if(huart->Instance == USART1){// 发送完成处理}
}

6.5 串口调试

为了实现更好的串口调试功能,我们通常会重定向printf函数到串口:

// 重定向printf到串口
int fputc(int ch, FILE *f)
{uint8_t temp[1] = {ch};HAL_UART_Transmit(&huart1, temp, 1, HAL_MAX_DELAY);return ch;
}

在实际应用中,还需要考虑数据的封装和解析。这里给出一个简单的数据帧处理示例:

// 定义数据帧结构
typedef struct
{uint8_t header;    // 帧头 0xAAuint8_t length;    // 数据长度uint8_t data[32];  // 数据uint8_t checksum;  // 校验和
} UART_Frame_TypeDef;// 数据帧处理
void UART_Frame_Process(uint8_t data)
{static UART_Frame_TypeDef frame;static uint8_t rxState = 0;static uint8_t rxCount = 0;switch(rxState){case 0:  // 等待帧头if(data == 0xAA){frame.header = data;rxState = 1;}break;case 1:  // 接收长度frame.length = data;rxCount = 0;rxState = 2;break;case 2:  // 接收数据frame.data[rxCount++] = data;if(rxCount >= frame.length)rxState = 3;break;case 3:  // 接收校验和frame.checksum = data;// 验证校验和if(Check_Sum(&frame) == HAL_OK){// 数据帧处理}rxState = 0;break;}
}

6.6 注意事项

在使用UART时,还需要注意以下几点:

  1. 波特率计算:实际波特率可能与设定值有偏差,需要考虑时钟频率的影响。
  2. 数据缓冲:在中断或DMA接收时,要注意缓冲区大小的设置,避免溢出。
  3. 错误处理:要处理好帧错误、噪声错误、溢出错误等异常情况:
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{if(huart->Instance == USART1){if(__HAL_UART_GET_FLAG(huart, UART_FLAG_ORE)){__HAL_UART_CLEAR_OREFLAG(huart);}// 重新启动接收HAL_UART_Receive_IT(huart, RxData, 1);}
}

7  ADC转换器的HAL库函数

ADC(模数转换器)是STM32中重要的模拟外设,它能将模拟信号转换为数字信号。STM32的ADC具有多通道、高精度、可配置采样时间等特点。HAL库提供了完整的ADC操作接口,使得ADC的配置和使用变得简单直观。

ADC配置步骤:

  1. 配置ADC时钟
  2. 配置ADC通道
  3. 配置采样时间
  4. 启动ADC转换

7.1 ADC基本配置

使用ADC前需要完成时钟使能和GPIO配置:

// 定义ADC句柄
ADC_HandleTypeDef hadc1;void ADC1_Init(void)
{// 使能时钟__HAL_RCC_ADC1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();// 配置ADC引脚GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.Pin = GPIO_PIN_0;          // PA0作为ADC通道0GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;   // 模拟输入模式GPIO_InitStruct.Pull = GPIO_NOPULL;        // 无上下拉HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);// 配置ADC参数hadc1.Instance = ADC1;hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;     // ADC时钟4分频hadc1.Init.Resolution = ADC_RESOLUTION_12B;                // 12位分辨率hadc1.Init.ScanConvMode = DISABLE;                        // 禁用扫描模式hadc1.Init.ContinuousConvMode = ENABLE;                   // 连续转换模式hadc1.Init.DiscontinuousConvMode = DISABLE;               // 禁用不连续模式hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;  // 禁用外部触发hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;               // 数据右对齐hadc1.Init.NbrOfConversion = 1;                           // 转换通道数量hadc1.Init.DMAContinuousRequests = DISABLE;               // 禁用DMA连续请求hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;            // 单次转换结束选择if (HAL_ADC_Init(&hadc1) != HAL_OK){Error_Handler();}
}

7.2 配置ADC通道

STM32的ADC支持多个通道,每个通道都可以单独配置采样时间:

void ADC_Channel_Config(void)
{ADC_ChannelConfTypeDef sConfig = {0};// 配置通道0sConfig.Channel = ADC_CHANNEL_0;           // 选择通道0sConfig.Rank = 1;                          // 转换序列顺序sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;  // 采样时间if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){Error_Handler();}
}

7.3 ADC的采集方式

包括单次采集、连续采集、DMA采集等。下面分别介绍这些模式:

  • 单次采集模式:
// 启动单次转换
HAL_ADC_Start(&hadc1);
// 等待转换完成
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
// 获取转换结果
uint32_t adcValue = HAL_ADC_GetValue(&hadc1);
// 停止ADC转换
HAL_ADC_Stop(&hadc1);
  • 连续采集模式:
// 启动连续转换
HAL_ADC_Start(&hadc1);// 在主循环中读取数据
while(1)
{if(HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY) == HAL_OK){uint32_t value = HAL_ADC_GetValue(&hadc1);// 处理ADC数据}
}
  • 中断模式:
// 配置ADC中断
HAL_NVIC_SetPriority(ADC_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ADC_IRQn);// 启动中断模式转换
HAL_ADC_Start_IT(&hadc1);// ADC转换完成回调函数
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{if(hadc->Instance == ADC1){uint32_t value = HAL_ADC_GetValue(hadc);// 处理ADC数据}
}
  • DMA模式(适合多通道采集):
// DMA配置
DMA_HandleTypeDef hdma_adc1;
uint16_t ADC_DMA_Buffer[8];  // DMA缓冲区void ADC_DMA_Init(void)
{__HAL_RCC_DMA2_CLK_ENABLE();hdma_adc1.Instance = DMA2_Stream0;hdma_adc1.Init.Channel = DMA_CHANNEL_0;hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;hdma_adc1.Init.Mode = DMA_CIRCULAR;hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;HAL_DMA_Init(&hdma_adc1);__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);// 启动ADC DMA传输HAL_ADC_Start_DMA(&hadc1, (uint32_t*)ADC_DMA_Buffer, 8);
}

7.4 实际应用

我们经常需要对ADC数据进行处理,例如滤波、校准等:

// 移动平均滤波
#define FILTER_LENGTH 16
uint16_t filter_buffer[FILTER_LENGTH];
uint8_t filter_index = 0;uint16_t ADC_Filter(uint16_t new_value)
{uint32_t sum = 0;filter_buffer[filter_index] = new_value;filter_index = (filter_index + 1) % FILTER_LENGTH;for(uint8_t i = 0; i < FILTER_LENGTH; i++){sum += filter_buffer[i];}return sum / FILTER_LENGTH;
}// ADC值转换为实际电压
float ADC_To_Voltage(uint16_t adc_value)
{return (float)adc_value * 3.3f / 4096.0f;  // 12位ADC, 参考电压3.3V
}

7.5 多通道扫描

ADC还支持多通道扫描模式,适合需要采集多个通道的应用:

// 多通道配置
void ADC_MultiChannel_Config(void)
{ADC_ChannelConfTypeDef sConfig = {0};// 配置通道0sConfig.Channel = ADC_CHANNEL_0;sConfig.Rank = 1;sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;HAL_ADC_ConfigChannel(&hadc1, &sConfig);// 配置通道1sConfig.Channel = ADC_CHANNEL_1;sConfig.Rank = 2;HAL_ADC_ConfigChannel(&hadc1, &sConfig);// 启动DMA传输HAL_ADC_Start_DMA(&hadc1, (uint32_t*)ADC_DMA_Buffer, 2);
}

在使用ADC时,需要注意以下几点:

  1. 采样时间的选择:采样时间越长,转换结果越准确,但会降低采样速率。
  2. 参考电压的影响:ADC转换结果与参考电压有关,需要保证参考电压的稳定性。
  3. 输入信号范围:确保输入信号不超过ADC的量程范围(0~VREF)。
  4. 抗干扰措施:在ADC输入端加入RC滤波电路;PCB布局时注意模拟地和数字地的分离;使用独立的模拟电源供电

8 DMA的HAL库函数

STM32的HAL库提供了一系列用于配置和控制DMA传输的函数。DMA初始化的核心函数是HAL_DMA_Init(),该函数需要传入一个DMA_HandleTypeDef结构体指针,该结构体包含了DMA的配置信息。在使用DMA之前,我们首先需要配置DMA的基本参数,包括传输方向、源地址和目标地址的数据宽度、地址是否自增、传输优先级等。

DMA配置步骤:

  1. 使能DMA时钟
  2. 配置DMA传输参数
  3. 配置DMA中断
  4. 启动DMA传输

8.1 DMA初始化

以下是DMA初始化的核心代码示例:

void DMA_Init(void) {hdma_usart1_rx.Instance = DMA1_Stream5;hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4;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_CIRCULAR;hdma_usart1_rx.Init.Priority = DMA_PRIORITY_HIGH;hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;HAL_DMA_Init(&hdma_usart1_rx);
}

8.2 启动普通传输

除了初始化函数,HAL库还提供了启动传输、停止传输、查询状态等功能函数。HAL_DMA_Start()用于启动普通传输,HAL_DMA_Start_IT()用于启动带中断的传输。这些函数的原型分别如下:

HAL_StatusTypeDef HAL_DMA_Start(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength);HAL_StatusTypeDef HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength);

8.3 DMA使用实例

ADC连续采样

在这个例子中,我们使用DMA将ADC采样数据直接传输到内存数组中,无需CPU干预:

#define ADC_BUFFER_SIZE 1000uint16_t adc_buffer[ADC_BUFFER_SIZE];void ADC_DMA_Config(void) {// ADC配置部分hadc1.Instance = ADC1;hadc1.Init.Resolution = ADC_RESOLUTION_12B;hadc1.Init.ContinuousConvMode = ENABLE;hadc1.Init.ScanConvMode = DISABLE;HAL_ADC_Init(&hadc1);// DMA配置部分hdma_adc1.Instance = DMA2_Stream0;hdma_adc1.Init.Channel = DMA_CHANNEL_0;hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;hdma_adc1.Init.Mode = DMA_CIRCULAR;hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;HAL_DMA_Init(&hdma_adc1);// 关联ADC和DMA__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);// 启动ADC和DMA传输HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_BUFFER_SIZE);
}

在实际开发中,建议参考ST官方提供的示例代码和文档,深入理解每个模块的具体使用方法。同时,建议在使用HAL库时养成良好的错误处理习惯,确保程序的稳定性和可靠性。

相关文章:

STM32 HAL库函数入门指南:从原理到实践

1 STM32 HAL库概述 STM32 HAL(Hardware Abstraction Layer)库是ST公司专门为STM32系列微控制器开发的一套硬件抽象层函数库。它的核心设计理念是在应用层与硬件层之间建立一个抽象层&#xff0c;这个抽象层屏蔽了底层硬件的具体实现细节&#xff0c;为开发者提供了一套统一的、…...

Harmony面试模版

1. 自我介绍 看表达能力、沟通能力 面试记录&#xff1a; 2. 进一步挖掘 2.1. 现状 目前是在职还是离职&#xff0c;如果离职&#xff0c;从上一家公司离职的原因 2.2. 项目经验 如果自我介绍工作项目经验讲的不够清楚&#xff0c;可以根据简历上的信息再进一步了解 面试记…...

数据结构知识点

【1】栈&#xff08;stack&#xff09; C 标准库提供了 std::stack 模板类&#xff0c;用于实现栈的功能。std::stack 是基于其他容器&#xff08;如 std::vector、std::deque 或 std::list&#xff09;实现的适配器类。 std::stack 可以使用不同的底层容器来实现&#xff0c…...

RPC 简介

RPC&#xff08;Remote Procedure Call&#xff0c;远程过程调用&#xff09;是一种通过网络请求执行远程服务器上的代码的技术&#xff0c;使得开发者可以调用远程系统中的函数&#xff0c;就像调用本地函数一样。它隐藏了底层网络通信的细节&#xff0c;简化了分布式系统的开…...

qBittorent访问webui时提示unauthorized解决方法

现象描述 QNAP使用Container Station运行容器&#xff0c;使用Docker封装qBittorrent时&#xff0c;访问IP:PORT的方式后无法访问到webui&#xff0c;而是提示unauthorized&#xff0c;如图&#xff1a; 原因分析 此时通常是由于设备IP与qBittorrent的ip地址不在同一个网段导致…...

Windows部署NVM并下载多版本Node.js的方法(含删除原有Node的方法)

本文介绍在Windows电脑中&#xff0c;下载、部署NVM&#xff08;node.js version management&#xff09;环境&#xff0c;并基于其安装不同版本的Node.js的方法。 在之前的文章Windows系统下载、部署Node.js与npm环境的方法&#xff08;https://blog.csdn.net/zhebushibiaoshi…...

【大数据】机器学习 -----关于data.csv数据集分析案例

打开表 import pandas as pd df2 pd.read_csv("data.csv",encoding"gbk") df2.head()查看数据属性&#xff08;列标题&#xff0c;表形状&#xff0c;类型&#xff0c;行标题&#xff0c;值&#xff09; print("列标题:",df2.columns)Data…...

使用Newtonsoft.Json插件,打包至Windows平台显示不支持

在unity中使用Newtonsoft.Json插件&#xff0c;打包至Windows遇到的问题 string json JsonConvert.SerializeObject(msg); 打包windows平台&#xff0c;显示该平台不支持 出现问题的原因 JsonConvert.SerializeObject 使用了反射来动态遍历对象的属性和字段。这可能在某些平台…...

树莓派5--系统问题汇总

前言&#xff1a; 该文章是我在使用树莓派5时所遇到的问题以及解决方案&#xff0c;希望对遇到相同问题的能够有所帮助。我的树莓派系统版本为&#xff1a;Pi-OS-ROS_2024_09_29 注意&#xff1a;如果没有什么需求千万不要更新树莓派中任何软件或者系统&#xff0c;除非你真的…...

Level2逐笔成交逐笔委托毫秒记录:今日分享优质股票数据20250116

逐笔成交逐笔委托下载 链接: https://pan.baidu.com/s/11nYhOf5TL9haZdXcYTxmHQ?pwdfwg8 提取码: fwg8 -------------------- Level2逐笔成交逐笔委托数据分享下载 利用Level2的逐笔交易和委托数据&#xff0c;这种以毫秒为单位的详细信息能揭露众多关键信息&#xff0c;如庄…...

小白:react antd 搭建框架关于 RangePicker DatePicker 时间组件使用记录 2

文章目录 一、 关于 RangePicker 组件返回的moment 方法示例 一、 关于 RangePicker 组件返回的moment 方法示例 moment方法中日后开发有用的方法如下&#xff1a; form.getFieldsValue().date[0].weeksInWeekYear(),form.getFieldsValue().date[0].zoneName(), form.getFiel…...

windows远程桌面连接限定ip

1&#xff0c;Windows防火墙->高级设置->远程桌面 - 用户模式(TCP-In)->作用域->远程IP地址 2&#xff0c;启用规则...

强化学习-蒙特卡洛方法

强化学习-数学理论 强化学习-基本概念强化学习-贝尔曼公式强化学习-贝尔曼最优公式强化学习-值迭代与策略迭代强化学习-蒙特卡洛方法 文章目录 强化学习-数学理论一、蒙特卡洛方法理论(Monte Carlo, MC)二、MC Basic2.1 算法拆解2.2 MC Basic算法 三、MC Exploring Starts3.1 …...

Java并发编程——线程池(基础,使用,拒绝策略,命名,提交方式,状态)

我是一个计算机专业研0的学生卡蒙Camel&#x1f42b;&#x1f42b;&#x1f42b;&#xff08;刚保研&#xff09; 记录每天学习过程&#xff08;主要学习Java、python、人工智能&#xff09;&#xff0c;总结知识点&#xff08;内容来自&#xff1a;自我总结网上借鉴&#xff0…...

浅谈云计算08 | 基本云架构

浅谈基本云架构 一、负载分布架构二、资源池架构三、动态可扩展架构四、弹性资源容量架构五、服务负载均衡架构六、云爆发架构七、弹性磁盘供给架构八、冗余存储架构 在当今数字化时代&#xff0c;云计算已成为企业发展的核心驱动力&#xff0c;而其背后的一系列关键架构则是支…...

element select 绑定一个对象{}

背景&#xff1a; select组件的使用&#xff0c;适用广泛的基础单选 v-model 的值为当前被选中的 el-option 的 value 属性值。但是我们这里想绑定一个对象&#xff0c;一个el-option对应的对象。 <el-select v-model"state.form.modelA" …...

MySQL SQL优化技巧与原理

前言 随着业务数据量的不断增加&#xff0c;MySQL查询语句的执行效率对程序的运行效率影响逐渐增大。因此&#xff0c;进行SQL优化变得至关重要。本文将结合SQL的执行语句顺序和各种SQL场景&#xff0c;介绍一些常见的MySQL SQL优化技巧及其背后的原理。 一、MySQL SQL执行语…...

SSL:WRONG_VERSION_NUMBER 或者 net::ERR_SSL_PROTOCAL_ERROR

记录一次SSL部署成功后&#xff0c;接口请求依然报SSL错误的问题。 首先排除SSL证据链不足&#xff0c;系统时间不正确&#xff0c;TLS配置不正确等问题。 浏览器直接访问可以打开网站&#xff0c;但是postman等接口访问就一直报SSL异常&#xff1a; 类似参考资料中的这种异…...

51单片机——DS18B20温度传感器

由于DS18B20数字温度传感器是单总线接口&#xff0c;所以需要使用51单片机的一个IO口模拟单总线时序与DS18B20通信&#xff0c;将检测的环境温度读取出来 1、DS18B20模块电路 传感器接口的单总线管脚接至单片机P3.7IO口上 2、DS18B20介绍 2.1 DS18B20外观实物图 管脚1为GN…...

单细胞组学大模型(8)--- scGenePT,scGPT和GenePT的结合,实验数据和文本数据的交融模型

–https://doi.org/10.1101/2024.10.23.619972 研究团队和单位 Theofanis Karaletsos–Head Of AI - Science at Chan Zuckerberg Initiative &#xff08;Chan Zuckerberg Initiative是扎克伯格和他妻子Chan成立的科研&教育机构&#xff09; 研究简介 研究背景&…...

详解如何自定义 Android Dex VMP 保护壳

版权归作者所有&#xff0c;如有转发&#xff0c;请注明文章出处&#xff1a;https://cyrus-studio.github.io/blog/ 前言 Android Dex VMP&#xff08;Virtual Machine Protection&#xff0c;虚拟机保护&#xff09;壳是一种常见的应用保护技术&#xff0c;主要用于保护 And…...

PHP中的魔术函数

PHP 魔术函数是在某些情况下会自动调用的特殊函数&#xff0c;它们以双下划线 __ 开头&#xff0c;以下是对常见 PHP 魔术函数的详细介绍&#xff1a; ### 构造函数和析构函数 - **__construct()**&#xff1a; - 这是一个构造函数&#xff0c;在创建类的新对象时会自动调…...

excel 判断某个单元格的日期,如果超过3天,则在另一个单元格显示超过三天的公式

excel 判断某个单元格的日期&#xff0c;如果超过3天&#xff0c;则在另一个单元格显示超过三天的公式&#xff0c;公式如下&#xff1a; IF(DATEDIF(C627,TODAY(),"d")<4,"3天以内","超过三天") IF(D627"超过3天","文件赶紧…...

TCP 序列和确认号说明 | seq 和 ack 号计算方法

注&#xff1a;本文为 “TCP 序列” 相关文章合辑。 英文引文机翻未校。 TCP Sequence and Acknowledgement Numbers Explained TCP 序列和确认编号说明 TCP Sequence (seq) and Acknowledgement (ack) numbers help enable ordered reliable data transfer for TCP streams…...

【Linux】Mysql部署步骤

一、JDK安装配置 在home目录下执行命令&#xff1a;mkdir Jdk 1.将JDK 上传至该文件夹&#xff0c;有些终端工具可以直接上传文件&#xff0c;比如&#xff1a;MobaXterm 可以看到安装包已经上传上来了 2.直接安装 命令&#xff1a;rpm -ivh jdk-8u311-linux-x64.rpm 3.安装成…...

【算法】枚举

枚举 普通枚举1.铺地毯2.回文日期3.扫雷 二进制枚举1.子集2.费解的开关3.Even Parity 顾名思义&#xff0c;就是把所有情况全都罗列出来&#xff0c;然后找出符合题目要求的那一个。因此&#xff0c;枚举是一种纯暴力的算法。一般情况下&#xff0c;枚举策略都是会超时的。此时…...

【C++】构造函数与析构函数

写在前面 构造函数与析构函数都是属于类的默认成员函数&#xff01; 默认成员函数是程序猿不显示声明定义&#xff0c;编译器会中生成。 构造函数和析构函数的知识需要建立在有初步类与对象的基础之上的&#xff0c;关于类与对象不才在前面笔记中有详细的介绍&#xff1a;点我…...

力扣刷题汇总

动态规划 1 . 最大子序和 (Maximum Subarray Sum) Leetcode 53. 最大子数组和 经典dp 问题描述&#xff1a;给定一个整数数组&#xff0c;求其中和最大的连续子数组的和。 状态定义&#xff1a;dp[i] 表示以第 i 个元素结尾的最大子序和。 2 . 最长公共子序列 (Longest Commo…...

Ansible自动化运维:基础与实践

在当今的IT运维领域&#xff0c;Ansible作为一款强大的自动化运维工具&#xff0c;正发挥着日益重要的作用。本文将详细介绍Ansible的相关知识&#xff0c;包括其作用、特点、安装配置以及常用模块的使用方法&#xff0c;旨在帮助读者快速上手并熟练运用Ansible进行自动化运维工…...

微信小程序在使用页面栈保存页面信息时,如何避免数据丢失?

微信小程序在使用页面栈保存页面信息时避免数据丢失的方法&#xff1a; 一、使用全局变量存储关键数据&#xff1a; 定义一个全局变量&#xff0c;例如在 app.js 中&#xff0c;用于存储页面的重要信息。在页面的 onHide 或 onUnload 生命周期中&#xff0c;将需要保存的数据…...

我国无人机新增实名登记110.3 万架,累计完成飞行2666万小时

据央视新闻从中国民航局了解到&#xff0c;2024 年我国全年新增通航企业 145 家、通用机场 26 个&#xff0c;颁发无人驾驶航空器型号合格证 6 个、新增实名登记无人机 110.3 万架&#xff0c;无人机运营单位总数超过 2 万家&#xff0c;累计完成无人机飞行 2666 万小时&#x…...

vue3+vite+ts+router4+Pinia+Axios+sass 从0到1搭建

1、使用vite构建项目 npm create vitelatest 填写项目名的时候不能大写 2、跑起来之后配置下 import { defineConfig } from vite import vue from vitejs/plugin-vue import { resolve } from path // https://vite.dev/config/ export default defineConfig({plugins: [vue…...

C语言:-三子棋游戏代码:分支-循环-数组-函数集合

思路分析&#xff1a; 1、写菜单 2、菜单之后进入游戏的操作 3、写函数 实现游戏 3.1、初始化棋盘函数&#xff0c;使数组元素都为空格 3.2、打印棋盘 棋盘的大概样子 3.3、玩家出棋 3.3.1、限制玩家要下的坐标位置 3.3.2、判断玩家要下的位置是否由棋子 3.4、电脑出棋 3.4.1、…...

前端调试遇到的无限debugger的原理与绕过

背景 debugger 是 JavaScript 中定义的一个专门用于断点调试的关键字,只要遇到它,JavaScript 的执行便会在此处中断,进入调试模式。有了 debugger 这个关键字,我们就可以非常方便地对 JavaScript 代码进行调试,比如使用 JavaScript Hook 时,我们可以加入 debugger 关键字…...

Java负载均衡

Java中的负载均衡原理是指通过合理分配网络请求或计算任务的方式&#xff0c;将工作负载分配到多个服务器、处理单元或服务实例上&#xff0c;从而提高系统的性能、可扩展性和可用性。负载均衡不仅可以分散请求压力&#xff0c;还能增强系统的容错能力&#xff0c;避免单点故障…...

spark,读取和写入同一张表问题

读取a表&#xff0c;写入a表 1.写入的是分区表&#xff0c;不报错 2.读取上来之后&#xff0c;创建为临时视图temp&#xff0c;然后先写入a表&#xff0c;再使用temp&#xff0c;就会报错 解决办法&#xff1a;可以先使用temp&#xff0c;再写入a表 3.写入的不是分区表&…...

用gpg和sha256验证ubuntu.iso

链接 https://ubuntu.com/tutorials/how-to-verify-ubuntuhttps://releases.ubuntu.com/jammy/ 本文是2的简明版 sha256sum介绍 sha256sum -c SHA256SUMS 2>&1这段脚本的作用是验证文件的 SHA-256 校验和。具体来说&#xff0c;命令的各个部分含义如下&#xff1a; …...

HIVE技术

本文章基于黑马免费资料编写。 hive介绍 简介 hive架构 hive需要启动的配置 执行元数据库初始化命令 使用hive必须启动的服务 ./schematool -initSchema -dbType mysql -verbos启动 Hive 创建一个 hive 的日志文件夹 mkdir /export/server/hive/logs启动元数据管理服务 n…...

我的世界-与门、或门、非门等基本门电路实现

一、红石比较器 (1) 红石比较器结构 红石比较器有前端单火把、后端双火把以及两个侧端 其中后端和侧端是输入信号,前端是输出信号 (2) 红石比较器的两种模式 比较模式 前端火把未点亮时处于比较模式 侧端>后端 → 0 当任一侧端强度大于后端强度时,输出…...

GPU 硬件原理架构(一)

这张费米管线架构图能看懂了&#xff0c;整个GPU的架构基本就熟了。市面上有很多GPU厂家&#xff0c;他们产品的架构各不相同&#xff0c;但是核心往往差不多&#xff0c;整明白一了个基本上就可以触类旁通了。下面这张图信息量很大&#xff0c;可以结合博客GPU 英伟达GPU架构回…...

[Qt]窗口-QMainWindow类-QMenuBar、QToolBar、QStatusBar、QDockWidget控件

目录 1.QMainWindow类介绍 2.菜单栏-QMenuBar控件 创建菜单栏 添加菜单和菜单选项 triggered信号 设置快捷键 添加分割线 添加图标 使用案例 3.工具栏-QToolBar控件 使用介绍 设置停靠位置 设置浮动属性 设置移动属性 使用案例 4.状态栏-QStatusBar控件 状…...

Linux命令行工具-使用方法

参考资料 Linux网络命令&#xff1a;网络工具socat详解-CSDN博客 arm-linux-gnueabihf、aarch64-linux-gnu等ARM交叉编译GCC的区别_aarch64-elf-gcc aarch64-linux-gnu-CSDN博客 解决Linux内核问题实用技巧之-dev/mem的新玩法-腾讯云开发者社区-腾讯云 热爱学习地派大星-CS…...

HTML中如何保留字符串的空白符和换行符号的效果

有个字符串 储值门店{{thing3.DATA}}\n储值卡号{{character_string1.DATA}}\n储值金额{{amount4.DATA}}\n当前余额{{amount5.DATA}}\n储值时间{{time2.DATA}} &#xff0c; HTML中想要保留 \n的换行效果的有下面3种方法&#xff1a; 1、style 中 设置 white-space: pre-lin…...

ASP.NET Core WebApi接口IP限流实践技术指南

在当今的Web开发中&#xff0c;接口的安全性和稳定性至关重要。面对恶意请求或频繁访问&#xff0c;我们需要采取有效的措施来保护我们的WebApi接口。IP限流是一种常见的技术手段&#xff0c;通过对来自同一IP地址的请求进行频率控制&#xff0c;可以有效地防止恶意攻击和过度消…...

SparkSQL数据模型综合实践

文章目录 1. 实战概述2. 实战步骤2.1 创建数据集2.2 创建数据模型对象2.2.1 创建常量2.2.2 创建加载数据方法2.2.3 创建过滤年龄方法2.2.4 创建平均薪水方法2.2.5 创建主方法2.2.6 查看完整代码 2.3 运行程序&#xff0c;查看结果 3. 实战小结 1. 实战概述 在本次实战中&#…...

C++实现设计模式---外观模式 (Facade)

外观模式 (Facade) 外观模式 是一种结构型设计模式&#xff0c;为子系统中的一组接口提供一个一致的界面。外观模式定义了一个更高层次的接口&#xff0c;使得子系统更容易使用。 意图 简化复杂子系统的接口。为客户端提供一个统一的入口&#xff0c;屏蔽子系统的内部细节。 …...

计算机网络 (43)万维网WWW

前言 万维网&#xff08;World Wide Web&#xff0c;WWW&#xff09;是Internet上集文本、声音、动画、视频等多种媒体信息于一身的信息服务系统。 一、基本概念与组成 定义&#xff1a;万维网是一个分布式、联机式的信息存储空间&#xff0c;通过超文本链接的方式将分散的信息…...

C# 获取PDF文档中的字体信息(字体名、大小、颜色、样式等

在设计和出版行业中&#xff0c;字体的选择和使用对最终作品的质量有着重要影响。然而&#xff0c;有时我们可能会遇到包含未知字体的PDF文件&#xff0c;这使得我们无法准确地复制或修改文档。获取PDF中的字体信息可以解决这个问题&#xff0c;让我们能够更好地处理这些文件。…...

Docker Desktop 中安装 MySQL 并开启远程访问的详细教程

是在 Docker Desktop 中安装 MySQL 并开启远程访问的详细教程&#xff1a; 一、安装 MySQL 容器 拉取 MySQL 镜像&#xff1a; docker pull mysql:latest这将从 Docker Hub 上拉取最新版本的 MySQL 镜像。如果你想使用特定版本的 MySQL&#xff0c;可以将 latest 替换为具体…...

沸点 | 聚焦嬴图Cloud V2.1:具备水平可扩展性+深度计算的云原生嬴图动力站!

近日&#xff0c;嬴图正式推出嬴图Cloud V2.1&#xff0c;此次发布专注于提供无与伦比的用户体验&#xff0c;包括具有水平可扩展性的嬴图Powerhouse的一键部署、具有灵活定制功能的管理控制台、VPC / 专用链接等&#xff0c;旨在满足用户不断变化需求的各项前沿功能&#xff0…...