stm32学习笔记:USART串口通信

news2024/11/20 4:28:31

1、串口通信协议(简介+软硬件规则)

全双工:打电话。半双工:对讲机。单工:广播 

时钟:I2C和SPI有单独的时钟线,所以它们是同步的,接收方可以在时钟信号的指引下进行采样。串口、CAN和USB没有时钟线,需要双方约定一个采样频率,它们是异步的,并且需要加一些帧头帧尾等进行采样位置的对齐。

电平:1、单端->引脚的高低电平都是对GND的电压差,所以单端信号通信的双方必须要共地,就是把GND接在一起。2、

差分->它是靠两个差分引脚的电压差来传输信号的,在通讯的时候,可以不需要共地,可极大提高抗干扰特性,所以差分信号一般传输速度和距离都会非常高。

设备:点对点->老师单独找一位学生谈话;多设备->老师在教室里面对所有同学谈话,需要有一个寻址的过程,以确定通信的对象。寻址:给不同的设备编号,对应不同的学生的名字


 

 

 

 波特率:如果双方规定波特率为1000bps,表示1s要发送1000位,每一位的时间就是1ms。 发送方每隔1ms发送1位,接收方每隔1ms接收1位。波特率,它决定了每隔多久发送一位。

起始位:首先,串口的空闲状态是高电平,也就是没有数据传输的时候,引脚必须要置高电平,作为空闲状态,然后需要传输的时候,必须要先发送一个起始位,这个起始位必须是低电平,来打破空闲状态的高电平,产生一个下降沿,该下降沿就告诉设备这一帧数据要开始了。如果没有起始位,数据线就一直都是高电平,没有任何波动,这样接收方怎么知道要接收数据呢。

停止位:为下一个起始位做准备。如果没有停止位,那当我数据最后一位是0的时候,下次再发送新的一帧,就没有办法产生下降沿了。
例子,连续发送两个0x55,1个停止位和2个停止位

 校验位:串口使用的是一种叫奇偶校验的数据验证方法。奇偶校验可以判断数据传输是否出错。如果数据出错了,可以选择丢弃或者要求重传。

串口通信总结:TX引脚输出定时翻转的高低电平,RX引脚定时读取引脚的高低电平。每个字节的数据加上起始位,停止位,可选的校验位(无,奇,偶),打包成数据帧,一次输出在TX引脚,另一端RX引脚依次接收,这样就完成了字节数据的传递。

2、STM32内部的USART外设

USART外设就是串口通信的硬件支持电路。
常用配置:波特率9600或者115200,数据位8位,停止位1位,无校验
USART1:APB2总线的设备
USART2、USART3:APB1总线的设备

 

USART功能框图

发送数据寄存器和发送移位寄存器怎么工作的呢?
比如在某一时刻给TDR写入0x55数据

发送端
此时,硬件检测到你写入数据,它就会检查当前一位寄存器是不是有数据正在移位,如果没有,这个01010101就会立刻全部移动到发送移位寄存器,准备发送。当数据从TDR移动到移位寄存器时,会置一个标志位,叫TXE(TX Empty),发送寄存器空。如果该标志位置1,可在TDR写入下一个数据。注意,当TXE标志位置1时,数据还没有发送出去,只要数据从TDR转移到移位寄存器,TXE就会置1,此时可写入新的数据。然后发送移位寄存器就会在发生器控制的驱动下,向右移位,然后一位一位地把数据输出到TX引脚,正好与串口协议规定的低位先行一致。当数据移位完成时,新的数据就会再次自动地从TDR转移到发送移位寄存器里来。如果当前移位寄存器移位还没有完成,TDR的数据就会进行等待,一旦移位完成,就会立刻转移过来。有了TDR和移位寄存器的双重缓存,可以保证连续发送数据的时候,数据帧之间不会有空闲。简单来说,数据一旦从TDR转移到移位寄存器,管你有没有移位完成,就会把下一个数据放在TDR等待。一旦移位寄存器移动完成,新的数据就会立刻跟上。

