STM32F4 | 串行通信基本原理 | 串口通信实验

news2024/11/23 21:14:29

文章目录

    • 一、串行通信基本原理
      • 1.串行通信接口背景知识
      • 2.异步串口通信UART知识
      • 3.STM32串口数据格式和通信过程
      • 4.STM32串口框图
      • 5.波特率计算方法
    • 二、STM32F429 串口简介
    • 三、硬件设计
    • 四、软件设计
    • 五、实验现象
    • 六、STM32CubeMX 配置串口

  本章介绍如何使用 STM32F429 的串口来发送和接收数据。本章将实现如下功能: STM32F429 通过串口和上位机的对话, STM32F429 在收到上位机发过来的字符串后,原原本本的返回给上位机。

一、串行通信基本原理

1.串行通信接口背景知识

  处理器与外部设备通信有两种方式:

  • 并行通信
    • 传输原理:数据各个位同时传输。
    • 优点:速度快
    • 缺点:占用引脚资源多
  • 串行通信
    • 传输原理:数据按位顺序传输。
    • 优点:占用引脚资源少
    • 缺点:速度相对较慢

  串行通信按照数据传送方向,分为:

  • 单工
    数据传输只支持数据在一个方向上传输
  • 半双工
    允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信
  • 全双工
    允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。

  串行通信的三种传送方式如下:
在这里插入图片描述
  串行通信按照通信方式来分

  • 同步通信:带时钟同步信号传输
    • SPIIIC通信接口
  • 异步通信:不带时钟同步信号
    • UART(通用异步收发器),单总线

  常见的串行通信接口为
在这里插入图片描述

2.异步串口通信UART知识

  异步通信UART包含三点知识:

  • 物理层(电气层:接口决定):通信接口(RS232,RS485,RS422,TTL)
  • 数据格式(数据层:芯片决定)
  • 通信协议(协议层:程序决定)

  UART异步通信方式引脚连接方法:

  • RXD:数据输入引脚。数据接受。
  • TXD:数据发送引脚。数据发送。

在这里插入图片描述
  接口类型如下:
在这里插入图片描述
  STM32 UART异步通信方式引脚:
在这里插入图片描述
查看芯片数据手册引脚功能表可得:
在这里插入图片描述
在这里插入图片描述
PA9PA10可做串口1的发送和接受引脚;PB10PB11可做串口3的发送和接受引脚。

3.STM32串口数据格式和通信过程

  STM32串口异步通信需要定义的参数:

  • 起始位:1个逻辑0数据位开始
  • 数据位(8位或者9位)
  • 奇偶校验位(第9位)
  • 停止位(1,1.5,2位)
  • 波特率设置

在这里插入图片描述
  STM32串口通信过程如下:
在这里插入图片描述

4.STM32串口框图

在这里插入图片描述
由图中可以看出:

  • 发送端与接收端共用一个波特率,即确定串行通信的速度
  • USART_BRR为分数波特率发生器

5.波特率计算方法

  波特率计算在串口框图如下部分:
在这里插入图片描述
OVER8由控制寄存器USART_CR1中的位15来控制。
在这里插入图片描述
波特率的计算公式如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  OVER8=0时波特率计算公式:
T x / R x 波 特 率 = f P C L K x 16 ∗ U S A R T D I V Tx/Rx波特率=\frac{f_{PCLKx}}{16*USARTDIV} Tx/Rx=16USARTDIVfPCLKx

  • 根据波特率和串口时钟频率,计算出USARTDIV的值。
  • DIV_Fraction=USART的小数部分 X16所得的整数
    DIV_Mantissa=USART的整数部分

那么,假如串口时钟为90M,需要得到115200的波特率;则

在这里插入图片描述

