江科大笔记—读写内部闪存FLASH读取芯片ID

读写内部闪存FLASH

右下角是OLED,然后左上角在PB1和PB11两个引脚,插上两个按键用于控制。下一个代码读取芯片ID,这个也是接上一个OLED,能显示测试数据就可以了。
在这里插入图片描述

STM32-STLINK Utility

在这里插入图片描述
本节的代码调试,使用辅助软件STM32-STLINK Utility,在使用之前我们需要用stink把STM32连接好,然后我们点击这个按钮连接,可以看到下面这个窗口里显示的,就是闪存里面存储的数据了。这个软件可以直接修改闪存里的数据,可以修改选项字节数据,不用写任何代码,非常方便。

Address:可以指定想要查看的起始地址,目前是0x0800 0000,也就是整个闪存的起始地址。
Size:就是从起始地址开始,总共查看多少个字节,目前0x10000,就是查看64KB的字节数
Data Width:数据宽度,可以指定以32位/16位/8位的形式显示,分别对应,字/半字/字节。

16位
在这里插入图片描述
8位
在这里插入图片描述
闪存的操作:

第一步,读取数据,直接封装一下uint16_t Data = *((__IO uint16_t *)(0x08000000));
第二步,擦除:全擦除或页擦除,这两个功能分别对应一个库函数。执行之前,手动调用,解锁;执行之后,在加锁。
第三步,编程,有对应库函数直接调用。执行之前,手动调用,解锁;执行之后,在加锁。

选项字节的擦除和编成与主闪存的擦除和编程类似,有对应的函数。

在这里插入图片描述

用代码配置读写保护,容易造成芯片自锁,如把闪存写保护了,但程序里并没有预留解除写保护的代码,这样锁住之后,芯片就没法下载程序。但还可以再软件里自救,在这个选项字节配置里,把读写保护去掉,再Apply就能救活芯片了。

flash.h库函数

在这里插入图片描述

在这里插入图片描述
FLASH_Status返回值,是这个操作的完成状态,函数返回状态功能:返回第一个,表示芯片当前忙;返回第二个,表示编程错误;返回第三个,表示写保护错误;返回第四个,表示返回完成;返回第五个,表示等待超时。

//这三个是内核相关,不用过多了解与调用
void FLASH_SetLatency(uint32_t FLASH_Latency);
void FLASH_HalfCycleAccessCmd(uint32_t FLASH_HalfCycleAccess);
void FLASH_PrefetchBufferCmd(uint32_t FLASH_PrefetchBuffer);
//加锁解锁
void FLASH_Unlock(void);//解锁
void FLASH_Lock(void);//加锁,把CR寄存器LOCK位置1
//主闪存
FLASH_Status FLASH_ErasePage(uint32_t Page_Address); //闪存擦除某一页
FLASH_Status FLASH_EraseAllPages(void);//全擦除
FLASH_Status FLASH_EraseOptionBytes(void);//擦除选项字节FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);//在指定地址,写入字
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);//在指定地址,写入半字//选项字节的写入
FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);//自定义的Data0和Data1
FLASH_Status FLASH_EnableWriteProtection(uint32_t FLASH_Pages);//写保护
FLASH_Status FLASH_ReadOutProtection(FunctionalState NewState);//读保护
FLASH_Status FLASH_UserOptionByteConfig(uint16_t OB_IWDG, uint16_t OB_STOP, uint16_t OB_STDBY);//用户选项的3个配置位//获取选项字节当前状态
uint32_t FLASH_GetUserOptionByte(void);//获取用户选项的三个配置位
uint32_t FLASH_GetWriteProtectionOptionByte(void);//获取写保护状态
FlagStatus FLASH_GetReadOutProtectionStatus(void);//获取读保护状态  
............................//获取自定义的Data0和Data1没有给现成函数,直接指针访问就可以了。FlagStatus FLASH_GetPrefetchBufferStatus(void);//获取预取缓冲区状态 不用了解
void FLASH_ITConfig(uint32_t FLASH_IT, FunctionalState NewState);//中断使能
FlagStatus FLASH_GetFlagStatus(uint32_t FLASH_FLAG);//获取标志位
void FLASH_ClearFlag(uint32_t FLASH_FLAG);//清除标志位
FLASH_Status FLASH_GetStatus(void);//获取状态
FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);//等待上一次操作 等待忙等待BSY为0(上面主闪存这些函数内部已经实现等待忙操作了,此函数不用我们单独调用)

