STM32CubeMX学习笔记19——SD卡(SDIO接口)

1、简介

1.1 SD卡简介

很多单片机系统都需要大容量存储设备,以存储数据(常用的有U盘、FLASH芯片、SD卡等),比较而言SD卡是单片机大容量外部存储的首选,只需要少数几个IO口即可外扩一个容量从几十M到几十G的,且有多种体积尺寸可选(标准SD卡、TF卡等)的外部存储器

SD卡(Secure Digital Memory Card)即:安全数码卡,它是在MMC的基础上发展而来,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(PDA)和多媒体播放器等。SD卡由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制。 SD卡按容量分类,可以分为3类:SD卡、SDHC卡、SDXC卡,如下表所示:

SD卡和SDHC卡协议基本兼容,但是SDXC卡的区别比较大,这里仅介绍SD/SDHC卡(简称SD卡),SD卡由9个引脚与外部通讯,支持SPI和SDIO两种操作模式,不同模式下SD卡引脚功能描叙如下图表示:

1.2 SD卡的物理结构及内部框图

SD卡的物理结构一般包括以下5个部分:

- 存储单元:是存储数据部件;
- 存储单元接口:存储单元通过存储单元接口与卡控制单元进行数据传输;
- 电源检测单元:保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接口复位;
- 卡及接口控制单元:控制SD卡的运行状态,它包括有8个寄存器;
- 接口驱动器:控制SD卡引脚的输入输出

SDIO由SDIO适配器和APB2接口两部分组成:

- SDIO适配器:提供特定于MMC/SD/SD I/O卡的所有功能,如时钟生成单元、命令和数据传输
- APB2接口:访问SDIO适配器寄存器,并且生成中断和DMA请求信号

下图是SDIO功能框图及SDIO适配器框图:

1.3 SD卡命令

SD卡命令由主机发出,命令格式固定为48位,通过CMD线连续传输,数据线不参与。SD命令结构如下图示:由6个字节组成,字节1的最高2位固定为01、低6位为命令号(比如CMD16);字节2 ~ 5为命令参数(有的命令没有参数);字节6的高7位为CRC、最低位恒定为1

SD命令组成的详细说明如下:
起始位和终止位:命令的主体包含在起始位与终止位之间,它们都只包含一个数据位,起始位为 0,终止位为 1。
传输标志:用于区分传输方向,该位为 1 时表示命令,方向为主机传输到 SD 卡,该位为 0时表示响应,方向为 SD卡传输到主机。 - 命令主体内容包括命令、地址信息/参数和 CRC 校验三个部分
命令号:它固定占用 6bit,所以总共有 64个命令(代号:CMD0~CMD63),每个命令都有特定的用途,部分命令不适用于 SD 卡操作,只是专门用于 MMC卡或者SD I/O卡。
地址/参数:每个命令有 32bit地址信息/参数用于命令附加内容,例如,广播命令没有地址信息,这 32bit用于指定参数,而寻址命令这 32bit用于指定目标 SD卡的地址。
CRC7 校验:长度为 7bit的校验位用于验证命令传输内容正确性,如果发生外部干扰导致传输数据个别位状态改变将导致校准失败,也意味着命令传输失败,SD卡不执行命令。

1.4 SD卡响应

SD卡命令的响应由SD卡向主机发出,部分命令要求SD卡作出响应,这些响应多用于反馈SD卡的状态。基本特性如下:

- SDIO总共有7个响应类型(代号:R1~R7),其中SD卡没有R4、R5类型响应。特定的命令对应有特定的响应类型,比如当主机发送CMD3命令时,可以得到响应R6。
- 与命令一样,SD卡的响应也是通过CMD线连续传输的。
- 根据响应内容大小可以分为短响应和长响应。短响应是48bit长度,只有R2类型是长响应,其长度为136bit。

SD的读写操作是以块为操作对象。先发送命令开始传输,然后传输数据块,传输完数据块紧接着传输CRC检验值。最好发送停止命令停止数据传输

