当前位置: 首页 > news >正文

STM32之SDIO通讯接口和SD卡(九)

STM32F407 系列文章 - SDIO-To-SD Card(九)


目录

前言

一、SDIO接口

二、SD卡

三、实现程序

1.SD卡结构体参数说明

2.头文件定义

3.函数sd_init()

4.函数HAL_SD_MspInit()

5.函数get_sd_card_info()

6.函数get_sd_card_state()

7.函数sd_read_disk()

8.函数sd_write_disk()

9.函数show_sdcard_info()

总结


前言

当单片机需要保存大量数据时,靠它自身的容量往往是不能满足需求的,一般采取的措施是外挂一个存储器。目前市面上存储器的种类繁多,根据它们各自特点,选择一款最适合单片机存储器,莫过于SD卡了,它不仅价格便宜、体积小、速度快、而且容量可以做到很大,并支持SPI/SDIO驱动,能满足单片机的要求。只需要少数几个IO口即可外扩一个高达32GB或以上的外部存储器,容量从几十M到几十G选择范围很大,更换也很方便,编程也简单,是单片机大容量外部存储器的首选。一般MCU都会自带SDIO接口,因此要实现此功能,需准备一块带SD卡接口的开发板,在本章中,将向大家介绍,如何通过SDIO通讯接口实现对Micor SD卡数据的读取。


一、SDIO接口

SDIO(Secure Digital Input and Output,安全数字输入输出),最早由SD协会(SD Association, SDA)于2001年发布,是在SD标准上定义的一种外设接口,不仅支持传统的存储功能,还允许设备通过SD卡接口进行输入输出操作,常见的多媒体卡(MMC卡)、SD存储卡、SDI/O卡和CEATA设备都有SDIO接口。关于SDIO更为详细的介绍可以参考下知乎上面的一篇文章,链接如下SDIO协议从入门到精通 - 知乎 (zhihu.com)。

SDIO遵循SD物理标准,因此支持SD卡的设备通常也能够兼容SDIO设备。常见的SDIO卡尺寸和普通的SD卡相同,包括标准尺寸(SD)、迷你尺寸(miniSD)和微型尺寸(microSD)‌,本文将选用microSD作为设计目标,另采用的407芯片使用自带的SDIO接口驱动,使用4位数据总线模式,最高通信速度可达 48Mhz(分频器旁路时),最高每秒可传输数据24M字节,对于一般应用足够了。407芯片的SDIO控制器包含2部分,SDIO适配器模块和APB2总线接口,其功能框图如下所示:

二、SD卡

SD卡(Secure Digital Memory Card,SD存储卡,简称SD卡),SD卡主要有SD、Mini SD和microSD三种类型,Mini SD已经被microSD取代,使用得不多。本文选用microSD作为设计目标,microSD原名Trans-flash Card(TF卡),2004年正式更名为Micro SD Card,由SanDisk(闪迪)公司发明,主要应用于嵌入式设备,具体介绍参考Micro SD卡_百度百科 (baidu.com)。

市场上卖microSD卡样式如下(示例),及对应SD通讯模式下相应引脚的含义:

与microSD卡匹配的卡座(自锁式)样式如下:

SD卡的驱动方式之一是用SDIO接口通讯,F407芯片自带SDIO接口,MCU单片机与SD卡连接示意图如下所示:

三、实现程序

SD卡的驱动

1.SD卡结构体参数说明

该结构体参数定义来自于STM32的HAL库,主要是完成对SD卡详细信息以参数化定义,具体说明及代码如下:

/** * @brief SD 操作句柄结构体定义 */ 
typedef struct { SD_TypeDef *Instance;     /* SD 相关寄存器基地址 */ SD_InitTypeDef Init;      /* SDIO 初始化变量 */ HAL_LockTypeDef Lock;     /* 互斥锁,用于解决外设访问冲突 */ uint8_t *pTxBuffPtr;      /* SD 发送数据指针 */ uint32_t TxXferSize;      /* SD 发送缓存按字节数的大小 */ uint8_t *pRxBuffPtr;      /* SD 接收数据指针 */ uint32_t RxXferSize;      /* SD 接收缓存按字节数的大小 */ __IO uint32_t Context;    /* HAL 库对 SD 卡的操作阶段 */ __IO HAL_SD_StateTypeDef State; /* SD 卡操作状态 */ __IO uint32_t ErrorCode;        /* SD 卡错误代码 */ DMA_HandleTypeDef *hdmatx;      /* SD DMA 数据发送指针 */ DMA_HandleTypeDef *hdmarx;      /* SD DMA 数据接收指针 */ HAL_SD_CardInfoTypeDef SdCard;  /* SD 卡信息的 */ uint32_t CSD[4];                /* 保存 SD 卡 CSD 寄存器信息 */ uint32_t CID[4];                /* 保存 SD 卡 CID 寄存器信息 */ 
} SD_HandleTypeDef;/** * @brief SD 卡信息结构定义 */ 
typedef struct { uint32_t CardType;     /* 存储卡类型标记:标准卡、高速卡 */ uint32_t CardVersion;  /* 存储卡版本 */ uint32_t Class;        /* 卡类型 */ uint32_t RelCardAdd;   /* 卡相对地址 */ uint32_t BlockNbr;     /* 卡存储块数 */ uint32_t BlockSize;    /* SD 卡每个存储块大小 */ uint32_t LogBlockNbr;  /* 以块表示的卡逻辑容量 */ uint32_t LogBlockSize; /* 以字节为单位的逻辑块大小 */ 
} HAL_SD_CardInfoTypeDef;