二、STM32F429 串口简介

  STM32F429 的串口资源相当丰富的,功能也相当强劲。ALIENTEK 阿波罗 STM32F429 开发板所使用的 STM32F429IGT6 最多可提供 8 路串口,有分数波特率发生器、支持同步单线通信和半双工单线通讯、支持 LIN、支持调制解调器操作、智能卡协议和 IrDA SIR ENDEC 规范、具有 DMA 等。
  阿波罗 STM32F429 开发板板载了 1 个 USB 串口和 2 个 RS232 串口,我们本章介绍的是通过 USB 串口和电脑通信。
  串口最基本的设置,就是波特率的设置。STM32F429 的串口使用起来还是蛮简单的,只要你开启了串口时钟,并设置相应 IO 口的模式,然后配置一下波特率,数据位长度,奇偶校验位等信息,就可以使用了。下面介绍几个与串口基本配置直接相关的寄存器

  1. 串口时钟使能。串口作为 STM32F429 的一个外设,其时钟由外设时钟使能寄存器控制,这里我们使用的串口 1 是在 APB2ENR 寄存器的第 4 位。APB2ENR 寄存器在之前已经介绍过了,这里不再介绍。只是说明一点,就是除了串口 1 和串口 6 的时钟使能在 APB2ENR 寄存器,其他串口的时钟使能位都在 APB1ENR 寄存器。

  2. 串口波特率设置。每个串口都有一个自己独立的波特率寄存器 USART_BRR,通过设置该寄存器就可以达到配置不同波特率的目的。

  3. 串口控制STM32F429 的每个串口都有 3 个控制寄存器 USART_CR1~3,串口的很多配置都是通过这 3 个寄存器来设置的。这里我们只要用到 USART_CR1 就可以实现我们的功能了,该寄存器的各位描述如图所示:
    在这里插入图片描述
    该寄存器的高16位没有用到,低16位用于串口的功能设置。

    • OVER8为过采样模式设置位,我们一般设置位 0,即 16 倍过采样已获得更好的容错性;
    • UE 为串口使能位,通过该位置 1,以使能串口;
    • M 为字长选择位,当该位为 0 的时候设置串口为 8 个字长外加 n 个停止位,停止位的个数(n)是根据 USART_CR2[13:12]位设置来决定的,默认为 0;
    • PCE 为校验使能位,设置为 0,则禁止校验,否则使能校验;
    • PS 为校验位选择位,设置为 0 则为偶校验,否则为奇校验;
    • TXEIE 为发送缓冲区空中断使能位,设置该位为 1,当 USART_SR 中的 TXE 位为 1 时,将产生串口中断;
    • TCIE 为发送完成中断使能位,设置该位为 1,当 USART_SR 中的 TC 位为 1 时,将产生串口中断;
    • RXNEIE 为接收缓冲区非空中断使能,设置该位为 1,当 USART_SR 中的 ORE 或者 RXNE 位为 1 时,将产生串口中断;
    • TE 为发送使能位,设置为 1,将开启串口的发送功能;
    • RE 为接收使能位,用法同 TE。
  4. 数据发送与接收STM32F429 的发送与接收是通过数据寄存器 USART_DR 来实现的,这是一个双寄存器,包含了 TDRRDR。当向 DR 寄存器写数据的时候,实际是写入 TDR,串口就会自动发送数据;当收到数据,读 DR 寄存器的时候,实际读取的是 RDRTDRRDR对外是不可见的,所以,我们操作的就只有 DR 寄存器,该寄存器的各位描述如图所示:
    在这里插入图片描述
    可以看出,虽然是一个 32 位寄存器,但是只用了低 9 位(DR[8:0]),其他都是保留。DR[8:0]为串口数据,包含了发送或接收的数据。由于它是由两个寄存器(TDRRDR)组成的,一个给发送用(TDR),一个给接收用(RDR),该寄存器兼具读和写的功能。TDR 寄存器提供了内部总线和输出移位寄存器之间的并行接口。RDR 寄存器提供了输入移位寄存器和内部总线之间的并行接口。当使能校验位(USART_CR1PCE 位被置位)进行发送时,写到 MSB 的值(根据数据的长度不同,MSB 是第 7 位或者第 8 位)会被后来的校验位取代。当使能校验位进行接收时,读到的 MSB 位是接收到的校验位。

  5. 串口状态。串口的状态可以通过状态寄存器 USART_SR 读取。USART_SR 的各位描述如图所示:
    在这里插入图片描述
    这里我们关注一下两个位,第 5、6 位 RXNETC

    • RXNE(读数据寄存器非空),当该位被置 1 的时候,就是提示已经有数据被接收到了,并且可以读出来了。这时候我们要做的就是尽快去读取 USART_DR,通过读USART_DR 可以将该位清零,也可以向该位写 0,直接清除。
    • TC(发送完成),当该位被置位的时候,表示 USART_DR 内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式:
      • USART_SR,写USART_DR
      • 直接向该位写 0。

    通过以上一些寄存器的操作外加 IO 口的配置,我们就可以达到串口最基本的配置了。

  串口字节的发送流程如下:

  • 编程USARTx_CR1M位来定义字长
  • 编程USARTx_CR2STOP位来定义停止位位数
  • 编程USARTx_BRR寄存器确定波特率
  • 使能USARTx_CR1UE使能USARTX
  • 如果进行多缓冲通信,配置USARTx_CR3DMA使能DMAT
  • 使能USARTx_CR1TE使能发送器
  • 向发送数据寄存器TDR写入要发送的数据(对于M3,发送和接收共用DR寄存器)。
  • TRD寄存器写入最后一个数据后,等待状态寄存器USARTx_SR(ISR)TC位置1,传输完成。

上面是基于寄存器进行描述的。接下来,使用HAL库实现串口发送程序配置:

  • 初始化串口相关参数使能串口:HAL UART Init();
  • 串口相关IO口配置,复用配置:在HAL_UART_MspInit中调用HAL GPIO Init函数。
  • 发送数据并等待数据发送完成:HAL_UART_Transmit()函数;

  串口接收流程如下:

  • 编程USARTx_CR1M位来定义字长
  • 编程USARTx_CR2STOP位来定义停止位位数
  • 编程USARTx_BRR寄存器确定波特率
  • 使能USARTx_CR1UE使能USARTX
  • 如果进行多缓冲通信,配置USARTx_CR3DMA使能DMAT
  • 使能USARTx_CR1RE位为1使能接收器
  • 如果要使能接收中断(接收到数据后产生中断),使能USARTx_CR1RXNEIE位为1。

当串口接收到数据时:

  • USARTx_SR(ISR)RXNE位置1。表明移位寄存器内容已经传输到RDR(DR)寄存器。已经接收到数据并且等待读取。
  • 如果开启了接收数据中断(USARTx_CR1寄存器的RXNEIE位为1),则会产生中断。(程序上会执行中断服务函数)
  • 如果开启了其他中断(帧错误等),相应标志位会置1。
  • 读取USARTx_RDR(DR)寄存器的值,该操作会自动将RXNE位清零,等待下次接收后置位。

接收数据过程:

  • 步骤1获取状态标志位通过标识符实现:

      __HAL_UART_GET_FLAG            //判断状态标志位
       __HAL_UART_GET_IT_SOURCE   //判断中断标志位
    
  • 步骤2-3中断服务函数:

    void USARTx_IRQHandler(void) ;//(x=1~3,6)
    void USARTx_IRQHandler(void) ;//(x=4,5,7,8)
    
  • 步骤4读取接收数据

    HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, 
                                     uint8_t *pData, uint16_t Size, uint32_t Timeout);
    