1.5 SD卡的操作模式及切换

SD卡有多个版本,STM32控制器目前最高支持《Physical Layer Simplified Specification V2.0》定义的SD卡,STM32控制器对SD卡进行数据读写之前需要识别卡的种类:V1.0标准卡、V2.0标准卡、V2.0高容量卡或者不被识别卡。

SD卡系统定义了两种操作模式:卡识别模式数据传输模式

在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备;同时,SD卡也处于卡识别模式,直到被主机识别到,即当SD卡接收到SEND_RCA(CMD3)命令后,SD卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。

2. 硬件设计

LED2指示灯用来提示系统运行状态,S1写入数据,S2读取数据,串口用来打印SD卡的容量、类型等信息

  • LED2指示灯
  • USART1
  • S1,S2按键
  • TF卡

 3、 STM32CubeMX设置

RCC设置外接HSE,时钟设置为72M

  • PE5设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平
  • PE3/PE4设置为GPIO输入模式、上拉模式
  • USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位
  • 激活SDIO,选择4线SD模式,分频因子设为4,使能流控,其余默认设置

在 Parameter Settings 进行具体参数配置。

Clock transition on which the bit capture is made: Rising transition。主时钟 SDIOCLK 产生 CLK 引脚时钟有效沿选择,可选上升沿或下降沿,它设定 SDIO 时钟控制寄存器(SDIO_CLKCR)的 NEGEDGE 位的值,一般选择设置为上升沿。

SDIO Clock divider bypass: Disable。时钟分频旁路使用,可选使能或禁用,它设定 SDIO_CLKCR 寄存器的 BYPASS 位。如果使能旁路,SDIOCLK 直接驱动 CLK 线输出时钟;如果禁用,使用 SDIO_CLKCR 寄存器的 CLKDIV 位值分频 SDIOCLK,然后输出到 CLK 线。一般选择禁用时钟分频旁路。

SDIO Clock output enable when the bus is idle: Disable the power save for the clock。节能模式选择,可选使能或禁用,它设定 SDIO_CLKCR 寄存器的 PWRSAV 位的值。如果使能节能模式,CLK 线只有在总线激活时才有时钟输出;如果禁用节能模式,始终使能 CLK 线输出时钟。

SDIO hardware flow control: The hardware control flow is enabled。硬件流控制选择,可选使能或禁用,它设定 SDIO_CLKCR 寄存器的 HWFC_EN 位的值。硬件流控制功能可以避免 FIFO 发送上溢和下溢错误。

SDIOCLK clock divide factor:4。时钟分频系数,它设定 SDIO_CLKCR 寄存器的 CLKDIV 位的值,设置 SDIOCLK 与 CLK 线输出时钟分频系数:CLK 线时钟频率=SDIOCLK/([CLKDIV+2])。

SDIO_CK 引脚的时钟信号在卡识别模式时要求不超过 400KHz,而在识别后的数据传输模式时则希望有更高的速度(最大不超过 25MHz),所以会针对这两种模式配置 SDIOCLK 的时钟。

这里参数描述建议将SDIOCLK clock divede factor 参数使用默认值为0,SDIOCLK为72MHz,可以得到最大频率36MHz,但请注意,有些型号的SD卡可能不支持36MHz这么高的频率,所以还是要以实际情况而定。

  •  添加 SDIO 对应 DMA2 的通道4。DMA模式选择循环模式,方向选为内存到外设,优先级设置为low

SDIO 外设支持生成 DMA 请求,使用 DMA 传输可以提高数据传输效率,因此在 SDIO 的控制代码中,可以把它设置为 DMA 传输模式或轮询模式,ST 标准库提供 SDIO 示例中针对这两个模式做了区分处理。应用中一般都使用DMA 传输模式。

