单片机-STM32部分:10、串口UART

news2025/5/10 12:02:12

飞书文档https://x509p6c8to.feishu.cn/wiki/W7ZGwKJCeiGjqmkvTpJcjT2HnNf

串口说明

电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:

TTL电平:+3.3V或+5V表示1,0V表示0

RS232电平:-3~ -15V表示1,+3~ +15V表示0

RS485电平:两线压差+2~+6V表示1,-2 ~-6V表示0(差分信号)

STM32F103RC系列芯片中,有五个串口

3个USART,2个UART
USART:通用同步和异步收发器
UART:通用异步收发器
当进行异步通信时,这两者是没有区别的。区别在于USART比UART多了同步通信功能。

IO口说明:

点击图片可查看完整电子表格

TX:发送数据输出引脚。
RX:接收数据输入引脚。
如果发送设备发送太快,接收设备来不及处理,可以通过流控来控制传输的速度。
SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式,用于时钟同步
nRTS是请求发送,是输出脚,就是告诉别人,我当前能不能接收,用于硬件流控
nCTS是清除发送,是输入脚,用于接收别人nRTS的信号,用于硬件流控

硬件流控说明,例如:
接收端可以接收数据时,会设置nRTS输出低电平,此时发送端读取到低电平,开始发送数据。
接收端处理不过来时,设置nRTS为高电平,此时发送端读取到高电平,停止发送数据。

创建工程,设置SWD,设置时钟。

配置USART1为异步通信方式,不需要硬件流控制。

Asynchronous(异步通讯)主要使用
Synchronous(同步通讯,同步通讯相比于异步通讯多了个时钟CLK输出)
Single Wire (Half-Duplex)(单线(半双工)通讯)
Multiprocessor Communication(多处理器通信)
SmartCard、IrDA、LIN 智能卡、IrDA、LIN,这些是其他的一些协议,这些协议与串口非常相似,所以STM32对USART加了些改动,可兼容这些协议。IrDA用于红外通信的,一边红外发光管,另一边红外接收管,靠闪烁红外光通信,与遥控器的红外不同。LIN是局域网的通信协议,具体可以查看芯片手册。

关于硬件流控制,比如A设备有个TX向B设备RX发送数据,A设备发的太快导致B处理不过来,如果没有硬件流控制,B就只能抛弃新数据或者覆盖原数据了,如果有硬件流控制,在硬件电路上会多出一根线,如果B没准备好接收就置于高电平,准备好了就置低电平,A只会在B准备好的时候发送数据。
硬件流控制需要多使用两个IO,所以大部分情况都不使用,直接用软件做数据处理。

然后设置波特率为115200bps 数据长度8bit 没有校验位 1位停止位。

串口中,每个字节都装载在一个数据帧(10或11位)里,每个数据帧都由起始位数据位停止位,数据位有8个代表一个字节的8位。参数如下
波特率:串口通信的速率。波特率本来的意思是每秒传输码元的个数,单位是码元/s,或者直接叫波特(Baud),还有个速率叫比特率,每秒传输的比特数,单位是bit/s,或者是bps。在二进制调制下,一个码元就是一个bit,此时波特率等于比特率,单片机的串口通信基本都是二进制调制(高电平表示1,低电平表示0,一位就是1bit),所以串口的波特率经常会和比特率混用。
起始位:标志一个数据帧的开始,固定为低电平。空闲状态为高电平,起始位产生下降沿,来告诉设备要开始发送数据了
数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行
校验位:用于数据验证,根据数据位计算得来。这里串口用的是奇偶校验的数据验证方法,可以判断数据传输是否出错,如果出错可选择丢弃或者重传。可选择三种方式,无校验、奇校验、偶校验。奇校验,包括校验位在内的9个数据位会出现奇数个1,根据8位数据情况奇校验位补0或1,保证1的个数位奇数,接收方接收数据时,会验证数据位和校验位,检出率不高比如有两位同时出错,只校验奇偶特性是检验不出的。偶校验同理,只能保证一定检出率,所以一般不需要校验位,如果要更高检出率可以在软件层使用CRC校验
停止位:用于数据帧间隔,固定为高电平。也是为下一个起始位做准备(切换到高电平空闲状态)