接收端
数据从RX引脚通向接受移位寄存器,在接收器控制的驱动下,一位一位地读取RX电平,先放在最高位,然后向右移动,移位8次后就可以接受一个字节。

同样,因为串口协议规定是低位先行,所以接受移位寄存器是从高位往低位这个方向移动,当一个字节移位完成后,这一个字节的数据就会整体地转移到接收数据寄存器RDR里,在转移的过程中会置一个标志位,叫RXNE(RX Not Empty),接收数据寄存器非空。当我们检测到RXNE置1之后,就可以把数据读走。同时,这个标志位可以去申请中断,在收到数据时,直接进入中断函数。
这里也是两个寄存器进行缓存,当数据从移位寄存器转移到RDR时,就可以直接移位接受下一帧数据。

发送器控制:用来控制发送移位寄存器的工作
接受器控制:用来控制接收移位寄存器的工作

以下模块用于产生同步时钟信号,它是配合发送移位寄存器输出的,发送寄存器每移位一次,同步时钟电平就跳变一个周期,时钟告诉对方我移出去移位数据了 。该时钟只支持输出,不支持输入,因此两个USART之间不能实现同步的串口通信。
时钟作用
1、兼容别的协议,比如串口加时钟,与SPI协议相似,因此可兼容SPI
2、自适应波特率,如接收设备不确定发送设备给的是什么波特率,可通过测量时钟周期,再计算得到波特率。 

波特率发生器就是分频器,APB时钟进行分频,得到发送和接收移位的时钟。

串口的引脚

3、USART基本结构

‘>>’右移,表示数据低位先行
开关控制->配置完成时,用cmd开启USART外设

输入的采样频率和波特率一致,还要保证每次输入采样的位置,要正好处于每一位的正中间,只有在每一位的正中间采样,这样的高低电平读进来,才是最可靠的。如果采样点过于靠前或靠后,那有可能高低电平还正在翻转,电平不稳定或者稍有误差,数据就采样出错了。 

 

4、串口发送代码


4-1 基本流程
1、第一步开启时钟,把需要用到的USART和GPIO的时钟打开
2、第二步,GPIO初始化,把Tx配置成复用输出,Rx配置成输入
3、第三步,配置USART,直接使用一个结构体
4、如果你只需要发送的功能,就直接开启USART,初始化就结束了;如果你需要接收的功能,可能还需要配置中断,那就在开启USART之前,再加上ITConfig和NVIC的代码

 

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"

int main(void)
{
	OLED_Init();
	
	Serial_Init();
	
	Serial_SendByte(0x41);
	
	while (1)
		{
			
		}
}

serial.h

#ifndef __SERIAL_H
#define __SERIAL_H

void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);

#endif

serial.c

#include "stm32f10x.h"                  // Device header

void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);  
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  
	
	GPIO_InitTypeDef GPIO_InitStructure;
	

	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
//USART
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;  
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  
	USART_InitStructure.USART_Mode = USART_Mode_Tx;    
	USART_InitStructure.USART_Parity = USART_Parity_No;  
	USART_InitStructure.USART_StopBits = USART_StopBits_1;  
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;  
	USART_Init(USART1, &USART_InitStructure);
	//USART
	USART_Cmd(USART1, ENABLE);
}

void Serial_SendByte(uint8_t Byte)
{ 
	USART_SendData(USART1, Byte);
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); 
}

 

static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 嵌套向量中断控制器组选择 */
	/* 提示 NVIC_PriorityGroupConfig() 在整个工程只需要调用一次来配置优先级分组*/
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* 配置USART为中断源 ,默认使用串口1作为中断源*/
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}

// 串口中断优先级配置
	NVIC_Configuration();
	
	// 使能串口接收中断,接收数据寄存器非空,表示接收到数据就产生中断
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);