注意:使用完STLINK这个软件,要及时断开连接,要不然设备占用,keil下载出错。

在Store模块,要有SRAM缓存数组来管理FLASH的最后一页,实现参数任意读写和保存

MyFLASH.c

#include "stm32f10x.h"                  // Device header/*** 函    数:FLASH读取一个32位的字* 参    数:Address 要读取数据的字地址* 返 回 值:指定地址下的数据*/
uint32_t MyFLASH_ReadWord(uint32_t Address)
{return *((__IO uint32_t *)(Address));	//使用指针访问指定地址下的数据并返回
}/*** 函    数:FLASH读取一个16位的半字* 参    数:Address 要读取数据的半字地址* 返 回 值:指定地址下的数据*/
uint16_t MyFLASH_ReadHalfWord(uint32_t Address)
{return *((__IO uint16_t *)(Address));	//使用指针访问指定地址下的数据并返回
}/*** 函    数:FLASH读取一个8位的字节* 参    数:Address 要读取数据的字节地址* 返 回 值:指定地址下的数据*/
uint8_t MyFLASH_ReadByte(uint32_t Address)
{return *((__IO uint8_t *)(Address));	//使用指针访问指定地址下的数据并返回
}/*** 函    数:FLASH全擦除* 参    数:无* 返 回 值:无* 说    明:调用此函数后,FLASH的所有页都会被擦除,包括程序文件本身,擦除后,程序将不复存在*/
void MyFLASH_EraseAllPages(void)
{FLASH_Unlock();					//解锁FLASH_EraseAllPages();			//全擦除FLASH_Lock();					//加锁
}/*** 函    数:FLASH页擦除* 参    数:PageAddress 要擦除页的页地址* 返 回 值:无*/
void MyFLASH_ErasePage(uint32_t PageAddress)
{FLASH_Unlock();					//解锁FLASH_ErasePage(PageAddress);	//页擦除FLASH_Lock();					//加锁
}/*** 函    数:FLASH编程字* 参    数:Address 要写入数据的字地址* 参    数:Data 要写入的32位数据* 返 回 值:无*/
void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data)
{FLASH_Unlock();							//解锁FLASH_ProgramWord(Address, Data);		//编程字FLASH_Lock();							//加锁
}/*** 函    数:FLASH编程半字* 参    数:Address 要写入数据的半字地址* 参    数:Data 要写入的16位数据* 返 回 值:无*/
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{FLASH_Unlock();							//解锁FLASH_ProgramHalfWord(Address, Data);	//编程半字FLASH_Lock();							//加锁
}

MyFLASH.h

#ifndef __MYFLASH_H
#define __MYFLASH_Huint32_t MyFLASH_ReadWord(uint32_t Address);
uint16_t MyFLASH_ReadHalfWord(uint32_t Address);
uint8_t MyFLASH_ReadByte(uint32_t Address);void MyFLASH_EraseAllPages(void);
void MyFLASH_ErasePage(uint32_t PageAddress);void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data);
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);#endif

Store.c