波特率9600代表1s发送9600bit,也就是1bit发送需要100us左右
 

这时,软件会自动选择PA9与PA10做为串口的发送与接收引脚。

这时,我们可以生成工程

main.c
MX_USART1_UART_Init();

usart.c
void MX_USART1_UART_Init(void)
{
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
}

那如何实现串口发送或接收数据呢?

stm32f1xx_hal_uart.h
HAL_UART_Transmit();                    串口轮询模式发送,使用超时管理机制。 
HAL_UART_Receive();                     串口轮询模式接收,使用超时管理机制。
HAL_UART_Transmit_IT();                 串口中断模式发送, 
HAL_UART_Receive_IT();                  串口中断模式接收
HAL_UART_Transmit_DMA();                串口DMA模式发送 
HAL_UART_Receive_DMA();                 串口DMA模式接收

HAL_UART_TxHalfCpltCallback();          发送过半,通过中断处理函数调用。
HAL_UART_TxCpltCallback();              发送完成后,通过中断处理函数调用。
HAL_UART_RxHalfCpltCallback();          接收过半,通过中断处理函数调用。
HAL_UART_RxCpltCallback();              接收完成后,通过中断处理函数调用。
HAL_UART_ErrorCallback();               传输过程中出现错误时,通过中断处理函数调用。

第一种是上面用到的轮询的模式。
CPU会不断查询串口是否传输完成,如传输超过则返回超时错误。轮询方式会占用CPU处理时间,效率较低,在实时性要求较高的产品中不宜使用。
第二种就是中断控制方式。
当I/O操作完成时,输入输出设备控制器通过中断请求线向处理器发出中断信号,处理器收到中断信号之后,转到中断处理程序,对数据传送工作进行相应的处理。
第三种就是直接内存存取技术(DMA)方式。
所谓直接传送,先发送到DMA,即在内存与IO设备间传送一个数据块的过程中,不需要CPU的任何中间干涉,只需要CPU在过程开始时向设备发出“传送块数据”的命令,然后通过中断来得知过程是否结束和下次操作是否准备就绪。
整个过程只产生两次中断,第一次是进入DMAx_Streamy_IRQHandler;第二次进入USARTx_IRQHandler。

 

有多种方式,我们先来了解第一种,阻塞轮询模式

HAL_UART_Transmit (UART_HandleTypeDef *huart, const uint8 t *pData, uint16 t Size, uint32 t Timeout)
这个函数是一个阻塞函数,即当调用此函数时,程序会一直等待数据发送完成或超时后才会继续执行后面的代码。
第一个参数是UART句柄,比如要使用USART1,参数就设置为USART1的句柄地址&huart1
第二个参数是需要发送的数据。
第三个参数是需要发送数据的大小。
第四个参数是发送超时时间,单位是毫秒,如果超过设置的时间,则函数返回HAL_TIMEOUT,如果设置为HAL_MAX_DELAY,处理器就会一直等到数据发送完成再执行下一条语句。


HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
第一个参数是要使用的串口句柄地址,比如要使用USART1,参数就设置为USART1的句柄地址&huart1
第二个参数是接受数据的缓冲区首地址
第三个参数是接受的数据长度,这里可以直接用sizeof()函数获取接受缓冲区的长度
第四个参数是超时时间,单位是ms,如果超过设置的时间,则函数返回HAL_TIMEOUT,如果设置为HAL_MAX_DELAY,处理器就会一直等到接收到设置好的数据数量再执行下一条语句。

