STM32CubeMX学习笔记20——SD卡FATFS文件系统
1. FATFS文件系统简介
文件系统是操作系统用于明确存储设备或分区上的文件的方法和数据结构(即在存储设备上组织文件的方法)。操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统;不带文件系统的SD卡仅能实现简单的读写扇区操作,要真正应用SD卡必须要使用文件系统
FATFS文件存储原理(以FAT32为例),下图为FAT32文件系统布局:
簇是文件存储的最小单元,FAT32分区大小与对应簇空间大小关系如下表示:
分区空间大小 | 簇空间大小 | 每个簇包含的扇区数 |
---|---|---|
< 8GB | 4KB | 8 |
[ 8GB, 16GB ) | 8KB | 16 |
[ 16GB, 32GB ) | 16KB | 32 |
>= 32GB | 32KB | 64 |
例如:创建一个50字节的test.txt文件,文件大小是50字节,但是占用磁盘空间为4096字节(一个簇)
FATFS是一个完全免费开源,专为小型嵌入式系统设计的FAT(File Allocation Table)文件系统模块。FATFS的编写遵循ANSI C,并且完全与磁盘I/O层分开。支持FAT12/FAT16/FAT32,支持多个存储媒介,有独立的缓冲区,可对多个文件进行读写。兼容Windows文件系统。
FatFs 文件系统的源码可以从 fatfs 官网下载:
FatFs - Generic FAT Filesystem Module
FATFS模块的层次结构如下图示:
- 最顶层是应用层:使用者只需要调用FATFS模块提供给用户的一系列应用接口函数(如f_open, f_read, f_write和f_close等),就可以像在PC上读写文件那样简单
- 中间层FATFS模块:实现了FAT文件读写协议;它提供了 ff.c和 ff.h文件,一般情况下不用修改,使用时将头文件包含进去即可
- 最底层是FATFS模块的底层接口:包括存储媒介读写接口和供给文件创建修改时间的实时时钟,需要在移植时编写对应的代码
FATFS源码相关文件介绍如下表示;移植FATFS模块时,一般只需要修改2个文件(即ffconf.h和diskio.c)
与平台无关 | 文件描述 |
---|---|
ffconf.h | FATFS模块配置文件 |
ff.h | FATFS和应用模块公用的包含文件 |
ff.c | FATFS模块 |
diskio.h | FATFS和disk I/O模块公用的包含文件 |
interger.h | 数据类型定义 |
option | 可选的外部功能(比如支持中文) |
与平台相关 | 文件描述 |
---|---|
diskio.c | FATFS和disk I/O模块接口层文件 |
FATFS的移植主要分为三步:
- 数据类型:在integer.h里面去定义好数据的类型
- 配置:通过ffconf.h配置FATFS相关功能
- 函数编写:在diskio.c文件中进行底层驱动编写(6个接口函数disk_initialize、disk_status、disk_read、disk_write、disk_ioctl、get_fattime)
本例程使用的是STM32CubeMX图形化软件配置FATFS,只需要简单设置即可以完成以上三步的工作。下图是STM32CubeMX的FATFS底层实现框图:
STM32CubeMX生成的代码工程里,涉及底层驱动实现的文件主要有以下三个:
1、ff_gen_drv.c文件:FATFS提供的通用驱动文件的实现
//ff_gen_drv.h文件内容
/*Disk IO Driver structure definition */
typedef struct
{DSTATUS (*disk_initialize) (BYTE); //通过指针指向diskio.c中的disk_initialize()DSTATUS (*disk_status) (BYTE); //通过指针指向diskio.c中的disk_status()DRESULT (*disk_read) (BYTE, BYTE*, DWORD, UINT); //指向disk_read()
#if _USE_WRITE == 1 DRESULT (*disk_write) (BYTE, const BYTE*, DWORD, UINT); //指向disk_write()
#endif
#if _USE_IOCTL == 1 DRESULT (*disk_ioctl) (BYTE, BYTE, void*); //指向disk_ioctl()
#endif
}Diskio_drvTypeDef;/*brief Global Disk IO Drivers structure definition*/
typedef struct{ uint8_t is_initialized[_VOLUMES]; //磁盘是否初始化Diskio_drvTypeDef *drv[_VOLUMES]; //磁盘的驱动uint8_t lun[_VOLUMES]; //磁盘的编号__IO uint8_t nbr;
}Disk_drvTypeDef;
/* Exported functions ------------------------------------------------------- */
uint8_t FATFS_LinkDriverEx(Diskio_drvTypeDef *drv, char *path, uint8_t lun);
uint8_t FATFS_LinkDriver(Diskio_drvTypeDef *drv, char *path);
uint8_t FATFS_UnLinkDriver(char *path);
uint8_t FATFS_LinkDriverEx(Diskio_drvTypeDef *drv, char *path, BYTE lun);
uint8_t FATFS_UnLinkDriverEx(char *path, BYTE lun);
uint8_t FATFS_GetAttachedDriversNbr(void);
2、sd_diskio.c:针对SD底层驱动实现,封装成为通用的底层驱动API
//sd_diskio.c文件内容
const Diskio_drvTypeDef SD_Driver = {SD_initialize, //指向diskio.c中的disk_initialize()SD_status, //指向diskio.c中的disk_status()SD_read, //指向diskio.c中的disk_read()
#if _USE_WRITE == 1SD_write, //指向diskio.c中的disk_write()
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1SD_ioctl, //指向diskio.c中的disk_ioctl()
#endif /* _USE_IOCTL == 1 */
}; //定义Diskio_drvTypeDef类型的SD_Driver/* -------------------以上各函数的实现---------------------------*/
DSTATUS SD_initialize(BYTE lun){Stat = STA_NOINIT; if(BSP_SD_Init() == MSD_OK){Stat &= ~STA_NOINIT;}return Stat;
}DSTATUS SD_status(BYTE lun){Stat = STA_NOINIT;if(BSP_SD_GetCardState() == MSD_OK){Stat &= ~STA_NOINIT;}return Stat;
}DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count){DRESULT res = RES_ERROR;uint32_t timeout = 100000;if(BSP_SD_ReadBlocks((uint32_t*)buff,(uint32_t) (sector), count, SD_DATATIMEOUT) == MSD_OK){while(BSP_SD_GetCardState()!= MSD_OK){if (timeout-- == 0)return RES_ERROR;}res = RES_OK;} return res;
}#if _USE_WRITE == 1
DRESULT SD_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count){DRESULT res = RES_ERROR;uint32_t timeout = 100000;if(BSP_SD_WriteBlocks((uint32_t*)buff,(uint32_t)(sector),count, SD_DATATIMEOUT) == MSD_OK){while(BSP_SD_GetCardState()!= MSD_OK){if (timeout-- == 0)return RES_ERROR;} res = RES_OK;}return res;
}
#endif /* _USE_WRITE == 1 */#if _USE_IOCTL == 1
DRESULT SD_ioctl(BYTE lun, BYTE cmd, void *buff){DRESULT res = RES_ERROR;BSP_SD_CardInfo CardInfo; if (Stat & STA_NOINIT) return RES_NOTRDY; switch (cmd){/* Make sure that no pending write process */case CTRL_SYNC :res = RES_OK;break;/* Get number of sectors on the disk (DWORD) */case GET_SECTOR_COUNT :BSP_SD_GetCardInfo(&CardInfo);*(DWORD*)buff = CardInfo.LogBlockNbr;res = RES_OK;break;/* Get R/W sector size (WORD) */case GET_SECTOR_SIZE :BSP_SD_GetCardInfo(&CardInfo);*(WORD*)buff = CardInfo.LogBlockSize;res = RES_OK;break;/* Get erase block size in unit of sector (DWORD) */case GET_BLOCK_SIZE :BSP_SD_GetCardInfo(&CardInfo);*(DWORD*)buff = CardInfo.LogBlockSize;res = RES_OK;break;default:res = RES_PARERR;} return res;
}
#endif /* _USE_IOCTL == 1 */
3、bsp_driver_sd.c:HAL库的二次封装,将基于SD卡的操作都在该文件下实现
//bsp_driver_sd.h文件内容
uint8_t BSP_SD_Init(void);
uint8_t BSP_SD_ITConfig(void);
void BSP_SD_DetectIT(void);
void BSP_SD_DetectCallback(void);
uint8_t BSP_SD_ReadBlocks(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout);
uint8_t BSP_SD_WriteBlocks(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout);
uint8_t BSP_SD_ReadBlocks_DMA(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks);
uint8_t BSP_SD_WriteBlocks_DMA(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks);
uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr);
void BSP_SD_IRQHandler(void);
void BSP_SD_DMA_Tx_IRQHandler(void);
void BSP_SD_DMA_Rx_IRQHandler(void);
uint8_t BSP_SD_GetCardState(void);
void BSP_SD_GetCardInfo(HAL_SD_CardInfoTypeDef *CardInfo);
uint8_t BSP_SD_IsDetected(void);
void BSP_SD_AbortCallback(void);
void BSP_SD_WriteCpltCallback(void);
void BSP_SD_ReadCpltCallback(void);
FATFS提供的应用接口如下图示:
下面简单介绍几个常用操作函数:
f_mount:在FATFS模块上注册/注销一个工作区(文件系统对象)
FRESULT f_mount(FATFS* fs, const TCHAR* path, BYTE opt);
参数--> fs:fs工作区(文件系统对象)指针,如果赋值为 NULL 可以取消物理设备挂载path:注册/注销工作区的逻辑设备编号,使用设备根路径表示opt:注册或注销选项(可选0或1),0表示不立即挂载,1表示立即挂载
f_mkfs:格式化物理设备
FRESULT f_mkfs(const TCHAR* path, BYTE sfd, UINT au);
参数--> path:逻辑设备编号,使用设备根路径表示sfd:0或1,0表示为硬盘设备;1表示为软盘设备au:指定扇区大小,若为0表示通过disk_ioctl函数获取
f_open:创建/打开一个文件对象
FRESULT f_open(FIL* fp, const TCHAR* path, BYTE mode);
参数--> fp:将创建或打开的文件对象指针path:文件名指针,指定将创建或打开的文件名(包含文件类型后缀名)mode:访问类型和打开方法
mode可选值:
FA_READ 指定读访问对象。可以从文件中读取数据。 与FA_WRITE结合可以进行读写访问。
FA_WRITE 指定写访问对象。可以向文件中写入数据。与FA_READ结合可以进行读写访问。
FA_OPEN_EXISTING 打开文件。如果文件不存在,则打开失败。(默认)
FA_OPEN_ALWAYS 如果文件存在,则打开;否则,创建一个新文件。
FA_CREATE_NEW 创建一个新文件。如果文件已存在,则创建失败。
FA_CREATE_ALWAYS 创建一个新文件。如果文件已存在,则它将被截断并覆盖。
f_close:关闭一个打开的文件
FRESULT f_close (FIL *fp)
参数--> fp:将被关闭的已打开的文件对象结构的指针
f_write:写入数据到一个已打开的文件
FRESULT f_write (FIL* fp, const void *buff, UINT btw, UINT* bw)
参数--> fp:指向将被写入的已打开的文件对象结构的指针buff:指向存储写入数据的缓冲区的指针btw:要写入的字节数bw:指向返回已写入字节数的UINT变量的指针,返回为实际写入的字节数
f_read:从一个打开的文件中读取数据
FRESULT f_read (FIL* fp, const void *buff, UINT btr, UINT* br)
参数--> fp:指向将被读取的已打开的文件对象结构的指针buff:指向存储读取数据的缓冲区的指针btr:要读取的字节数br:指向返回已读取字节数的UINT变量的指针,返回为实际读取的字节数
2. 硬件设计
LED2指示灯用来提示系统运行状态,串口1用来打印调试信息,使用FATFS文件系统对SD卡进行相关操作
- LED2指示灯
- USART1
- TF卡
3 、 STM32CubeMX设置
- RCC设置外接HSE,时钟设置为72M
- PC0设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平
- 激活SDIO,详细请参考SD卡例程
- 打开FATFS文件系统,选择SD卡模式,配置为中文编码,选择内存空间为HEAP,其余默认值
-
Function Parameters
跳过
-
Locale and Namespace Parameters:
CODE_PAGE(Code page on target): Simplified Chinese GBK(DBCS,OEM,Windows) 支持简体中文编码
USE_LFN(Use Long Filename): Enabled with dynamic working buffer on the STACK 支持长文件名,并指定使用栈空间为缓冲区
缓存工作区为什么放在栈?其实fatfs提供了三个选项:BSS,STACK , HEAP,根据个人情况选一个。
在BSS上启用带有静态工作缓冲区的LFN,不能动态分配。
如果选择了HEAP(堆)且自己有属于自己的malloc就去重写ff_memalloc ff_memfree函数。如果是库的malloc就不需要。
一般都选择使用STACK(栈),能动态分配。
当使用堆栈作为工作缓冲区时,请注意堆栈溢出。
Physical Drive Parameters:
VOLUMES(Logical drivers): 2 指定物理设备数量,这里设置为 2,包括预留 SD 卡和 SPI Flash 芯片
MAX_SS(Maximum Sector Size): 512 指定扇区大小的最大值。SD 卡扇区大小一般都为 512 字节,SPI Flash 芯片扇区大小一般设置为 4096 字节,所以需要把 _MAX_SS 改为 512
MIN_SS(Minimum Sector Size): 512 指定扇区大小的最小值
配置SD卡检测引脚
SD卡插入检测引脚,如果不配置一个引脚生成文件时会报错,所以这里即使没有硬件连接,也可以任意设置一引脚使用,生成工程后注释代码。
增大栈空间
将最小栈空间改到 0x1000
注意:由于刚才设置长文件名动态缓存存储在堆中,故需要增大堆大小,如果不修改则程序运行时堆会生成溢出,程序进入硬件错误中断(HardFault),死循环。
输入工程名,选择工程路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码
4、程序编程
int main(void){HAL_Init();SystemClock_Config();/* Initialize all configured peripherals */MX_GPIO_Init();MX_CRC_Init();MX_FSMC_Init();MX_SDIO_SD_Init();MX_USART1_UART_Init();MX_FATFS_Init();/* USER CODE BEGIN 2 */uint32_t byteswritten; /* File write counts */uint32_t bytesread; /* File read counts */uint8_t wtext[] = "This is STM32 working with FatFs"; /* File write buffer */uint8_t rtext[100]; /* File read buffers */char filename[] = "STM32cube.txt";char SensorBuff[100];printf("********* STM32CubeMX FatFs Example *********\r\n\r\n"); if(f_mount(&SDFatFS,SDPath,1) == FR_OK){printf("f_mount sucess!!! \r\n");if(f_open(&SDFile,filename,FA_CREATE_ALWAYS|FA_WRITE) == FR_OK){printf("f_open file sucess!!! \r\n");if(f_write(&SDFile,wtext,sizeof(wtext),&byteswritten) == FR_OK){printf("f_write file sucess!!! \r\n");printf("f_write Data : %s\r\n",wtext);if(f_close(&SDFile) == FR_OK)printf("f_close sucess!!! \r\n");elseprintf("f_close error : %d\r\n",retSD);}elseprintf("f_write file error\r\n"); }elseprintf("f_open file error\r\n"); }elseprintf("f_mount error : %d \r\n",retSD);retSD = f_open(&SDFile, filename, FA_READ);if(retSD)printf("f_open file error : %d\r\n",retSD);elseprintf("f_open file sucess!!! \r\n");retSD = f_read(&SDFile, rtext, sizeof(rtext), (UINT*)&bytesread);if(retSD)printf("f_read error!!! %d\r\n",retSD);else{printf("f_read sucess!!! \r\n");printf("f_read Data : %s\r\n",rtext);}retSD = f_close(&SDFile);if(retSD)printf("f_close error!!! %d\r\n",retSD);elseprintf("f_close sucess!!! \r\n");if(bytesread == byteswritten)printf("FatFs is working well!!!\r\n");if(f_open(&SDFile,(const char*)"Sensor.csv",FA_CREATE_ALWAYS|FA_WRITE) == FR_OK){printf("Sensor.csv was opened/created!!!\r\n");sprintf(SensorBuff, "Item,Temp,Humi,Light\r\n");f_write(&SDFile,SensorBuff,strlen(SensorBuff),&byteswritten);for(int i = 0; i < 10; i++){sprintf(SensorBuff, "%d,%d,%d,%d\r\n",i + 1, i + 20, i + 30, i + 40);f_write(&SDFile,SensorBuff,strlen(SensorBuff),&byteswritten);f_sync(&SDFile);}f_close(&SDFile);}while (1){HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);HAL_Delay(500);}
}
5. 下载验证
如果使用的是新SD卡,需要在电脑上进行格式化,建立FAT文件系统,FATFS才能识别并操作SD卡
编译无误下载到开发板后,可以看到LED2指示灯不断闪烁,串口打印出如下信息:
将SD卡插入PC后,可以看到SD卡里建立了2个文件:Sensor.csv和STM32cube.txt,打开文件后可以看到写入的数据
6、参考文献
STM32CubeMX学习笔记(27)——FatFs文件系统使用(操作SD卡)_stm32cubemx fatfs-CSDN博客
STM32CubeMX系列 | FATFS文件系统 - 知乎 (zhihu.com)
相关文章:

STM32CubeMX学习笔记20——SD卡FATFS文件系统
1. FATFS文件系统简介 文件系统是操作系统用于明确存储设备或分区上的文件的方法和数据结构(即在存储设备上组织文件的方法)。操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统;不带文件系统的SD卡仅能…...

Facebook商城号为什么被封?如何防封?
由于Facebook商城的高利润空间,越来越多的跨境电商商家注意到它的存在。Facebook作为全球最大、用户量最大的社媒平台,同时也孕育了一个巨大的商业生态,包括广告投放、商城交易等。依托背后的大流量,Facebook商城起号较快…...

【教程】APP备案全攻略:确保你的应用合规上线
【教程】APP备案全攻略:确保你的应用合规上线 摘要 本文详细介绍了中国大陆地区互联网信息服务提供者(AP)进行APP备案的流程、要求和注意事项。包括备案对象、备案方式、备案内容、备案流程等方面的详细说明,帮助开发者顺利完成…...
Vue入门2
v-model 原理:v-model本质上是一个语法糖。例如应用于输入框,就是value属性和input事件的合写。 作用:提供数据的双向绑定 数据变,视图跟着变 :value视图变,数据跟着变 input 注意:$event用于在模板中&…...

简介:CMMI软件能力成熟度集成模型
前言 CMMI是英文Capability Maturity Model Integration的缩写。 CMMI认证简称软件能力成熟度集成模型,是鉴定企业在开发流程化和质量管理上的国际通行标准,全球软件生产标准大都以此为基点,并都努力争取成为CMMI认证队伍中的一分子。 对一个…...
mysql的其他问题
1.MySQL数据库作发布系统的存储,一天五万条以上的增量,预计运维三年,怎么优化? a. 设计良好的数据库结构,允许部分数据冗余,尽量避免join查询,提高效率。 b. 选择合适的表字段数据类型和存储引擎…...