Priority:
当发生多个 DMA 通道请求时,就意味着有先后响应处理的顺序问题,这个就由仲裁器也管理。仲裁器管理 DMA 通道请求分为两个阶段。第一阶段属于软件阶段,可以在 DMA_CCRx 寄存器中设置,有 4 个等级:非常高、高、中和低四个优先级。第二阶段属于硬件阶段,如果两个或以上的 DMA 通道请求设置的优先级一样,则他们优先级取决于通 道编号,编号越低优先权越高,比如通道 0 高于通道 1。在大容量产品和互联型产品中,DMA1 控制器拥有高于 DMA2 控制器的优先级。
Mode:
Normal 表示单次传输,传输一次后终止传输。
Circular 表示循环传输,传输完成后又重新开始继续传输,不断循环永不停止。
Increment Address:
Peripheral 表示外设地址自增。
Memory 表示内存地址自增。
Data Width:
Byte 一个字节。
Half Word 半个字,等于两字节。
Word 一个字,等于四字节。

  •  设置SDIO和DMA的中断,原则是全局中断优先级高于DMA中断

  •  最好激活CRC功能,以避免后续读写SD卡报CRC校验错误

  • 输入工程名,选择工程路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码 

 4、程序编程

在sdio.c文件下可以看到sdio初始化函数,在stm32f1xx_hal_sd.c文件中可以查看SDIO的相关操作函数,主要用到的函数有;

//可读取SD卡的基础信息,如内存
HAL_SD_CardStateTypeDef HAL_SD_GetCardState(SD_HandleTypeDef *hsd)

//获取SD卡的ID
HAL_StatusTypeDef HAL_SD_GetCardCID(SD_HandleTypeDef *hsd, HAL_SD_CardCIDTypeDef *pCID)

//擦除SD卡
/**
  * @brief  擦除给定SD卡的指定存储区域。
  * @param  hsd: 指向SD句柄的指针
  * @param  BlockStartAdd:起始块地址
  * @param  BlockEndAdd:结束块地址
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SD_Erase(SD_HandleTypeDef *hsd, uint32_t BlockStartAdd, uint32_t BlockEndAdd)//擦除SD卡内容块
//例:
HAL_SD_Erase(&hsd,0,1);


//SD 卡写入数据
/**
  * @brief 将块写入卡中的指定地址
  * @param  hsd: 指向SD句柄的指针
  * @param  pData: 指向将包含要传输的数据的缓冲区的指针
  * @param  BlockAdd: 写入数据的块地址
  * @param  NumberOfBlocks: 要写入的SD块数
  * @param  Timeout: 指定超时值
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)
//例:
HAL_SD_WriteBlocks(&hsd,Buffer_Tx,0,1,0xff);

//SD 卡读取数据
/**
  * @brief  从卡中的指定地址读取块. 
  * @param  hsd: 指向SD句柄的指针
  * @param  pData: 指向将包含接收到的数据的缓冲区的指针
  * @param  BlockAdd: 读取数据的块地址
  * @param  NumberOfBlocks: 要读取的SD块数
  * @param  Timeout: 指定超时值
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SD_ReadBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout);
//例:
HAL_SD_ReadBlocks(&hsd,Buffer_Rx,0,1,0xff);

在 main.c 头部添加全局变量 

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define BLOCK_START_ADDR         0     /* Block start address      */
#define NUM_OF_BLOCKS            1   /* Total number of blocks   */
#define BUFFER_WORDS_SIZE        ((BLOCKSIZE * NUM_OF_BLOCKS) >> 2) /* Total data size in bytes */
/* USER CODE END PD */

/* USER CODE BEGIN PV */
uint8_t Buffer_Tx[512],Buffer_Rx[512] = {0};
uint32_t i;
/* USER CODE END PV */

main函数中添加测试程序