5、初始化完成后,如果要发送数据,调用一个发送函数即可;如果要接收数据,就调用接收的函数。如果要获取发送和接收的状态,就调用获取位的函数

4-2 整体代码
4-2-1 main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"

int main(void)
{
	OLED_Init();
	//初始化串口
	Serial_Init();
	//发送数据,调用该函数后,TX引脚就会产生一个0x41对应的波形,
	//可将该波形发送给其他支持串口的模块,也可以通过USB转串口的模块发送到电脑端
	Serial_SendByte(0x41);
	
	uint8_t MyArray[] = {0x42, 0x43, 0x44, 0x45};
	Serial_SendArray(MyArray, 4);//传入数组的首地址,指定传输4个字节  
	
	Serial_SendString("\r\nNum1=");
	
	Serial_SendNumber(111, 3);
	
	printf("\r\nNum2=%d", 222);
	
	char String[100];  //定义字符串
	sprintf(String, "\r\nNum3=%d", 333);  //打印字符串
	Serial_SendString(String);  //发送字符串
	
	Serial_Printf("\r\nNum4=%d", 444);
	Serial_Printf("\r\n");
	
	while (1)
	{
		
	}
}
4-2-2 Serial.c

取某一位就是–>数字 / 10^x % 10
/ 10^x 去掉右边
% 10 去掉左边

重定向fputc跟printf有什么关系呢?
这是因为fputc是printf函数的底层实现,printf函数在打印的时候就是不断调用fputc函数一个个打印的,我们把fputc函数重定向到串口,那printf自然久输出到串口。

 #include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);  //USART1的外设时钟时APB2
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   //开启GPIO时钟
	//初始G化PIO
	GPIO_InitTypeDef GPIO_InitStructure;
	
	/* 将PA9配置为复用推挽输出,供USART的Tx使用 */
	//TX引脚是USART外设控制的输出脚,所以要选复用推挽输出,RX引脚是USART外设数据输入脚,
	//所以要选择输入模式,一根线只能有一个输出,但可以有多个输入
	//因为串口波形空闲状态是高电平,所以不使用下拉输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   //复用推挽输出 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//初始化USART
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;  //Init函数内部会自动算好9600对应的分频系数,然后写到BRR寄存器
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  //不用流控
	USART_InitStructure.USART_Mode = USART_Mode_Tx;    //只有发送模式
	USART_InitStructure.USART_Parity = USART_Parity_No;  //无校验
	USART_InitStructure.USART_StopBits = USART_StopBits_1;  //1位停止位
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;  //字长8位
	USART_Init(USART1, &USART_InitStructure);
	//使能USART
	USART_Cmd(USART1, ENABLE);
}

void Serial_SendByte(uint8_t Byte)
{ 
	//将Byte变量写入到TDR
	USART_SendData(USART1, Byte);
	//等待TXE置1,表明TDR数据已经转移到移位数据寄存器,要不然如果数据
	//还在TDR进行等待,我们再写入数据就会产生数据覆盖,所以在数据发送之后,还需要等待以下标志位
	//如果TXE标志位==RESET,就一直循环,直到SET,结束等待,标志位置1后,不需要手动清零,
	//当下一次SendData时,该标志位自动清零
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); 
}

//uint8_t *Array这是一个uint8_t 的指针类型,指向待发送数组的首地址,传递数组需要使用指针
//length由于数组无法判断是否结束,所以需要再传递一个length进来
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Array[i]);
	}
}

void Serial_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)  //数据0,对应空字符,是字符串结束标志位,如果不等于0,就是还没有结束,进行循环,如果等于0,就是结束,停止循环
	{
		Serial_SendByte(String[i]);
	}
}
//返回值为 x的y次方
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;  //X的Y次方
	}
	return Result;
}
//发送一个数字,最终能在电脑显示字符串形式的数字
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');  //目前循环,参数会以10进制从高位到低位依次发送,再加上偏移量
		//假设length为2,当i=0时,发送的是10位,当i=1时,发送的是个位
		//从高位到低位依次发送
		
	}
}
//
//单个串口重定向,只能有一个串口用来打印
//printf重定向,fputc是printf的底层,将fputc函数重定向到了串口,那printf自然就输出到串口
int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

