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

【STM32CubeMX串口通信详解】USART2 -- DMA发送 + DMA空闲中断 接收不定长数据

( 本篇正在编写、更新状态中.....) 

文章目录:

前言        


前言        

        本篇,详细地用截图解释 CubeMX 对 USART2 的配置,HAL函数使用,和收发程序的编写。

        收、发机制:DMA发送 + DAM空闲中断接收。

        DMA+空闲中断的搭配,相当高效,而且最大地节省芯片运行资源。

        本篇代码,适用于绝大部分的串口模块通信,如ESP8266、串口屏、蓝牙模块等。

        不讲解串口通信原理, 串口通信原理,请自行扫盲:串口通讯工作原理!  

        只详细地截图:CubeMX配置、代码编写过程。

        如有错漏,欢迎指正。     

        约定1:本篇所述USART通信,是指常用的异步通信,即UART。    


一、准备工作

1、接线

        为了方便测试,在上篇USART1通信的基础上,增加USART2的代码。

        使用USART2接串口通信模块,本文所用,是蓝牙模块:ECB02;

        实验将通过串口助手(USART1), 对蓝牙模块ECB02 (USART2),进行调试和通信。

       

        ①  USART1的接线

             使用了板载USB转TTL。USART1的PA9、PA10, 已连接USB转TTL。

             使用USB线,插到开发板上的CMSIS-DAP接口,无需外接USB转TTL模块。

        

        ②  USART2接线

         本文引脚连接,如下表:

蓝牙 ECB02 模块STM32 开发板
RXDTX-PC10  (UART4)
TXDRX-PC11    (UART4)
GNDGND
VCC3.3V 

2、新建工程        

        为了减少篇幅长度,复制之前已建立的工程:USART1 -- DMA发送+DMA空闲中断接收

        复制它整个文件夹、重新修改文件夹名称,如:USART2 -- DMA发送+DMA空闲中断接收

        技巧:只能修改工程文件夹名称,不要修改工程文件的名称,否则,CubeMX无法重新生成。

        技巧:复制已有工程,是老司机的日常操作,能延用已写好的代码和配置,大大减少开发时间。

        相关基础文章链接:

        新建一个工程(STM32F103)

        新建一个工程(STM32F407)

        GPIO 推挽输出模式,点亮LED灯
 

        USART1 -- DMA发送 + DMA空闲中断 接收不定长数据


二、CubeMX的配置

        串口通信的收发机制,有很多方式、组合。

        本文,介绍操作最简单、实用的一种组合:

        发送:DMA。

        接收:DMA+空闲中断接收。

        适用于绝大部分的串口模块通信,如ESP8266、串口屏、蓝牙模块等。

1、USART2 配置 异步通信

        在选择异步通信后,将会使用默认引脚:TX-PA2, RX-PA3.

        我们无需对引脚进行任何配置,CubeMX帮我们自动配置好!

2、通信协议参数

        本篇,使用常用配置:115200-None-8-1。

        如下图,4个主要的通信协议参数,一般只需修改波特率。蓝色的3项,基本万年不动。

3、使能中断

        USART1配置DMA后,会自动使能中断。

        USART2要手动打开中断。

        

4、打开DMA发送、接收        

        网上很多教程,只使能了USART_RX的DMA,而不使能USART_TX的DMA。

        除非,TX所用的DMA通道,已被其它设备占用了,否则,你为何能容忍它躺平不干活?!

       

        添加完成后的状态:

5、设置RX引脚上拉

        在选择异步通信后,CubeMX会自动配置引脚的工作模式。

        这时的默认配置:不打开上下拉。这可能会使引脚在悬空状态时电平不确定,产生误接收。

        我们把RX接收引脚的PA3,修改为:上拉(Pull-up),给引脚固定一个弱上拉,以避免悬空时产生误接收。

       

        好了,就这么简单。

        优先级配置,默认就行。DMA配置,默认就行。   

        然后,点击 GENERATE CODE,生成工程吧!