数据结构---复杂度(2)
1.斐波那契数列的时间复杂度问题 每一行分别是2^0---2^1---2^2-----2^3-------------------------------------------2^(n-2) 利用错位相减法,可以得到结果是,2^(n-1)-1,其实还是要减去右下角的灰色部分,我们可以拿简单的数字进行举例子&…...
【设计模式】设计原则和常见的23种经典设计模式
设计模式 1. 设计原则(记忆口诀:SOLID)【记忆口诀:单开里依接迪合(单开礼仪接地和)】 (1)单一职责原则(Single Responsibility Principle, SRP) ÿ…...

Spring Cloud Gateway自定义断言
问题:Spring Cloud Gateway自带的断言(Predicate)不满足业务怎么办?可以自定义断言! 先看Spring Cloud Gateway是如何实现断言的 Gateway中断言的整体架构如下: public abstract class AbstractRoutePred…...

智能测径仪在胶管行业的应用
关键字:胶管外径尺寸测量,胶管检测仪器,胶管外径检测,高温胶管外径检测,软硬胶管检测, 智能测径仪在家胶管行业中的应用主要体现在对胶管外径的精确测量和控制上。在胶管生产过程中,外径的大小直…...

vue自定义主题皮肤方案
方案一:CSS变量换肤(推荐) 利用css定义变量的方法,用var在全局定义颜色变量(需将变量提升到全局即伪类选择器 :root)然后利用js操作css变量,document.getElementsByTagName(‘body’)[0].style…...
iOS中使用schema协议调用APP和使用iframe打开APP的例子
大家好我是咕噜美乐蒂,很高兴又和大家见面了! 当调用自定义 URL scheme 或使用 iframe 打开应用程序时,可以采取以下详细步骤: 使用自定义 URL scheme 协议调用应用程序 1.首先,确认目标应用程序已经注册了自定义 U…...