接下来,使用HAL库实现串口接收中断程序配置:

  • 初始化串口相关参数,使能串口:HAL_UART_Init();

  • 串口相关IO口配置,复用配置:在HAL_UART_MspInit中调用HAL_GPIO_Init函数。

  • 串口接收中断优先级配置和使能:

    HAL_NVIC_EnableIRQ()HAL_NVIC_SetPriority();
    
  • 使能串口接收中断:HAL_UART_Receive_IT();

  • 编写中断服务函数:USARTx_IRQHandler

  接下来使用 HAL 库实现串口配置和使用的方法。在 HAL 库中,串口相关的函数和定义主要在文件 stm32f4xx_hal_uart.cstm32f4xx_hal_uart.h 中。接下来我们看看 HAL 库提供的串口相关操作函数。

  • 串口参数初始化(波特率/停止位等),并使能串口
      串口作为 STM32 的一个外设,HAL 库为其配置了串口初始化函数。串口初始化函数 HAL_UART_Init定义如下:

    HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);
    

    该函数只有一个入口参数 huart,为UART_HandleTypeDef 结构体指针类型。一般情况下,我们会定义一个 UART_HandleTypeDef结构体类型全局变量,然后初始化各个成员变量。结构体 UART_HandleTypeDef的定义如下:

    typedef struct
    {
    	USART_TypeDef *Instance; 
    	UART_InitTypeDef Init; 
    	uint8_t *pTxBuffPtr; 
    	uint16_t TxXferSize; 
    	uint16_t TxXferCount; 
    	uint8_t *pRxBuffPtr; 
    	uint16_t RxXferSize; 
    	uint16_t RxXferCount; 
    	DMA_HandleTypeDef *hdmatx; 
    	DMA_HandleTypeDef *hdmarx; 
    	HAL_LockTypeDef Lock; 
    	__IO HAL_UART_StateTypeDef State; 
    	__IO uint32_t ErrorCode; 
    }UART_HandleTypeDef;
    

    该结构体成员变量非常多,一般情况调用函数 HAL_UART_Init 对串口进行初始化的时候,我们只需要先设置 InstanceInit 两个成员变量的值。

    • InstanceUSART_TypeDef 结构体指针类型变量,它是执行寄存器基地址,实际上这个基地址 HAL 库已经定义好了,如果是串口 1,取值为 USART1 即可。

    • InitUART_InitTypeDef 结构体类型变量,它是用来设置串口的各个参数,包括波特率,停止位等。UART_InitTypeDef 结构体定义如下:

      typedef struct
      {
      	uint32_t BaudRate; //波特率
      	uint32_t WordLength; //字长
      	uint32_t StopBits; //停止位
      	uint32_t Parity; //奇偶校验
      	uint32_t Mode; //收/发模式设置
      	uint32_t HwFlowCtl; //硬件流设置
      	uint32_t OverSampling; //过采样设置
      }UART_InitTypeDef
      
      • 第一个参数 BaudRate 为串口波特率,波特率可以说是串口最重要的参数了,它用来确定串口通信的速率。
      • 第二个参数 WordLength 为字长,可以设置为 8 位字长或者 9 位字长,这里我们设置为 8 位字长数据格式 UART_WORDLENGTH_8B
      • 第三个参数 StopBits 为停止位设置,可以设置为 1 个停止位或者 2 个停止位,这里我们设置为 1 位停止位 UART_STOPBITS_1
      • 第四个参数 Parity 设定是否需要奇偶校验,我们设定为无奇偶校验位。
      • 第五个参数 Mode 为串口模式,可以设置为只收模式,只发模式,或者收发模式。这里我们设置为全双工收发模式。
      • 第六个参数 HwFlowCtl 为是否支持硬件流控制,我们设置为无硬件流控制。
      • 第七个参数OverSampling 用来设置过采样为 16 倍还是 8 倍。

    pTxBuffPtrTxXferSizeTxXferCount 三个变量分别用来设置串口发送的数据缓存指针,发送的数据量和还剩余的要发送的数据量。而接下来的三个变量pRxBuffPtrRxXferSizeRxXferCount 则是用来设置接收的数据缓存指针,接收的最大数据量以及还剩余的要接收的数据量。这六个变量是 HAL 库处理中间变量。hdmatxhdmarx 是串口 DMA 相关的变量,指向 DMA 句柄。其他的三个变量就是一些 HAL 库处理过程状态标志位和串口通信的错误码。
      函数 HAL_UART_Init 使用的一般格式为:

    UART_HandleTypeDef UART1_Handler; //UART 句柄
    
    UART1_Handler.Instance=USART1; //USART1
    UART1_Handler.Init.BaudRate=115200; //波特率
    UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字长为 8 位格式
    UART1_Handler.Init.StopBits=UART_STOPBITS_1; //一个停止位
    UART1_Handler.Init.Parity=UART_PARITY_NONE; //无奇偶校验位
    UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //无硬件流控
    UART1_Handler.Init.Mode=UART_MODE_TX_RX; //收发模式
    HAL_UART_Init(&UART1_Handler); //HAL_UART_Init()会使能 UART1
    

    这里我们需要说明的是,函数HAL_UART_Init 内部会调用串口使能函数使能相应串口,所以调用了该函数之后我们就不需要重复使能串口了。当然,HAL 库也提供了具体的串口使能和关闭方法,具体使用方法如下:

    __HAL_UART_ENABLE(handler); //使能句柄 handler 指定的串口
    __HAL_UART_DISABLE(handler); //关闭句柄 handler 指定的串口
    

    串口作为一个重要外设,在调用的初始化函数 HAL_UART_Init 内部,会先调用 MSP 初始化回调函数进行 MCU 相关的初始化,函数为:

    void HAL_UART_MspInit(UART_HandleTypeDef *huart);
    

    我们在程序中,只需要重写该函数(__weak弱函数,可重写)即可。一般情况下,该函数内部用来编写 IO 口初始化,时钟使能以及 NVIC 配置。重写后的函数如下:

    //UART底层初始化,时钟使能,引脚配置,中断配置
    //此函数会被HAL_UART_Init()调用
    //huart:串口句柄
    
    void HAL_UART_MspInit(UART_HandleTypeDef *huart)
    {
        //GPIO端口设置
    	GPIO_InitTypeDef GPIO_Initure;
    	
    	if(huart->Instance==USART1)//如果是串口1,进行串口1 MSP初始化
    	{
    		__HAL_RCC_GPIOA_CLK_ENABLE();			//使能GPIOA时钟
    		__HAL_RCC_USART1_CLK_ENABLE();			//使能USART1时钟
    	
    		GPIO_Initure.Pin=GPIO_PIN_9;			//PA9
    		GPIO_Initure.Mode=GPIO_MODE_AF_PP;		//复用推挽输出
    		GPIO_Initure.Pull=GPIO_PULLUP;			//上拉
    		GPIO_Initure.Speed=GPIO_SPEED_FAST;		//高速
    		GPIO_Initure.Alternate=GPIO_AF7_USART1;	//复用为USART1
    		HAL_GPIO_Init(GPIOA,&GPIO_Initure);	   	//初始化PA9
    
    		GPIO_Initure.Pin=GPIO_PIN_10;			//PA10
    		HAL_GPIO_Init(GPIOA,&GPIO_Initure);	   	//初始化PA10
    		
    #if EN_USART1_RX
    		HAL_NVIC_EnableIRQ(USART1_IRQn);		//使能USART1中断通道
    		HAL_NVIC_SetPriority(USART1_IRQn,3,3);	//抢占优先级3,子优先级3
    #endif	
    	}
    
    }
    
  • 使能串口和 GPIO 口时钟
      我们要使用串口,所以我们必须使能串口时钟和使用到的 GPIO 口时钟。例如我们要使用串口 1,所以我们必须使能串口 1 时钟和 GPIOA 时钟(串口 1 使用的是 PA9PA10)。具体方法如下:

    __HAL_RCC_USART1_CLK_ENABLE(); //使能 USART1 时钟
    __HAL_RCC_GPIOA_CLK_ENABLE(); //使能 GPIOA 时钟
    
  • GPIO 口初始化设置(速度,上下拉等)以及复用映射配置
      我们要复用 PA9PA10 为串口发送接收相关引脚,我们需要配置 IO 口为复用,同时复用映射到串口 1。配置源码如下:

    GPIO_InitTypeDef GPIO_Initure;
    GPIO_Initure.Pin=GPIO_PIN_9|GPIO_PIN_10; //PA9/PA10
    GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
    GPIO_Initure.Pull=GPIO_PULLUP; //上拉
    GPIO_Initure.Speed=GPIO_SPEED_FAST; //高速
    GPIO_Initure.Alternate=GPIO_AF7_USART1; //复用为 USART1
    HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化 PA9/PA10
    
  • 开启串口相关中断,配置串口中断优先级
      HAL 库中定义了一个使能串口中断的标识符__HAL_UART_ENABLE_IT,大家可以把它当一个函数来使用,具体定义请参考 HAL 库文件stm32f4xx_hal_uart.h 中该标识符定义。例如我们要使能接收完成中断,方法如下:

    __HAL_UART_ENABLE_IT(huart,UART_IT_RXNE); //开启接收完成中断
    
    • 第一个参数为串口句柄,类型为UART_HandleTypeDef 结构体类型。
    • 第二个参数为我们要开启的中断类型值,可选值在头文件 stm32f4xx_hal_uart.h 中有宏定义。

    有开启中断就有关闭中断,操作方法为:

    __HAL_UART_DISABLE_IT(huart,UART_IT_RXNE); //关闭接收完成中断
    

    对于中断优先级配置,方法就非常简单。参考方法为:

    HAL_NVIC_EnableIRQ(USART1_IRQn); //使能 USART1 中断通道
    HAL_NVIC_SetPriority(USART1_IRQn,3,3); //抢占优先级 3,子优先级 3
    
  • 编写中断函数
      串口 1 中断服务函数为:

    //串口1中断服务程序
    void USART1_IRQHandler(void)                	
    { 
    	u32 timeout=0;
    	u32 maxDelay=0x1FFFF;
    #if SYSTEM_SUPPORT_OS	 	//使用OS
    	OSIntEnter();    
    #endif
    	
    	HAL_UART_IRQHandler(&UART1_Handler);	//调用HAL库中断处理公用函数
    	
    	timeout=0;
        while (HAL_UART_GetState(&UART1_Handler) != HAL_UART_STATE_READY)//等待就绪
    	{
    	 timeout++;超时处理
         if(timeout>maxDelay) break;		
    	}
         
    	timeout=0;
    	while(HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)//一次处理完成之后,重新开启中断并设置RxXferCount为1
    	{
    	 timeout++; //超时处理
    	 if(timeout>maxDelay) break;	
    	}
    #if SYSTEM_SUPPORT_OS	 	//使用OS
    	OSIntExit();  											 
    #endif
    } 
    

    当发生中断的时候,程序就会执行中断服务函数。然后我们在中断服务函数中编写们相应的逻辑代码即可。HAL 库实际上对中断处理过程进行了完整的封装。

  • 串口数据接收和发送
      STM32F4 的发送与接收是通过数据寄存器 USART_DR 来实现的,这是一个双寄存器,包含了 TDRRDR。当向该寄存器写数据的时候,串口就会自动发送,当收到数据的时候,也是存在该寄存器内。HAL 库操作 USART_DR 寄存器发送数据的函数是:

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

    通过该函数向串口寄存器 USART_DR 写入一个数据。
      HAL 库操作 USART_DR 寄存器读取串口接收到的数据的函数是

    HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, 
    								   uint8_t *pData, uint16_t Size, uint32_t Timeout);
    

    通过该函数可以读取串口接受到的数据。