现在,我们先实现发送功能,在main.c中添加发送代码

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

    /* USER CODE BEGIN 3 */
    uint8_t txbuf[]="Hello,world!";
    HAL_UART_Transmit(&huart1,txbuf,sizeof(txbuf),1000);
    HAL_Delay(500);                                                       
  }
  /* USER CODE END 3 */
  1. 编译烧录至板卡,然后接好串口线连接到电脑。
  2. 打开串口调试助手,选择COM口,例如下方是COM5,根据自己电脑设备管理器的COM选择,插拔USB线,会显示新COM,如果提示COM口有叹号,则需要自行搜索CH340驱动安装。

串口调试助手软件:自行安装即可:参考飞书文档

  • 然后设置波特率115200 8 N 1,即可看到间隔500ms打印信息。

参考工程:

如果烧录完没打印,可以重启或复位下

  /* USER CODE BEGIN 2 */
  uint8_t rxbuf[12];
  /* USER CODE END 2 */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    if(HAL_UART_Receive(&huart1,rxbuf,sizeof(rxbuf),1000) == HAL_OK){
          HAL_UART_Transmit(&huart1,rxbuf,sizeof(rxbuf),1000);
    }
  }
  /* USER CODE END 3 */

编译烧录至板卡,然后接好串口线连接到电脑,打开串口调试助手,设置波特率115200 8 N 1,发送ASCII码“Hello world”,

为什么“Hello world”是11个字符,我们需要接收rxbuf[12]是12个字节呢?

因为串口助手工具,会自动加上换行符,点击右侧的发送后,我们可以看到TX是12个字节。

串口中断方式

我们可以看到,上方的方式都是阻塞式发送,轮询接收的,简单的产品这样设计没有问题,但是做一些复杂的,对实时性有要求的产品时,就满足不了了,所以我们可以用到串口中断的功能,在CUBEMX中使能中断。

阻塞方式就好比你要拿快递,就一遍遍都前台询问快递到没到,在这期间你不能干别的,
中断方式是你告诉前台快递到了给你打电话,在这期间你是可以腾出身子来干别的事情。

生成工程后,可以在stm32f1xx_it.c中看到生成了中断相关函数

/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
  HAL_UART_IRQHandler(&huart1);
}

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart){
    xxxx
    UART_Receive_IT(huart);
    xxxx
}

static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart){
    xxxx
    HAL_UART_RxCpltCallback(huart);
    xxxx
}
最终找到需要重写的虚函数
/**
  * @brief  Rx Transfer completed callbacks.
  * @param  huart  Pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  * @retval None
  */
__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(huart);
  /* NOTE: This function should not be modified, when the callback is needed,
           the HAL_UART_RxCpltCallback could be implemented in the user file
   */
}