三、发送操作、代码解释

        我们打开生成后的Keil工程。   

        在main.c文件能看到,已增加了DMA和USART1的初始化代码。

        初始化部分,CubeMX已经帮我们生成好了。

        发送数据的函数,CubeMX也已经生成好了。                

        在工程中,现在就能直接使用下面这 3个 函数,发送任何数据:

HAL_UART_Transmit     (&huart1, uint8_t *pData, uint16_t Num, 超时值);    
HAL_UART_Transmit_IT  (&huart1, uint8_t *pData, uint16_t Num);         
HAL_UART_Transmit_DMA (&huart1, uint8_t *pData, uint16_t Num);         

        先上板测试,后面再解释!

        在/* USER CODE BEGIN 2 */  与 /* USER CODE END 2 */ 之间,敲入以下发送代码:

    /* USER CODE BEGIN 2 *//* 用户代码,必须写在配对的BEGIN与END之间 */static char  strTem[100] = "Hello World!\r";                            // 定义一个数组,也可以是其它的数据,如结构体等HAL_UART_Transmit (&huart1, (uint8_t*)strTem, strlen(strTem), 0xFFFF);  // 发送方式2:HAL_UART_Transmit(), 不推荐使用; 阻塞式发送,当调用后,程序会一直死等,不干其它事了(中断除外),直到发送完毕HAL_UART_Transmit_IT (&huart1, (uint8_t*)strTem, strlen(strTem));       // 发送方式3:HAL_UART_Transmit_IT(), 推荐使用; 利用中断发送,非阻塞式,大大减少资源占用; 注意:当上次的调用还没完成发送,下次的调用会直接返回(放弃),所以,要想连接发送,两行调用间,要么判断串口结构体gState的值,要么调用延时HAL_Delay(ms), ms值要大于前一帧发送用时, 用时计算:1/(波特率*11*前一帧字节数) while((&huart1)->gState != HAL_UART_STATE_READY);                       // 等待上条发送结束; 也可以用HAL_Delay延时法,但就要计算发送用时; 两种方法都是死等法,程序暂时卡死不会往下运行; 如果两次发送间隔时间大,如,大于100ms, 就不用判断语句了。HAL_UART_Transmit_DMA (&huart1,(uint8_t*)strTem, strlen(strTem));       // 发送方式4:HAL_UART_Transmit_DMA(),推荐使用; 利用DMA发送,非阻塞式,最大限度减少资源占用; 注意:当上次的调用还没完成发送,下次的调用会直接返回(放弃); 所以,要想连接发送,两行调用间,要么判断串口结构体gState的值,要么调用延时HAL_Delay(ms), ms值要大于前一帧发送用时,用时计算:1/(波特率*11*前一帧字节数)/* USER CODE END 2 */

        打开电脑的串口助手。

        编译、烧录。串口助手马上有显示:

        如果你那边,烧录后没有显示,要么是串口号错了,要么是没有打勾keil的自动复位。

        

        下面,对3个函数的使用,逐一解释,不建议新手跳过,有避坑干货。

        1、HAL_UART_Transmit   (&huart1,  uint8_t *pData,  uint16_t Num,  超时值);  

        阻塞式发送。参数:串口,数据地址,发送的字节数,ms超时值 

        每发送一个字节,死等,好了继续发下一个,再死等,不断重复。

        就是以前标准库种那最普通的死等法,只是它增加了一个超时值。

        超时值:如果指定时间内没发送完毕,就直接返回,防止卡死。数据发送通信需时:

        1秒 ÷ 波特率 × 字节数 × 10 × 1000ms。举例:115200波特率,100字节,大约用时 9ms。

        新手如果不会计算,直接把超时值填大一点,如50ms。

        2、HAL_UART_Transmit_IT  (&huart1,  uint8_t *pData,  uint16_t Num);  

        利用中断发送。参数:串口,数据地址,发送的字节数

        向寄存器填入一个字节,程序就继续干其它的事去,当一个字节发送完成后会产生发送中断,CubeMX生成的回调函数,自动填入下一个字节,不断重复,不用干预。   

        非阻塞式发送。能大大地减少程序运行时间的占用。

        有一点要注意:当连续地调用本中断发送函数时,调用的间隔时间,小于通信所需的用时(按上),这时,后面那条函数调用,会直接返回,放弃发送。 因为函数内部,在发送前会判断串口的忙状态,如果在忙(还在发送上一包数据),就放弃本包数据,返回。   

        解决的方法,有两个:

        ①  最常用的,两行中断发送函数间,插入:HAL_Delay(10),原理参考上面的发送需时。  

        ②  两行中断发送函数间,插入 while((&huart1)->gState != HAL_UART_STATE_READY); 和 HAL_Delay() 一样,都是死等,但能省了那么一点点运行时间。

  3、HAL_UART_Transmit_DMA (&huart1,  uint8_t *pData,  uint16_t Num);      

        DMA发送。参数:串口,数据地址,发送的字节数。       

        上面的中断发送函数,100个字节,会产生100次中断。这个DMA发送函数,全程只产生一次中断。

        调用后,函数给DMA数据地址,DMA就自动开始搬砖,它会把数据逐字节搬运到串口的DR寄存器上,等串口发送完这个字节了,再自动搬运下一个,过程完全不占用程序运行资源。搬完了,就产生一个中断,给程序打个招呼。通常,我们程序上,把这个“招呼”也省略了,不用理会它。

        3个发送函数中,推荐使用这个DMA发送函数,发送的最优解。

        同样的,两行DMA发送函数间,注意发送间隔,否则放弃发送直接返回。处理方法同上。


    

