F103串口和DMA配合使用总结

news2024/10/6 16:16:44

常规的串口使用是这样的:先配置基本的GPIO和串口,然后调用发送和接收函数,如果需要中断,可以根据情况配置发送中断和接收中断。

比如:

//PB10:UT3_TX
//PB11:UT3_RX
void lcd_usart_init(uint32_t bound)
{
    //GPIO端口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);	//使能USART3时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	//使能GPIOB时钟

	//USART3_TX   GPIOB.10
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PB.10
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB.10

	//USART3_RX	  GPIOB.11
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;//PB11
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB.11  

	//Usart3 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化NVIC寄存器

	//USART 初始化设置
	USART_InitStructure.USART_BaudRate = bound;//串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式
    USART_Init(USART3, &USART_InitStructure); //初始化串口3
    
    USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启串口接收中断
//    USART_ITConfig(USART3, USART_IT_TC, ENABLE);//开启串口接收中断
    USART_Cmd(USART3, ENABLE); //使能串口3
}

void USART3_IRQHandler(void)
{
    
	if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
    {
        printf("get a data\r\n");
    }

    if(USART_GetITStatus(USART3, USART_IT_TXE) != RESET)
    {
        printf("send a data\r\n");

    }
}

关于串口中断,有几个问题不太明白。

第一,发送函数USART_SendData(USART_TypeDef* USARTx, uint16_t Data)和接收函数USART_ReceiveData(USART_TypeDef* USARTx),都是一次发送1个字节和接收1个字节吗?

第二,中断,是每发送完和接收完1个字节后产生吗?

第三,发送中断标志位有两个USART_IT_TXE和USART_IT_TC,二者有何异同?

先解决这三个问题,然后再继续串口+DMA。

 关于串口发送和接收的中断,手册里有三种类型:

─ 发送数据寄存器空

─ 发送完成

─ 接收数据寄存器满

先看下数据寄存器

 

可以看到,数据寄存器只有8:0位是有用的,其他都保留。

我一开始想的是只有7:0,刚好一个字节,但是这里多了一位,为什么?

数据寄存器 (USART_DR) 只有低 9 位有效,并且第 9 位数据是否有效要取决于 USART控制寄存器1(USART_CR1) 的 M 位设置,当 M 位为 0 时表示 8 位数据字长,当 M 位为 1 表示 9位数据字长(最后一位为奇偶校验位),我们一般使用 8 位数据字长,即无奇偶校验。

根据以上内容,结合USART_SendData(USART_TypeDef* USARTx, uint16_t Data)和接收函数USART_ReceiveData(USART_TypeDef* USARTx)的源码,可以知道,这两个函数都是一次发送或者接收1个字节。

另外,每发送完1个字节,就会产生“发送数据寄存器空”和“发送完成”中断,每接收完1个字节,就会产生“接收数据寄存器满”中断。

这里有个问题,就是“发送数据寄存器空”和“发送完成”中断二者有何区别?

首先要知道,都是针对1个字节来说的。

其中,USART_IT_TXE是在TDR寄存器为空时产生的中断标志位;USART_IT_TC是在DR寄存器发送完最后一个位时产生的中断标志位。

手册里的说法是,TXE置位,意味着TDR的数据移位到DR寄存器,并已启动发送,此时TDR寄存器为空,可以发送下一字节数据到TDR寄存器,并且不会覆盖之前DR寄存器的内容。

这里有个重点问题,容易错。

我在进行串口发送数据时,没有判断这个标志位,直接连续发送,结果数据全乱了,这就是因为没有判断TDR寄存器为空,数据发送过快,导致TDR数据寄存器中的数据位混在一起了,产生了溢出。

所以,需要判断再发送下一个字节

USART_SendData(USART3, 0x5A);
while(USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET){};
USART_SendData(USART3, 0xA5);
while(USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET){};
USART_SendData(USART3, 0x07);
while(USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET){};
USART_SendData(USART3, 0x10);
while(USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET){};
USART_SendData(USART3, 0x70);
while(USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET){};
……

上面只是说明,常规写法如下

for(uint8_t i = 0; i < allProtocolLength; i++)
{
    USART_SendData(USART3, sendData[i]);
    while(USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET){};
}

千万不要在不做任何判断的情况下连续发数据。

现象和解决方式参考这篇文章,讲的很详细