然后在

  main.c
  /* USER CODE BEGIN 0 */
  uint8_t rxbuf[10];
  uint8_t ackbuf[] = "ack pack";
  /* USER CODE END 0 */
 
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart1,rxbuf,sizeof(rxbuf));
  /* USER CODE END 2 */
 
  /* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){       
      if(huart == &huart1)   //判断中断是否来自于串口1
      {
          HAL_UART_Transmit_IT(&huart1,ackbuf,sizeof(ackbuf));  //通过中断的方式发送应答数据出去
          HAL_UART_Receive_IT(&huart1,rxbuf,sizeof(rxbuf));   //开始接收下一轮数据
      }
}

下载完成,点击复位。打开串口助手,连接到相应的端口,设置波特率为115200,从串口助手向单片机发送10个字节的数据,单片机将会把发过去的数据在返回给串口助手。必须发够10个字节以上的数据,才能够触发中断。

参考工程:

如果烧录完没打印,可以重启或复位下

串口中断+DMA方式

这时候,如果我们在开发产品过程中,需要频繁收发数据,且通信波特率较高时,如果采用中断方式,每收发一个字节的数据,CPU都会被打断,造成CPU无法处理其他事务。 因此在批量数据传输,通信波特率较高时,建议采用DMA方式

串口中断每收发一个字节数据,CPU都会被打断
 

CPU只需要设置开始传输和处理传输结束的中断
 

DMA,全称Direct Memory Access,即直接存储器访问。

DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。

我们知道CPU无时不刻的在处理着大量的事务,但有些事情却没有那么重要,比方说数据的复制和存储数据,如果我们把这部分的CPU资源拿出来,让CPU去处理其他的复杂计算事务,是不是能够更好的利用CPU的资源呢?

所以串口收发数据量大时可借助DMA,减轻CPU负担。即在内存与IO设备间传送一个数据块的过程中,不需要CPU的任何中间干涉,只需要CPU在过程开始时向设备发出“传送块数据”的命令,然后通过中断来得知过程是否结束和下次操作是否准备就绪。
整个过程只产生两次中断,第一次是进入DMAx_Streamy_IRQHandler;第二次进入USARTx_IRQHandler。

前文说过中断方式就好比你告诉前台,等快递到了给你打电话,让你亲自来取,假设你正在做着一些重要的事情,正好来了电话让你取快递,这样一来就会耽误事。
这怎么办呢?
雇个保姆不就好了吗,DMA就好比这个保姆,你告诉她在哪里取快递,她就会等快递到了之后自己帮你把快递拿回家。

STM32F103RC有12个独立的可配置的通道(请求):DMA1有7个通道,DMA2有5个通道

每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过 软件来配置。

这里,我们切换CUBEMX的USART1中,设置DMA,点击Add,把USART1_TX USART1_RX都添加进来。

注意,RX和TX下方的DMA Request Settings都需要设置为一样。

Channel:通道
例如USART1_RX会对应着上面表格的DMA1的通道5

Direction:方向
因为RX是接收接口,接收来自外设的数据,所以方向是Peripheral To Memory(外设到内存),TX相反

Priority: 优先级,当存在多个DMA传输时才需要根据具体业务设置,默认选择Low即可
最高优先级 Very Hight
高优先级 Hight
中等优先级 Medium
低优先级;Low

Mode:模式
Normal:正常模式
当一次DMA数据传输完后,停止DMA传送 ,下次传输则需要重新开启DMA传输
Circular: 循环模式
传输完成后又重新开始继续传输,不断循环永不停止
根据不同场景选择灵活不同模式:
例如:
正常模式:一次性传输固定长度的数据,例如发送一次性消息、接收固定长度的数据包等。
循环模式:需要连续不断地发送或接收数据的场景,如连续的传感器数据采集等。
通讯类场景,一般TX使用正常模式,RX使用正常/循环模式,如果TX使用循环模式,需要注意数据同步问题,处理不好会导致新旧数据一起发送,而采集数据,使用循环模式就可以大大降低CPU的压力。

Increment Address:地址递增器
左侧Peripheral表示外设地址寄存器
功能:设置传输数据的时候外设地址是不变还是递增。如果设置为递增,那么下一次传输的时候地址加Data Width个字节
右侧Memory表示内存地址寄存器
功能:设置传输数据时候内存地址是否递增。如果设置为递增,那么下一次传输的时候地址加Data Width个字节,
例如:
串口发送数据是将数据不断存进固定外设地址的发送数据寄存器,所以外设的地址是不递增。
而内存储器存储的是要发送或接收数据,地址指针要递增,保证数据依次被发出或不被覆盖保存。

Data Width 数据宽度
一般的串口都是8位,因此使用默认的DMA配置即可,也就是指针自增为Byte

这里有个需要注意的地方,就是函数调用顺序

MX_DMA_Init()函数需要在其他初始化前调用,特别是在这个串口初始化前,不然会发送使用DMA发送会发送失败,在如下图位置配置调用顺序,必须先配置时钟再配置外设,MX_DMA_Init()里面有DMA时钟初始化

设置完成上面步骤,生成工程后,我们会发现DMA初始化在USART1之前,如果不进行这步设置,可能会出现发送失败的情况哦。

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();

然后,我们可以使用DMA方式实现串口发送

main.c
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    uint8_t txbuf[]="Hello,world!";
    HAL_UART_Transmit_DMA(&huart1,txbuf,sizeof(txbuf));
    HAL_Delay(500);                                                       
  }
  /* USER CODE END 3 */

