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

串口DMA接收数据基本思路

串口DMA接收基本思路

  • 串口DMA接收数据基本思路
    • 一、串口处理使用背景及常用处理方法
    • 二、串口DMA接收相关思路
    • 三、串口DMA发送相关思路

串口DMA接收数据基本思路

一、串口处理使用背景及常用处理方法

  单片机经常有串口处理大量数据的场景,常用的串口处理数据方式有如下方式:

   a.串口接收中断接收 + 串口阻塞发送,即使用串口接收中断对数据进行接收。然后处理完毕后在主程序中调用串口发送函数进行发送。该方式每接收一个数据便进入一次接收中断,且发送是阻塞发送,会阻碍其他处理操作运行。适合数据量不大的场景。

   b.串口空闲中断(接收中断) + 环形缓冲区 + 串口阻塞发送,该方法使用串口空闲中断和接收中断配合环形缓冲区可以解决大部分的串口数据处理场景。适合数据量较大的场景。

   c.串口中断 + DMA接收 + DMA发送,该方法使用了DMA可以释放CPU的占用,CPU由每次接收触发中断变成接收完毕触发或DMA发送完毕触发,减少中断触发频率。适合数据量较大的场景。

  以上三种方式各有自己的优缺点,DMA串口尽管处理效率看起来是最高的,但是耗费了DMA资源。而DMA资源往往是单片机较为稀缺的资源。因此还是需要根据项目需求合理选择。本次主要是在资源富裕的情况下使用DMA进行串口的收发测试。

二、串口DMA接收相关思路

  DMA串口接收,大致流程如上图所示;因为本次使用的是STM32F1系列的DMA,只有传送任务完成一半的中断(DMA1_IT_HTx)。因此借用HTx中断实现双缓冲区效果进行接收。[本篇只针对串口DMA进行解析流程,DMA相关基础知识请参考其他文章]。

  如上图中,需要准备一个缓冲区(即用户自己管理的数组),这个数组需要在程序编译后有确定的地址(不要使用会被析构掉的数组地址)。原因是:开启DMA传送后,DMA传送是不经过CPU的直接地址之间的快速传送。此时CPU也会同步正常运行,而如果该地址会因CPU程序运行用作别的功能使用,则会使DMA传送数据出错。

  简单点来说,串口接收DMA传送过程类似:CPU跟DMA控制器下发一个指令说,等串口收到数据了,你把收到的数据搬运到仓库里存着(发送缓冲区),这次就先搬运100个。CPU下完这个指令后,就能去执行别的指令处理任务,DMA同时也会开始搬运的工作;这样就可以实现CPU等100个数据都接收完了,再一次性处理,降低了CPU一个一个数据处理的工作量。这里的等串口收到数据了,就是DMA请求,串口每接收完毕一个数据,就会通知DMA,我收到了一个数据,你可以来搬运了。而100个就是DMA传送的数量,即这次DMA只搬运100个数据就完成本次DMA传送。

  这样的好处就是,CPU只在下发DMA指令的时候占用一瞬间,便可以去干别的工作,等DMA传送完毕之后再进行数据批量处理,极大减少了CPU占用的时间。那么想想一下如下的场景,假如一次DMA传送完成了,CPU正在处理数据的时间,串口又来了新数据,这时候因为数据还没处理完,发送缓冲区还不能释放出来进行下一次DMA接收,这个时候就会导致数据丢失。于是这种机制还需要进一步改进。