三、硬件设计

  本实验需要用到的硬件资源有:

  • 指示灯 DS0
  • 串口 1

  本实验用到的串口 1 与 USB 串口并没有在 PCB 上连接在一起,需要通过跳线帽来连接一下。这里我们把 P4RXDTXD 用跳线帽与 PA9PA10 连接起来。如图所示:
在这里插入图片描述
连接上这里之后,我们在硬件上就设置完成了,可以开始软件设计了。

四、软件设计

  ALIENTEK 编写的串口相关的源码在 SYSTEM 分组之下的 usart.cusart.h中。第二章讲解了 HAL 库中串口操作的一般步骤以及操作函数。在使用 HAL 库配置串口的时候,HAL 库为我们封装了串口配置步骤。接下来,我们以串口接收中断为例讲解 HAL 库串口程序执行流程。
  和其他外设一样,HAL 库为串口的使用开放了 MSP 函数。在串口初始化函数HAL_UART_Init内部,会调用串口 MSP 函数 HAL_UART_MspInit来设置与 MCU 相关的配置。根据前面的讲解,函数 HAL_UART_Init 主要用来初始化与串口相关的参数(这些参数与 MCU无关),包括波特率,停止位等。而串口 MSP 函数HAL_UART_MspInit 用来设置 GPIO 初始化,NVIC 配置等于 MCU 相关的配置。这里我们定义了一个函数 uart_init 用来调用 HAL_UART_Init 初始化串口参数配置,具体函数如下:

UART_HandleTypeDef UART1_Handler; //UART 句柄
//初始化 IO 串口 1 bound:波特率
void uart_init(u32 bound)
{
	//UART 初始化设置
	UART1_Handler.Instance=USART1; //USART1
	UART1_Handler.Init.BaudRate=bound; //波特率
	UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字长为 8 位格式
	UART1_Handler.Init.StopBits=UART_STOPBITS_1; //一个停止位
	UART1_Handler.Init.Parity=UART_PARITY_NONE; //无奇偶校验位
	UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //无硬件流控
	UART1_Handler.Init.Mode=UART_MODE_TX_RX; //收发模式
	HAL_UART_Init(&UART1_Handler); //HAL_UART_Init()会使能 UART1
	HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, 1);
	//该函数会开启接收中断并且设置接收缓冲以及接收缓冲接收最大数据量
}

该函数实现的是第二章讲解的步骤1的内容。步骤1:串口参数初始化(波特率/停止位等),并使能串口。
  串口 MSP 函数 HAL_UART_MspInit 函数代码如下:

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
	//GPIO 端口设置
	GPIO_InitTypeDef GPIO_Initure;
	
	if(huart->Instance==USART1) //如果是串口 1,进行串口 1 MSP 初始化
	{
		__HAL_RCC_GPIOA_CLK_ENABLE(); //使能 GPIOA 时钟
		__HAL_RCC_USART1_CLK_ENABLE(); //使能 USART1 时钟
		GPIO_Initure.Pin=GPIO_PIN_9; //PA9
		GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
		GPIO_Initure.Pull=GPIO_PULLUP; //上拉
		GPIO_Initure.Speed=GPIO_SPEED_FAST; //高速
		GPIO_Initure.Alternate=GPIO_AF7_USART1; //复用为 USART1
		HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化 PA9
		GPIO_Initure.Pin=GPIO_PIN_10; //PA10
		HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化 PA10
		
#if EN_USART1_RX
		HAL_NVIC_EnableIRQ(USART1_IRQn); //使能 USART1 中断通道
		HAL_NVIC_SetPriority(USART1_IRQn,3,3); //抢占优先级 3,子优先级 3
#endif
	}
}

该函数代码实现的是步骤2到4的内容。

步骤2:使能串口和 GPIO 口时钟
步骤3:GPIO 口初始化设置(速度,上下拉等)以及复用映射配置
步骤4:开启串口相关中断,配置串口中断优先级

  在该段代码中,通过判断宏定义标识符EN_USART1_RX 的值来确定是否开启串口中断通道和设置串口 1 中断优先级。标识符 EN_USART1_RX 在头文件 usart.h 中有定义,默认情况下我们设置为1。

#define EN_USART1_RX 1 //使能(1)/禁止(0)串口 1 接收

  通过上面两个函数,我们就配置了串口相关设置。接下来就是步骤5:编写中断服务函数USART1_IRQHandler
  首先,HAL 库定义了一个串口中断处理通用函数 HAL_UART_IRQHandler,该函数声明如下:

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);

  该函数只有一个入口参数就是 UART_HandleTypeDef 结构体指针类型的串口句柄 huart,使用我们在调用 HAL_UART_Init 函数时设置的同一个变量即可。该函数一般在中断服务函数中调用,作为串口中断处理的通用入口。一般调用方法为:

void USART1_IRQHandler(void) 
{ 
	HAL_UART_IRQHandler(&UART1_Handler); //调用 HAL 库中断处理公用函数//中断处理完成后的结束工作
}

  也就是说,真正的串口中断处理逻辑我们会最终在函数 HAL_UART_IRQHandler内部执行。而该函数是 HAL 库已经定义好,而且用户一般不能随意修改。那么我们的中断控制逻辑编写在哪里呢?为了把这个问题理解清楚,我们要来看看函数HAL_UART_IRQHandler 内部具体实现过程。因为本章实验,我们主要实现的是串口中断接收,也就是每次接收到一个字符后进入中断服务函数来处理。所以我们就以中断接收为例来分析。这里为了篇幅考虑,我们仅仅列出串口中断执行流程中与接收相关的源码。
  函数 HAL_UART_IRQHandler 关于串口接收相关源码如下:

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
	uint32_t tmp1 = 0, tmp2 = 0;//此处省略部分代码
	tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_RXNE);
	tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_RXNE);
	if((tmp1 != RESET) && (tmp2 != RESET))
	{ 
		UART_Receive_IT(huart);
	}//此处省略部分代码
}

  从代码逻辑可以看出,在函数HAL_UART_IRQHandler 内部通过判断中断类型是否为接收完成中断,确定是否调用 HAL 另外一个函数 UART_Receive_IT()。函数 UART_Receive_IT()的作用是把每次中断接收到的字符保存在串口句柄的缓存指针 pRxBuffPtr 中,同时每次接收一个字符,其计数器 RxXferCount 减 1,直到接收完成 RxXferSize 个字符之后 RxXferCount 设置为0,同时调用接收完成回调函数 HAL_UART_RxCpltCallback 进行处理。为了篇幅考虑,这里我们仅列出 UART_Receive_IT()函数调用回调函数 HAL_UART_RxCpltCallback 的处理逻辑,代码如下:

static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
{
	...//此处省略部分代码
	if(--huart->RxXferCount == 0)
	{
		HAL_UART_RxCpltCallback(huart);
	}
	...//此处省略部分代码
}

最后,我们列出串口接收中断的一般流程,如图所示:
在这里插入图片描述
  这里,我们把串口接收中断的一般流程进行概括:当接收到一个字符之后,在函数UART_Receive_IT中会把数据保存在串口句柄的成员变量pRxBuffPtr缓存中,同时RxXferCount计数器减 1。如果我们设置RxXferSize=10,那么当接收到 10 个字符之后,RxXferCount 会由 10 减到 0(RxXferCount 初始值等于RxXferSize),这个时候再调用接收完成回调函数HAL_UART_RxCpltCallback 进行处理。接下来,我们看看我们的配置。
  首先,我们回到用户函数 uart_init 定义可以看到,在 uart_init 函数中调用完 HAL_UART_Init后我们还调用了 HAL_UART_Receive_IT 开启接收中断,并且初始化串口句柄的缓存相关参数。代码如下:

HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);