也可以使用DMA方式实现串口收发

如果需要实时处理串口的数据,则需要打开串口全局中断。

UART一旦开启DMA之后,DMA通道全局中断都是强制开启的,DMA传输完整数据后,会触发HAL_UART_RxCpltCallbackHAL_UART_TxCpltCallback中断产生。

  main.c
  /* USER CODE BEGIN 0 */
  uint8_t rxbuf[10];
  uint8_t ackbuf[] = "ack pack";
 
  /* USER CODE BEGIN 2 */
  //初始化DMA串口接收需要在串口初始化前?
  HAL_UART_Receive_DMA(&huart1,rxbuf,sizeof(rxbuf));
  /* USER CODE END 2 */


  /* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){       
      if(huart == &huart1)   //判断中断是否来自于串口1
      {
          HAL_UART_Transmit_DMA(&huart1,ackbuf,sizeof(ackbuf));  //通过中断的方式发送应答数据出去
          //如果接收使用循环模式,则不用重新开启
          HAL_UART_Receive_DMA(&huart1,rxbuf,sizeof(rxbuf));   //开始接收下一轮数据
      }
}

参考工程:参考飞书文档

使用USART+DMA接收中断不定长数据

可以使用STM32 IDLE空闲中断实现,IDLE的中断产生条件:在串口无数据接收的情况下,不会产生,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一但接收的数据断流,没有接收到数据,即产生IDLE中断

main.c
/* USER CODE BEGIN 0 */
extern DMA_HandleTypeDef hdma_usart1_rx;
#define BUFFER_SIZE  100 
uint8_t rxbuf[BUFFER_SIZE];

/* USER CODE BEGIN 2 */
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart1,rxbuf,sizeof(rxbuf));

/* USER CODE BEGIN 4 */
void UART_IDLEHandler(){
        if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) == SET) //如果串口处于空闲状态
        {
                __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_IDLE);//清空空闲状态标志
                HAL_UART_DMAStop(&huart1); //关闭DMA传输
                //计算接收到的数据长度 ,已接收长度=需要接收总长度-剩余待接收长度
                uint8_t rlen = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
                //发送数据到上位机,当然,这里可以把数据复制到其它位置进行处理             
                HAL_UART_Transmit_DMA(&huart1,rxbuf,rlen);
                //重新打开DMA接收
                HAL_UART_Receive_DMA(&huart1,rxbuf,sizeof(rxbuf));               
        }
}

main.h
/* USER CODE BEGIN EFP */
void UART_IDLEHandler(void);
/* USER CODE END EFP */

stm32f1xx_it.c
/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
  UART_IDLEHandler();
  /* USER CODE END USART1_IRQn 1 */
}

参考工程:参考飞书文档

端口复用

当然,USART1是支持复用功能的,可以重映像到其它IO上,如果我们在进行硬件设计时,发现PA9、PA10走线不好走,或者需要作为其它用途,我们可以把USART1映射到PB6 PB7,如何知道是否支持重映像,可以查看手册8.3章节。


 

可以在右侧的芯片图中找到PB6,设置为USART1_TX,PB7,设置为USART1_RX

串口重定向

在单片机中使用printf打印

使用HAL_UART_Transmit发送字符串很不方便,可以重定向printf()函数使printf通过串口打印字符串

使用串口重定向,必须勾选MicroLIB
MicroLib 是一个高度优化的库,适用于用 C 编写的基于 ARM 的嵌入式应用程序。
与 ARM 编译器工具链中包含的标准 C 库相比,MicroLib 提供了许多嵌入式系统所需的显着代码大小优势。

main.c
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */

/* USER CODE BEGIN 4 */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
    HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
    return ch;
}

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

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

相关文章

RabittMQ-高级特性2-应用问题