五、接收代码的编写

        发送数据可以调用现成的函数,而接收数据,现成函数不太好用。

        接收也有3个函数,和发送的3个函数相对应:

HAL_UART_Receive     (&huart1, uint8_t *pData, uint16_t Num, 超时值);    
HAL_UART_Receive_IT  (&huart1, uint8_t *pData, uint16_t Num);         
HAL_UART_Receive_DMA (&huart1, uint8_t *pData, uint16_t Num);         

        一般,大家都不使用这三个函数,太TM的难用了,有兴趣的可csdn搜它们的使用优劣分析。

        我们利用HAL库现成的资源,另敲十来行代码,令串口的接收机制:更实用、更灵活。

        完成后,整个接收过程,1个结构体 + 1个HAL库函数 + 1个回调函数,全程自动接收。

        共分4小项,下面将有详细操作图解:

        ①  定义一个结构体变量:存放接收的字节数、数据数组。

        ②  开启DMA:让硬件自动接收数据放到缓存

        ③  重写回调函数:当一帧数据接收好了,把缓存的数据,转存到全局结构体变量里,备用。

        ④  在需要使用串口接收的地方,如在while中,判断接收字节数>0,  即为接收到新一帧数据了。

1、定义一个结构体变量:存放接收的字节数、数据

          首先,在main.h文件,新建一个结构体类型

        在 /* USER CODE BEGIN ET */  与 /* USER CODE END ET */ 之间,新建一个结构体类型。

/* USER CODE BEGIN ET */
/* 所有用户代码,必须写在配对的BEGIO与END注释行之间,否则重新生成时会被删除 */typedef struct                         // 声明一个结构体,方便管理变量
{uint16_t  ReceiveNum;              // 接收字节数uint8_t   ReceiveData[512];        // 接收到的数据uint8_t   BuffTemp[512];           // 接收缓存; 注意:这个数组,只是一个缓存,用于DMA逐个字节接收,当接收完一帧后,数据在回调函数中,转存到 ReceiveData[ ] 存放。即:双缓冲,有效减少单缓冲的接收过程新数据覆盖旧数据
} xUSATR_TypeDef;extern xUSATR_TypeDef xUSART1 ;        // 定义结构体,方便管理变量。也可以不用结构体,用单独的变量/* USER CODE END ET */

        它有3个成员:   

        uint16_t  ReceiveNum;            // 接收字节数,只要字节数>0,即为接收到新一帧数据
        uint8_t   ReceiveData[512];     // 接收到的数据
        uint8_t   BuffTemp[512];           // 临时缓存,在DMA空闲中断中将把一帧数据复制到ReceivedData[ ]   

        有些教程,还有一个Flag变量,用来标记是否接收到数据。我们直接用ReceiveNum判断,更简单。

        在定义结构体类型的下面一行代码中,用extern声明了一个结构体变量,它将在main.c中定义。    

        技巧:如果希望定义的结构体类型,工程全局可用,就要在h文件中定义,其它文件引用这个h文件。        

        技巧:如果希望定义的变量,能被工程全局调用,就在h文件中用extern声明,然后在某个c文件中定义。

       ②  回到main.c,定义结构体变量

        在 /*  USER CODE BEGIN 0 */ 与 /*  END 0 */ 之间,使用新建的结构体类型,定义我们的结构体变量。

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* 所有用户代码,必须写在配对的BEGIO与END注释行之间,否则重新生成时会被删除 */xUSATR_TypeDef xUSART1 = {0};          // 定义结构体,方便管理变量。也可以不用结构体,用单独的变量/* USER CODE END 0 */

        现在,我们拥有一个了全局变量:xUSART1。

        以后的其它文件,如蓝牙模块驱动、串口屏驱动,只要在文件中引用:main.h,就能通过这个结构体变量,使用串口1接收的数据了。    

         