//多个串口重定向,可多个串口用来打印
//封装sprintf
void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Serial_SendString(String);
}
4-2-3 Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H

#include <stdio.h>

void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);

#endif

程序现象:就看串口助手软件

5、串口接收代码

5-1 查询

在主函数里不断判断RXNE标志位,如果置1,表明收到数据,再调用ReceiveData,读取DR寄存器即可

//查询
	while(1)
	{
		if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == SET)
		{
			RxData = USART_ReceiveData(USART1);  //读完DR寄存器,该标志位自动清除
			OLED_ShowHexNum(1,1,RxData,2);
		}
	}
5-2 中断
/*===================================================*/
	/* 串口接收 可使用 查询和中断两种方法 以下是中断*/
	//开启中断,选择RXNE,表示这一个字节的数据就会整体地转移到接收数据寄存器RDR里
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	//中断优先级分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	//配置NVIC
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	/*===================================================*/

/*==============中断接收和变量的封装====================*/
uint8_t Serial_GetRxFlag(void)
{
	if (Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;
		return 1;
	}
	return 0;
}

uint8_t Serial_GetRxData(void)
{
	return Serial_RxData;
}
//中断函数的名字在启动文件里面(startup_stm32f10x_md.s)
void USART1_IRQHandler(void)
{
	//先判断标志位
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{
		//将接收寄存器里的数据放到自定义变量里
		Serial_RxData = USART_ReceiveData(USART1);
		//读完数据后置标志位1
		Serial_RxFlag = 1;
		//如果读取DR,则自动清除标志位,否则,手动清除标志位
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);  
	}
}
5-3-2 Serial.c
#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

uint8_t Serial_RxData;
uint8_t Serial_RxFlag;

void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;  //引脚模式为上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;  //或,同时开启发送和接收部分
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1, &USART_InitStructure);
	
	/*===================================================*/
	/* 串口接收 可使用 查询和中断两种方法 以下是中断*/
	//开启中断,选择RXNE,表示这一个字节的数据就会整体地转移到接收数据寄存器RDR里
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	//中断优先级分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	//配置NVIC
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	/*===================================================*/
	USART_Cmd(USART1, ENABLE);
}

void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Array[i]);
	}
}

void Serial_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)
	{
		Serial_SendByte(String[i]);
	}
}

uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}

void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
	}
}

int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Serial_SendString(String);
}

/*==============中断接收和变量的封装====================*/
//对Serial_RxFlag变量封装get函数,实现读后自动清除的作用
uint8_t Serial_GetRxFlag(void)
{
	if (Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;
		return 1;
	}
	return 0;
}
//对Serial_RxData变量封装get函数
uint8_t Serial_GetRxData(void)
{
	return Serial_RxData;
}
//中断函数的名字在启动文件里面(startup_stm32f10x_md.s)
void USART1_IRQHandler(void)
{
	//先判断标志位
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{
		//将接收寄存器里的数据放到自定义变量里
		Serial_RxData = USART_ReceiveData(USART1);
		//读完数据后置标志位1
		Serial_RxFlag = 1;
		//如果读取DR,则自动清除标志位,否则,手动清除标志位,此处手动清除没影响
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);  
	}
}
/*==========================================================*/
5-3-3 Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H

#include <stdio.h>

void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);

uint8_t Serial_GetRxFlag(void);
uint8_t Serial_GetRxData(void);

#endif

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

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

相关文章

18 串口通讯