在这里插入图片描述

  假如我们将接收缓冲区Buffer人工区分成两个片区,CPU要一次性处理100个数据,分为每次处理50个数据,再借助半满中断就能实现如上图的过程。

  1、CPU下发DMA传送指令,传送100个数据。此时设置接收缓冲区大小为100个。

  2、当接收到50个数据时,即接收的数据填满接收缓冲区前50个数据时,进入DMA半满中断。在半满中断中通知CPU将接收到的数据搬入FIFO中(此时DMA仍旧在向后50个地址写入数据)。

  3、当接收到100个数据时,进入DMA完成中断。在DMA完成中断中,首先重置DMA继续进入下一轮的100个数据接收状态(这时由于接收缓冲区前50个地址数据已经被搬入FIFO中,可以直接使能DMA进行继续接收),然后CPU将后50个数据搬入FIFO中。

  4、CPU在前台对FIFO中数据进行逐一处理。

  可以看到,如果整体流程变为这样时,CPU提前对数据进行搬运,耗费一定CPU资源,但能够做到串口丢数据最少的操作。当然这样能够持续进行的前提一定是CPU将数据搬入FIFO的时间,要小于DMA将数据搬入数组的时间。以串口波特率为115200(bit/s)为例,数据位8位,停止位1位,总计一个有效数据为9位。接收1个数据耗时:1/115200 * 9 = 7.8e-5 (该时间还未加上DMA搬运时间);而单片机假如按照1M的速率进行处理1Byte数据,也能到达1e-6速率。因此CPU肯定是能够在DMA传送完毕之前,完成前半个缓冲区域的数据处理。

  即使这样,目前还剩最后一个问题需要解决;假设我们串口目前只有80个数据需要接收,按照我们优化后的程序去执行,想想会发生什么?由于我们CPU给DMA下发的是接收100个数据才完成。在接收到50个数据时,进入半满中断搬运;然后继续接收后面的50个数据。显而易见,DMA只能再接收到30个数据,便再也等不到数据了。这时会一直等不到DMA完成中断,等不到DMA完成中断,DMA接收的数据便无法被存在入用户FIFO中。造成数据本次不能完整的接收(只有再接收20个数据,凑满100个才能触发搬运)。

  为了解决该问题,需要加入串口空闲中断,即在串口空闲时,通知CPU来将数据搬运到FIFO中,同时重置DMA接收。这样便能接收不定长的数据,下一次数据变成30个也能完成接收。而串口空闲中断需要的信息有:当前接收缓冲区的首地址(是起始地址?还是一半的地址?)和需要搬移的数量。缓冲区首地址可以通过变量进行记录,DMA启动后起始地址为0,DMA半满中断触发后起始地址为缓冲区的一半。搬移数据量可以通过DMA计数器获得,DMA内部存在一个计数器,比如我们这里给的是初值100,DMA控制器每搬运一个,该值就会自减1,如变为99,98,97……。计数器变为50,触发半满中断;直到计数器变为0,则触发DMA完成中断。这样我们便可以通过获取该计数器值来计算本次DMA已经传送了多少数据。如上面所说的80个数据,此时计算规则为:100-20-50 = 30;100为DMA总传送数量;20为当前DMA计数器的值;50为buffer的起始地址(前50个数据已经传送完毕),程序部分如下:

#define USART1_DMA_BUF_MAX_LEN 128
#define USART1_DMA_FIFO_MAX_LEN 512_fifo_t   dma_rx_fifo_uart1;
uint8_t   dma_uart1_fifo_buf[USART1_DMA_FIFO_MAX_LEN]={0};
uint8_t   dma_uart1_rx_buf[USART1_DMA_BUF_MAX_LEN] = {0};
uint8_t   dma_uart1_tx_buf[USART1_DMA_BUF_MAX_LEN] = {0x01,0x02};
uint8_t   lock_state = 0;uart_dev uart_dev_uart1 =
{USART1 ,   //串口外设1115200 ,   //buad&dma_rx_fifo_uart1,  //DMA_RX_FIFOdma_uart1_rx_buf  ,  //DMA_RX_BUFdma_uart1_tx_buf     //DMA_TX_BUF
};/*** @brief  初始化串口GPIO* @param  * @retval 0:成功; 1:失败
*/
uint32_t uart_dev_gpio_init()
{GPIO_InitTypeDef  gpio_initstruct;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//PA9	TXDgpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP;gpio_initstruct.GPIO_Pin = GPIO_Pin_9;gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &gpio_initstruct);//PA10	RXDgpio_initstruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;gpio_initstruct.GPIO_Pin = GPIO_Pin_10;gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &gpio_initstruct);return 0;
}/*** @brief  初始化串口外设* @param  * @retval 0:成功; 1:失败
*/
uint32_t uart_dev_uart_init (uart_dev* uart_dev)
{  USART_InitTypeDef usart_initstruct;NVIC_InitTypeDef  nvic_initstruct;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);usart_initstruct.USART_BaudRate = uart_dev ->baud;usart_initstruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;		//无硬件流控usart_initstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;						//接收和发送usart_initstruct.USART_Parity = USART_Parity_No;									//无校验usart_initstruct.USART_StopBits = USART_StopBits_1;								    //1位停止位usart_initstruct.USART_WordLength = USART_WordLength_8b;							//8位数据位USART_Init(uart_dev->USARTx, &usart_initstruct);USART_ClearFlag(USART1, USART_FLAG_TC|USART_FLAG_RXNE);                       //清除默认标志位(主要是TC)USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);                                  //串口DMA接收使能USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);                                  //串口DMA发送使能USART_Cmd(USART1, ENABLE);													  //使能串口USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);					              //使能空闲中断nvic_initstruct.NVIC_IRQChannel = USART1_IRQn;nvic_initstruct.NVIC_IRQChannelCmd = ENABLE;nvic_initstruct.NVIC_IRQChannelPreemptionPriority = 0;nvic_initstruct.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&nvic_initstruct);return 0;
}/*** @brief  初始化串口DMA* @param  * @retval 0:成功; 1:失败
*/
uint32_t uart_dev_DMA_init(uart_dev* uart_dev)
{NVIC_InitTypeDef  nvic_initstruct;DMA_InitTypeDef   DMA_USART1_InitStructure;RCC_AHBPeriphClockCmd (RCC_AHBPeriph_DMA1,ENABLE);DMA_DeInit(DMA1_Channel5);DMA_USART1_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &USART1->DR           ;    //USART1数据接收寄存器DMA_USART1_InitStructure.DMA_MemoryBaseAddr     = (uint32_t) uart_dev ->dma_rx_buff;    //接收寄存器区间地址DMA_USART1_InitStructure.DMA_DIR                = DMA_DIR_PeripheralSRC      ;          //从外设读取; 外设 ——> 内存DMA_USART1_InitStructure.DMA_BufferSize         = USART1_DMA_BUF_MAX_LEN     ;          //传送长度DMA_USART1_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable  ;          //外设地址不增DMA_USART1_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable       ;          //内存地址自增DMA_USART1_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;          //传送数据为8位DMA_USART1_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte    ;          //MemoryDataSizeDMA_USART1_InitStructure.DMA_Mode               = DMA_Mode_Normal            ;          //普通模式 单次DMA_USART1_InitStructure.DMA_Priority           = DMA_Priority_VeryHigh      ;          //传送优先级非常高DMA_USART1_InitStructure.DMA_M2M                = DMA_M2M_Disable            ;          //从外设触发DMA_Init(DMA1_Channel5,&DMA_USART1_InitStructure);//开启DMADMA_Cmd(DMA1_Channel5,DISABLE);DMA_SetCurrDataCounter(DMA1_Channel5, USART1_DMA_BUF_MAX_LEN);DMA_Cmd(DMA1_Channel5,ENABLE);DMA_ClearFlag(DMA1_IT_TC5|DMA1_IT_HT5); DMA_ITConfig(DMA1_Channel5,DMA_IT_HT|DMA_IT_TC|DMA_IT_TE,ENABLE);  //使能传送一半及全部传送完毕中断nvic_initstruct.NVIC_IRQChannel = DMA1_Channel5_IRQn;nvic_initstruct.NVIC_IRQChannelCmd = ENABLE;nvic_initstruct.NVIC_IRQChannelPreemptionPriority = 0;nvic_initstruct.NVIC_IRQChannelSubPriority = 2;NVIC_Init(&nvic_initstruct);   return 0;
}/*** @brief  清除DMAx通道* @param  * @retval 0:成功; 1:失败
*/uint32_t uart_dev_clear_dma_channel(DMA_Channel_TypeDef* DMAy_Channelx)
{DMA_Cmd(DMAy_Channelx,DISABLE);DMA_SetCurrDataCounter(DMAy_Channelx,USART1_DMA_BUF_MAX_LEN);DMA_Cmd(DMAy_Channelx,ENABLE);return 0;
}/*
************************************************************
*	函数名称:	USART1_IRQHandler
*
*	函数功能:	串口1收发中断
*
*	入口参数:	无
*
*	返回参数:	无
*
*	说明:		
************************************************************
*/
void USART1_IRQHandler(void)
{uint8_t temp = 0;uint16_t recv_size = 0;if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断{temp = USART1->DR;USART_ClearFlag(USART1, USART_FLAG_RXNE);}else if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) //空闲中断{temp = USART1->SR; //清除中断标志temp = USART1->DR;USART_ClearFlag(USART1, USART_IT_IDLE);recv_size = USART1_DMA_BUF_MAX_LEN - DMA_GetCurrDataCounter(DMA1_Channel5) - uart_dev_uart1.dma_rx_buff_curr_addr; //重置DMAuart_dev_clear_dma_channel(DMA1_Channel5);fifo_write(uart_dev_uart1.dma_rx_fifo, &uart_dev_uart1.dma_rx_buff[uart_dev_uart1.dma_rx_buff_curr_addr], recv_size);uart_dev_uart1.dma_rx_buff_curr_addr = 0;     }}/*
************************************************************
*	函数名称:	DMA1_Channel5_IRQHandler(串口接收中断)
*
*	函数功能:	DMA1_Channel5中断
*
*	入口参数:	无
*
*	返回参数:	无
*
*	说明:		
************************************************************
*/
void DMA1_Channel5_IRQHandler(void)
{uint16_t recv_size = 0;if(DMA_GetITStatus(DMA1_IT_HT5) != RESET)   //DMA传送一半中断{DMA_ClearITPendingBit(DMA1_IT_HT5);    // 清除传输传送一半中断中断标志位	//搬移前半个buf数据recv_size = USART1_DMA_BUF_MAX_LEN - DMA_GetCurrDataCounter(DMA1_Channel5); fifo_write(uart_dev_uart1.dma_rx_fifo, uart_dev_uart1.dma_rx_buff, recv_size); //更新当前地址uart_dev_uart1.dma_rx_buff_curr_addr += recv_size;}else if(DMA_GetITStatus(DMA1_IT_TC5) != RESET)  //DMA传送完成中断{DMA_ClearITPendingBit(DMA1_IT_TC5);    // 清除传输完成中断标志位	recv_size = USART1_DMA_BUF_MAX_LEN - DMA_GetCurrDataCounter(DMA1_Channel5) - uart_dev_uart1.dma_rx_buff_curr_addr;      //重置DMAuart_dev_clear_dma_channel(DMA1_Channel5);fifo_write(uart_dev_uart1.dma_rx_fifo, &uart_dev_uart1.dma_rx_buff[uart_dev_uart1.dma_rx_buff_curr_addr], recv_size);uart_dev_uart1.dma_rx_buff_curr_addr = 0;}else if(DMA_GetITStatus(DMA1_IT_TE5) != RESET){UsartPrintf(USART2," DMA ERROR \r\n");}}

三、串口DMA发送相关思路

  如果理解了发送原理,接收原理相对来说便简单的多;将需要发送的数据填入发送buff,通知DMA将数据进行发送便可以实现。这里需要注意的是因为串口接收和发送DMA同属于一个DMA的不同通道,两个是没法同时进行的,如果是收发双工同时使用这一个DMA,可能会地效率有所影响。

  需要注意的是,因为发送是非阻塞进行的;一定要获取该次发送完成后,再进行下一次发送;当然若发送大量数据可以仿照接收,也使用类似与双buffer的方式进行发送。

/*** @brief  初始化串口DMA 并发送数据(发送串口)* @param  * @retval 
*/
void uart_dev_DMA_tx_data(uart_dev* uart_dev )
{NVIC_InitTypeDef  nvic_initstruct;DMA_InitTypeDef   DMA_USART1_InitStructure;//使能DMA1 时钟RCC_AHBPeriphClockCmd (RCC_AHBPeriph_DMA1,ENABLE);DMA_DeInit(DMA1_Channel4);DMA_USART1_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &USART1->DR           ;    //USART1数据接收寄存器DMA_USART1_InitStructure.DMA_MemoryBaseAddr     = (uint32_t) uart_dev ->dma_tx_buff;    //发送寄存器区间地址DMA_USART1_InitStructure.DMA_DIR                = DMA_DIR_PeripheralDST            ;  //往外设发送; 内村 ——> 外设DMA_USART1_InitStructure.DMA_BufferSize         = USART1_DMA_BUF_MAX_LEN           ;  //传送长度DMA_USART1_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable  ;  //外设地址不增DMA_USART1_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable       ;  //内存地址自增DMA_USART1_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //传送数据为8位DMA_USART1_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte    ;  //MemoryDataSizeDMA_USART1_InitStructure.DMA_Mode               = DMA_Mode_Normal            ;  //普通模式 单次DMA_USART1_InitStructure.DMA_Priority           = DMA_Priority_High          ;  //传送优先级高DMA_USART1_InitStructure.DMA_M2M                = DMA_M2M_Disable            ;  //从外设触发DMA_Init(DMA1_Channel4,&DMA_USART1_InitStructure);//开启DMADMA_Cmd(DMA1_Channel4,DISABLE);DMA_SetCurrDataCounter(DMA1_Channel4, USART1_DMA_BUF_MAX_LEN);DMA_Cmd(DMA1_Channel4,ENABLE);DMA_ClearFlag(DMA1_IT_TC4); DMA_ITConfig(DMA1_Channel4,DMA_IT_TC|DMA_IT_TE,ENABLE);  //使能传送一半及全部传送完毕中断nvic_initstruct.NVIC_IRQChannel = DMA1_Channel4_IRQn;nvic_initstruct.NVIC_IRQChannelCmd = ENABLE;nvic_initstruct.NVIC_IRQChannelPreemptionPriority = 0;nvic_initstruct.NVIC_IRQChannelSubPriority = 3;NVIC_Init(&nvic_initstruct);   }/*
************************************************************
*	函数名称:	DMA1_Channel4_IRQHandler
*
*	函数功能:	DMA1_Channel4中断
*
*	入口参数:	无
*
*	返回参数:	无
*
*	说明:		
************************************************************
*/void DMA1_Channel4_IRQHandler(void)
{uint16_t recv_size = 0;if(DMA_GetITStatus(DMA1_IT_TC4) != RESET)   //DMA传送完成{uart_dev_uart1.dma_tx_idle_state = 1;DMA_ClearITPendingBit(DMA1_IT_TC4);    // 清除传输完成	}else if(DMA_GetITStatus(DMA1_IT_TE4) != RESET){UsartPrintf(USART2," DMA uart tx ERROR \r\n");}}

相关文章:

串口DMA接收数据基本思路

串口DMA接收基本思路 串口DMA接收数据基本思路一、串口处理使用背景及常用处理方法二、串口DMA接收相关思路三、串口DMA发送相关思路 串口DMA接收数据基本思路 一、串口处理使用背景及常用处理方法 单片机经常有串口处理大量数据的场景,常用的串口处理数据方式有如…...

数据结构复习 (二叉查找树,高度平衡树AVL)

1.二叉查找树: 为了更好的实现动态的查找(可以插入/删除),并且不超过logn的时间下达成目的 定义: 二叉查找树(亦称二叉搜索树、二叉排序树)是一棵二叉树,其各结点关键词互异,且中根序列按其关键词递增排列。 等价描述: 二叉查找…...

FreeSWITCH 简单图形化界面39 - Windows安装FreeSWITCH For IPPBX(WSL环境)

FreeSWITCH 简单图形化界面39 - Windows安装FreeSWITCH For IPPBX(WSL环境) 0、界面预览1、部署WSL1.1 安装WSL1.2 安装Windows Terminal1.3 安装WSL配置工具 2、安装Ubuntu24.043、安装FreeSWITCH4、登录Web4.1 80端口占用了 5、测试6、卸载 0、界面预览…...

uniapp - 小程序实现摄像头拍照 + 水印绘制 + 反转摄像头 + 拍之前显示时间+地点 + 图片上传到阿里云服务器

前言 uniapp,碰到新需求,反转摄像头,需要在打卡的时候对上传图片加上水印,拍照前就显示当前时间日期地点,拍摄后在呈现刚才拍摄的图加上水印,最好还需要将图片上传到阿里云。 声明 水印部分代码是借鉴的…...

Qt天气预报系统设计界面布局第四部分左边

Qt天气预报系统设计 1、第四部分左边的第一部分1.1添加控件1.2修改控件名字 2、第四部分左边的第二部分2.1添加控件2.2修改控件名字 3、第四部分左边的第三部分3.1添加控件3.2修改控件名字 4、对整个widget04l调整 1、第四部分左边的第一部分 1.1添加控件 拖入一个widget&…...

VS无法找到低版本的.net,vs2022创建不了.net6的项目

很多人会遇到安装完vs最新版(目前是2022)之后,创建不了旧版本的.net项目了,比如我在学习.net core 6,我的2022无法创建,只能创建.netcore8的项目,以及又安装了2019,同样无法创建,接下来介绍怎么…...

C++软件设计模式之解释器模式

解释器模式的目的和意图 解释器模式(Interpreter Pattern)是一种行为设计模式,主要用于定义一种语言的文法,并通过该文法解释语言中的句子(表达式)。解释器模式的核心思想是将一个特定的语言表示为其文法规…...

小程序发版后,用户使用时,强制更新为最新版本

为什么要强制更新为最新版本? 在小程序的开发和运营过程中,强制用户更新到最新版本是一项重要的策略,能够有效提升用户体验并保障系统的稳定性与安全性。以下是一些主要原因: 1. 功能兼容 新功能或服务通常需要最新版本的支持&…...

如何使用AI工具cursor(内置ChatGPT 4o+claude-3.5)

⚠️温馨提示: 禁止商业用途,请支持正版,充值使用,尊重知识产权! 免责声明: 1、本教程仅用于学习和研究使用,不得用于商业或非法行为。 2、请遵守Cursor的服务条款以及相关法律法规。 3、本…...

说说缓存使用的具体场景都有哪些?缓存和数据库一致性问题该如何解决?缓存使用常见问题有哪些?

面试官:说说缓存使用的具体场景都有哪些?缓存和数据库一致性问题该如何解决?缓存使用常见问题有哪些? 缓存的具体使用场景有这些: 数据频繁读取: 当某些数据频繁被读取而不常变化时,可以将这些…...

2025-01-01 NO2. XRHands 介绍

文章目录 软件配置1 XR Hands 简介2 XRHand2.1 Pose2.2 Handedness 3 XRHandJoint3.1 XRHandJointID3.2 XRHandJointTrackingState 4 XRHandSubsystem4.1 数据属性4.1.1 UpdateSuccessFlags4.1.2 UpdateType 4.2 处理器管理:注册和注销4.3 更新手部数据:…...

Java开发-后端请求成功,前端显示失败

文章目录 报错解决方案1. 后端未配置跨域支持2. 后端响应的 Content-Type 或 CORS 配置问题3. 前端 request 配置问题4. 浏览器缓存或代理问题5. 后端端口未被正确映射 报错 如下图,后端显示请求成功,前端显示失败 解决方案 1. 后端未配置跨域支持 …...

未来20年在大语言模型相关研究方向--大语言模型的优化与改进

未来20年在大语言模型相关研究方向 模型性能优化 模型架构创新:研究新型的模型架构,如探索更高效的Transformer变体、融合递归神经网络(RNN)和卷积神经网络(CNN)的优点,以提高模型的性能、可扩展性和适应性,满足不同应用场景对模型效率和效果的要求。高效训练算法:开…...

[react] 纯组件优化子

有组件如下,上面变化秒数, 下面是大量计算的子组件,上面每一秒钟变化一次,这时候子组件会不断重新渲染, 浪费资源 父组件如下 import React, { memo, useEffect, useMemo, useState } from react; import type { ReactNode, FC } from react; import HugeCount from ./Te; int…...

美观强大的文件保险库Chibisafe

简介 什么是 Chibisafe ? Chibisafe 是一款用 Typescript 编写的快速文件上传服务,非常实用。它接受文件、照片、文档以及您能想到的任何内容,并返回可共享的链接,供您发送给其他人。它易于使用、易于部署、免费且开源&#xff0…...

详细教程:SQL2008数据库备份与还原全流程!

数据的安全性至关重要,无论是操作系统、重要文件、磁盘存储,还是企业数据库,备份都是保障其安全和完整性的关键手段。拥有备份意味着即使发生误删、系统崩溃或病毒攻击等问题,也能迅速通过恢复功能解决,避免数据丢失带…...

HTML——49.header和footer标签

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>header和footer标签</title></head><body><!--header和footer标签:是html5中新标签--><!--header:定义文档的页眉&#xff0c;通常用来定义可见…...

【蓝桥杯选拔赛真题87】python输出字符串 第十五届青少年组蓝桥杯python选拔赛真题 算法思维真题解析

目录 python输出字符串 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、 推荐资料 1、蓝桥杯比赛 2、考级资料 3、其它资料 python输出字符串 第十五届蓝桥杯青少年组python比赛选拔赛真题详细解析…...

OpenStack-Dashboard界面简单修改

OpenStack Dashboard界面替换图片 一、dashboard界面Logo的路径及文件 dashboard的Logo存放&#xff08;在Controller节点&#xff09;的路径&#xff1a; /usr/share/openstack-dashboard/openstack_dashboard/static/dashboard/img/涉及需要修改的文件&#xff08;3个&…...

DevOps工程技术价值流:Ansible自动化与Semaphore集成

在DevOps的浪潮中&#xff0c;自动化运维工具扮演着举足轻重的角色。Ansible&#xff0c;作为一款新兴的自动化运维工具&#xff0c;凭借其强大的功能和灵活性&#xff0c;在运维领域迅速崭露头角。本文将深入探讨Ansible的特点、架构、工作原理&#xff0c;以及其应用场景&…...

【服务器】上传文件到服务器并训练深度学习模型下载服务器文件到本地

前言&#xff1a;本文教程为&#xff0c;上传文件到服务器并训练深度学习模型&#xff0c;与下载服务器文件到本地。演示指令输入&#xff0c;完整的上传文件到服务器&#xff0c;并训练模型过程&#xff1b;并演示完整的下载服务器文件到本地的过程。 本文使用的服务器为云服…...

第四届电子信息工程与数据处理(EIEDP 2025)

第四届电子信息工程与数据处理 2025 4th International Conference on Electronic Information Engineering and Data Processing 2025年1月17-19日 马来西亚 吉隆坡 重要信息 会议官网&#xff1a;www.eiedp.net 大会时间&#xff1a;2025年1月17-19日 大会地点&#…...

模型预测控制(MPC)算法介绍

模型预测控制&#xff08;Model Predictive Control&#xff0c;MPC&#xff09;是一种先进的控制策略&#xff0c;广泛应用于工业过程控制、机器人控制、电力系统等领域。它基于系统的模型&#xff0c;通过滚动优化来预测系统未来的行为&#xff0c;并据此确定当前的最优控制输…...

设计模式 创建型 建造者模式(Builder Pattern)与 常见技术框架应用 解析

建造者模式&#xff0c;又称生成器模式&#xff0c;是一种对象构建模式。它主要用于构建复杂对象&#xff0c;通过将复杂对象的构建过程与其表示分离&#xff0c;使得同样的构建过程可以创建出具有不同表示的对象。该模式的核心思想是将一个复杂对象的构建过程分解为多个简单的…...

嵌入式系统中C++的基本使用方法

大家好,今天主要给大家分享一下,最近操作C++代码的控制方法。 什么是构造函数?构造函数在对象实例化时被系统自动调用,仅且调用一次。 什么是析构函数?与构造函数相反, 在对象结束其生命周期时系统自动执行析构函数。 第一个:析构函数与构造函数区别 实例代码: #inclu…...

机器人C++开源库The Robotics Library (RL)使用手册(四)

建立自己的机器人3D模型和运动学模型 这里以国产机器人天机TR8为例,使用最普遍的DH运动学模型,结合RL所需的描述文件,进行生成。 最终,需要的有两个文件,一个是.wrl三维模型描述文件;一个是.xml运动学模型描述文件。 1、通过STEP/STP三维文件生成wrl三维文件 机器人的…...

在 uni-app 中使用 wxml-to-canvas 的踩坑经验总结

在 uni-app 中使用 wxml-to-canvas 的踩坑经验总结 wxml-to-canvas 是一款非常强大的小程序工具&#xff0c;可以将 WXML 转换为 Canvas 绘图&#xff0c;用于生成海报、分享图片等。将其应用于 uni-app 项目中&#xff0c;可以为多端开发带来极大的便利&#xff0c;但也有一些…...

视频智能翻译

i68,爱六八,链接你我他 EasyVideoTrans英文视频转换成中文视频 EasyVideoTrans简要 最快的英文视频转中文方案由B站多位程序员Up主共同协作开发开源的项目在线Demo:EasyVideoTrans前端项目:https://github.com/sutro-planet/easyvideotrans-frontend后端项目:https://github…...

《Python加解密小实验:探索数据加密与解密的世界》

铺垫&#xff08;1&#xff09;-源码 import hashlib source "你好" # print(hashlib.md5(source.encode()).hexdigest())# 文件加解密 with open(../文件引用/index.png, rb) as file:data file.read() # print(hashlib.md5(data).hexdigest())# SHA也是摘要算法…...

C高级day四shell脚本

1.思维导图 2.终端输入一个C源文件名&#xff08;.c结尾&#xff09;判断文件是否有内容&#xff0c;如果没有内容删除文件&#xff0c;如果有内容编译并执行该文件。 #!/bin/bashread -p "请输入一个.c脚本名&#xff1a;" n if [ -s "$n" ] thenecho $n…...