2、开启DMA,让硬件自动接收数据

        我们整个接收过程,仅使用到1个HAL库函数,

        只需在main()函数的初始化部分,调用HAL库函数:

        HAL_UARTEx_ReceiveToIdle_DMA (串口、缓存、字节数) ;

        参数:串口、接收缓存区、最大接收字节数   

        作用:使能DMA、使能串口的空闲中断,正式进入接收状态。                   

        操作:在 main.c的   /* USER CODE BEGIN 2 */  与  /* END 2 */ 之间,插入函数:

HAL_UARTEx_ReceiveToIdle_DMA(&huart1, xUSART1.BuffTemp, sizeof(xUSART1.BuffTemp));  // 开启DMA空闲中断    

        插入后的位置,如下图:

        

        调用函数后,硬件就会立刻进入自动接收状态:从RX引脚接收到的数据,会逐个字节顺序存放到指定缓存中,这里我们指定的缓存是:xUSART1.BuffTemp。

        因为函数内部,开启了DMA中断、空闲中断,所以达成下列两个条件之一,就会触发中断:

        ①  DMA接收的字节数,达到了参数中的最大值
        ②  串口发生空闲中断,即RX引脚,超过1字节的时间,没有新信号。        

        当上述中断产生时,硬件自动调用其相关的中断服务函数,再继而调用回调函数。

        CubeMX生成的代码,已编写好上述两个中断服务函数,还定义了一个它俩最终调用的回调函数。注意,这个回调函数是一个弱函数。

        因此,我们不用管中断服务函数,只需重写这个回调函数,就能实现对接收数据的处理。   

3、重写DMA空闲中断回调函数

        DMA完成中断、空闲中断,所调用的回调函数:

        HAL_UARTEx_RxEventCallback(串口,接收到的字节数);       

        弱函数定义在stm32xx_hal_gpio.c文件的底部。      

        现在,我们对它进行重写,以实现对接收数据的处理。

        在main.c的底部,/* USER CODE BEGIN 4 */ 与 /*  END 4 */ 之间,新建函数,并编写其代码:

/* USER CODE BEGIN 4 */
/* 所有用户代码,必须写在配对的BEGIN与 END之间 *//******************************************************************************* 函  数: HAL_UARTEx_RxEventCallback* 功  能: DMA+空闲中断回调函数* 参  数: UART_HandleTypeDef  *huart   // 触发的串口*          uint16_t             Size    // 接收字节* 返回值: 无* 备  注: 1:这个是回调函数,不是中断服务函数。技巧:使用CubeMX生成的工程中,中断服务函数已被CubeMX安排妥当,我们只管重写回调函数*          2:触发条件:当DMA接收到指定字节数时,或产生空闲中断时,硬件就会自动调用本回调函数,无需进行人工调用;*          2:必须使用这个函数名称,因为它在CubeMX生成时,已被写好了各种函数调用、函数弱定义(在stm32xx_hal_uart.c的底部); 不要在原弱定义中增添代码,而是重写本函数*          3:无需进行中断标志的清理,它在被调用前,已有清中断的操作;*          4:生成的所有DMA+空闲中断服务函数,都会统一调用这个函数,以引脚编号作参数*          5:判断参数传进来的引脚编号,即可知道是哪个串口接收收了多少字节
******************************************************************************/
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{if (huart == &huart1)                                                                    // 判断串口{__HAL_UNLOCK(huart);                                                                 // 解锁串口状态xUSART1.ReceiveNum  = Size;                                                          // 把接收字节数,存入结构体xUSART1.ReceiveNum,以备使用memset(xUSART1.ReceiveData, 0, sizeof(xUSART1.ReceiveData));                         // 清0前一帧的接收数据memcpy(xUSART1.ReceiveData, xUSART1.BuffTemp, Size);                                 // 把新数据,从临时缓存中,复制到xUSART1.ReceiveData[], 以备使用HAL_UARTEx_ReceiveToIdle_DMA(&huart1, xUSART1.BuffTemp, sizeof(xUSART1.BuffTemp));   // 再次开启DMA空闲中断; 每当接收完指定长度,或者产生空闲中断时,就会来到这个}
}/* USER CODE END 4 */

        上面代码,我们重点解释后四行:         

        ①  xUSART1.ReceiveNum  = Size;  
             把接收的字节数,存入结构体 xUSART1.ReceiveNum,以备使用 。
             在程序的其它地方,判断 ReceivNum > 0, 就能知道是否收到新一帧数据了。        

        ②  memset(xUSART1.ReceivedData, 0, sizeof(xUSART1.ReceivedData));  
             清0前一帧的数据缓存              

        ③  memcpy(xUSART1.ReceivedData, xUSART1.BuffTemp, Size);
             把新数据,从临时缓存中,复制到xUSART1.ReceivedData[], 以备使用  
             从结构体和这段回调函数中,可以发现,这是一个双缓存的操作思路。
             .ReceivedData用于存放接收后完整的一帧数据,对外使用 。
             .BuffTemp用于DMA接收过程,是一个中间缓存。

        ④  HAL_UARTEx_ReceiveToIdle_DMA(&huart1, xUSART1.BuffTemp, sizeof(xUSART1.BuffTemp));
             再次开启DMA空闲中断,进入接收状态。
             我们在main()函数的初始化部分,已调用过这个函数了,为什么要在回调函数中再次调用?
             因为在DMA的中断服务函数里,会关闭DMA,即只接收一次。所以,在接收完一帧后,再次调用函数,就能让DMA开始工作接收下一帧。在这个位置调用 ,能让DMA不断地循环工作。
             其实,在CubeMX配置中,DMA有一个选项 :Mode的circular, 可以让DMA进行连续地的工作,接收完成后,无需在回调函数里再次开启DMA 。但是,目前的CubeMX版本(V6.10),这个参数的选择,会使我们上面的DMA接收与发送,相冲突。那我们二选一好了,自行手工调用。

        注意一点:本篇的处理,是保存最后一帧数据。当有新一帧数据来了,会自动盖掉旧帧数据。

        至此,接收工作已准备妥当。程序运行后,硬件会自动接收,并把接收的帧数据,存放到结构体中。   

4、接收的使用示范

        我们来试试使用的效果吧!

        ①  在main.c的while函数里,编写接收判断代码:

/* USER CODE BEGIN WHILE */
    while (1)
    {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
        /* 用户代码,必须写在配对的BEGIN与END之间 */

        if (xUSART1.ReceiveNum)                                                  // 判断字节数
        {
            printf("\r<<<<< USART1 接收到一帧数据 \r");                  // 提示
            printf("字节数:%d \r", xUSART1.ReceiveNum);             // 显示字节数
            printf("ASCII : %s\r", (char *)xUSART1.ReceiveData);    // 显示数据,以ASCII方式显示,即以字符串的方式显示
            printf("16进制: ");                                                              // 显示数据,以16进制方式,显示每一个字节的值
            for (uint16_t i = 0; i < xUSART1.ReceiveNum; i++)          // 逐个字节输出
                printf("0x%X ", xUSART1.ReceiveData[i]);                   // 以16进制显示
            printf("\r\r");                                                                       // 显示换行

            xUSART1.ReceiveNum = 0;                                             // 清0接收标记
        }

    }
    /* USER CODE END 3 */

             