STM32库函数USART_SendData的缺陷和解决方法-文章-单片机-STM32 - 畅学电子网

回到USART_IT_TC标志位,表示发送完成,当包含有数据的一帧发送完成后,并且TXE=1时,由硬件将该位置“1” ,也就是说,当TDR寄存器的数据被赋值到移位寄存器的时候,TXE会置位,当移位寄存器中的数据发完之后,并且,TDR寄存器依然为空,则TC会置位。

一般,只要TXE置位,就可以继续发送下一个字节数据了。

另外,还有个问题,是否要手动清除该标志位?答案是不用。

当TDR寄存器中的数据被硬件转移到移位寄存器的时候,TXE位被硬件置1,对USART_DR的写操作,该位自动清零。所以,不必手动清除。

以上就是串口的基本用法,串口收发数据时,很少会用到发送中断和接收中断。

发送一般都是直接发,接收时也是一个字节一个字节去接收,速度不快。

所以,很多情况下,串口都会结合DMA一起使用,提高效率。

关于DMA的基本内容可直接查阅相关手册,此处仅记录重点内容及DMA+串口的常见用法。

DMA在初始化时,会设置一个缓冲区buffer_size,当缓冲区满的时候,就会产生中断,最常见的就是传输完成TC中断(可以不关心中断)。

到底要不要用DMA的中断呢?

一般来说,发送可用可不用(可直接用串口发送),发送时,可以通过设置缓冲区大小,让DMA在传输完设定长度的字节数据后停止。

但是接收时,也要设定这个缓冲区的大小,只有填满这个缓冲区的时候,才会产生中断(这一点待确认),问题是,我不知道接收的字节数到底是多少,缓冲设小了数据收不完整,缓冲设大了,又没那么多数据可接收,这样,就不会触发接收中断(待确认),就算能触发,也会多出一些没用的数据。

……

有以下几种思路:

DMA直接发送(单次)+DMA直接接收(单次);

DMA发送中断+DMA接收中断;

串口直接发送+循环DMA接收; 

结合空闲中断;

……

那么,到底要如何合理地使用串口+DMA呢?

了解什么是空闲中断

在串口的状态寄存器里面,有个IDLE标志

位说明如下:

这里的总线空闲,指的是串口线处于空闲状态。

可以参考这个问答:

串口的空闲中断和普通中断相比有什么优势

关于空闲中断,记录如下重点内容:

串口中断标志有很多,接收完成、发送完成、CTS、过载错误、噪声错误和空闲等,每个中断标志代表的功能不一样。

普通的有接收中断和发送中断,即每接收或者发送一个字节,就产生一个中断,在接收比较长的数据时会频繁地进中断,可能数据会来不及处理。而空闲中断是一帧数据接收结束后收到一个字节的空闲帧才中断,空闲中断配合DMA可以很好的实现不定长数据接收,出现空闲标志时,认为一帧报文发送完毕,然后进行报文分析。

空闲中断是接收到一个数据以后,接收停顿超过一字节时间认为桢收完,总线空闲中断是在检测到在接收数据后,数据总线上一个字节的时间内,没有再接到数据后发生。也就是RXNE位被置位之后,才开始检测,只被置位一次,除非再次检测到RXNE位被置位,然后才开始检测下一次的总线空闲。一次RXNE位被置位只进行一次。

具体用法参考下面三篇文章: 

STM32学习之串口采用DMA收发数据:需要利用状态机加DMA加串口_暮尘依旧的博客-CSDN博客

STM32F103 串口DMA + 空闲中断 实现不定长数据收发_stm32f103空闲中断_夏夜晚风_的博客-CSDN博客

STM32F103 串口 +DMA中断实现数据收发_stm32f103 dma中断_夏夜晚风_的博客-CSDN博客

几点验证

要发的字节数10个,存在一个50字节大小的数组里。

经验证,缓存要设置比实际传输字节数少1,才会置位发送标志位,比如这里要设置9,但是设置为9,数据就发不完整了。奇怪。不知道哪里出了问题。

首先能确认,发送时,缓存要大于等于实际发送的数据字节数,要不数据发不完整,缓存一满就会停止DMA传输。

如果不是已经发生事件跳到中断里,就不要直接用if来判断标志位有没有置位。

在中断里直接if是因为标志位已经发生了,现在要判断是哪种中断标志。