文章目录 18.0 前言18.1 串口通讯协议简介18.1.1 物理层 18.2 RT1052 的 LPUART 简介18.3 UART 功能框图18.3.1 中断控制 18.4 UART 初始化结构体详解18.4.1 baudRate_Bps18.4.2 parityMode18.4.3 dataBitsCount18.4.4 isMsb18.4.5 stopBitCount18.4.6 txFifoWatermark与rxFifo…

计算机体系结构基础复习

1. 计算机系统可划分为哪几个层次,各层次之间的界面是什么? 你认为这样划分层次的意义何在? 答&#xff1a; 计算机系统可划分为四个层次&#xff0c;分别是&#xff1a;应用程序、 操作系统、 硬件系统、 晶体管四个大的层次。 注意把这四个层次联系起来的三个界面。各层次…

WIndows系统重装、备份与恢复实操问题笔记

一 windows重装 1.1 基本步骤 下载大白菜根据官网使用教程制作启动u盘从MSDN或者微软官网下载Windows镜像根据查询的快捷键进入BIOS系统&#xff0c;设置U盘为第一启动 重装 1.2 Windows 11 激活 微软其实在2023年9月20日的公告中宣布停掉免费升级&#xff0c;数字激活工具…

使用pygame实现简单的烟花效果

import pygame import sys import random import math# 初始化 Pygame pygame.init()# 设置窗口大小 width, height 800, 600 screen pygame.display.set_mode((width, height)) pygame.display.set_caption("Fireworks Explosion")# 定义颜色 black (0, 0, 0) wh…

YOLOv7涨点改进:多层次特征融合(SDI),小目标涨点明显,| UNet v2,比UNet显存占用更少、参数更少

💡💡💡本文全网独家改进:多层次特征融合(SDI),能够显著提升不同尺度和小目标的识别率 💡💡💡在YOLOv7中如何使用 1)iAFF加入Neck替代Concat; 收录: YOLOv7高阶自研专栏介绍: http://t.csdnimg.cn/tYI0c ✨✨✨前沿最新计算机顶会复现 🚀🚀🚀YOL…

Linux远程登陆协议ssh

目录 一、SSH服务 1. ssh基础 2. 原理 3. 服务端配置 3.1 常用配置项 3.2 具体操作 3.2.1 修改默认端口号 3.2.2 禁止root用户登录 3.2.3 白名单列表 3.2.4 黑名单列表 3.2.5 使用秘钥对及免交互验证登录 3.2.6 免交互式登录 一、SSH服务 1. ssh基础 SSH&…

Microsoft Excel 直方图

Microsoft Excel 直方图 1. 数据示例2. 打开 EXCEL3. settings4. 单击直方图柱&#xff0c;右键“添加数据标签”References 1. 数据示例 2. 打开 EXCEL 数据 -> 数据分析 -> 直方图 3. settings 输入区域样本值、接受区域分类间距&#xff0c;输出选项选择“新工作表组…

玩转Mysql 八 (MySQ优化入门篇)

一路走来&#xff0c;所有遇到的人&#xff0c;帮助过我的、伤害过我的都是朋友&#xff0c;没有一个是敌人。如有侵权&#xff0c;请留言&#xff0c;我及时删除&#xff01; 前言&#xff1a; 一个高性能&#xff0c;稳定的数据库集群并不是指的某一特性优化&#xff0c;就…

3 - AOP

1. 快速入门 1.1 基本说明 AOP(aspect oriented programming) &#xff0c;面向切面编程 切面类中声明通知方法&#xff1a; 前置通知&#xff1a;Before返回通知&#xff1a;AfterReturning异常通知&#xff1a;AfterThrowing后置通知&#xff1a;After环绕通知&#xff1…

并发List源码剖析

并发包中的并发List只有CopyOnWriteArrayList。 CopyOnWriteArrayList是一个线程安全的ArrayList,对其进行的修改操作都是在底层的一个复制的数组(快照)上进行的&#xff0c;也就是使用了写时复制策略。 在CopyOnWriteArrayList的类图中&#xff0c;每个CopyOnWriteArrayList对…