aRxBuffer 是我们定义的一个全局数组变量,RXBUFFERSIZE 是我们定义的一个标识符:

#define RXBUFFERSIZE 1
u8 aRxBuffer[RXBUFFERSIZE];

所以,调用 HAL_UART_Receive_IT 函数后,除了开启接收中断外还确定了每次接收RXBUFFERSIZE 个字符后标示接收结束从而进入回调函数HAL_UART_RxCpltCallback 进行相应处理。最后,我们看看 HAL_UART_RxCpltCallback 函数定义:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance==USART1)//如果是串口 1
		if((USART_RX_STA&0x8000)==0)//接收未完成
		{
			if(USART_RX_STA&0x4000)//接收到了 0x0d
			{
				if(aRxBuffer[0]!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000; //接收完成了
			}
			else //还没收到 0X0D
			{
				if(aRxBuffer[0]==0x0d)USART_RX_STA|=0x4000;
				else
				{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=aRxBuffer[0] ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;
					//接收数据错误,重新开始接收 
				}
			}
		}
	}
}

  因为我们设置了串口句柄成员变量 RxXferSize 为 1,也就是每当串口 1 发生了接收完成中断后(接收到一个字符),就会跳到该函数执行。当串口接受到一个字符后,它会保存在缓存aRxBuffer 中,由于我们设置了缓存大小为 1,而且 RxXferSize=1,所以每次接受一个字符,会直接保存到 aRxBuffer[0]中,我们直接通过读取 aRxBuffer[0]的值就是本次接收到的字符。这里我们设计了一个小小的接收协议:通过这个函数,配合一个数组 USART_RX_BUF[],一个接收状态寄存器 USART_RX_STA 实现对串口数据的接收管理。USART_RX_BUF 的大小由 USART_REC_LEN 定义,也就是一次接收的数据最大不能超过USART_REC_LEN 个字节。USART_RX_STA 是一个接收状态寄存器其定义如表所示:
在这里插入图片描述
  设计思路:当接收到从电脑发过来的数据,把接收到的数据保存在 USART_RX_BUF 中,同时在接收状态寄存器(USART_RX_STA)中计数接收到的有效数据个数,当收到回车(回车的表示由 2 个字节组成:0X0D0X0A)的第一个字节 0X0D 时,计数器将不再增加,等待 0X0A 的到来,而如果 0X0A 没有来到,则认为这次接收失败,重新开始下一次接收。如果顺利接收到 0X0A,则标记 USART_RX_STA 的第 15 位,这样完成一次接收,并等待该位被其他程序清除,从而开始下一次的接收,而如果迟迟没有收到0X0D,那么,在接收数据超过USART_REC_LEN的时候,则会丢弃前面的数据,重新接收。
  在函数 USART1_IRQHandler 的结尾还有几行行代码,其中部分代码是超时退出逻辑,关键逻辑代码如下:

while (HAL_UART_GetState(&UART1_Handler) != HAL_UART_STATE_READY);
while(HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, 1) != HAL_OK);

这两行代码作用非常简单。

  • 第一行代码是判断串口是否就绪,如果没有就绪就等待就绪。
  • 第二行代码是继续调用 HAL_UART_Receive_IT 函数来开启中断和重新设置 RxXferSizeRxXferCount 的初始值为 1,也就是开启新的接收中断。

这里我们需要说明的是,在中断服务函数中,大家也可以不用调用HAL_UART_IRQHandler 函数,而是直接编写自己的中断服务函数

  如果我们不用中断处理回调函数,那么就不用初始化串口句柄的中断接收缓存,所以,HAL_UART_Receive_IT 函数就不用出现在初始化函数 uart_init 中,而是直接在要开启中断的地方通过调用__HAL_UART_ENABLE_IT 单独开启中断即可。如果不用中断回调函数处理,中断服务函数内容为:

//串口1中断服务程序
void USART1_IRQHandler(void)                	
{ 
	u8 Res;
#if SYSTEM_SUPPORT_OS	 	//使用OS
	OSIntEnter();    
#endif
	if((__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_RXNE)!=RESET))  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
	{
        HAL_UART_Receive(&UART1_Handler,&Res,1,1000); 
		if((USART_RX_STA&0x8000)==0)//接收未完成
		{
			if(USART_RX_STA&0x4000)//接收到了0x0d
			{
				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了 
			}
			else //还没收到0X0D
			{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
				{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
				}		 
			}
		}   		 
	}
	HAL_UART_IRQHandler(&UART1_Handler);	
#if SYSTEM_SUPPORT_OS	 	//使用OS
	OSIntExit();  											 
#endif
} 

这段代码逻辑跟上面的中断回调函数类似,只不过这里还需要通过 HAL 库串口接收函数HAL_UART_Receive 来获取接收到的字符进行相应的处理。
  HAL 库一共提供了 5 个中断处理回调函数

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);//错误处理回调函数

  主函数代码为:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"



int main(void)
{
    u8 len;	
	u16 times=0; 
    HAL_Init();                     //初始化HAL库   
    Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhz
    delay_init(180);                //初始化延时函数
    uart_init(115200);              //初始化USART
    LED_Init();                     //初始化LED 
    KEY_Init();                     //初始化按键

    while(1)
    {
			
       if(USART_RX_STA&0x8000)
		{					   
			len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
			printf("\r\n您发送的消息为:\r\n");
			HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000);	//发送接收到的数据
			while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET);		//等待发送结束
			printf("\r\n\r\n");//插入换行
			USART_RX_STA=0;
		}else
		{
			times++;
			if(times%5000==0)
			{
				printf("\r\nALIENTEK 阿波罗STM32F429开发板 串口实验\r\n");
				printf("正点原子@ALIENTEK\r\n\r\n\r\n");
			}
			if(times%200==0)printf("请输入数据,以回车键结束\r\n");  
			if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.
			delay_ms(10);   
		} 
    } 
}

这段代码首先判断全局变量 USART_RX_STA 的最高位是否为 1,如果为 1 的话,那么代表前一次数据接收已经完成,接下来就是把我们自定义接收缓冲的数据发送到串口。

HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000);

这一行代码就是调用 HAL 串口发送函数HAL_UART_Transmit 来发送一个字符到串口。

while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET);