int main(void)
{
//***省略**//
	printf("Micro SD Card Test...\r\n");
/* 检测SD卡是否正常(处于数据传输模式的传输状态) */
if(HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_TRANSFER) //获取当前sd卡数据状态
{      
    printf("Initialize SD card successfully!\r\n");
    // 打印SD卡基本信息
    printf(" SD card information! \r\n");
    printf(" CardCapacity  : %llu \r\n", (unsigned long long)hsd.SdCard.BlockSize * hsd.SdCard.BlockNbr);// 显示容量
    printf(" CardBlockSize : %d \r\n", hsd.SdCard.BlockSize);   // 块大小
    printf(" LogBlockNbr   : %d \r\n", hsd.SdCard.LogBlockNbr);	// 逻辑块数量
		printf(" LogBlockSize  : %d \r\n", hsd.SdCard.LogBlockSize);// 逻辑块大小
    printf(" RCA           : %d \r\n", hsd.SdCard.RelCardAdd);  // 卡相对地址
    printf(" CardType      : %d \r\n", hsd.SdCard.CardType);    // 卡类型
    // 读取并打印SD卡的CID信息
    HAL_SD_CardCIDTypeDef sdcard_cid;
    HAL_SD_GetCardCID(&hsd,&sdcard_cid);//读取SD卡的信息CID寄存器。
    printf(" ManufacturerID: %d \r\n",sdcard_cid.ManufacturerID);
}
else
{
    printf("SD card init fail!\r\n" );
}


/* 擦除SD卡块 */
printf("------------------- Block Erase -------------------------------\r\n");
if(HAL_SD_Erase(&hsd, BLOCK_START_ADDR, NUM_OF_BLOCKS) == HAL_OK)
{
    /* Wait until SD cards are ready to use for new operation */
    while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER)
    {
    }
    printf("\r\nErase Block Success!\r\n");
}
else
{
      printf("\r\nErase Block Failed!\r\n");					
}


/* 填充缓冲区数据 */
memset(Buffer_Tx, 0x15, sizeof(Buffer_Tx));

/* 向SD卡块写入数据 */
printf("------------------- Write SD card block data Test ------------------\r\n");
if(HAL_SD_WriteBlocks(&hsd, Buffer_Tx, BLOCK_START_ADDR, NUM_OF_BLOCKS, 10) == HAL_OK)
{
    while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER)
    {
    }
    printf("\r\nWrite Block Success!\r\n");
    for(i = 0; i < sizeof(Buffer_Tx); i++)
    {
      printf("0x%02x:%02x ", i, Buffer_Tx[i]);
    }
		//printf("%s",Buffer_Tx);
    printf("\r\n");
}
else
{
    printf("\r\nWrite Block Failed!\r\n");
}


/* 读取操作之后的数据 */
printf("------------------- Read SD card block data after Write ------------------\r\n");

if(HAL_SD_ReadBlocks(&hsd, Buffer_Rx, BLOCK_START_ADDR, NUM_OF_BLOCKS, 10) == HAL_OK)
{
	
    while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER)
    {
    }
    printf("\r\nRead Block Success!\r\n");
    for(i = 0; i < sizeof(Buffer_Rx); i++)
    {
      printf("0x%02x:%02x ", i, Buffer_Rx[i]);
    }
		printf("\r\n");
		printf("中考%s",&Buffer_Rx[0]);
    printf("\r\n");
}
else
{
    printf("\r\nRead Block Failed!\r\n");				
}


  while (1)
  {

		HAL_GPIO_TogglePin(LED3_GPIO_Port,LED3_Pin);
		HAL_Delay(500);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }


}

 注意:

  • 要先插入SD卡,要不然SDIO初始化时会失败(仅针对原始的HAl生成程序)
  • 如果读写失败,可能SD通信速度太高,可将hsd.Init.ClockDiv值改大
  • 操作SD卡后最好先用函数HAL_SD_GetCardState()确定一下卡的状态再进行其他操作。
  • 注意先擦除后写入。

 5、下载验证

编译无误后下载到板子上,查看串口的打印信息:

6 、扩展DMA

DMA的读取和写入其实跟普通的方法相识,主要用到的是以下的读写函数