#include "stm32f10x.h"                  // Device header
#include "MyFLASH.h"#define STORE_START_ADDRESS		0x0800FC00		//宏定义,存储的起始地址  (闪存的最后一页)
#define STORE_COUNT				512				//宏定义,存储数据的个数uint16_t Store_Data[STORE_COUNT];				//定义SRAM数组/*** 函    数:参数存储模块初始化    * 参    数:无* 返 回 值:无*/void Store_Init(void)      //第一大步,第一次使用的时候,对闪存进行初始化
{/*判断是不是第一次使用*/if (MyFLASH_ReadHalfWord(STORE_START_ADDRESS) != 0xA5A5)	//读取第一个半字的标志位,if成立,则执行第一次使用的初始化{MyFLASH_ErasePage(STORE_START_ADDRESS);					//擦除指定页MyFLASH_ProgramHalfWord(STORE_START_ADDRESS, 0xA5A5);	//在第一个半字写入自己规定的标志位,用于判断是不是第一次使用//把剩余的存储空间,全都置为默认值0for (uint16_t i = 1; i < STORE_COUNT; i ++)				//循环STORE_COUNT次,除了第一个标志位。注意;要从1开始,而不是0,因为第一个半字是标志位,剩下的才是有效数据{MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, 0x0000);		//除了标志位的有效数据全部清0。注:因为一个半字要占用两个地址,所以i要乘2}}/*第2大步,上电时,将闪存数据加载回SRAM数组,就是上电时恢复数据,实现SRAM数组的掉电不丢失*/for (uint16_t i = 0; i < STORE_COUNT; i ++)					//循环STORE_COUNT次,包括第一个标志位{Store_Data[i] = MyFLASH_ReadHalfWord(STORE_START_ADDRESS + i * 2);		//将闪存的数据加载回SRAM数组}
}/*** 函    数:参数存储模块保存数据到闪存* 参    数:无* 返 回 值:无*/
void Store_Save(void)  //备份保存
{MyFLASH_ErasePage(STORE_START_ADDRESS);				//擦除指定页for (uint16_t i = 0; i < STORE_COUNT; i ++)			//循环STORE_COUNT次,包括第一个标志位{MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, Store_Data[i]);	//将SRAM数组的数据,备份保存到闪存}
}/*** 函    数:参数存储模块将所有有效数据清0* 参    数:无* 返 回 值:无*/
void Store_Clear(void)
{for (uint16_t i = 1; i < STORE_COUNT; i ++)			//循环STORE_COUNT次,除了第一个标志位。注:i要从1开始,不用把标志位清0{Store_Data[i] = 0x0000;							//SRAM数组有效数据清0}Store_Save();										//更新保存数据到闪存。每次修改完数组后,都要Store_Save();保证数值和闪存数据一样;如果不Save,那下次上电,加载的是以前保存的数据
}

Store.h

#ifndef __STORE_H
#define __STORE_Hextern uint16_t Store_Data[];void Store_Init(void);
void Store_Save(void);
void Store_Clear(void);#endif

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Store.h"
#include "Key.h"uint8_t KeyNum;					//定义用于接收按键键码的变量int main(void)
{/*模块初始化*/OLED_Init();				//OLED初始化Key_Init();					//按键初始化Store_Init();				//参数存储模块初始化,在上电的时候将闪存的数据加载回Store_Data,实现掉电不丢失/*显示静态字符串*/OLED_ShowString(1, 1, "Flag:");OLED_ShowString(2, 1, "Data:");while (1){KeyNum = Key_GetNum();		//获取按键键码if (KeyNum == 1)			//按键1按下{Store_Data[1] ++;		//变换测试数据Store_Data[2] += 2;Store_Data[3] += 3;Store_Data[4] += 4;Store_Save();			//将Store_Data的数据备份保存到闪存,实现掉电不丢失}if (KeyNum == 2)			//按键2按下{Store_Clear();			//将Store_Data的数据全部清0}OLED_ShowHexNum(1, 6, Store_Data[0], 4);	//显示Store_Data的第一位标志位OLED_ShowHexNum(3, 1, Store_Data[1], 4);	//显示Store_Data的有效存储数据OLED_ShowHexNum(3, 6, Store_Data[2], 4);    //3行6列显示Store_Data[2],长度为4OLED_ShowHexNum(4, 1, Store_Data[3], 4);OLED_ShowHexNum(4, 6, Store_Data[4], 4);}
}