这一行代码就是我们发送一个字节之后之后,要检测这个数据是否已经被发送完成了。

五、实验现象

  我们把程序下载到阿波罗 STM32F429 开发板,可以看到板子上的 DS0 开始闪烁,说明程序已经在跑了。接着我们打开 XCOM V2.0,设置串口为开发板的 USB 转串口(CH340 虚拟串口,得根据你自己的电脑选择,我的电脑是 COM3,另外,请注意:波特率是 115200),可以看到如图所示:
在这里插入图片描述
可以看出,STM32F429 的串口数据发送是没问题的了。但是,因为我们在程序上面设置了必须输入回车,串口才认可接收到的数据,所以必须在发送数据后再发送一个回车符,这里 XCOM 提供的发送方法是通过勾选发送新行实现,如上图,只要勾选了这个选项,每次发送数据后,XCOM 都会自动多发一个回车(0X0D+0X0A)。设置好了发送新行,我们再在发送区输入你想要发送的文字,然后单击发送,可以得到下图所示结果:
在这里插入图片描述
可以看到,我们发送的消息被发送回来了(图中圈圈内)。

六、STM32CubeMX 配置串口

  本小节将讲解使用 STM32CubeMX 配置串口方法。这里不再讲解RCC相关配置,仅讲解串口相关配置方法。
  这里我们要配置串口 1,所以首先我们要使能串口 1 然后设置相应通信模式。打开 Pinout选项卡界面,进入USART1 配置栏,如图所示:
在这里插入图片描述
  USART1 配置栏有 2 个选项。第一个选项 Mode 用来设置串口 1 的模式或者关闭串口 1。第二个选项 Hardware Flow Control(RS232)用来开启/关闭串口 1 的硬件流控制,该选项只有在Mode 选项值为 Asynchronous(异步通信)模式的前提下才有效。这里我们要开启串口 1 的异步模式,并且不使用硬件流控制,所以这里我们直接选择 Mode 值为 Asynchronous 即可。配置好的USART1 界面如下图所示:
在这里插入图片描述
  配置好串口 1 为异步通信模式后,那么在硬件上会使用到 PA9PA10作为串口 1 的发送接收引脚。在 STM32CubeMX 中,当我们选择好外设的工作模式之后,软件会自动配置 GPIO口的相关模式和参数。在 Pinout 界面我们看看芯片引脚图会发现,PA9PA10 端口的模式会自动复用为发送和接收模式,如下图所示:
在这里插入图片描述
同时,进入 GPIO 配置详细界面会发现,IO 口的模式等参数都做了相应的修改。Pin Configuration 界面多了一个 USART1选项卡,该选项卡界面便是用来配置和查看串口引脚 PA9PA10 配置参数的。如下图所示:
在这里插入图片描述
  对于外设的功能引脚,在我们使能相应的外设(比如 USART1)之后,STM32CubeMX 会自动设置 GPIO 相关配置,一般情况下用户不再需要去修改。所以这里,对于PA9PA10 的配置我们就保留软件配置即可。
  接下来我们需要配置 USART1 外设相关的参数,包括波特率,停止位等。接下来我们点击USART1 配置按钮,进入 USART1 详细参数配置界面。在弹出的 USART1 Configuration 界面会出现 5 个配置选项卡。

  • Parameter Settings 选项卡用来配置 USART1 的初始化参数,包括波特率停止位等等。这里我们将 USART1 配置为:波特率 115200,8 位字长模式,无奇偶校验位,1 个停止位,发送/接收均开启。
    在这里插入图片描述

  • User Constants 是用来配置用户常量。
    在这里插入图片描述

  • NVIC 选项卡用来使能 USART1 中断。这里我们勾上 Enabled 选项。
    在这里插入图片描述

  • DMA Setting 是在使用 USART1 DMA 的情况才需要配置。

  • GPIO Setting 便是查看和配置 USART1 相关的 IO
    在这里插入图片描述

  配置完 USART1 相关 IO 口和 USART1 参数之后,如果我们使用到串口中断,那么我们还
需要设置中断优先级分组。接下来便是配置 NVIC相关参数。点击 NVIC 按钮之后,弹出 NVIC 配置界面 NVIC Configuration,如图所示:
在这里插入图片描述
在弹出的 NVIC Configuration 界面,我们首先设置中断优先级分组级别,我们系统初始化设置为分组 2,那么就是 2 位抢占优先级和 2 位响应优先级。所以这里的参数我们选择“2 bits for pre-emption priority”,也就是 2 位抢占优先级。配置完中断优先级分组之后,接下来我们要配置的是 USART1 的抢占优先级和响应优先级值,这里我们设置抢占和响应优先级均为 3 即可。进行完上面的操作之后,接下来我们便是生成工程代码。
  打开生成的工程可以看到,在 main.c 文件中生成了如下串口初始化关键代码:

/* USART1 init function */
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;
  HAL_UART_Init(&huart1);

}

  同时在 stm32f4xx_hal_msp.c 中,生成了串口 MSP 函数 HAL_UART_MspInit 内容如下:

void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(huart->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_USART1_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_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* Peripheral interrupt init */
    HAL_NVIC_SetPriority(USART1_IRQn, 3, 3);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */
  }

}

  函数 MX_USART1_UART_Init 的内容和本章串口实验源码中函数 uart_init 中调用HAL_UART_Init 函数作用类似,只不过波特率是通过入口参数动态设置。而生成的 MSP 函数HAL_UART_MspInit 内容和实验中该函数的作用就几乎是一模一样了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/91126.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

手把手教你Spring Cloud Alibaba教程:nacos安装

我们在学习springCloud的时候用的注册中心是Eureka: springBoot集成springCloud(一)注册中心 但是由于houlai Eureka2.0后续不维护,国内就需要一个可靠的注册中心。但是换了一套后,有哪些不同呢 SpringCloud和SpringCloudAliba…

fl21怎么换主题flstudio皮肤怎么换?

FLstudio21如何更改皮肤主题?不光是背景,还有按键什么的? 请参考下面的步骤进行设置fl视图设置。 第1步,打开visualstudiofl视图设置。 第2步fl视图设置,打开“工具”--“选项” 第3步,在“环境”--“常…