如果就是要判断标志位有没有发生,要放到while里,而不是用if,或者在while(1)里if判断,因为如果你直接if判断,说不定还没来得及置位,但是因为if不满足条件直接就跳过了,后面就算再置位也不会进行判断了。

比如,这样就不对

这样比较合理

这样调整之后,发送缓存只要大于等于要发送的数据,就能保证数据发送没问题,并且也会置位发送标志位,所以,应该是发送完了或者缓冲区满了都会置位标志位吧,具体以后再慢慢研究吧。

另外,仔细想了想,发送完成之后再开启接收,和发送方的数据不太匹配,可能会漏接收,或者出现其他问题,这一点不太合理。

直接这样吧。

DMA_ClearFlag和DMA_ClearITPendingBit效果是一样的。

前者常在非中断中使用,后者常在中断中使用。 

我的实践方案总结

串口+DMA涉及的东西比较多,两个都能普通收发,都能中断。

比如,串口普通收,串口普通发,串口中断收,串口中断发,DMA普通收,DMA普通发,DMA中断收,DMA中断发,再加上个空闲中断。

其中,我们要明白一点,使用DMA只是不用CPU来运输数据,而是使用DMA控制器来运输数据,但是,串口那边的功能并不影响,寄存器还是一样的。所以,DMA可以不用中断,而是通过串口的中断来判断数据有没有收发完,串口才是最知道它自己的数据有没有发完和收完的。

发送的时候,因为DMA可以指定传输的长度,所以和串口中断的作用是差不多的。但是,在接收时,就推荐使用串口来判断,因为接受时,DMA并不知道串口数据有没有接收完。

发送和接收数据都是由DMA完成,但是判断有没有完成,就需要选择了。

我在实践中是这样选择的,发送完成由DMA发送完成中断来判断,接收完成由串口空闲中断来判断,串口接收完成中断是每个字节产生一个中断,需要一个字节一个字节地去判断,但是空闲中断是接收完一帧数据后再去统一处理,效率高很多。

一个字节一个字节接收时,需要在中断里判断有没有接收到帧头,之后的数据才会保存到数组里,而空闲中断是先接收完一帧数据,然后再去处理。

我们初始化时就开启DMA接收,然后设置空闲中断,发生空闲中断时,就表示一帧数据接收完成了。DMA开启后,有数据就搬,没数据就等待。选择DMA的正常模式,则来一次数据,搬一次,就停了,即DMA只传输一次。如果当传输完一次后,还想再传输下一次,就需要重启DMA接收,依然从头开始存起。如果是循环模式,就会一直继续接收,此时地址是不断增长并循环的。

以下给出方案中这部分代码。

串口初始化

//LCD串口
//PB10:UT3_TX
//PB11:UT3_RX
//单片机串口3接到LCD芯片的串口1(仅串口1支持协议解析)
void lcd_usart_init(uint32_t bound)
{
    //GPIO端口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);	//使能USART3时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	//使能GPIOB时钟

	//USART3_TX   GPIOB.10
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PB.10
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB.10

	//USART3_RX	  GPIOB.11
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;//PB11
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB.11  

	//USART 初始化设置
	USART_InitStructure.USART_BaudRate = bound;//串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式
    USART_Init(USART3, &USART_InitStructure); //初始化串口3
    
    USART_DMACmd(USART3,USART_DMAReq_Tx, ENABLE); //使能串口3的DMA发送
    USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE); //使能串口3的DMA接收
    USART_Cmd(USART3, ENABLE); //使能串口3
    
    /* 串口中断配置 */
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             // 使能
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;   // 抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;          // 子优先级
    NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;           // 串口3中断
    NVIC_Init(&NVIC_InitStructure);     // 嵌套向量中断控制器初始化

    //使能串口空闲中断
    //接收一帧数据产生 USART_IT_IDLE 空闲中断
    USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);
}

DMA发送初始化