//DMA读取函数
/**
  * @brief  从卡中的指定地址读取块。数据传输由DMA模式管理。
  * @param  hsd:指针SD句柄
  * @param  pData: 指向将包含接收数据的缓冲区的指针
  * @param  BlockAdd:读取数据的块地址
  * @param  NumberOfBlocks: 要读取的块数。
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SD_ReadBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks);



//DMA写入函数
/**
  * @brief 将块写入卡中的指定地址。数据传输由DMA模式管理。
  * @param  hsd: Pointer to SD handle
  * @param  pData: 指向将包含要传输的数据的缓冲区的指针
  * @param  BlockAdd:写入数据的块地址
  * @param  NumberOfBlocks: 要写入的块数
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks);

但是要注意的是STM32F103的SDIO DMA每次由读数据变为写数据或者由写数据变为读数据时,都需要重新初始化DMA(主要是为了更改数据传输的方向)。

编写读写函数

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
HAL_StatusTypeDef SDIO_ReadBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks)
{
	HAL_StatusTypeDef Return_Status;
	HAL_SD_CardStateTypeDef SD_Card_Status;
	
	do
	{
		SD_Card_Status = HAL_SD_GetCardState(hsd);
	}while(SD_Card_Status != HAL_SD_CARD_TRANSFER );
 
	/* SDIO DMA DeInit */
	/* SDIO DeInit */
	HAL_DMA_DeInit(&hdma_sdio);
	/* SDIO DMA Init */
	/* SDIO Init */
	hdma_sdio.Instance = DMA2_Channel4;
	hdma_sdio.Init.Direction = DMA_PERIPH_TO_MEMORY;
	hdma_sdio.Init.PeriphInc = DMA_PINC_DISABLE;
	hdma_sdio.Init.MemInc = DMA_MINC_ENABLE;
	hdma_sdio.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
	hdma_sdio.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
	hdma_sdio.Init.Mode = DMA_NORMAL;
	hdma_sdio.Init.Priority = DMA_PRIORITY_LOW;
	if (HAL_DMA_Init(&hdma_sdio) != HAL_OK)
	{
		Error_Handler();
	}
 
	__HAL_LINKDMA( hsd,hdmarx,hdma_sdio);
 
	Return_Status = HAL_SD_ReadBlocks_DMA( hsd,pData, BlockAdd, NumberOfBlocks);
	
	return Return_Status;
}

HAL_StatusTypeDef SDIO_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks)
{
	HAL_StatusTypeDef Return_Status;
	HAL_SD_CardStateTypeDef SD_Card_Status;
	
	do
	{
		SD_Card_Status = HAL_SD_GetCardState(hsd);
	}while(SD_Card_Status != HAL_SD_CARD_TRANSFER );
 
	/* SDIO DMA DeInit */
	/* SDIO DeInit */
	HAL_DMA_DeInit(&hdma_sdio);
	/* SDIO DMA Init */
	/* SDIO Init */
	hdma_sdio.Instance = DMA2_Channel4;
	hdma_sdio.Init.Direction = DMA_MEMORY_TO_PERIPH;
	hdma_sdio.Init.PeriphInc = DMA_PINC_DISABLE;
	hdma_sdio.Init.MemInc = DMA_MINC_ENABLE;
	hdma_sdio.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
	hdma_sdio.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
	hdma_sdio.Init.Mode = DMA_NORMAL;
	hdma_sdio.Init.Priority = DMA_PRIORITY_LOW;
	if (HAL_DMA_Init(&hdma_sdio) != HAL_OK)
	{
		Error_Handler();
	}
 
	__HAL_LINKDMA(hsd,hdmatx,hdma_sdio);	
 
	Return_Status = HAL_SD_WriteBlocks_DMA(hsd,pData, BlockAdd, NumberOfBlocks);
	
	return Return_Status;
}
/* USER CODE END 0 */

main函数中直接调用读写函数即可