文章目录 前言延迟队列介绍ttl死信队列存在问题延迟队列插件安装延迟插件使用事务消息分发概念介绍限流非公平分发&#xff08;负载均衡&#xff09; 限流负载均衡RabbitMQ应用问题-幂等性保障顺序性保障介绍1顺序性保障介绍2消息积压总结 前言 延迟队列介绍 延迟队列(Delaye…

React 播客专栏 Vol.5|从“显示”到“消失”:打造你的第一个交互式 Alert 组件!

&#x1f44b; 欢迎回到《前端达人 播客书单》第 5 期&#xff08;正文内容为学习笔记摘要&#xff0c;音频内容是详细的解读&#xff0c;方便你理解&#xff09;&#xff0c;请点击下方收听 &#x1f4cc; 今天我们不再停留在看代码&#xff0c;而是动手实现一个真正的 React…

解密火星文:LeetCode 269 题详解与 Swift 实现

文章目录 摘要描述题解答案题解代码分析构建图&#xff08;Graph&#xff09;拓扑排序&#xff08;Topological Sort&#xff09; 示例测试及结果时间复杂度空间复杂度实际场景类比总结 摘要 这篇文章我们来聊聊 LeetCode 269 题&#xff1a;火星词典&#xff08;Alien Dictio…

动态规划-62.不同路径-力扣(LeetCode)

一、题目解析 机器人只能向下或向左&#xff0c;要从Start位置到Finish位置。 二、算法原理 1.状态表示 我们要求到Finish位置一共有多少种方法&#xff0c;记Finish为[i,j]&#xff0c;此时dp[i,j]表示&#xff1a;到[i,j]位置时&#xff0c;一共有多少种方法&#xff0c;满…

5月9号.

v-for: v-bind: v-if&v-show: v-model: v-on: Ajax: Axios: async&await: Vue生命周期: Maven: Maven坐标:

从 Git 到 GitHub - 使用 Git 进行版本控制 - Git 常用命令

希望本贴能从零开始带您一起学习如何使用 Git 进行版本控制&#xff0c;并结合远程仓库 GitHub。这会是一个循序渐进的指南&#xff0c;我们开始吧&#xff01; 学习 Git 和 GitHub 的路线图&#xff1a; 理解核心概念&#xff1a;什么是版本控制&#xff1f;Git 是什么&…

双指针算法详解(含力扣和蓝桥杯例题)

目录 一、双指针算法核心概念 二、常用的双指针类型&#xff1a; 2.1 对撞指针 例题1&#xff1a;盛最多水的容器 例题2&#xff1a;神奇的数组 2.2 快慢指针&#xff1a; 例题1&#xff1a;移动零 例题2&#xff1a;美丽的区间&#xff08;蓝桥OJ1372&#xff09; 3.总…

【网络编程】二、UDP网络套接字编程详解

文章目录 前言Ⅰ. UDP服务端一、服务器创建流程二、创建套接字 -- socketsocket 属于什么类型的接口❓❓❓socket 是被谁调用的❓❓❓socket 底层做了什么❓❓❓和其函数返回值有没有什么关系❓❓❓ 三、绑定对应端口号、IP地址到套接字 -- bind四、数据的发送和接收 -- sendto…

【应急响应】- 日志流量如何分析?

【应急响应】- 日志流量如何下手&#xff1f;https://mp.weixin.qq.com/s/dKl8ZLZ0wjuqUezKo4eUSQ

djinn: 3靶场渗透

djinn: 3 来自 <https://www.vulnhub.com/entry/djinn-3,492/> 1&#xff0c;将两台虚拟机网络连接都改为NAT模式 2&#xff0c;攻击机上做namp局域网扫描发现靶机 nmap -sn 192.168.23.0/24 那么攻击机IP为192.168.23.182&#xff0c;靶场IP192.168.23.243 3&#xff0…

VS Code配置指南:打造高效的QMK开发环境