//初始化串口3的DMA发送功能
void usart3_dma_send_init()
{
    DMA_InitTypeDef DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    
    //开启DMA1时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
    //串口3发送DMA初始化,DMA1通道2
    DMA_DeInit(DMA1_Channel2);   //将DMA1的通道2寄存器重设为缺省值
    
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART3->DR;  //DMA外设基地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)sendData;  //DMA内存基地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  //数据传输方向,从内存读取发送到外设
	DMA_InitStructure.DMA_BufferSize = 0;  //DMA通道的DMA缓存的大小,初始化为0不发送
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //数据宽度为8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //工作在正常模式,一次传输后自动结束
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级 
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
	DMA_Init(DMA1_Channel2, &DMA_InitStructure);
    
    DMA_Cmd(DMA1_Channel2, DISABLE);//初始化时禁止DMA发送
    
    //配置DMA发送中断,发送完成后,清除标志位即可
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  // 抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;         // 子优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            // 使能
    NVIC_Init(&NVIC_InitStructure);     // 嵌套向量中断控制器初始化
    
    //开启DMA1通道2的传输中断,用来判断发送完成
    DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE);
}

DMA接收初始化

//初始化串口3的DMA接收功能
void usart3_dma_receive_init()
{
    DMA_InitTypeDef DMA_InitStructure;
    
    //开启DMA1时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    //串口3接收DMA初始化,DMA1通道3
    DMA_DeInit(DMA1_Channel3);   //将DMA1的通道3寄存器重设为缺省值
    
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART3->DR;  //DMA外设基地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)receiveData;  //DMA内存基地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //数据传输方向,从外设读取发送到内存
	DMA_InitStructure.DMA_BufferSize = sizeof(receiveData);  //DMA通道的DMA缓存的大小
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //数据宽度为8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //工作在正常模式,一次传输后自动结束
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级 
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
	DMA_Init(DMA1_Channel3, &DMA_InitStructure);
    
    //发送是主动的,但是接收是被动的,上电就要打开,等着接收HMI的数据
    DMA_Cmd(DMA1_Channel3, ENABLE);//初始化时即开启接收
}

//开启一次DMA发送
void USART3_DMA_SEND_Enable(uint16_t buffer_size)
{ 
	DMA_Cmd(DMA1_Channel2, DISABLE);
 	DMA_SetCurrDataCounter(DMA1_Channel2, buffer_size);//DMA通道的DMA缓存的大小
 	DMA_Cmd(DMA1_Channel2, ENABLE);
}

开启一次DMA发送

//开启一次DMA发送
void USART3_DMA_SEND_Enable(uint16_t buffer_size)
{ 
	DMA_Cmd(DMA1_Channel2, DISABLE);
 	DMA_SetCurrDataCounter(DMA1_Channel2, buffer_size);//DMA通道的DMA缓存的大小
 	DMA_Cmd(DMA1_Channel2, ENABLE);
}

开启一次DAM接收

//开启一次DMA接收
void USART3_DMA_RECEIVE_Enable()
{  
    //先将接收的内存部分数据清0
    memset(receiveData, 0, sizeof(receiveData));
 	DMA_Cmd(DMA1_Channel3, ENABLE);
}

DMA发送完成的中断函数

//DMA1发送完成的中断函数
void DMA1_Channel2_IRQHandler()
{
    if(DMA_GetITStatus(DMA1_IT_TC2) != RESET)
    {
        sendCFlag = 1;
        DMA_ClearITPendingBit(DMA1_IT_TC2);    // 清除传输完成中断标志位
        DMA_Cmd(DMA1_Channel2, DISABLE);       // 关闭DMA发送
    }
}

串口的空闲中断

//串口3的空闲中断处理函数
//判断DMA数据是不是接收完
void USART3_IRQHandler(void)
{
    uint8_t clear;

    if(USART_GetITStatus(USART3, USART_IT_IDLE) != RESET)   // 空闲中断
    {
            clear = USART3->SR; // 清除空闲中断
            clear = USART3->DR; // 清除空闲中断
        
            receiveCFlag = 1;  // 置接收标志位
            //空闲中断产生,但是DMA后续可能还有数据,不关心
            //所以主动关闭DMA接收,
            DMA_Cmd(DMA1_Channel3, DISABLE);
    }   
}

数据准备好后,就开启发送

……
USART3_DMA_SEND_Enable(allProtocolLength);//开启DMA传输进行数据发送

空闲中断里接收完成标志位置位后就开始处理数据