(学习日记)2024.01.05:一份关于自行车定位的调研

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

电流检测方法

电路检测电路常用于&#xff1a;高压短路保护、电机控制、DC/DC换流器、系统功耗管理、二次电池的电流管理、蓄电池管理等电流检测等场景。 对于大部分应用&#xff0c;都是通过感测电阻两端的压降测量电流。 一般使用电流通过时的压降为数十mV&#xff5e;数百mV的电阻值&…

BLDC 电机和 PMSM 的结构区别

BLDC 电机和 PMSM 的结构类似&#xff0c;其永磁体均置于转子&#xff0c;并被定义为同步电机。在同步电机中&#xff0c;转子与定子磁场同步&#xff0c;即转子的旋转速度与定子磁场相同。它们的主要区别在于其反电动势&#xff08;反 EMF&#xff09;的形状。电机在旋转时充当…

NPS配置https访问web管理页面

因为NPS默认也支持http的访问&#xff0c;所以在部署完后就一直没在意这个事情。 因为服务器是暴露在公网内的&#xff0c;所以还是要安全一点才行。不然一旦远控的机器被破解了就很危险了 一、使用nginx反向代理访问 1、首先在nps的配置文件里关闭使用https选项&#xff0c;…

midjourney教程【--niji 5】

博客底部扫码加微信&#xff0c;免费领mj Niji Model Version 5还可以使用不同的美学使用--style选项&#xff0c;以实现独特的外观。试试 --style cute, --style scenic, --style original , or --style expressive mj&#xff0c;a boy sitting on the ground looking soci…

Logstash:迁移数据到 Elasticsearch

在生产环境中&#xff0c;不使用 Apache Kafka 等流平台进行数据迁移并不是一个好的做法。 在这篇文章中&#xff0c;我们将详细探讨 Apache Kafka 和 Logstash 的关系。 但首先让我们简单了解一下 Apache Kafka 的含义。 Apache Kafka 是分布式流平台&#xff0c;擅长实时数据…

计算机找不到vcomp140.dll怎样修复?马上教会你修复dll问题

在计算机系统运行过程中&#xff0c;遭遇“vcomp140.dll丢失”的场景并不少见&#xff0c;这一问题的出现往往伴随着软件无法正常启动、运行时错误提示或者系统性能下降等现象。具体场景可能包括但不限于&#xff1a;用户在尝试打开某个依赖于Visual C Redistributable库的应用…

有趣的事,讲给有趣的人听

哈哈哈&#xff0c;今天不写技术了&#xff0c;今天分享一下生活&#xff0c;技术我们什么时候都可以学&#xff0c;但是生活更值得我们现在就去更好的体验&#xff01; 两年多的涤生大数据&#xff0c;认识了形形色色的小伙伴&#xff0c;陆续沟通下来6000多人&#xff0c;彼时…

代码随想录算法训练营第三十二天|122.买卖股票的最佳时机II、55. 跳跃游戏、45.跳跃游戏II

题目&#xff1a;122.买卖股票的最佳时机II 文章链接&#xff1a;代码随想录 视频链接&#xff1a;LeetCode:122.买卖股票的最佳时机|| 题目链接&#xff1a;力扣题目链接 图释&#xff1a; class Solution { public:int maxProfit(vector<int>& prices) {// 查看…

【笔记】书生·浦语大模型实战营——第四课(XTuner 大模型单卡低成本微调实战)

【参考&#xff1a;tutorial/xtuner/README.md at main InternLM/tutorial】 【参考&#xff1a;(4)XTuner 大模型单卡低成本微调实战_哔哩哔哩_bilibili-【OpenMMLab】】 总结 学到了 linux系统中 tmux 的使用 了解了 XTuner 大模型微调框架的使用 pth格式参数转Hugging …