2024.3.11
提示并输入一个字符串,统计该字符中大写、小写字母个数、数字个数、空格个数以及其他字符个数 #include <iostream> #include<string> using namespace std;int main() {cout << "please input a string:" << endl;string str;g…...
Web服务器需要警惕的一些安全隐患
Web服务器需要警惕的一些安全隐患有哪些,今天德迅云安全就带您来了解下。熟悉了解了就知道怎么规避风险。不过无论是什么漏洞,都体现着安全是一个整体的真理,考虑Web服务器的安全性,必须要考虑到与之相配合的操作系统。 1.物理路径…...

MinGW-w64的下载与安装
文章目录 1 下载2 安装3 配置环境变量4 验证 1 下载 官网地址:https://www.mingw-w64.org/github地址:https://github.com/niXman/mingw-builds-binaries/releases windows下载 跳转github下载 版本号选择:13.2.0是GCC的版本号;…...
docker使用笔记
查看本机上所有镜像 docker images打包项目(打包完成后自动载入镜像) The command docker build -t search-server . you provided is a standard way to build a Docker image. The -t flag tags the resulting image, and search-server is the tag …...

新规正式发布 | 百度深度参编《生成式人工智能服务安全基本要求》
2024年2月29日,全国网络安全标准化技术委员会( TC260 )正式发布《生成式人工智能服务安全基本要求》(以下简称《基本要求》)。《基本要求》规定了生成式人工智能服务在安全方面的基本要求,包括语料安全、模…...
2024年AI辅助研发的趋势和影响
摘要:随着人工智能技术的迅猛发展,2024年AI辅助研发正成为科技界和工业界的瞩目焦点。本文将探讨AI辅助研发在各个领域的应用和影响,并展望2024年AI辅助研发的趋势。 引言 随着人工智能技术的不断进步,AI辅助研发正逐渐渗透到各…...
2k_Day1:今天是设计模式的大白话1
大白话: 原则有一点很难做到,就是定义好的类,只能加不能改(开放-关闭原则) 1.工厂模式就是,比如你定了一个汽车接口,然后小车、中车、大车都继承这个接口,这时,定一个汽…...

面试官:说说你对事件循环的理解
一、事件循环是什么 首先,JavaScript是一门单线程的语言,意味着同一时间内只能做一件事,但是这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环 在JavaScript中,所有的任务都可以分为 同步任务&#…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...

【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...

五子棋测试用例
一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏,有着深厚的文化底蕴。通过将五子棋制作成网页游戏,可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家,都可以通过网页五子棋感受到东方棋类…...

大模型——基于Docker+DeepSeek+Dify :搭建企业级本地私有化知识库超详细教程
基于Docker+DeepSeek+Dify :搭建企业级本地私有化知识库超详细教程 下载安装Docker Docker官网:https://www.docker.com/ 自定义Docker安装路径 Docker默认安装在C盘,大小大概2.9G,做这行最忌讳的就是安装软件全装C盘,所以我调整了下安装路径。 新建安装目录:E:\MyS…...