//主函数
int main(void)
{			
    system_init();
    
    while(1)//整体的逻辑就是,串口只要有数据来,就会DMA搬运并置位接收完成标志位
    {       //主循环判断标志位然后处理数据,处理完成后再次开启DMA接收
        if(receiveCFlag && IS_DATA_OK())
        {
            printf("ok\r\n");
            receiveCFlag = 0;//取消接收完成标志位
            //开启DMA接收,为下一次接收做准备
            USART3_DMA_RECEIVE_Enable();
        }
    }
}

DMA和串口遗漏内容补充

摘自数据手册。

串口的发送和接收寄存器地址是一样的&USARTx->DR

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

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

相关文章

Linux内核模块开发 第 7 章

The Linux Kernel Module Programming Guide Peter Jay Salzman, Michael Burian, Ori Pomerantz, Bob Mottram, Jim Huang译 断水客&#xff08;WaterCutter&#xff09;源 LKMPG 7 /proc 文件系统 Linux 中有一个额外的机制——/proc file system&#xff0c;用于支持内核…

Linux学习之分区挂载

VMWare 16虚拟机添加一块硬盘 点击“硬盘&#xff08;SCSI&#xff09;”&#xff0c;在弹出来的窗口点击“硬盘&#xff08;SCSI&#xff09;”之后添加。 选择硬盘之后&#xff0c;点击下一步。 虚拟磁盘类型&#xff0c;就选择默认的SCSI(S)。 选择“创建新虚拟机磁盘…

SpringBoot整合minio笔记

SpringBoot整合minio笔记 物料准备&#xff1a; 1.引入minio以及 spring-file-storage 相关依赖 2.配置yml 3.配置 FileRecorder实现类 4.启用EnableFileStorage注解 5.测试上传 引入minio以及 spring-file-storage 相关依赖 minio是一个OSS云存储服务&#xff0c;mini…

web 页面布局:(二)float 浮动布局

web 页面布局&#xff1a;&#xff08;二&#xff09;float 浮动布局 页面宽度使用样式居中 使用浮动浮动元素外的剩余空间使用浮动的高度细节 页面宽度 在很早很早的时期&#xff0c;各大网站的底部都会有这么类似的一句声明&#xff1a;最佳分辨率 sss x ttt&#xff0c;比如…

web前端设计师的具体职责

web前端设计师的具体职责 web前端设计师负责将设计图转化成页面&#xff0c;并实现页面动态效果&#xff0c;并配合后端程序员嵌入数据。下面是学习啦小编整理的web前端设计师的具体职责。 web前端设计师的具体职责1 职责&#xff1a; 1.负责网站页面的整体美工创意、设计与…

redis 笔记

文章学习参考&#xff1a;Redis 教程 | 菜鸟教程 (runoob.com) 1、安装并执行 1.1 安装 下载地址&#xff1a;https://github.com/tporadowski/redis/releases 点击下面的压缩文件下载&#xff0c;版本不一定是这个。 1.2 执行redis 打开cmd窗口&#xff0c;跳转到redis解压的…

void QWidget::stackUnder(QWidget *w)

Places the widget under w in the parent widgets stack. To make this work, the widget itself and w must be siblings. 在父窗口的栈中&#xff0c;放置widget在w下面。 为了生效&#xff0c;widget和w必须是兄弟。 什么意思呢&#xff1f; widget和w的父窗口必须是同一个…

计算机与网络发展的7个阶段【图解TCP/IP(笔记一)】

文章目录 计算机与网络发展的7个阶段批处理分时系统计算机之间的通信计算机网络的产生互联网的普及以互联网技术为中心的时代从“单纯建立连接”到“安全建立连接” 计算机与网络发展的7个阶段 批处理 所谓批处理&#xff0c;是指事先将用户程序和数据装入卡带或磁带&#xf…

从小白到大神:可能是最全的前端学习大纲

导读 俗话说得好&#xff1a;“不谋全局者不足谋一域。”前端开发是一个广泛而复杂的领域&#xff0c;需要掌握多方面的知识。 在之前的文章中&#xff0c;我们介绍了计算机领域的各种方向&#xff0c;以及前端在公司中的地位和日常工作内容&#xff0c;相信你对前端开发已经…

冰冰学习笔记:初识网络

欢迎各位大佬光临本文章&#xff01;&#xff01;&#xff01; 还请各位大佬提出宝贵的意见&#xff0c;如发现文章错误请联系冰冰&#xff0c;冰冰一定会虚心接受&#xff0c;及时改正。 本系列文章为冰冰学习编程的学习笔记&#xff0c;如果对您也有帮助&#xff0c;还请各位…