②  工程,编译,烧录!

③  打开串口助手,参数设置 115200-None-8-1, 打开对应的串口端口。

     按一下板子右下角的复位键,串口输出,如下图:

③  在串口的发送区,输入字符串 "天气不错喔~~",或者其它数据。

     点击发送:串口助手将通过PA10,发送到开发板。在程序的while函数中,那段代码判断接收到数据后,为了方便观察,将通过USART1的PA9发出数据,串口助手接收后,显示如下:

④  试试16进制数据的发送。

     发送区:打勾16进制发送,输入随意16进制值,不用加0x,用空格作间隔。

     注意,16进制的值,不一定是ASCII码表的显示范围值,所以在ASCII显示中,会出现乱码,正常现象。     

  

至此,USART1的收发,已完整地展示完毕。

如有错漏,欢迎留言指正修改~~


USART2

USART3

UART4

UART5

USART6

相关文章:

【STM32CubeMX串口通信详解】USART2 -- DMA发送 + DMA空闲中断 接收不定长数据

&#xff08; 本篇正在编写、更新状态中.....) 文章目录&#xff1a; 前言 前言 本篇&#xff0c;详细地用截图解释 CubeMX 对 USART2 的配置&#xff0c;HAL函数使用&#xff0c;和收发程序的编写。 收、发机制&#xff1a;DMA发送 DAM空闲中断接收。 DMA空…...

Webpack5入门到原理19:React 脚手架搭建

开发模式配置 // webpack.dev.js const path require("path"); const ESLintWebpackPlugin require("eslint-webpack-plugin"); const HtmlWebpackPlugin require("html-webpack-plugin"); const ReactRefreshWebpackPlugin require("…...

苹果眼镜(Vision Pro)的开发者指南(6)-实战应用场景开发 - 游戏、协作、空间音频、WebXR

第一部分:【构建游戏和媒体体验】 了解如何使用visionOS在游戏和媒体体验中创建真正身临其境的时刻。游戏和媒体可以利用全方位的沉浸感来讲述令人难以置信的故事,并以一种新的方式与人们联系。将向你展示可供你入门的visionOS游戏和叙事开发途径。了解如何使用RealityKit有…...

flutter底层架构初探

本文出处&#xff1a;​​​​​​​​​​​​​Flutter 中文开发者网站 架构 embedder嵌入层 提供程序入口&#xff08;其他原生应用也采用此方式&#xff09;&#xff0c;程序由此和底层操作系统协调&#xff08;surface渲染、辅助功能和输入服务&#xff0c;管理事件循环…...

初识SQL注入

目录 注入攻击 SQL注入 手工注入 Information_schema数据库 自动注入 介绍一下这款工具&#xff1a;sqlmap 半自动注入 前面给大家通过学习练习的方式将XSS攻击的几种形式和一些简单的靶场和例题的演示&#xff0c;从本篇开始我将和小伙伴们通过边复习、边练习的方式来进…...

React初探:从环境搭建到Hooks应用全解析

React初探&#xff1a;从环境搭建到Hooks应用全解析 一、React介绍 1、React是什么 React是由Facebook开发的一款用于构建用户界面的JavaScript库。它主要用于构建单页面应用中的UI组件&#xff0c;通过组件化的方式让开发者能够更轻松地构建可维护且高效的用户界面。 Reac…...

设计模式——1_6 代理(Proxy)

诗有可解不可解&#xff0c;若镜花水月勿泥其迹可也 —— 谢榛 文章目录 定义图纸一个例子&#xff1a;图片搜索器图片加载搜索器直接在Image添加组合他们 各种各样的代理远程代理&#xff1a;镜中月&#xff0c;水中花保护代理&#xff1a;对象也该有隐私引用代理&#xff1a;…...

性能优化(CPU优化技术)-NEON 介绍

「发表于知乎专栏《移动端算法优化》」 本节主要介绍基本 SIMD 及其他的指令流与数据流的处理方式&#xff0c;NEON 的基本原理、指令以及与其他平台及硬件的对比。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;…...