int main()
{
//****省略***//
  /* 向SD卡块写入数据 */
  printf("------------------- Write SD card block data Test ------------------\r\n");
  SDIO_WriteBlocks_DMA(&hsd,Buffer_Tx, BLOCK_START_ADDR, NUM_OF_BLOCKS);
  printf("write status :%d\r\n",Return_Status);
  /* 读取SD卡块数据 */	
  Return_Status=SDIO_ReadBlocks_DMA(&hsd,Buffer_Rx, BLOCK_START_ADDR, NUM_OF_BLOCKS);
  printf("read status :%d\r\n",Return_Status);
 
  for(i = 0; i < sizeof(Buffer_Rx); i++)
  {
    printf("0x%02x:%02x ", i, Buffer_Rx[i]);
  }

while(1)
{
}

}

编译无误后下载验证:

 7、参考文献

STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)_cubemx sdio-CSDN博客

 STM32CubeMX系列 | SD卡 - 知乎 (zhihu.com)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端实现 查询包含分页 以及封装table表格 上手即用!

表格组件是 element plus 中的table 又经过了一层封装 封装的table代码在最底下 <div class"box2"><el-radio-group v-model"radio" style"margin-bottom: 16px"><el-radio-button label"1">类型1</el-radio…

vue3 uniapp 项目初始化集成配置【开箱即用】

https://gitee.com/charrie/vue3-uniapp-init 技术说明 采用vue3viteuniapp技术栈&#xff0c;setup语法糖编码方式引入unocss量子化样式引擎&#xff0c;动态css不用自己写样式&#xff0c;引用class即可&#xff0c;降低代码体积全局请求入口已封装&#xff0c;使用时自己封…

易基因: WGBS+ChIP-seq揭示食管癌的细胞类型和癌症特异性表观遗传调控|Genome Biol

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 食管癌是一种常见的恶性肿瘤&#xff0c;有两种亚型&#xff1a;鳞状细胞癌&#xff08;squamous cell carcinoma&#xff0c;ESCC&#xff09;和腺癌&#xff08;adenocarcinoma&#x…

【C++】string类(介绍、常用接口)

&#x1f308;个人主页&#xff1a;秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343&#x1f525; 系列专栏&#xff1a;http://t.csdnimg.cn/eCa5z 目录 string类的常用接口说明 string类对象的常见构造 ​编辑 string字符串的遍历&#xff08;迭代器&#xf…

力扣刷题Days12--104二叉树最大深度(js)

目录 1&#xff0c;题目 2&#xff0c;代码 2.1深度优先遍历--递归思想 2.2-0广度优先搜索--错误版 2.2广度优先搜索 3&#xff0c;学习与总结 3.1二叉树的复习 3.2array常用函数复习 1&#xff0c;题目 给定一个二叉树 root &#xff0c;返回其最大深度。二叉树的 最大…

解决Iterm2升级后遇到“Stashed changes“的问题

&#xff1c;&#xff1c;&#xff1c;&#xff1c;&#xff1c;&#xff1c;&#xff1c; Updated upstream ...... &#xff1e;&#xff1e;&#xff1e;&#xff1e;&#xff1e;&#xff1e;&#xff1e; Stashed changes冲突标记符的代码如题&#xff0c;最近有升级Item2…

鸿蒙原生应用元服务开发-WebGL网页图形库开发接口说明

一、场景介绍 WebGL主要帮助开发者在前端开发中完成图形图像的相关处理&#xff0c;比如绘制彩色图形等。目前该功能仅支持使用兼容JS的类Web开发范式开发。 二、接口说明 表1 WebGL主要接口列表 本文参考引用HarmonyOS官方开发文档&#xff0c;基于API9。

RStudio更换R语言版本

今天下载R语言用于读取.xlsx文件的readxl包时&#xff0c;RStudio提示该包是使用R-4.3.3版本构建&#xff0c;而我现在使用的是R-4.3.2版本&#xff0c;所以需要升级一下R语言版本&#xff0c;这里先下载最新版本的R语言&#xff0c; 下载地址&#xff1a;The Comprehensive R…

