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.mzph.cn/news/739032.shtml

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

相关文章

力扣--动态规划/深度优先算法/回溯算法93.复原IP地址

这题主要用了动态规划和回溯算法。 动态规划数组初始化&#xff08;DP数组&#xff09;: 首先&#xff0c;创建一个二维数组dp&#xff0c;用于记录字符串中哪些部分是合法的IP地址。对字符串进行遍历&#xff0c;同时考虑每个可能的IP地址部分&#xff08;每部分由1到3个字符组…

共同聚焦空气污染治理,打造可持续发展未来|中联环保圈

在2024年全国生态环境保护工作会议上&#xff0c;我国生态环境部明确提出&#xff0c;“加强重点区域空气质量改善的监督帮扶和统筹强化监督”将成为未来生态环境保护工作的重中之重。这一战略方向的转变&#xff0c;清晰地展现了我国在空气污染治理政策上的优化和深化。 回顾2…

antd vue Tabs控件的使用

Ant Design Vue-------Tabs标签页 今天就讲讲Ant Design Vue下的控件----tabs 标签页 结合项目中的需求&#xff0c;讲一下该控件如何使用&#xff0c;需求&#xff1a; &#xff08;1&#xff09;竖排样式 &#xff08;2&#xff09;如何使用v-for绑定数据源 &#xff08;3…

当HR问你为什么申请这个职业,你该怎么回答?【文章底部添加进大学生就业交流群】

目录 强调对公司的了解&#xff1a; 突出你的技能和经验&#xff1a; 表达对行业的热情&#xff1a; 谈论个人发展&#xff1a; 对公司的价值观的契合&#xff1a; 当HR问你为什么申请这个职业时&#xff0c;你可以通过以下方式回答&#xff1a; 强调对公司的了解&#xf…

物联网导论

物联网起源 物联网&#xff1a;是一个基于互联网、传统电信网等信息承载体&#xff0c;让所有能够被独立寻址的普通物理对象实现互联互通的网络。它具有普通对象设备化、自治终端互联化和普适服务智能化三个重要特征。 按照规定的协议&#xff0c;将具有感知、通信、计算等功…

Linux入门基本指令(1)

✨前言✨ &#x1f4d8; 博客主页&#xff1a;to Keep博客主页 &#x1f646;欢迎关注&#xff0c;&#x1f44d;点赞&#xff0c;&#x1f4dd;留言评论 ⏳首发时间&#xff1a;2024年3月12日 &#x1f4e8; 博主码云地址&#xff1a;渣渣C &#x1f4d5;参考书籍&#xff1a;…

【Python】新手入门学习:什么是工作目录?

【Python】新手入门学习&#xff1a;什么是工作目录&#xff1f; &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望得…

L2-006 树的遍历(Java)

给定一棵二叉树的后序遍历和中序遍历&#xff0c;请你输出其层序遍历的序列。这里假设键值都是互不相等的正整数。 输入格式&#xff1a; 输入第一行给出一个正整数N&#xff08;≤30&#xff09;&#xff0c;是二叉树中结点的个数。第二行给出其后序遍历序列。第三行给出其中…

使用OCC进行阵列操作

OCC中实现阵列操作&#xff0c;本质上计算出物体的位置&#xff0c;然后进行位置变换&#xff0c;复制出一个新的物体的过程&#xff0c;下列用直线的阵列和环形的阵列作为例子&#xff1a; 直线阵列&#xff1a;以一个在z轴正方向的直线向y轴方向阵列2个距离为5的对象 #inclu…

hcia datacom课程学习(2):telnet与ssh

telnetssh所属层应用层应用层所应用的传输层协议tcptcp功能远程连接远程连接默认端口2322安全性很低较高功能组件分布客户端、服务器端客户端、服务器端linux环境不自带自带windows环境 win7有客户端和服务器端&#xff0c;但需要手动打开。 win10只有客户端&#xff0c;也需要…

基于STM32G031LORA开发板的时间同步项目

一、前言 本项目采用淘宝购买的STM32G031开发板&#xff0c;板上预留了oled和LORA模块的IO&#xff0c;一次性买了四套&#xff0c;资料虽然质量不高&#xff0c;但是覆盖面挺广&#xff0c;有一定的学习价值。 实验目的是需要三个从机实现时间轴的一致&#xff0c;考虑到现有环…

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

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

期货开户市场的风险在哪里?

期货市场的风险在哪里&#xff1f;强平和穿仓是什么&#xff1f; 期货市场是一个自带杠杆的市场&#xff0c;简单理解就是我们只需要用10W就能买到价值100万的商品。期货主要的风险来源于仓位风险和交割风险&#xff0c;仓位风险就是我们是采用满仓还是轻仓方式交易。比如我们…

32个关键字详解①(C语言)

目录 关键字分类&#xff1a; 第一个C程序 - 补充内容 变量的定义与声明 - 补充内容 变量的分类 - 补充内容 变量的作用域 - 补充内容 变量的生命周期 - 补充内容 auto 关键字 register 关键字 static 关键字 static 修饰变量&#xff1a; static修饰函数 sizeof 关键字 基本数…

docker私有仓库-harbor的搭建

docker 官方提供的私有仓库 registry&#xff0c;用起来虽然简单 &#xff0c;但在管理的功能上存在不足。 Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器&#xff0c;harbor使用的是官方的docker registry(v2命名是distribution)服务去完成。harbor在docker di…

全排列+力扣

题目 题目链接 . - 力扣&#xff08;LeetCode&#xff09; 题目描述 代码实现 class Solution {vector<vector<int>> ret;vector<int> path;bool used[7]; public:vector<vector<int>> permute(vector<int>& nums) {_permute(nums…

C++_包装器

目录 1、包装器的用法 2、包装器的类型 3、包装器的作用 4、包装成员函数 5、bind&#xff08;绑定&#xff09; 5.1 bind的用法 5.2 bind减少参数个数 结语 前言&#xff1a; C11的包装器&#xff0c;总称为function包装器&#xff0c;而包装器又称适配器…

allegro PCB设计心得笔记(二) -- ERROR(SPMHUT-144): Illegal arc specification

使用Allegro PCB Editor设计PCB&#xff0c;其中使用了中文丝印&#xff0c;设计完成后&#xff0c;进行Tools -> Database Check&#xff0c;提示如下错误&#xff1a; 对PCB文件进行反复检查&#xff0c;也没有找到具体问题&#xff0c;但是删除中文丝印封装后&#xff0c…

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

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

宝塔面板配置网站通过ip地址+端口,页面刷新无效。nginx反向代理后端端口。添加/xxx文件然后再访问项目。

宝塔面板配置网站通过ip地址端口&#xff0c;页面刷新无效。nginx反向代理后端端口。添加/xxx文件然后再访问项目。 一般来说vue项目build打包以后 如果 直接发布服务器 并且不使用后端接口。 是不需要配置nginx的。 假设我vue部署好了。我的ip是106.66.66.66. 但是我页面里面…