2.头文件定义

这里完成对IO引脚的定义和操作方式,并定义了SD卡的一些操作函数定义,代码如下(示例):

/* SDIO的信号线: SD_D0 ~ SD_D3/SD_CLK/SD_CMD 引脚 定义 * 如果你使用了其他引脚做SDIO的信号线,修改这里写定义即可适配.*/
#define SD_D0_GPIO_PORT                GPIOC
#define SD_D0_GPIO_PIN                 GPIO_PIN_8
/* 所在IO口时钟使能 */
#define SD_D0_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0) #define SD_D1_GPIO_PORT                GPIOC
#define SD_D1_GPIO_PIN                 GPIO_PIN_9
/* 所在IO口时钟使能 */
#define SD_D1_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0)    #define SD_D2_GPIO_PORT                GPIOC
#define SD_D2_GPIO_PIN                 GPIO_PIN_10
/* 所在IO口时钟使能 */
#define SD_D2_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0)    #define SD_D3_GPIO_PORT                GPIOC
#define SD_D3_GPIO_PIN                 GPIO_PIN_11
/* 所在IO口时钟使能 */
#define SD_D3_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0)   #define SD_CLK_GPIO_PORT               GPIOC
#define SD_CLK_GPIO_PIN                GPIO_PIN_12
/* 所在IO口时钟使能 */
#define SD_CLK_GPIO_CLK_ENABLE()       do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0)   #define SD_CMD_GPIO_PORT               GPIOD
#define SD_CMD_GPIO_PIN                GPIO_PIN_2
/* 所在IO口时钟使能 */
#define SD_CMD_GPIO_CLK_ENABLE()       do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0)    #define SD_TIMEOUT             ((uint32_t)100000000)    /* 超时时间 */
#define SD_TRANSFER_OK         ((uint8_t)0x00)
#define SD_TRANSFER_BUSY       ((uint8_t)0x01)/* 根据 SD_HandleTypeDef 定义的宏,用于快速计算容量 */
#define SD_TOTAL_SIZE_BYTE(__Handle__)  (((uint64_t)((__Handle__)->SdCard.LogBlockNbr) * ((__Handle__)->SdCard.LogBlockSize)) >> 0)
#define SD_TOTAL_SIZE_KB(__Handle__)    (((uint64_t)((__Handle__)->SdCard.LogBlockNbr) * ((__Handle__)->SdCard.LogBlockSize)) >> 10)
#define SD_TOTAL_SIZE_MB(__Handle__)    (((uint64_t)((__Handle__)->SdCard.LogBlockNbr) * ((__Handle__)->SdCard.LogBlockSize)) >> 20)
#define SD_TOTAL_SIZE_GB(__Handle__)    (((uint64_t)((__Handle__)->SdCard.LogBlockNbr) * ((__Handle__)->SdCard.LogBlockSize)) >> 30)/*  *  SD传输时钟分频,由于HAL库运行效率低,很容易产生上溢(读SD卡时)/下溢错误(写SD卡时)*  使用4bit模式时,需降低SDIO时钟频率,将该宏改为 1,SDIO时钟频率:48/( SDIO_TRANSF_CLK_DIV + 2 ) = 16M * 4bit = 64Mbps *  使用1bit模式时,该宏SDIO_TRANSF_CLK_DIV改为 0,SDIO时钟频率:48/( SDIO_TRANSF_CLK_DIV + 2 ) = 24M * 1bit = 24Mbps */
#define  SDIO_TRANSF_CLK_DIV        1   extern SD_HandleTypeDef        g_sdcard_handler;        /* SD卡句柄 */
extern HAL_SD_CardInfoTypeDef  g_sd_card_info_handle;   /* SD卡信息结构体 *//* 函数声明 */
uint8_t sd_init(void);                                              /* 初始化SD卡 */
uint8_t get_sd_card_info(HAL_SD_CardInfoTypeDef *cardinfo);         /* 获取卡信息函数 */
uint8_t get_sd_card_state(void);                                    /* 获取卡的状态 */
uint8_t sd_read_disk(uint8_t *pbuf, uint32_t saddr, uint32_t cnt);  /* 读SD卡 */
uint8_t sd_write_disk(uint8_t *pbuf, uint32_t saddr, uint32_t cnt); /* 写SD卡 */
void show_sdcard_info(void);  