Early if-conversion - 优化阅读笔记

Early if-conversion 用于对于没有很多可预测指令的乱序CPU。目标是消除可能误预测的条件分支。 来自分支两侧的指令都会被推测性地执行&#xff0c;并使用 cmov 指令选择结果。 // SSAIfConv 类在确定可能的情况下&#xff0c;对SSA形式的机器码执行if-conversion。该类不包…

基于JAVA实现自由教学平台设计【附项目源码】分享

基于JAVA实现自由教学平台系统演示 视频&#xff1a;ssm自由教学平台演示录像-CSDN直播基于JAVA实现自由教学平台设计https://live.csdn.net/v/369811 项目源码地址&#xff1a;https://download.csdn.net/download/weixin_43894652/88842681 一、目标 构建一个基于JAVA的网…

第八十天 WAF攻防-漏洞利用HPP污染分块传输垃圾数据

第80天 WAF攻防-漏洞利用&HPP污染&分块传输&垃圾数据 参考点&#xff1a; #将MySQL注入函数分为几类 拆分字符串函数&#xff1a;mid、1eft、1pad等 编码函数&#xff1a;ord、hex、a3ci等 运算函数&#xff1a;*/&^&#xff01;1 ike rlike reg等 空格替换部…

Python-Pong-Game

我还加了音效&#xff0c;类似于小时候游戏机上的弹球游戏 import os import turtle import pygame#初始化pygame pygame.init()#加载声音文件 bounce_sound pygame.mixer.Sound("bounce.mp3")wn turtle.Screen() wn.title("Pong by ") wn.bgcolor(&qu…

【猫头虎科技解码】探秘Drools语法:规则引擎在实战中的应用️

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

关于yolov8文档的记录,补充一些整理的知识点

2023年由Ultralytics 提供了YOLOv8开源项目。YOLOv8 支持全方位的视觉 AI 任务&#xff0c;包括检测、分割、姿态估计、跟踪和分类。这种多功能性使用户能够在各种应用和领域中利用YOLOv8 的功能。安装yolov8开源项目 pip install githttps://github.com/ultralytics/ultralyti…

SPEL表达式及注入漏洞

SPEL,全称为Spring表达式语言&#xff0c;是一个由 Spring 框架提供的表达式语言。它是一种基于字符串的表达式语言&#xff0c;可以在运行时对对象进行查询和操作。 SpEL 支持在XML和注解配置中使用&#xff0c;它可以在Spring框架的各种组件中使用&#xff0c;如Spring MVC …

7.无重复字符的最长字串

给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长 子串 的长度。 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc"&#xff0c;所以其长度为 3。示例 2: 输入: s "bbbbb" 输出: 1 解释: 因为…

Flink技术简介与入门实践

架构简介 Flink 是一个分布式流处理和批处理计算框架&#xff0c;具有高性能、容错性和灵活性。下面是 Flink 的架构概述&#xff1a; JobManager&#xff1a;JobManager 是 Flink 集群的主节点&#xff0c;负责接收和处理用户提交的作业。JobManager 的主要职责包括&#xff1…

【wps】wps与office办公函数储备使用(结合了使用案例 持续更新)

【wps】wps与office办公函数储备使用(结合了使用案例 持续更新) 1、TODAY函数 返回当前电脑系统显示的日期 TODAY函数&#xff1a;表示返回当前电脑系统显示的日期。 公式用法&#xff1a;TODAY() 2、NOW函数 返回当前电脑系统显示的日期和时间 NOW函数&#xff1a;表示返…

案例分析篇11:一篇文章搞定UML设计考点(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章推荐: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12593400.html 【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例分析篇-…

【linux线程(一)】什么是线程?怎样操作线程?

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux线程 1. 前言2. 什么是线…