程序现象
Flag显示标志位:A5A5,这是之前保存的。按下Key2,可以去清0所以参数;按下Key1,可以变换测试数据,之后断定在重新上电,数据保持原样,按复位键,数据也不会丢失。
在这里插入图片描述
按下Key2,可以去清0所以参数
在这里插入图片描述
按下Key1,可以变换测试数据
在这里插入图片描述

前面一部分存储的是程序文件,最后一页存储的是用户数据,目前我们的假设是程序文件比较小,最后一页肯定是没有用到的,所以我们放心的使用最后一页,但是如果程序比较大,触及到了最后一页,那程序和用户数据存储的位置就冲突了,或者说如果你参数非常多,最后10页很大一部分都是留着存储用户数据的,这样如果前面的程序文件长一些,那样非常容易和用户数据冲突,并且这种冲突如果没发现,就会产生非常隐蔽的bug,那如何解决这个问题呢,这时我们可以给程序文件限定一个存储范围,不让它分配到后面我们用户数据的空间来。

在这里插入图片描述

这就是编译器,给各个数据分配的空间地址和范围了。如,片上的ROM,起始地址是0800 0000,注意,最高位的0省略了。它的Size:是0x10000,默认全部的64k闪存,都是程序代码分配的空间 ,

如果你想把闪存的尾部空间留着自己用,那可以把这个程序空间的size改小点,比如我们改成0XFC00,这样编译后的代码,无论如何也不会分配到最后一页了。如果Size过小,那编译的时候也会报错。

所以如果你计划把闪存尾部的很多空间都留着自己用,那就把这个程序代码的空间改小点,以免冲突。然后这个下载程序的起始地址也可以改,比如你想写个BOOTLOADER程序放在闪存尾部,那可以在这里修改下载到闪存的起始位置。

在这里插入图片描述

最右边这里是片上RAM的起始地址和大小,2000开始大小5000对应就是20K。

在这里插入图片描述

这里debug, settings,Flash download,在这里是配置下载选项,其中这个选项我们要选择第二个,擦除扇区,也就是页擦除。
第一个是,每次下载代码都全擦除再下载。
第二个是用到多少页,就擦多少页这个下载速度更快一些,如果你想在闪存尾部存储数据,那也最好选择页擦除的下载,要不然每次下载程序芯片都全擦除了。

在这里插入图片描述

程序编译之后,到底占用了多大的空间,我们可以全部编一下,在下面有一行信息就显示了Program Size,程序大小,其中有四个数,这四个数分别是什么意思,感兴趣的话可以网上搜索。
这里只需要记住,前三个数相加,得到的就是程序占用闪存的大小,后两个数相加,得到的是占用SRAM的大小。

在这里插入图片描述

这个程序大小,也可以在Target1这里(工程目录最上面)双击,会打开一个.map文件,这就是详细的编译信息。这个.map文件看完就删掉,要不然每次编译后,都会弹出窗口问你是不是要重新加载。

我们看最后面这里也有写程序的大小,并且有计算结果:
倒数第二行,是占用SRAM的大小,这里结果是2664字节,2.6kb,最后一行,是占用闪存的大小,这里是4576字节,4.47kb。

在这里插入图片描述

下载到STM32里,验证一下。可以看到,程序在0x0800 11E0地址终止,用计算机转换:16进制 11E0就是10进制的4576。观察的数据和编译的结果相符,没问题。

读取芯片ID

