STM32-HAL库串口DMA空闲中断的正确使用方式+解析SBUS信号

news2024/11/27 16:47:28

STM32-HAL库串口DMA空闲中断的正确使用方式+解析SBUS信号

  • 一. 问题描述
  • 二. 方法一——使用HAL_UART_Receive_DMA
  • 三. 方法二——使用HAL_UARTEx_ReceiveToIdle_DMA
  • 四. 方法三——使用HAL_UARTEx_ReceiveToIdle_IT(不使用DMA)
  • 五. 总结

一. 问题描述

能够点进这篇文章的小伙伴肯定是对STM32串口DMA空闲中断接收数据感兴趣的啦,今天用这一功能实现串口解析航模遥控器sbus信号时,查阅了很多网友发布的文章(勤劳的搬运工~),包括自己之前写过一篇博客 STM32_HAL库_CubeMx串口DMA通信(DMA发送+DMA空闲接收不定长数据)。本文进一步梳理一下HAL库串口空闲中断三种不同的使用方式,其中前两种使用DMA方式,最后一种使用HAL库自带的空闲中断机制。

本文环境:

  • Keil MDK5.14
  • STM32CubeMX6.2.1
  • 开发板/芯片:自制板/STM32F407ZGT6

实现功能:

  • 串口DMA/非DMA空闲中断接收不定长数据/解析航模遥控器SBUS信号

二. 方法一——使用HAL_UART_Receive_DMA

最常见的方法就是使用HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)这个库函数,其使用方法类似于HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size),初始化时需要调用一次,然后每次在中断服务函数里面处理完数据后重新调用一次。

使用HAL_UART_Receive_IT只需要打开串口接收中断,即HAL_NVIC_EnableIRQ(UART5_IRQn);

使用HAL_UART_Receive_DMA空闲中断需要在初始化时打开串口空闲中断使能,调用方式为:__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE)

此时DMA中断可开可不开,开了也不用管,因为数据处理是在串口空闲中断中进行的。

1. 串口初始化代码:

void MX_USART2_UART_Init(void)
{
	huart2.Instance = USART2;
	huart2.Init.BaudRate = 100000;
	huart2.Init.WordLength = UART_WORDLENGTH_9B;
	huart2.Init.StopBits = UART_STOPBITS_1;
	huart2.Init.Parity = UART_PARITY_EVEN;
	huart2.Init.Mode = UART_MODE_TX_RX;
	huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	huart2.Init.OverSampling = UART_OVERSAMPLING_16;
	if (HAL_UART_Init(&huart2) != HAL_OK)
	{
		Error_Handler();
	}

	__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);		// 开启串口空闲中断,必须调用
	HAL_UART_Receive_DMA(&huart2,USART2_RX_BUF,USART_REC_LEN);		// 启动DMA接收
}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
	if(uartHandle->Instance==USART2)
	{
		 __HAL_RCC_USART2_CLK_ENABLE();
		 __HAL_RCC_GPIOA_CLK_ENABLE();
		 
		 GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
		 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
		 GPIO_InitStruct.Pull = GPIO_NOPULL;
		 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
		 GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
		 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
		
		 hdma_usart2_rx.Instance = DMA1_Stream5;
		 hdma_usart2_rx.Init.Channel = DMA_CHANNEL_4;
		 hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
		 hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
		 hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
		 hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
		 hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
		 hdma_usart2_rx.Init.Mode = DMA_NORMAL;
		 hdma_usart2_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
		 hdma_usart2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
		 if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
		 {
		   Error_Handler();
		 }
		
		 __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart2_rx);
		
		 /* USART2 interrupt Init */
		 HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
		 HAL_NVIC_EnableIRQ(USART2_IRQn);
	}
}

初始化中调用__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE)开启空闲中断,HAL_UART_Receive_DMA(&huart2,USART2_RX_BUF,USART_REC_LEN)启动DMA串口接收,USART2_RX_BUF是接收缓冲区,USART_REC_LEN是定义的DMA接收缓冲区长度,接收的数据不能超过这个长度。