VS Code配置指南&#xff1a;打造高效的QMK开发环境 前言 你是否曾为QMK固件开发环境的搭建而头疼不已&#xff1f;本文将手把手教你使用Visual Studio Code&#xff08;简称VS Code&#xff09;这款强大的代码编辑器来构建一个完美的QMK开发环境&#xff0c;让你的键盘固件开…

服务器多客户端连接核心要点(1)

刷题 服务器多客户端连接核心要点 多进程服务器 实现原理 fork子进程&#xff1a;每次accept新客户端后&#xff0c;调用fork创建子进程。独立处理&#xff1a;子进程负责与客户端通信&#xff08;如read/write&#xff09;&#xff0c;父进程继续监听新连接。 特点 隔离性…

Stagehand:AI驱动的下一代浏览器自动化框架

Stagehand 是一个结合了 AI 代理、AI 工具和 Playwright 的浏览器自动化框架。核心理念是&#xff1a;让自动化任务既可控又智能。与传统工具不同&#xff0c;Stagehand 不仅仅依赖 AI 代理的“黑箱操作”&#xff0c;而是通过与 Playwright 的深度结合&#xff0c;赋予开发者对…

爱普生FA-238在车身控制模块中的应用

在汽车智能化、电子化飞速发展的当下&#xff0c;车身控制模块&#xff08;BCM&#xff09;作为车辆的 “智能管家”&#xff0c;肩负着协调和控制众多车身功能的重任&#xff0c;从车门的解锁与锁定、车窗的升降&#xff0c;到车灯的智能点亮与熄灭&#xff0c;再到雨刮器的自…

【A2A】管中窥豹,google源码python-demo介绍

前言 A2A&#xff08;Agent2Agent&#xff09;是 Google 推出的一项新协议&#xff0c;旨在解决多智能体&#xff08;Multi-Agent&#xff09;系统中跨平台、跨组织协作的难题。它为 AI 代理之间的通信、协作和任务分工提供了一个统一的标准&#xff0c;可以类比为网页世界的 H…

004-nlohmann/json 快速认识-C++开源库108杰

了解 nlohmann/json 的特点&#xff1b;理解编程中 “数据战场”划分的概念&#xff1b;迅速上手多种方式构建一个JSON对象&#xff1b; 1 特点与安装 nlohmann/json 是一个在 github 长期霸占 “JSON” 热搜版第1的CJSON处理库。它的最大优点是与 C 标准库的容器数据&#xf…

Matlab实现CNN-BiLSTM时间序列预测未来

Matlab实现CNN-BiLSTM时间序列预测未来 目录 Matlab实现CNN-BiLSTM时间序列预测未来效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现CNN-BiLSTM时间序列预测未来&#xff1b; 2.运行环境Matlab2023b及以上&#xff0c;data为数据集&#xff0c;单变量时间序…

C语言| sizeof(array)占多少字节

C语言| 数组名作为函数参数 sizeof(数组名); 可以求出整个数组在内存中所占的字节数。 被调函数Array_Sum()中&#xff0c;数组array使用sizeof会得到多少&#xff1f; 实参数组a占32字节&#xff0c;实参a传给形参array&#xff0c;只占4字节。 原因如下&#xff1a; 数组名做…

【文件系统—散列结构文件】

文章目录 一、实验目的实验内容设计思路 三、实验代码实现四、总结 一、实验目的 理解linux文件系统的内部技术&#xff0c;掌握linux与文件有关的系统调用命令&#xff0c;并在此基础上建立面向随机检索的散列结构文件&#xff1b;## 二、实验内容与设计思想 实验内容 1.设…

World of Warcraft [CLASSIC][80][Deluyia] [Fragment of Val‘anyr]

瓦兰奈尔的碎片 [Fragment of Valanyr] 有时候下个班打个游戏&#xff0c;没想到套路也这么多&#xff0c;唉&#xff0c;何况现实生活&#xff0c;这一个片版本末期才1000G&#xff0c;30个&#xff0c;也就30000G&#xff0c;时光徽章等同月卡15000G&#xff0c;折合一下也就…