基于SpringBoot+MySql的分页功能实现

分页功能是为了缓解数据库的压力而实现的功能,实际上是将数据库中的数据分段查询出来,避免一次性将所有的数据全部查出来,查出来的部分数据通过前端的页面中不同的页数来展现出来 实现基础 mysql数据库中的查询语句中提供的limit关键字 该关键字可以限制查询的记录数,例如 s…

【026】基于vue+springboot的教务信息管理系统(含源码、数据库、课设报告、运行教程)

文章目录一、项目介绍二、源码获取一、项目介绍 基于Vue+springbootmysql的教务信息管理系统,UI设计主要采用element-ui,也使用了echarts做学生成绩的可视化界面,使用了xlsx表导入、导出数据,超级吊的教务管理系统&…

[操作系统笔记]页面置换算法

内容系听课复习所做笔记,图例多来自课程截图 常见的页面置换算法有:最优算法、随机算法、FIFO、LRU 随机算法顾名思义 FIFO(先进先出, First In First Out)算法:可能产生抖动现象和Belady现象 LRU(最近最少使用算法&#xff09…

基于FFmpeg的视频播放器开发系列教程(二)

对于ffmpeg的架构介绍,请参考24岁“封神”雷霄骅的博客,他已离开江湖,但江湖仍有他的传说。 FFmpeg源代码结构图 - 编码:https://blog.csdn.net/leixiaohua1020/article/details/44226355 FFmpeg源代码结构图 - 解码:h…

【剪辑教程】如何给视频添加配音旁白,三种文字转语音方法教给你

视频剪辑完成,应该如何给视频配音?三种配音方法快来学 现在许多自媒体都居家创作短视频,那么给短视频配音的问题就摆着了面前,怎么给我们的短视频配音呢,接下来就教大家三种给视频添加配音的方法。 方法一&#xff1a…

Linux权限(1)

Linux权限(1) 📟作者主页:慢热的陕西人 🌴专栏链接:Linux 📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言 文章目录Linux权限(1)1. Linux权限的概念1.1 Linux上的用…

NLP之文本分类模型调优(模型基于tensorflow1.14)

项目链接:https://pan.baidu.com/s/1yOu0DogWkL8WOJksJmeiPw?pwd4bsg 提取码: 4bsg 复制这段内容后打开百度网盘手机App,操作更方便哦 --来自百度网盘超级会员v4的分享 1.基于上一篇文章中的文本分类项目进行精度调优,提升模型准确率&…

CET-4 week10 语法

0基础入门 point 谓语中自带 助动词 Such as ,I do like computer (强调且默认带有这个属性 大多数情况隐藏状态) 所有时态都有被动 do ->donewill do -> will be donehave down ->have been downbe doing ->be being donedid -> been downhad down ->ha…

flink-cdc-connectors-release-2.3.0自己编译

最新的cdc是2.21仅支持flink 1.13* 1.14*,而flink已经有1.15版本;自己编译支持1.15 下载官方包 https://github.com/ververica/flink-cdc-connectors/releases/tag/release-2.3.0 我下载的是source包,大家也可以去下载源码 1、下载后我们只需…

JUC并发编程第十三篇,AQS的作用与体系结构

JUC并发编程第十三篇,AQS的作用与体系结构一、AQS是什么?二、AQS在JUC中的地位与作用三、AQS体系结构一、AQS是什么? AbstractQueuedSynchronizer(抽象队列同步器),是用来构建锁或者其它同步器组件的重量级…

[附源码]Node.js计算机毕业设计高校社团管理系统Express

项目运行 环境配置: Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术: Express框架 Node.js Vue 等等组成,B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境:最好是Nodejs最新版,我…

java EE初阶 — 多线程案例单例模式

文章目录1单例模式主要模式1.1 饿汉模式1.2 懒汉模式2 单例模式安全性问题1单例模式主要模式 在某些场景中,有些特定的类只能输出一个实例(对象),不应该创建多个实例,此时就可以使用 单例模式。 使用了单例模式后&…

5款轻量级小软件,第一款更是近期必备!

今天的主题是简洁,轻便,都是轻量级的小软件,界面都是非常简洁,而且无广告的。 1.自动抢火车票工具——12306Bypass 12306Bypass是一款专用于帮助用户抢购火车车票的工具,春运马上就到了,又到了抢票回家的…

Docker数据卷操作

1. 为什么使用数据卷 卷是在一个或多个容器内被选定的目录,为docker提供持久化数据或共享数据,是docker存储容器生成和使用的数据的首选机制。对卷的修改会直接生效,当提交或创建镜像时,卷不被包括在镜像中。 总结为两个作用&am…

功率放大器在压电传感器矩形阵列成像研究中的应用

实验名称:激光和压电传感器密集型矩形阵列成像质量的比较分析 研究方向:Lamb波、无损检测、缺陷成像和定位 测试目的: 将密集型矩形阵列分别与压电传感器检测技术和激光检测技术相结合,利用幅值成像和符号相干因子成像实现对铝板结…

vector模拟实现下篇及迭代器失效和深浅拷贝问题详解

文章目录1:构造函数1.1默认构造函数1.2迭代器构造1.3用n个val构造1.4拷贝构造2:operator3:析构函数和clear4:迭代器失效问题4.1:删除偶数深浅拷贝1:构造函数 1.1默认构造函数 vector():_start(nullptr),_end(nullptr),_endofstorage(nullptr){}1.2迭代器构造 template<clas…

手动安装Kylin5.0版本的过程

官方文档 https://kylin.apache.org/目前kylin3,4版本是有docker版本和安装包的,5.0只有docker没有安装包 安装包 https://kylin.apache.org/download/安装kylin5.0 Kylin5.0文档拉取镜像 docker pull apachekylin/apache-kylin-standalone:5.0.0运行镜像 docker run -d \ …

linux-jdk、nginx

一、安装nginx Nginx是一个web服务器也可以用来做负载均衡及反向代理使用&#xff0c; 目前使用最多的就是负载均衡&#xff0c;这篇文章主要介绍了centos8 安装 nginx Nginx是一种开源的高性能HTTP和反向代理服务器&#xff0c;负责处理Internet上一些最大站点的负载。 它…