3.函数sd_init()

SD卡初始化函数,被main函数调用,主要是完成对SDIO结构体的控制句柄填充,即将该结构体类型指针变量初始化,然后使用HAL库的HAL_SD_Init初始化函数即可,在此过程中HAL_SD_Init会调用函数 HAL_SD_MspInit回调函数,根据外设的情况,我们可以设置数据总线宽度为4位,代码如下(示例):

SD_HandleTypeDef g_sdcard_handler;            /* SD卡句柄 */
HAL_SD_CardInfoTypeDef g_sd_card_info_handle; /* SD卡信息结构体 *//*** @brief       初始化SD卡* @param       无* @retval      返回值:0 初始化正确;其他值,初始化错误*/
uint8_t sd_init(void)
{uint8_t SD_Error;/* 初始化时的时钟不能大于400KHZ */g_sdcard_handler.Instance = SDIO;g_sdcard_handler.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;  /* 上升沿 *//* 不使用bypass模式,直接用HCLK进行分频得到SDIO_CK */g_sdcard_handler.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;/* 空闲时不关闭时钟电源 */  g_sdcard_handler.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;           g_sdcard_handler.Init.BusWide = SDIO_BUS_WIDE_1B;      /* 1位数据线 *//* 关闭硬件流控 */g_sdcard_handler.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE; g_sdcard_handler.Init.ClockDiv = SDIO_TRANSF_CLK_DIV;  /* SD传输时钟频率最大25MHZ */SD_Error = HAL_SD_Init(&g_sdcard_handler);if (SD_Error != HAL_OK)return 1;HAL_SD_GetCardInfo(&g_sdcard_handler, &g_sd_card_info_handle);  /* 获取SD卡信息 *//* 使能4bit宽总线模式 */SD_Error = HAL_SD_ConfigWideBusOperation(&g_sdcard_handler, SDIO_BUS_WIDE_4B);  if (SD_Error != HAL_OK)return 2;return 0;
}

4.函数HAL_SD_MspInit()

完成SDIO底层驱动,时钟使能,引脚配置,被sd_init()函数中的HAL_SD_Init函数调用,代码如下(示例):

/*** @brief       SDIO底层驱动,时钟使能,引脚配置此函数会被HAL_SD_Init()调用* @param       hsd:SD卡句柄* @retval      无*/
void HAL_SD_MspInit(SD_HandleTypeDef *hsd)
{GPIO_InitTypeDef gpio_init_struct;__HAL_RCC_SDIO_CLK_ENABLE();    /* 使能SDIO时钟 */SD_D0_GPIO_CLK_ENABLE();        /* D0引脚IO时钟使能 */SD_D1_GPIO_CLK_ENABLE();        /* D1引脚IO时钟使能 */SD_D2_GPIO_CLK_ENABLE();        /* D2引脚IO时钟使能 */SD_D3_GPIO_CLK_ENABLE();        /* D3引脚IO时钟使能 */SD_CLK_GPIO_CLK_ENABLE();       /* CLK引脚IO时钟使能 */SD_CMD_GPIO_CLK_ENABLE();       /* CMD引脚IO时钟使能 */gpio_init_struct.Pin = SD_D0_GPIO_PIN;              /* SD_D0引脚模式设置 */gpio_init_struct.Mode = GPIO_MODE_AF_PP;            /* 推挽复用 */gpio_init_struct.Pull = GPIO_PULLUP;                /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;      /* 高速 */gpio_init_struct.Alternate = GPIO_AF12_SDIO;        /* 复用为SDIO */HAL_GPIO_Init(SD_D0_GPIO_PORT, &gpio_init_struct);  /* 初始化 */gpio_init_struct.Pin = SD_D1_GPIO_PIN;              /* SD_D1引脚模式设置 */HAL_GPIO_Init(SD_D1_GPIO_PORT, &gpio_init_struct);  /* 初始化 */gpio_init_struct.Pin = SD_D2_GPIO_PIN;              /* SD_D2引脚模式设置 */HAL_GPIO_Init(SD_D2_GPIO_PORT, &gpio_init_struct);  /* 初始化 */gpio_init_struct.Pin = SD_D3_GPIO_PIN;              /* SD_D3引脚模式设置 */HAL_GPIO_Init(SD_D3_GPIO_PORT, &gpio_init_struct);  /* 初始化 */gpio_init_struct.Pin = SD_CLK_GPIO_PIN;             /* SD_CLK引脚模式设置 */HAL_GPIO_Init(SD_CLK_GPIO_PORT, &gpio_init_struct); /* 初始化 */gpio_init_struct.Pin = SD_CMD_GPIO_PIN;             /* SD_CMD引脚模式设置 */HAL_GPIO_Init(SD_CMD_GPIO_PORT, &gpio_init_struct); /* 初始化 */
}