Kafka-服务端-KafkaController

Broker能够处理来自KafkaController的LeaderAndIsrRequest、StopReplicaRequest、UpdateMetadataRequest等请求。 在Kafka集群的多个Broker中&#xff0c;有一个Broker会被选举为Controller Leader,负责管理整个集群中所有的分区和副本的状态。 例如&#xff1a;当某分区的Le…...

ffmpeg使用手册

ffmpeg使用手册 文章目录 ffmpeg使用手册ffmpeg是什么指令总结1.查看ffmpeg版本2.mkv转mp43.裁剪 .mkv 视频4.不调节帧率&#xff0c;尽可能保证原视频质量的情况下将原始视频压缩4.1 crf4.2 preset 5.调节视频帧率6.调节帧率&#xff0c;尽可能保证原视频质量的情况下将原始视…...

操作系统导论-课后作业-ch15

对应异步社区资源HW-Relocation&#xff1a; 1. 种子1运行结果&#xff1a; 种子2运行结果&#xff1a; 种子3运行结果&#xff1a; 2. 需要将界限设置为930&#xff0c;结果如下&#xff1a; 3. 有人说原书翻译有误&#xff0c;原文如下所示&#xff1a; 原文翻译如…...

宝塔面板SRS音视频TRC服务器启动失败

首先&#xff0c;查找原因 1.先看srs服务在哪 find / -type f -name srs 2>/dev/null运行结果&#xff1a; /var/lib/docker/overlay2/5347867cc0ffed43f1ae24eba609637bfa3cc7cf5f8c660976d2286fa6a88d2b/diff/usr/local/srs/objs/srs /var/lib/docker/overlay2/5347867…...

04-Seata修改通信端口

基于docker环境部署下&#xff0c;可以翻看专栏之前的文章 配置文件 /home/server/seata/resources/application.yml 默认${server.port} 1000 1、修改服务端(TC)配置 seata:server:service-port: 7090 2、修改映射端口 在启动脚本中修改映射端口 docker run -id --nam…...

活动回顾丨云原生技术实践营上海站「云原生 AI 大数据」专场(附 PPT)

AI 势不可挡&#xff0c;“智算”赋能未来。2024 年 1 月 5 日&#xff0c;云原生技术实践营「云原生 AI &大数据」专场在上海落幕。活动聚焦容器、可观测、微服务产品技术领域&#xff0c;以云原生 AI 工程化落地为主要方向&#xff0c;希望帮助企业和开发者更快、更高效地…...

【数据结构与算法】4.自主实现单链表的增删查改

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢迎各位大佬指点&…...

Linux系统常用命令行指令

Linux系统是一种常用于开源项目开发的生产环境&#xff0c;因其免费、开源、安全、稳定的特点被广泛应用于手机、平板电脑、路由器、电视和电子游戏机等嵌入式系统中&#xff0c;能够更加简便地让用户知道系统是怎样工作的。前几日我安装好了Red Hat Enterprise Linux 9.0&…...

java SSM园林绿化管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM园林绿化管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代 码和数据库&#xff0c;系统主要采…...

【issue-halcon例程学习】edges_color.hdev

例程功能 演示如何使用edges_color&#xff0c;展示只能从彩色图像中提取某些边缘的图像&#xff0c;说明edges_color和edges_image输出之间的差异。 代码如下 dev_update_off () read_image (Image, olympic_stadium) get_image_size (Image, Width, Height) dev_close_wind…...

设计模式—行为型模式之备忘录模式

设计模式—行为型模式之备忘录模式 备忘录&#xff08;Memento&#xff09;模式&#xff1a;在不破坏封装性的前提下&#xff0c;捕获一个对象的内部状态&#xff0c;并在该对象之外保存这个状态&#xff0c;以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模…...

CMS如何调优

业务JVM频繁Full GC如何排查 原则是先止损&#xff0c;再排查。 FGC的原因是对象晋升失败或者并发模式失败&#xff0c;原因都是老年代放不下晋升的对象了。 1.可能是大对象导致的内存泄漏。快速排查方法&#xff1a;观察数据库网络IO是否和FGC时间点吻合&#xff0c;找到对应…...