剑指offer30天打卡活动

文章目录 Day1: 用两个栈实现队列包含min函数的栈 一、用两个栈实现队列OJ链接 本题思路&#xff1a;定义两个栈&#xff0c;&#xff08;Enqueue&#xff09;是用来存储入队的元素&#xff0c;&#xff08;Cnqueue&#xff09;用来出队的&#xff0c;那么如何进行操作呢&am…

Win10 显示WLAN不安全,并且链路速度54/54 (Mbps),通过K3C路由器修改协议解决,无线网卡连接速度只有54Mbps

省流 换个安全协议就好了。 使用有线等同隐私(WEP)或临时密钥完整性协议(TKIP)加密配置时&#xff0c;客户端设备的WiFi数据传输速率不会超过54Mbps&#xff0c; 问题 我用的是K3C路由器&#xff0c;今天跑百度网盘感觉很奇怪&#xff0c;突然就只有10MB/s了&#xff0c;感觉…

盖子的c++小课堂——第十八讲:栈

前言 OK呀&#xff0c;说到做到&#xff0c;我们的粉丝们也是很给力呀&#xff0c;终于破了400粉~~ 我太感动了aaaaaaaaaaaaaaaaaaaaaaaa 话不多说&#xff0c;我们直接开始&#xff01; 栈的定义 栈&#xff0c;是什么&#xff1f; 例1-弹夹 你见过手枪吗&#xff1f;它…

安装Node.js和创建Vue-cli工程

NodeJs中文网&#xff1a;下载 | Node.js (nodejs.org) 注意事项&#xff1a; 最好下载Node16版本&#xff0c;除非你后续使用的Vue-cli版本比较高&#xff0c;像我使用的Vue-cli3和4.1.1版本去创建工程&#xff0c;然后run运行会报错Error: error:0308010C:digital envelope …

echarts自定义legend样式

转载自&#xff1a; https://blog.csdn.net/changyana/article/details/126281275 目标样式&#xff1a; 使用legend中的formatter以及textStyle.rich legend: { // 对图形的解释部分orient: vertical,right: 10,y: center,icon: circle, // 添加formatter: function(name)…

绘制图形、ROI截取、高斯三角形

1、直线 2、圆形 3、椭圆 4、矩形 5、多边形 6、文字 //图形绘制 void test1() {Mat img Mat::zeros(Size(512, 512), CV_8UC3);//生成一个黑色图像用于绘制几何图形//绘制圆形circle(img, Point(50, 50), 25, Scalar(255, 255, 255), -1);//绘制一个实心圆circle(img, Point(…

Flutter 轮播图 flutter_swiper属性说明使用

今天分享的内容是关于图片轮播的实现&#xff0c;使用到的库是flutter_swiper&#xff0c;如果有出现空检查报错的&#xff0c;可以使用flutter_swiper_null_safety 轮播图效果如下&#xff1a; 先贴出基本参数详解&#xff1a; 参数说明itemBuilder列表的构造indicatorLayou…

Redis持久化之RDB和AOF

6、Redis持久化 6.1、背景 首先Redis作为一种缓存性数据库&#xff0c;如果缓存中有数据&#xff0c;他可以很快的把数据返回给客户&#xff0c;至于为什么他可以很快的将数据返回给客户&#xff0c;主要是因为他是一种内存性数据库&#xff0c;不需要额外的IO操作&#xff0…

报错:Destructuring assignments are not supported by current javaScript version

报错信息&#xff1a;当前JavaScript 版本不支持非结构化赋值 报错示例&#xff1a; 报错的原因是&#xff1a;这种语法是EcmaScript6才有的&#xff0c;以往的版本没有&#xff0c;修改一下javaScript的版本即可&#xff0c;方法如下&#xff1a;找到setting----->Languag…

接口自动化测试项目,让你像Postman一样编写测试用例,支持多环境切换、多业务依赖、数据库断言等

项目介绍 接口自动化测试项目2.0 软件架构 本框架主要是基于 Python unittest ddt HTMLTestRunner log excel mysql 企业微信通知 Jenkins 实现的接口自动化框架。 前言 公司突然要求你做自动化&#xff0c;但是没有代码基础不知道怎么做&#xff1f;或者有自动…