5.函数get_sd_card_info()

获取卡信息函数,代码如下(示例):

/*** @brief       获取卡信息函数* @param       cardinfo:SD卡信息句柄* @retval      返回值:读取卡信息状态值*/
uint8_t get_sd_card_info(HAL_SD_CardInfoTypeDef *cardinfo)
{uint8_t sta;sta = HAL_SD_GetCardInfo(&g_sdcard_handler, cardinfo);return sta;
}

6.函数get_sd_card_state()

获取卡状态函数,判断SD卡是否可以传输(读写)数据,代码如下(示例):

/*** @brief       判断SD卡是否可以传输(读写)数据* @param       无* @retval      返回值:SD_TRANSFER_OK      传输完成,可以继续下一次传输SD_TRANSFER_BUSY SD 卡正忙,不可以进行下一次传输*/
uint8_t get_sd_card_state(void)
{return ((HAL_SD_GetCardState(&g_sdcard_handler) == HAL_SD_CARD_TRANSFER) ?SD_TRANSFER_OK : SD_TRANSFER_BUSY);
}

7.函数sd_read_disk()

完成对SD卡中数据信息读取,代码如下(示例):

/*** @brief       读SD卡(fatfs/usb调用)* @param       pbuf  : 数据缓存区* @param       saddr : 扇区地址* @param       cnt   : 扇区个数* @retval      0, 正常;  其他, 错误代码(详见SD_Error定义);*/
uint8_t sd_read_disk(uint8_t *pbuf, uint32_t saddr, uint32_t cnt)
{uint8_t sta = HAL_OK;uint32_t timeout = SD_TIMEOUT;long long lsector = saddr;__disable_irq();   /* 关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!) */                                                                 sta = HAL_SD_ReadBlocks(&g_sdcard_handler, (uint8_t *)pbuf, lsector, cnt, SD_TIMEOUT); /* 多个sector的读操作 *//* 等待SD卡读完 */while (get_sd_card_state() != SD_TRANSFER_OK){if (timeout-- == 0)sta = SD_TRANSFER_BUSY;}__enable_irq();    /* 开启总中断 */return sta;
}

8.函数sd_write_disk()

完成对SD卡中数据信息写入,代码如下(示例):

/*** @brief       写SD卡(fatfs/usb调用)* @param       pbuf  : 数据缓存区* @param       saddr : 扇区地址* @param       cnt   : 扇区个数* @retval      0, 正常;  其他, 错误代码(详见SD_Error定义);*/
uint8_t sd_write_disk(uint8_t *pbuf, uint32_t saddr, uint32_t cnt)
{uint8_t sta = HAL_OK;uint32_t timeout = SD_TIMEOUT;long long lsector = saddr;__disable_irq();  /* 关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!) */sta = HAL_SD_WriteBlocks(&g_sdcard_handler, (uint8_t *)pbuf, lsector, cnt, SD_TIMEOUT); /* 多个sector的写操作 *//* 等待SD卡写完 */while (get_sd_card_state() != SD_TRANSFER_OK){if (timeout-- == 0)sta = SD_TRANSFER_BUSY;}__enable_irq();     /* 开启总中断 */return sta;
}

9.函数show_sdcard_info()

打印SD卡相关信息,代码如下(示例):

/*** @brief       打印SD卡相关信息* @param       无* @retval      无*/
void show_sdcard_info(void)
{HAL_SD_CardCIDTypeDef sd_card_cid;HAL_SD_GetCardCID(&g_sdcard_handler, &sd_card_cid); /* 获取CID */get_sd_card_info(&g_sd_card_info_handle);           /* 获取SD卡信息 */switch (g_sd_card_info_handle.CardType){case CARD_SDSC:{if (g_sd_card_info_handle.CardVersion == CARD_V1_X)printf("Card Type:SDSC V1\r\n");else if (g_sd_card_info_handle.CardVersion == CARD_V2_X)printf("Card Type:SDSC V2\r\n");}break;case CARD_SDHC_SDXC:printf("Card Type:SDHC\r\n");break;default: break;}/* 制造商ID */printf("Card ManufacturerID:%d\r\n", sd_card_cid.ManufacturerID); /* 卡相对地址 */                       printf("Card RCA:%d\r\n", g_sd_card_info_handle.RelCardAdd);          /* 显示逻辑块数量 */printf("LogBlockNbr:%d \r\n", (uint32_t)(g_sd_card_info_handle.LogBlockNbr));/* 显示逻辑块大小 */       printf("LogBlockSize:%d \r\n", (uint32_t)(g_sd_card_info_handle.LogBlockSize));/* 显示容量 */printf("Card Capacity:%d MB\r\n", (uint32_t)SD_TOTAL_SIZE_MB(&g_sdcard_handler));/* 显示块大小 */printf("Card BlockSize:%d\r\n\r\n", g_sd_card_info_handle.BlockSize); 
}