2. 中断处理:

中断处理有两种方式,第一种是直接定义在void USART1_IRQHandler(void)中,第二种是自定义一个函数,然后在void USART1_IRQHandler(void)中调用。注意网上很多资源自定义了中断回调函数但代码又没贴全,读者可能以为是HAL库自带的回调函数,结果因为没有在void USART1_IRQHandler(void)中调用导致失败。

第一种形式——直接定义:

void USART2_IRQHandler(void)
{
	uint32_t tmp_flag ;
	u8 len;
	u8 data[25];
	tmp_flag  = __HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE);

	if( tmp_flag  != RESET)
	{
		__HAL_UART_CLEAR_IDLEFLAG(&huart2);//清除标志位
		HAL_UART_DMAStop(&huart2); //停止DMA接收,防止数据出错
	
		len = USART_REC_LEN - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);// 获取DMA中传输的数据个数
		
		// 以下为用户数据处理,将数据拷贝出去
		if(len == 25)
		{
			memcpy(data,USART2_RX_BUF,len);
			update_sbus(data);
		}
		HAL_UART_Receive_DMA(&huart2,USART2_RX_BUF,USART_REC_LEN);   //打开DMA接收,数据存入rx_buffer数组中。
	}
	
	HAL_UART_IRQHandler(&huart2); //调用HAL库中断处理公用函数
}

第二种形式——自定义回调函数:

void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)
{
	uint32_t tmp_flag ;
	u8 len;
	u8 data[25];

	if (huart->Instance == USART2)
	{
		tmp_flag  = __HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE);
		if( tmp_flag  != RESET)
		{
			__HAL_UART_CLEAR_IDLEFLAG(huart);//清除标志位
			HAL_UART_DMAStop(huart); //停止DMA接收
		
			len = USART_REC_LEN - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);// 获取DMA中传输的数据个数
			
			// 以下为用户数据处理,将数据拷贝出去
			if(len == 25)
			{
				memcpy(data,USART2_RX_BUF,len);
				update_sbus(data);
			}
			HAL_UART_Receive_DMA(huart,USART2_RX_BUF,USART_REC_LEN);   //打开DMA接收,数据存入rx_buffer数组中。
		}
	}
}

void USART2_IRQHandler(void)
{
	HAL_UART_IdleCpltCallback(&huart2)
	HAL_UART_IRQHandler(&huart2); //调用HAL库中断处理公用函数
}

再次提醒一下,HAL库中并没有类似void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)这样的回调函数,大家不要看到了以为是自带的回调函数而不在void USART2_IRQHandler(void)中调用而导致失败。

中断处理中的函数说明:
__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE):返回中断标志位,产生空闲中断后会返回1;
__HAL_UART_CLEAR_IDLEFLAG(&huart2):清除空闲中断标志位,必须调用;
HAL_UART_DMAStop(&huart2):停止DMA接收,防止数据处理出错,数据处理完成后重新打开;
__HAL_DMA_GET_COUNTER(&hdma_usart2_rx):返回DMA中未传输的数据量,用缓冲区总长减去未传输数量就是已接收的数据长度;
HAL_UART_Receive_DMA(huart,USART2_RX_BUF,USART_REC_LEN):重新打开DMA接收。

以上就能成功实现不定长数据的接受了,其实只要步骤对了还是很简单的。

三. 方法二——使用HAL_UARTEx_ReceiveToIdle_DMA

第二种方式是使用HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)这个库函数,并重定义void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)这个中断回调函数。

Attention:__weak void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)是正儿八经的库函数(弱函数),不是自定义的,我们可以重定义它。

使用这个库函数,串口初始化代码中就不需要调用__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE)了,因为HAL_UARTEx_ReceiveToIdle_DMA函数内部已经打开了空闲中断,当然你加上也没事。