复制OLED的工程。

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"int main(void)
{OLED_Init();						//OLED初始化OLED_ShowString(1, 1, "F_SIZE:");	//显示静态字符串OLED_ShowHexNum(1, 8, *((__IO uint16_t *)(0x1FFFF7E0)), 4);		//使用指针读取指定地址下的闪存容量寄存器OLED_ShowString(2, 1, "U_ID:");		//显示静态字符串OLED_ShowHexNum(2, 6, *((__IO uint16_t *)(0x1FFFF7E8)), 4);		//使用指针读取指定地址下的产品唯一身份标识寄存器OLED_ShowHexNum(2, 11, *((__IO uint16_t *)(0x1FFFF7E8 + 0x02)), 4);  //第2次读是,基地址+0x02地址偏移OLED_ShowHexNum(3, 1, *((__IO uint32_t *)(0x1FFFF7E8 + 0x04)), 8);OLED_ShowHexNum(4, 1, *((__IO uint32_t *)(0x1FFFF7E8 + 0x08)), 8);while (1){}
}

程序现象
在这里插入图片描述
ID号对不对,可以用软件来验证。

在这里插入图片描述

手册

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

Langchain-实战篇-搭建本地问答机器人-01

项目背景 在这个快节奏的职场世界里&#xff0c;我们每个人都可能在某个时刻&#xff0c;化身为一头辛勤的牛或一匹奔腾的马&#xff0c;面对入职签合同时的迷茫&#xff0c;或是离职时的纠纷&#xff0c;心中难免会涌现出各种疑问。比如&#xff1a; "这份合同里的条款…

Python在Excel中设置数字格式和获取应用数字格式后的值

目录 安装Python Excel库 Python在Excel中设置数字格式 Python获取Excel中应用数字格式的单元格的显示值 总结 Excel 数字格式是用于控制单元格中数字显示方式的一组规则或代码。通过设置不同的数字格式&#xff0c;可以定义数字的显示方式&#xff0c;如小数位数、货币符号…

Java对应C++ STL的用法

sort&#xff1a; 1&#xff1a;java.util.Arrays中的静态方法Arrays.sort()方法&#xff0c;针对基本数据类型和引用对象类型的数组元素排序 2&#xff1a;java.util.Collections中的静态方法的Collections.sort()方法&#xff0c;针对集合框架中的动态数组&#xff0c;链表&…

iOS17系统适配

iOS17 新功能 文章目录 iOS17 新功能iOS17支持哪几款机型Xcode15新特性iOS17-开发适配指南 横屏待机 在iOS 17中&#xff0c;还带来了横屏待机功能&#xff0c;苹果将这个新功能命名为“Standby”模式&#xff0c;为 iPhone 带来了全新的玩法。iPhone启用之后&#xff0c;默认情…

1-Pandas是什么

Pandas是什么 Pandas 是一个开源的第三方 Python 库&#xff0c;从 Numpy 和 Matplotlib 的基础上构建而来&#xff0c;享有数据分析“三剑客之一”的盛名&#xff08;NumPy、Matplotlib、Pandas&#xff09;。Pandas 已经成为 Python 数据分析的必备高级工具&#xff0c;它的…

PyTorch Tensor进阶操作指南(二):深度学习中的关键技巧

本文主要讲tensor的裁剪、索引、降维和增维 Tensor与numpy互转、Tensor运算等&#xff0c;请看这篇文章 目录 9.1、首先看torch.squeeze()函数&#xff1a; 示例9.1&#xff1a;&#xff08;基本的使用&#xff09; 小技巧1&#xff1a;如何看维数 示例9.2&#xff1a;&a…

安全架构概述_1.信息安全面临的威胁

在当今以计算机、网络和软件为载体的数字化服务几乎成为人类赖以生存的手段。与之而来的计算机犯罪呈现指数上升趋势&#xff0c;因此&#xff0c;信息的可用性、完整性、机密性、可控性和不可抵赖性等安全保障显得尤为重要&#xff0c;而满足这些诉求&#xff0c;离不开好的安…

Vulnhub-AdmX