总结

这章完成是STM32的SDIO通讯接口和SD卡介绍,为下一章FATFS文件系统做铺垫。

下面提供的代码,基于STM32F407ZGT芯片编写,可直接在原子开发板上运行,也可运行在各工程项目上,但需要注意各接口以及相应的引脚应和原子开发板上保持一致。

相应的代码链接:代码程序

相关文章:

STM32之SDIO通讯接口和SD卡(九)

STM32F407 系列文章 - SDIO-To-SD Card(九) 目录 前言 一、SDIO接口 二、SD卡 三、实现程序 1.SD卡结构体参数说明 2.头文件定义 3.函数sd_init() 4.函数HAL_SD_MspInit() 5.函数get_sd_card_info() 6.函数get_sd_card_state() 7.函数sd_read…...

【Rust在WASM中实现pdf文件的生成】

Rust在WASM中实现pdf文件的生成 前言概念和依赖问题描述分步实现pdf转Blob生成URL两种方式利用localstorage传递参数处理图片Vec<u8>到pdf格式的Vec<u8>使用rust创建iframe显示pdf的Blob最后 前言 实现了一个通用的前端jpg转pdf的wasm,因为动态响应框架无法直接打…...

蓝桥杯真题——砍竹子(C语言)

问题描述 这天, 小明在砍竹子, 他面前有 n 棵竹子排成一排, 一开始第 ii 棵竹子的 高度为 ​.他觉得一棵一棵砍太慢了, 决定使用魔法来砍竹子。魔法可以对连续的一 段相同高度的竹子使用, 假设这一段竹子的高度为 H, 那么用一次魔法可以,把这一段竹子的高度都变为, 其中 [x]表…...

技术栈4:Docker入门 Linux入门指令

目录 1.Linux系统目录结构 2.处理目录的常用命令 3.Docker概述 4.Docker历史 5.Docker基本组成 6.Docker底层原理 7.Docker修改镜像源 8.Docker基本命令 在学习docker之前我们先要熟悉Linux系统&#xff0c;推荐阅读&#xff1a;Linux笔记&#xff08;狂神说&#xff0…...

项目开发之Docker

文章目录 基础核心概念常用命令 实操1 windows11 docker mysql2 docker部署 xxljob3 container间调用异常问题4 部署mysql数据库5 docker desktop unexpected wsl error 基础 核心概念 其中的三个核心概念&#xff1a;dockerfile image/镜像 container/容器 image&#xff1a…...

状态模式的理解和实践

在软件开发中&#xff0c;我们经常遇到需要根据对象的不同状态执行不同行为的情况。如果直接将这些状态判断和行为逻辑写在同一个类中&#xff0c;会导致该类变得臃肿且难以维护。为了解决这个问题&#xff0c;状态模式&#xff08;State Pattern&#xff09;应运而生。状态模式…...

正点原子imx6ull配置MQTT客户端上传数据到Ubuntu MQTT服务器

目录 使用QT自带的MQTT模块部署客户端创建一个class专门用于MQTT客户端通讯使用QT在ui界面上生成按钮在Windows上订阅相应主题测试在imx6ull上订阅Windows发布的消息 在上一篇中介绍了在Ubuntu22.04的Docker中部署MQTT服务器&#xff0c;然后在window上测试订阅和发布&#xff…...

【联表查询】.NET开源 ORM 框架 SqlSugar 系列

.NET开源 ORM 框架 SqlSugar 系列 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列【Code First】.NET开源 ORM 框架 SqlSugar 系列【数据事务…...

CAN接口设计

CAN总线的拓扑结构 CAN总线的拓扑结构有点像485总线,都是差分的传输方式,总线上都可以支持多个设备,端接匹配电阻都是120Ω。 485和CAN通信方面最大的区别:网络特性。485是一主多从的通讯方式,CAN是多主通讯,多个设备都可以做主机。那多个设备都相要控制总线呢?…...

基于遗传优化SVM的电机参数预测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 数据收集与预处理 4.2模型构建与训练 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 输入&#xff1a;电机结构参数x1 x2 x3 x4 x5(分别是铁心高度 铁心厚度 绕组…...

C++设计模式之代理模式

动机 在面向对象系统中&#xff0c;有些对象由于某种原因&#xff08;比如对象创建的开销很大&#xff0c;或者某些操作需要安全控制&#xff0c;或者需要进程外的访问等&#xff09;&#xff0c;直接访问会给使用者、或者系统结构带来很多麻烦。 如何在不失去透明操作对象的…...

多线程JUC 第2季 控制线程的执行顺序,依次,交替,同时