1. 串口初始化代码:

void MX_USART2_UART_Init(void)
{
	huart2.Instance = USART2;
	huart2.Init.BaudRate = 100000;
	huart2.Init.WordLength = UART_WORDLENGTH_9B;
	huart2.Init.StopBits = UART_STOPBITS_1;
	huart2.Init.Parity = UART_PARITY_NONE;
	huart2.Init.Mode = UART_MODE_TX_RX;
	huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	huart2.Init.OverSampling = UART_OVERSAMPLING_16;
	if (HAL_UART_Init(&huart2) != HAL_OK)
	{
		Error_Handler();
	}

	//__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);// 开启串口空闲中断,可省略
	HAL_UARTEx_ReceiveToIdle_DMA(&huart2,USART2_RX_BUF,USART_REC_LEN);		// 使用串口DMA空闲中断,会自动开启空空闲中断
}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
	if(uartHandle->Instance==USART2)
	{
		 __HAL_RCC_USART2_CLK_ENABLE();
		 __HAL_RCC_GPIOA_CLK_ENABLE();
		 
		 GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
		 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
		 GPIO_InitStruct.Pull = GPIO_NOPULL;
		 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
		 GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
		 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
		
		 hdma_usart2_rx.Instance = DMA1_Stream5;
		 hdma_usart2_rx.Init.Channel = DMA_CHANNEL_4;
		 hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
		 hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
		 hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
		 hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
		 hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
		 hdma_usart2_rx.Init.Mode = DMA_NORMAL;
		 hdma_usart2_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
		 hdma_usart2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
		 if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
		 {
		   Error_Handler();
		 }
		
		 __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart2_rx);
		
		 /* USART2 interrupt Init */
		 HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
		 HAL_NVIC_EnableIRQ(USART2_IRQn);
	}
}

这里__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE)是可省略的,HAL_UARTEx_ReceiveToIdle_DMA(&huart2,USART2_RX_BUF,USART_REC_LEN)启动DMA串口空闲中断接收,USART2_RX_BUF是接收缓冲区,USART_REC_LEN是定义的DMA接收缓冲区长度,接收的数据不能超过这个长度。

2. 中断处理:

中断处理定义在void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)这个中断回调函数中。

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	u8 len;
	u8 data[25];
	if(huart->Instance == USART2)
	{
		HAL_UART_DMAStop(huart);
		len = USART_REC_LEN - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);// 获取DMA中传输的数据个数
		if (USART2_RX_BUF[0] == 0x0F && len == 25)	//接受完一帧数据
		{
			memcpy(data,USART2_RX_BUF,len);
			update_sbus(data);
		}
		HAL_UARTEx_ReceiveToIdle_DMA(&huart2,USART2_RX_BUF,USART_REC_LEN);		// 再次开启DMA空闲中断
	}
}	

void USART2_IRQHandler(void)
{
	HAL_UART_IRQHandler(&huart2); //调用HAL库中断处理公用函数
}

当然也可以像方法一种一样,中断逻辑处理直接定义在void USART2_IRQHandler(void)中。

四. 方法三——使用HAL_UARTEx_ReceiveToIdle_IT(不使用DMA)

这种方法参考这篇文章: STM32 hal库串口空闲中断最新用法

使用方法:
1、在主函数中调用HAL_UARTEx_ReceiveToIdle_IT()
2、在回调函数HAL_UARTEx_RxEventCallback()中做中断逻辑处理。

这里我换用串口4,实现上位机发送,单片机原封不动返回数据。

1. 串口初始化代码:

void MX_UART4_Init(void)
{
	huart4.Instance = UART4;
	huart4.Init.BaudRate = 115200;
	huart4.Init.WordLength = UART_WORDLENGTH_8B;
	huart4.Init.StopBits = UART_STOPBITS_1;
	huart4.Init.Parity = UART_PARITY_NONE;
	huart4.Init.Mode = UART_MODE_TX_RX;
	huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	huart4.Init.OverSampling = UART_OVERSAMPLING_16;
	if (HAL_UART_Init(&huart4) != HAL_OK)
	{
		Error_Handler();
	}
	// HAL_UARTEx_ReceiveToIdle_IT同时开启空闲中断,开启接收
	HAL_UARTEx_ReceiveToIdle_IT(&huart4,USART4_RX_BUF,USART_REC_LEN);
}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
	if(uartHandle->Instance==UART4)
	{
		 __HAL_RCC_UART4_CLK_ENABLE();
	    __HAL_RCC_GPIOC_CLK_ENABLE();
	  
	    GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
	    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	    GPIO_InitStruct.Pull = GPIO_PULLUP;
	    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	    GPIO_InitStruct.Alternate = GPIO_AF8_UART4;
	    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
	
	    HAL_NVIC_SetPriority(UART4_IRQn, 0, 0);
	    HAL_NVIC_EnableIRQ(UART4_IRQn);
	}
}

可以看到这里是没有使用DMA的,初始化中调用了HAL_UARTEx_ReceiveToIdle_IT(&huart4,USART4_RX_BUF,USART_REC_LEN)来开启串口4的空闲中断,USART4_RX_BUF是接收缓冲区,USART_REC_LEN是缓冲区的长度。这里可以看下库中HAL_UARTEx_ReceiveToIdle_IT的定义和提示:

在这里插入图片描述

框1的地方说,这个函数用于在中断模式下接收一定量数据,直到指定的数据长度被接收到或者空闲中断产生。也就是说,如果USART_REC_LEN定义很小,还没到产生空闲中断,接收就会完成。

框2的地方,即是打开串口空闲中断。所以不需要额外再去开启空闲中断了。

2. 中断处理:

中断处理定义在void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)这个中断回调函数中。注意到,和使用DMA传输时是同一个回调函数。

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	u8 len=0;
	u8 data[25];
	u8 uart4_len=0;
	u8 uart4_data[50];
	if(huart->Instance == USART2)
	{
		HAL_UART_DMAStop(huart);
		len = USART_REC_LEN - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);// 获取DMA中传输的数据个数
		if (USART2_RX_BUF[0] == 0x0F && len == 25)	//接受完一帧数据
		{
			memcpy(data,USART2_RX_BUF,len);
			update_sbus(data);
		}
		HAL_UARTEx_ReceiveToIdle_DMA(&huart2,USART2_RX_BUF,USART_REC_LEN);		// 再次开启DMA空闲中断
	}
	
	if(huart->Instance == UART4)
	{		
		while (USART4_RX_BUF[uart4_len] != '\0')	uart4_len++;
		memcpy(uart4_data,USART4_RX_BUF,uart4_len);
		HAL_UART_Transmit(&huart4,uart4_data,uart4_len,0xff);
		
		// 调用printf是使用串口1
		printf("data: %s\r\n", uart4_data);	
		printf("data length: %d\r\n", uart4_len);	
		
		memset(USART4_RX_BUF, 0, USART_REC_LEN); 
		memset(uart4_data, 0, sizeof(uart4_data)); 

		HAL_UARTEx_ReceiveToIdle_IT(&huart4,USART4_RX_BUF,USART_REC_LEN);
	}
}	

void USART2_IRQHandler(void)
{
	HAL_UART_IRQHandler(&huart2); //调用HAL库中断处理公用函数
}

void UART4_IRQHandler(void)
{
	HAL_UART_IRQHandler(&huart4); //调用HAL库中断处理公用函数
}

回调函数中就是读取USART4_RX_BUF缓冲区的数据和数据长度,然后做相应处理,这里只是简单的原数据发送回去,如果是SBSU信号或Modbus或其他带协议帧的数据,则可根据帧头帧尾、数据长度、校验位等做进一步处理。

最终实现的效果就是上位机发送什么,单片机返回什么,如图:

在这里插入图片描述