主机发现 靶机 &#xff1a; 192.168.145.131131 这台主机 存活 端口扫描 nmap -sV -O -p 1-65535 192.168.145.131 存在 80 端口 &#xff0c;这里连ssh 端口都没了 80 端口存在 Apache httpd 2.4.1 存在 Apache 默认页面 像这种页面 &#xff0c;没有什么具体的价值 扫描一…

嵌入式Linux系统编程 — 4.6 atoi、strtol等函数实现字符串与数字转换

目录 1 字符串转整形数据 1.1 函数功能 1.2 示例程序 2 字符串转浮点型数据 2.1 函数介绍 2.2 示例程序 3 数字转字符串 3.1 函数介绍 3.2 函数原型 在编程中&#xff0c;经常会需要将数字组成的字符串转换为相应的数字、或者将数字转换为字符串&#xff0c;在 C 函数…

连环计 | 第6集 | 百姓有倒悬之危,君臣有累卵之急 | 貂蝉 | 三国演义 | 逐鹿群雄

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 &#x1f4cc;这篇博客分享的是《三国演义》文学剧本第Ⅰ部分《群雄逐鹿》的第6️⃣集《连环计》的经典语句和文学剧本全集台词 文章目录 1.经典语句2.文学剧本台词 …

LabVIEW材料样本结构缺陷检测

本文介绍了一种基于LabVIEW的实验室振动特性分析测试装置&#xff0c;通过分析振动特性来检测结构缺陷。文章详细描述了具体案例、硬件型号、工作原理、软件功能以及注意事项。 硬件型号 振动传感器&#xff1a;PCB Piezotronics 352C33加速度计 数据采集卡&#xff1a;NI PXI…

python(基础语法,pandas,numpy,正则表达式,数据预处理)

python学习推荐网址&#xff1a; 白月黑羽 一、语法基础 目标&#xff1a; • list、tuple、set、dict的基本用法 • 内置函数 len&#xff08;&#xff09;&#xff0c; eval&#xff08;&#xff09;&#xff0c;range&#xff08;&#xff09;&#xff0c;sort&#xff08;…

Linux自动化交互脚本expect开发

在日常开发任务中&#xff0c;运行shell脚本有时候会提示输入密码的操作&#xff0c;如何让脚本自动输入密码呢&#xff1f;这时使用expect帮我们输入&#xff0c;Expect是基于Tcl发展而来的&#xff0c;它不仅可以进行交互&#xff0c;还可以根据程序的提示模拟标准输入&#…

西安高校大学智能制造实验室数字孪生可视化系统平台建设项目验收

随着工业4.0时代的到来&#xff0c;智能制造成为推动制造业转型升级的关键。为了培养学生的创新能力和实践能力&#xff0c;西安高校大学决定建设智能制造实验室&#xff0c;并引入数字孪生技术&#xff0c;构建可视化系统平台。项目旨在通过数字孪生技术&#xff0c;实现对制造…

微信小程序毕业设计-线上教育商城系统项目开发实战(附源码+论文)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…

刷代码随想录有感(120):贪心算法——买卖股票的最佳时机

题干&#xff1a; 代码&#xff1a; class Solution { public:int maxProfit(vector<int>& prices) {int low INT_MAX;int res INT_MIN;for(int i 0; i < prices.size(); i){low min(low, prices[i]);res max(res, prices[i] - low);}return res;} }; 贪心…

ARCGIS添加在线地图

地图服务地址&#xff1a;http://map.geoq.cn/ArcGIS/rest/services 具体方法&#xff1a; 结果展示&#xff1a;

T4打卡 学习笔记

所用环境 ● 语言环境&#xff1a;Python3.11 ● 编译器&#xff1a;jupyter notebook ● 深度学习框架&#xff1a;TensorFlow2.16.1 ● 显卡&#xff08;GPU&#xff09;&#xff1a;NVIDIA GeForce RTX 2070 设置GPU from tensorflow import keras from tensorflow.keras…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] LYA的字符串拼接游戏(200分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f…

GO sync包——读写锁

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…