一 场景1 交替执行 1.1 方案1使用completablefuture public class TestA {public static void main(String[] args) {for(int k0;k<10;k) {CompletableFuture<Void> t1 CompletableFuture.runAsync(() -> {System.out.println("A: >" Thread.curr…...

数据分析(一): 掌握STDF 掌握金钥匙-码农切入半导体的捷径

中国的半导体行业必然崛起&#xff01;看清这个大势&#xff0c;就会有很多机会。 今天&#xff0c;我们一起来了解一下半导体行业的一朵金花&#xff1a;STDF。 实际上这只是一种文件格式&#xff0c;但是当你熟练掌握解析这种文件的时候&#xff0c;你就已经打开在这个基础…...

NPM镜像详解

NPM镜像详解 什么是NPM镜像 NPM镜像&#xff08;NPM Mirror&#xff09;是一个完整的NPM包的副本服务器。由于npm的官方registry服务器部署在国外&#xff0c;国内访问可能会比较慢&#xff0c;因此使用镜像可以加快包的下载速度。 常用的NPM镜像源 npm官方镜像 https://reg…...

【springboot】 多数据源实现

文章目录 1. 引言&#xff1a;多数据源的必要性和应用场景**为什么需要多数据源&#xff1f;****应用场景** 2. Spring Boot中的数据源配置2.1 默认数据源配置简介2.2 如何在Spring Boot中配置多个数据源 3. 整合MyBatis与多数据源**配置MyBatis使用多数据源****Mapper接口的数…...

Zephyr 入门-设备树与设备驱动模型

学习链接&#xff1a;https://www.bilibili.com/video/BV1L94y1F7qS/?spm_id_from333.337.search-card.all.click&vd_source031c58084cf824f3b16987292f60ed3c 讲解清晰&#xff0c;逻辑清楚。 1. 设备树概述&#xff08;语法&#xff0c;如何配置硬件&#xff0c;c代码如…...

css实现圆周运动效果

在CSS中可以通过 keyframes 动画 和 transform 属性实现元素的圆周运动。以下是一个示例代码&#xff1a; 示例代码 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content…...

乐鑫科技嵌入式面试题及参考答案(3万字长文)

嵌入式开发为什么用 C 语言,而不用 C++ 语言? 在嵌入式开发中,C 语言被广泛使用而 C++ 相对少用有以下一些原因。 首先,C 语言具有更高的效率。嵌入式系统通常资源受限,包括处理器速度、内存容量等。C 语言的代码生成效率高,能够生成紧凑的机器码,占用较少的内存空间和处…...

这就是IoC容器

IoC(Inversion of Control,控制反转),也叫依赖注入(Dependency Injection),是一种决定容器如何装配组件的模式。使用 Spring 来实现 IoC,意味着将设计好的对象交给 Spring 容器控制,而不是直接在对象内部控制。控制反转不能很好地描述这个模式,依赖注入却能更好地描述…...

KVM OVS双网卡配置trunk模式

一、宿主机配置 1. 确保必要的软件包已安装 确保宿主机上已安装 Open vSwitch 和 VLAN 工具。如果尚未安装&#xff0c;可以使用以下命令进行安装&#xff1a; yum install openvswitch-switch vlan 2. 配置现有 OVS 桥接 br0 假设已有一个 OVS 桥接接口 br0&#xff0c;并…...

氢能源车和电动车,谁将成为未来?

很多人都觉得氢能源是未来的终极绿色能源&#xff0c;因为氢气燃烧后只产生水&#xff0c;听起来是不是很环保&#xff1f;但这只是从化学能的角度来看。实际上&#xff0c;氢能源汽车还有很多问题需要解决。氢气的制作成本高得吓人 目前&#xff0c;制作氢气最理想的方法是电…...

CTF-PWN: WEB_and_PWN [第一届“吾杯”网络安全技能大赛 Calculator] 赛后学习(不会)

附件 calculate.html <!DOCTYPE html> <html lang"en"> <head><!-- 设置字符编码为 UTF-8&#xff0c;支持多语言字符集 --><meta charset"UTF-8"><!-- 设置响应式视图&#xff0c;确保页面在不同设备上自适应显示 --&…...

解决Jupyter Notebook无法转化为Pdf的问题(基于Typora非常实用)

笔者在完成各项作业和做笔记时&#xff0c;经常用到jupyter notebook&#xff1b;其因为可以同时运行python并提供格式化的数字公式的输入方式&#xff0c;得到了广大用户的喜爱。 当我们想要将.ipynb文件导出为pdf时&#xff0c;有两种常用方法。 1.Ctrlp 2.通过File ->…...

矩阵转置        ‌‍‎‏

矩阵转置 C语言代码C 语言代码Java语言代码Python语言代码 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 输入一个n行m列的矩阵A&#xff0c;输出它的转置 A T A^T AT。 输入 第一行包含两个整数n和m&#xff0c;表示矩阵A的行数和列数。…...

【阅读笔记】Three ways ChatGPT helps me in my academic writing

Three ways ChatGPT helps me in my academic writing 论文地址 关于GPT进行润色的文章&#xff0c;摘取了里面的提示词做个记录。 1. Polishing academic writing&#xff08;学术润色&#xff09; 模板&#xff1a;I’m writing a paper on [topic] for a leading [discip…...

python学习笔记15 python中的类

上一篇我们介绍了python中的库 &#xff0c;学习了一些常见的内置库。详细内容可点击–>python学习笔记14 python中的库&#xff0c;常见的内置库&#xff08;random、hashlib、json、时间、os&#xff09; 这一篇我们来看一下python中的类 创建一个类 class 类的名称():de…...

3D数字化革新,探索博物馆的正确打开新方式!

3D数字化的发展&#xff0c;让博物馆也焕发新机&#xff0c;比如江苏省的“云上博物”&#xff0c;汇聚江苏全省博物馆展陈资源&#xff0c;采取线上展示和线下体验两种方式进行呈现的数字展览项目。在线上&#xff0c;用户可以通过H5或小程序进入“云上博物”数字展览空间&…...

金融数学在股市交易中的具体应用

### 1. 风险管理 - **VaR&#xff08;在险价值&#xff09;**: VaR是衡量投资组合潜在损失的指标。例如&#xff0c;如果一个投资组合的VaR为100万元&#xff0c;置信水平为95%&#xff0c;这意味着在未来的一个交易日内&#xff0c;有95%的可能性该投资组合的损失不会超过100…...

Springboot注解

什么是Spring Boot 注解 Spring Boot 注解是 Spring Boot 框架中的核心部分&#xff0c;它们用于简化配置和执行特定的编程任务。这些注解通常分为几个类别&#xff0c;包括用于 MVC 层的类注解、依赖注入注解、Web 开发常用注解、Spring Boot 常用注解、面向切面编程&#xf…...

CSS3 布局样式及其应用

深入探讨 CSS3 布局样式及其应用 引言 在现代网页设计中&#xff0c;CSS&#xff08;层叠样式表&#xff09;不仅是设计视觉样式的工具&#xff0c;也是布局的核心技术。CSS3引入了新的布局模型&#xff0c;其中Flexbox与Grid布局在满足复杂布局需求方面表现尤为出色。本文将…...

工业机器视觉-基于深度学习的水表表盘读数识别

字轮数字识别、指针读数识别&#xff08;角度换算&#xff09;、根据指针角度进行读数修正、根据最高位指针(x0.1)读数对字轮数字进行修正、得到最终读数。 基于深度学习的目标检测技术和OpenCV图像处理技术&#xff0c;可识别所有类型的表盘机械读数。...

kafka admin client 如何计算kafka发送速度

文章目录 方法 1&#xff1a;使用 AdminClient 获取消息数量示例代码&#xff1a;计算 Kafka 生产速度代码解释&#xff1a;解释&#xff1a;结果示例&#xff1a;方法 2&#xff1a;使用 Kafka JMX 监控JMX 指标&#xff1a; 总结&#xff1a; 要使用 Kafka Admin Client 来计…...

Ubuntu20.04 配置虚拟显示器和切回物理显示器

1、安装软件&#xff0c;用中软安装虚拟显示器软件 sudo apt-get install xserver-xorg-core-hwe-18.04 sudo apt-get install xserver-xorg-video-dummy2、添加配置文件 进入 /usr/share/X11/xorg.conf.d/ 文件夹下创建xorg.conf文件 # 创建xorg.conf文件 touch xorg.conf …...

【解决pycharm下site-packages文件标记为红色的问题】

怎么解决pycharm下site-packages文件标记为红色的问题 这是一个pycharm无法识别本地site-packages问题答案解释器设置路径如下&#xff1a; 这是一个pycharm无法识别本地site-packages问题 最近正在搭建一个python3requestsunittestHTMLTestRunner接口测试框架&#xff0c;发现…...

力扣-图论-2【算法学习day.52】

前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向和记录学习过程&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非…...

项目-02-数学学院后台项目开发过程中的问题总结

目录 一、后台&#xff08;pc端&#xff0c;vue2&#xff09;1. dialog对话框被黑色蒙层盖住2. 将前端表格导出为word文档3. 在线查看、下载 .docx、.doc、.pdf文档 一、后台&#xff08;pc端&#xff0c;vue2&#xff09; 1. dialog对话框被黑色蒙层盖住 问题&#xff1a; d…...

数据结构-排序

目录 一、冒泡排序 二、选择排序 三、插入排序 四、希尔排序 五、堆排 六、快速排序 1、hoare&#xff1a; 2、挖坑法&#xff1a; 3、前后指针法&#xff1a; 4、快排非递归 七、归并排序 1、递归写法&#xff1a; 2、非递归写法&#xff1a; 八、计数排序 九、排…...

神经网络中常见的激活函数Sigmoid、Tanh和ReLU

激活函数在神经网络中起着至关重要的作用&#xff0c;它们决定了神经元的输出是否应该被激活以及如何非线性地转换输入信号。不同的激活函数适用于不同的场景&#xff0c;选择合适的激活函数可以显著影响模型的性能和训练效率。以下是三种常见的激活函数&#xff1a;Sigmoid、T…...

[报错] Error: PostCSS plugin autoprefixer requires PostCSS 8 问题解决办法

报错&#xff1a;Error: PostCSS plugin autoprefixer requires PostCSS 8 原因&#xff1a;autoprefixer版本过高 解决方案&#xff1a; 降低autoprefixer版本 执行&#xff1a;npm i postcss-loader autoprefixer8.0.0 参考&#xff1a; Error: PostCSS plugin autoprefix…...

多线程---创建及方法

*线程创建的方式&#xff1a; 1.继承Thread类&#xff0c;重写run方法。 2.实现Runnable接口&#xff0c;重写run方法。 实际这两个run方法都重写的是Runnable中的run方法 简化方法&#xff1a; 1.匿名内部类创建Thread 子类对象 Thread thread new Thread(){Overridepub…...

C++设计模式之单例模式

动机 在软件系统中&#xff0c;经常有一些特殊的类&#xff0c;必须保证它们在系统中只存在一个实例&#xff0c;才能确保它们的逻辑正确性&#xff0c;以及良好的效率。 如何绕过常规的构造器&#xff0c;提供一种机制来保证一个类只有一个实例&#xff1f; 这应该是类设计…...

给你一个整数n, 判断它是否是素数。注意1不是素数。:JAVA

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 题目描述 给你一个整数n, 判断它是否是素数。注意1不是素数。 输入描述: 第一行输入一个整数T,表示需要判断的数的个数接下来T行每行一个整数n,表示需要判断的数。1<n<1e5,1<T<…...

Java项目实战II基于微信小程序的电子竞技信息交流平台的设计与实现(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、核心代码 五、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 随着互联网技术的飞速发展…...

temu登录接口逆向分析(含Py纯算)

文章目录 1. 写在前面2. 接口分析3. 算法还原 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python…...

Java ArrayList 详解

Java ArrayList 详解 ArrayList 是 Java 集合框架&#xff08;Collection Framework&#xff09;中最常用的类之一&#xff0c;是一种基于动态数组的数据结构&#xff0c;属于 List 接口的实现类。它允许存储重复的元素&#xff0c;有序&#xff0c;支持随机访问&#xff0c;且…...

CTF之密码学(仓颉编码)

一、仓颉码&#xff08;用于建立中文索引&#xff09; 定义与目标&#xff1a; 仓颉码是为了建立中文的索引观念而设计的一种编码方式。其主要目标是方便对中文资料或程式进行索引功能的处理。 工作原理&#xff1a; 仓颉码的索引以ASCII的字符码为基准&#xff0c;但在内部会转…...

OpenCV的圆形检测‌HoughCircles

HoughCircles 函数是 OpenCV 库中用于在灰度图像中检测圆的函数,它基于霍夫变换(Hough Transform)的一种变体——梯度霍夫变换(HOUGH_GRADIENT)函数原型如下: void HoughCircles( InputArray image, OutputArray circles,int method, double dp, double minDist,double …...

Latex 英文双栏排版时,摘要和引言之间的距离

目标&#xff1a;解决这个间隔 打开“cas-common.sty" 搜索“\NewDocumentCommand \dashrule" 本来是两个 m m 变成 m m m然后是增加下面这个代码&#xff0c;其他地方不动 \skip_vertical:n {#4} 搜索”MaketitleBox“和“LongMaketitleBox ” 本来的代码应该…...

AD学习笔记·空白工程的创建

编写不易&#xff0c;禁止搬运&#xff0c;仅供学习&#xff0c;感谢理解 序言 本文参考B站&#xff0c;凡亿教育&#xff0c;连接放在最后。 创建工程文件 在使用AD这个软件的电路板设计中&#xff0c;有很多的地方跟嘉立创eda还是有不一样的地方&#xff0c;其中一个地方就…...

非球面加工领域的佼佼者:倚光科技

在光学领域&#xff0c;非球面加工技术宛如一颗璀璨的明珠&#xff0c;以其独特的优势和广泛的应用&#xff0c;引领着光学元件制造的新潮流。 非球面透镜&#xff0c;其表面曲率半径从中心到边缘是变化的&#xff0c;这使得它在光学系统中具有诸多显著优势。首先&#xff0c;…...