五. 总结

以上三种方式都能实现串口空闲中断接收不定长数据,适合用于处理不定长数据接收、减少CPU负担,相比较而言,方法三不使用DMA,使用上更简洁。方法一、二使用DMA,好处是可以减少CPU负担。

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

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

相关文章

java springboot整合MyBatis-Plus 多用点Plus支持一下国人开发的东西吧

文章java springboot整合MyBatis做数据库查询操作讲述了boot项目整合MyBatis的操作方法 但现在就还有一个 MyBatis-Plus Plus是国内整合的一个技术 国内的很多人会喜欢用 特别是一些中小型公司 他们用着会比较舒服 好 然后我们打开idea 创建一个项目 选择 Spring Initializr…

(九)CSharp-数组

一、矩形数组 1、访问数组元素 class Program{static void Main(string[] args){int[] intArr1 new int[15];intArr1[2] 10;int var1 intArr1[2];int[,] intArr2 new int[5, 10];intArr2[2, 3] 7;int var2 intArr2[2, 3];int[] myIntArray new int[4];for (int i 0; i…

Git 报错 Updates were rejected because the remote contains work that you do

目录 Git 报错 Updates were rejected because the remote contains work that you do 1、命令行出现这种情况 2、idea出现同样的报错,解决方式同上 Git 报错 Updates were rejected because the remote contains work that you do 这个报错实在是让我受不了了&…

Kendo UI for jQuery---03.组件___网格---05.编辑---01.概述

编辑概述 编辑是剑道 UI 网格的一项基本功能,它允许您操作其数据的呈现方式。 网格提供以下编辑模式: 批量编辑 内联编辑 弹出窗口编辑 自定义编辑开始 要启用编辑: 熟悉剑道UI中的常见编辑概念 配置网格的数据源 通过配置定义字段schem…

PaddleOCR Windows下配置环境并测试

目录 1.PaddleOCR 介绍 1.2 PaddleOCR支持模型介绍 2.环境配置 3.PaddleOCR源码 1.PaddleOCR 介绍 PaddleOCR旨在打造一套丰富、领先、且实用的OCR工具库,助力开发者训练出更好的模型,并应用落地。 支持多种OCR相关前沿算法,在此基础上打…

简单的一批的DockerFile构建(内附超详细docker学习笔记)

目录 介绍 DockerFile常用保留字指令 演示自定义构建java8版本centos docker专用学习笔记 超全 介绍 总结: 从应用软件的角度来看,Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段, * Dockerfile是软件的原材料 * Docker镜像是软件…

SpringBoot参数校验入门

一、添加依赖 <!--参数校验--> <dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId> </dependency> <!--lombok--> <dependency><groupId>org.projectlombok&…

few-shot object counting论文汇总

文章目录 2021OBJECT COUNTING: YOU ONLY NEED TO LOOK AT ONE 2022CounTR: Transformer-based Generalised Visual CountingFew-shot Object Counting with Similarity-Aware Feature Enhancement 2023CAN SAM COUNT ANYTHING? AN EMPIRICAL STUDY ON SAM COUNTING 2021 OBJ…

【MSP432电机驱动学习】TB6612带稳压电机驱动模块、MG310电机、13线霍尔编码器

所用控制板型号&#xff1a;MSP432P401r 今日终于得以继续我的电赛小车速通之路&#xff1a; 苏轼云 “ 素面常嫌粉涴 &#xff0c; 洗妆不褪朱红。 ” 这告诫我们不能只注重在表面粉饰虚伪的自己&#xff0c;要像梅花一样&#xff0c;不断磨砺自己的内在~ 后半句是 “…

广告经济学与垄断竞争分析

产品与广告 产品的分类&#xff1a; 搜寻品&#xff1a;消费者在购买商品之前就可以知道其特征的产品经验品&#xff1a;只能够在使用后才能确认其特征的产品信任品&#xff1a;产品的质量即使在消费之后仍然不能确定&#xff0c;例如医学和法律服务 广告的分类&#xff1a;…

【C++】在线编译器推荐,让你随时随地编写代码

▒ 目录 ▒ &#x1f6eb; 问题描述环境 1️⃣ 支持调试网站Repl.itOnlineGDB 2️⃣ 不支持调试网站Wandboxjson.cnjdoodletutorialspointcppshellideonecoliruonline-ide 3️⃣ 性能分析网站Quick C BenchmarkCompare C Builds 4️⃣ 其它C Insights&#xff08;学习模板、C11…

Java阶段四Day04

Java阶段四Day04 文章目录 Java阶段四Day04关于SLF4j日志框架使用Sql注解关于DAO架构关于Service关于异常 关于SLF4j日志框架 在开发实践中&#xff0c;通常禁止使用System.out.println()这种语句输出信息&#xff0c;主要原因有&#xff1a; 输出效率低下&#xff0c;特别是字…

A fight among three “三国”混战 | 经济学人20230520版社论双语精翻

《经济学人》2023年5月20日封面&#xff08;社论&#xff09;文章精翻&#xff1a;《全球支付系统的“三国”混战》&#xff08;A fight among three&#xff09; A fight among three “三国”混战 The fight over the future of global payments 全球支付的未来之争 Digital …

函数重载分析

函数重载 (Function Overload) 用同一个函数名定义不同的函数 当函数名和不同的参数搭配时函数的含义不同 函数重载至少满足下面的一个条件&#xff1a; 参数个数不同 参数类型不同 参数顺序不同 上面的两个函数可以构成重载函数吗&#xff1f; 当默认参数遇上函数重载会发…

分布式系统概念和设计——分布式多媒体系统

分布式系统概念和设计 分布式多媒体系统 QoS QoS&#xff08;Quality of Service&#xff0c;服务质量&#xff09;是一种网络管理和控制技术&#xff0c;可以对网络流量进行管理和调度&#xff0c;以确保网络对不同类型的数据流的传输可以提供适当的带宽、传输延迟、抖动和可…

《交通规划》——最短路分配方法

《交通规划》——最短路分配方法 说明&#xff1a;下面内容&#xff0c;将用python、networkx实现刘博航、杜胜品主编的《交通规划》P198页的例题&#xff0c;主要是实现最短路径分配方法。 1. 题目描述如下&#xff1a; 2. networkx构建网络 import networkx as nx import …

Spring6 面向切面(AOP)

文章目录 1、场景模拟1.1、声明接口1.2、创建实现类1.3、创建带日志功能的实现类1.4、隐藏问题 2、代理模式2.1、概念2.2、静态代理2.3、动态代理2.4、测试 3、AOP概念及相关术语3.1、概述3.2、相关术语①横切关注点②通知&#xff08;增强&#xff09;③切面④目标⑤代理⑥连接…

跨模态检索论文阅读:Dissecting Deep Metric Learning Losses for Image-Text Retrieval(GOAL)

Dissecting Deep Metric Learning Losses for Image-Text Retrieval 剖析图像文本检索中的深度度量学习损失 2022.10 视觉语义嵌入&#xff08;VSE&#xff09;是图像-文本检索中的一种流行的应用方法&#xff0c;它通过学习图像和语言模式之间的联合嵌入空间来保留语义的相似性…

2023.06.14 QT day3

用QT实现的简单文本编辑器 头文件widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QPushButton> #include <QTextEdit> #include <QFont> #include <QFontDialog> #include <QColor> #include <QColorDialo…

C语言符合类型之结构篇(结构指针)

结构相关知识总结 什么是结构&#xff1f;结构的声明与简单使用结构的初始化结构中成员变量的访问结构的初始化器结构数组结构数组的声明结构数组的成员标识 结构的嵌套结构指针结构作为参数在函数中传递将结构成员作为参数进行传递将结构地址(指向结构的指针)作为参数进行传递…