在PyCharm中安装GitHub Copilot插件,login之后报出如下错误:

Sign in failed. Reason: Request signInInitiate failed with message: connect ECONNABORTED 20.205.243.166:443, request id: 7, error code: -32603 前提&#xff1a; 设置网址&#xff1a;https://github.com/settings/copilot&#xff0c;已设置为允许 或者&#xff1…...

L1-093 猜帽子游戏(Java)

宝宝们在一起玩一个猜帽子游戏。每人头上被扣了一顶帽子&#xff0c;有的是黑色的&#xff0c;有的是黄色的。每个人可以看到别人头上的帽子&#xff0c;但是看不到自己的。游戏开始后&#xff0c;每个人可以猜自己头上的帽子是什么颜色&#xff0c;或者可以弃权不猜。如果没有…...

JVM篇--JVM调优高频面试题

1 说一下 JVM 调优的工具&#xff1f; JDK 自带了很多监控工具&#xff0c;都位于 JDK 的 bin 目录下&#xff0c;其中最常用的是jconsole 和 jvisualvm 这两款视图监控工具。 jconsole&#xff1a;用于对 JVM 中的内存、线程和类等进行监控&#xff1b; jvisualvm&#xff1a…...

微软 AD 介绍 | 安全建议 | 防护

介绍&#xff1a; 什么是Active Directory&#xff08;AD&#xff09;&#xff1f; Active Directory 是由 微软开发的目录服务&#xff0c;用于存储和管理网络中的资源&#xff0c;如计算机、用户、组和其他网络对象。允许组织管理员轻松地管理和验证网络中的用户和计算机。 …...

React16源码: React中的reconcileChildren的源码实现

reconcileChildren 1 &#xff09;概述 在更新了一个节点之后&#xff0c;拿到它的props.children要根据这个children里面的 ReactElement 来去创建子树的所有的 fiber 对象要根据 props.children 来生成 fiber 子树&#xff0c;然后判断 fiber 对象它是否是可以复用的 因为我…...

幻兽帕鲁Docker服务端搭建

幻兽帕鲁Docker服务端搭建 各种命令 https://bbs.saraba1st.com/2b/thread-2168983-1-1.html 存档恢复 这里直接看这个工程的readme就行&#xff1a;https://github.com/yoko-murasame/palworld-host-save-fix 其他参考&#xff1a;https://forum.gamer.com.tw/C.php?bsn7…...

【ARM Cortex-M 系列 1.1 -- Cortex-M33 与 M4 差异 详细介绍】

请阅读【嵌入式开发学习必备专栏 之 Cortex-Mx 专栏】 文章目录 背景Cortex-M33 与 M4 差异Cortex-M33Cortex-M4关系和差异举例说明 背景 在移植 RT-Thread 到 瑞萨RA4M2&#xff08;Cortex-M33&#xff09;上时&#xff0c;遇到了hardfault 问题&#xff0c;最后使用了Cortex…...

docker 部署及命令

一、容器概述 1、为什么要用到容器&#xff1f; ①容器可以屏蔽底层操作系统的差异性&#xff0c;让业务应用不管在哪里都是使用容器的环境运行&#xff0c;从而保证开发测试环境与生产环境的一致性 ②容器部署起来非常便捷和迅速&#xff0c;缩短开发测试部署的周期时间 2…...

API接口安全总结

接口分类 HTTP接口 RPC接口&#xff08;客户端和服务器端的连接 例如游戏登陆&#xff09;非web协议&#xff0c;PRC 远程过程调用 Remote Procedure Call&#xff0c;其就是一个节点请求另外一个节点提供的服务。当两个物理分离的子系统需要建立逻辑上的关联时&#xff0c;R…...

性能优化-HVX 指令介绍

「发表于知乎专栏《移动端算法优化》」 本文主要介绍了 HVX 指令相关的知识&#xff0c;包括 HVX 寄存器相关内容&#xff0c;指令的背景依赖&#xff0c;部分常用 intrinsic HVX 指令。具体指令的详细内容及使用还需阅读 HVX 的指令文档&#xff0c;以及细致的实践操作。 &…...