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

STM32—FLASH闪存

1.FLASH简介

  • STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程
    • 我们怎么操作这些存储器呢?这就需要用到这个闪存存储器接口了,闪存存储器接口是一个外设,是这个闪存的管理员,毕竟闪存的操作很麻烦,涉及到擦除、编程、等待忙、解锁等等操作,所以这里,我们需要把我们的指令和数据,写入到这个外设的相应寄存器,然后这个外设就会自动去操作对应的存储空间,后面写这个外设可以对程序存储器和选项字节,这两部分,进行擦除和编程,对比上面的三个部分少了系统存储器这个区域,因为系统存储器是原厂写入的Bootloader程序,不允许我们修改
  • 读写FLASH的用途:
    • 利用程序存储器的剩余空间来保存掉电不丢失的用户数据
      • 对于我们这个C8T6芯片来说,它的程序存储器容量是64K,一般我们写个简单的程序,可能就只占前面的很小一部分空间,剩下的大片空余空间可以加以利用,比如存储一些我们自定义的数据,这样就非常方便,而且可以充分利用盗源,不过这里要注意,我们在选取存储区域时,一定不要覆盖了原有的程序,要不然程序自己把自己给破坏了,之后程序就运行不了了,一般存储少量的数据,我们就选最后几页存储就行了
    • 通过在程序中编程(IAP),实现程序的自我更新
      • 我们在存储用户数据时要避开程序本身,以免破坏程序,但如果,我们就非要修改程序本身会发生什么呢?那这就是第二点提到的功能,在程序中编程,利用程序,来修改程序本身,实现程序的自我更新,这个在程序中编程,就是IAP
  • 在线编程(In-Circuit Programming-lCP)也可以叫在电路中编程,用于更新程序存储器的全部内容,它通过JTAG、SWD协议或系统加载程序(Bootloader)下载程序
    • 这个JTAG、SWD,就是仿真器下载程序,就是我们目前用的STLINK使用SWD下载程序,每次下载,都是把整个程序壳全更新掉,那系统加载程序,就是系统存储器的Bootloader,也就是串口下载,串口下载,也是更新整个程序,这就是我们一直在用的ICP下载方式,
  • 在程序中编程(In-Application Programming-IAP)可以使用微控制器支持的任一种通信接口下载程序
    • 怎么实现?那比如,这是整个程序存储器,我们首先需要自己写一个Bootloader程序,并且存放在程序更新时,不会覆盖的地方,比如我们放在这后面,然后,需要更新程序时,我们控制程序跳转到这个自己写的Bootloader里来,在这里面,我们就可以接收任意一种通信接口传过来的数据,比如串口、USB、蓝牙转串口、WIFI转串口等等,这个传过来的数据,就是待更新的程序,然后我们控制FLASH读写,把收到的程序,写入到前面,程序正常运行的地方,写完之后,再控制程序跳转回正常运行的地方或者直接复位,这样程序就完成了自我升级,这个过程其实就是和系统存储器这个的Bootloader-样,因为程序要实现自我升级,在升级过程中肯定需要布置一个辅助的小机器人来临时干活,只不过是系统存储器的Bootloader写死了,只能用串口下载到指定位置 ,启动方式也不方便,只能配置BOOT引脚触发启动,而我们自己写Bootloader的话,就可以想怎么收怎么收,想写到哪就写到哪,想怎么启动就怎么启动,并且这整个升级过程,程序都可以自主完成,实现在程序中编程,更进一步,就可以直接实现远程升级了,对吧,非常方便      

2.闪存模块组织

我们C8T6芯片的闪存容量是64K,属于中容量产,对于小容量产品和大容量产品,闪存的分配方式有些区别,可以参考手册

首先看一下第一列的几个块,这里分为了三个块,第一个是主存储器也就是我们刚才说的程序存储器,用来存放程序代码的,这是最主要,也是容量最大的一块,下面第二个,是信息块,里面又可以分为启动程序代码和用户选择字节,其中启动程序代码就是刚才说的系统存储器,存放的是原厂写入的Bootloader,用于串口下载,然后下面这个用户选择字节也就是刚才说的选项字节,存放一些独立的参数,这个选项字节,在手册里一直都称作选择字节,英文是Option Bytes,然后最后一块是闪存存储器接口寄存器,这一块的存储器,实际上并不属于闪存,你看它的地址就知道,地址都是40开头的,说明这个存储器接口寄存器就是一个普通的外设,和之前讲的GPIO、定时器、串口等等都是一个性质的东西,这些存储器,它们的存储介质,也都是SRAM,这个闪存存储器接口就是上面这些闪存的管理员,这些寄存器,就是用来控制擦除和编程这个过程的,只有程序存储器、系统存储器和选项字节才是真正的闪存

对于主存储器这里对它进行了分页,分页是为了更好地管理闪存,擦除和写保护,都是以而为单位的,这一点和之前W25Q64那一节的闪存一样,同为闪存,它们的特性基本一样,写入前必须擦除,擦除必须以最小单位进行,擦除后数据位全变为1,数据只能1写0,不能0写1,擦除和写入之后都需要等待忙,这些都是一样的,那W25064的分配方式是先分为块Block,再分为康区Sector比较复杂,这里,就比较简单了,它只有一个基本单位,就是页,每页的大小都是1K,0~127,总共128页,总容量就是128K,对于C8T6来说,它只有64K,所以C8T6的页只有一半,0~63,总共64页,共64K,然后看一下页的地址范围:第一个页的起始地址,就是程序存储器的起始地址,0x0800 0000,之后就是一个字节一个地址,依次线分配了,看一下每页起始地址的规律首先是0000然后0400、0300、0C00...到FC00,所以,地址只要以000、400、800、C00结尾的,都一定是页的起始地址,对吧

系统存储器,它的起始地址是0x1FFF F000,它的容量是2K,再下面,选项字节,起始地址是0X1FFF F800,容量是16个字节,里面只有几个字节的配置参数,我们平时说的,芯片闪存容量是64K、128K,它指的只是主存储器的容量,下面信息块的两个东西,虽然也是闪存,但是并不统计在这个容量里,这就是闪存的分配方式

那最后,就是这个闪存接口寄存器了,里面包括KEYR键寄存器、SR状态寄存器、CR控制寄存器等等,外设的起始地址是0x4002 2000,每个寄存器都是4个字节,也就是32位,这就是这个外设的寄存器

3.FLASH基本结构

整个闪存分为程序存储器、系统存储器和选项字节三部分,这里程序存储器我以C8T6为例,它是64K的共64页,最后一页的起始地址是0800 FC00,左边这里,是闪存存储器接口,手册里还有个名称闪存编程和擦除控制器(FPEC),然后,这个控制器,就是闪存的管理员,它可以对程序存储器进行擦除和编程,也可以对选项字节进行擦除和编程,当然系统存储器是不能擦除和编程的,之后选项字节,里面有很大一部分配置位,其实是配置主程序存储器的读写保护的,所以右边画的,写入选项字节,可以配置程序存储器的读写保护,当然选项字节还有几个别的配置参数

4.操作控制器FPEC

细节问题,如何操作这个控制器FPEC,来对程序存储器和选项字节进行擦除和编程,首先,第一步,是FLASH解锁,这个和之前W25Q64-样,W25064操作之前需要写使能,这个FLASH,操作之前需要解锁,目的,都是为了防止误操作,那这里,解锁的方式,和之前独立看门狗一样,都是通过在键寄存器写入指定的键值来实现,使用键寄存器的好处就是,更能防止误操作,每一个指令,必须输密码才能完成,,,,,,,,,,,,,,,,,,,,,

4.1FLASH解锁

5.使用指针访问存储器

因为STM32内部的存储器是直接挂在总线上的,所以这时,再读写某个存储器就非常简单了,直接使用C语言的指针,来访问即可,__IO对应C语言的关键字,volatile,volatile,直译就是,易变的数据,在这个数据类型前面加上volatile,是一个安全保障措施,用一句话来说,就是防止编译器优化,首先说一下,Keil编译器默认情况下是最低优化等级,这时,加不加这个volatile,都没有影响,如果,你要提高编译器优化等级,这时候就会有问题了,用途就是,可以去除无用的繁架代码,降低代码空间,提升运行效率,但优化之后,编译器在某些地方可能会弄巧成拙,比如,你想用变量计数空循环的方式实现延时函数,那编译器优化的时候,可能会说你这段延时函数好像没用啊还自白浪赛时间,我直接给你优化掉,这就弄巧成拙了,因为我们本意就是靠浪装时间来延时,这时,我们就可以在延时的变量定义前面加上volatile,告诉编译器我无论对这个变量干什么,你都原封不动地去执行,别给我优化掉了

另外,编译器还会利用缓存来加速代码,比如如果你要频繁读写内存的某个变量,那最常见的优化方式就是先把变量转移到高速缓存里来,在STM32内核里,有一个类似缓存的工作组寄存器,这些寄存器的访问速度最快,我先把变量放在缓存里,需要读写的时候,直接访问缓存就行了,用完之后,再写回内存,这是一个优化方案,但是,如果你的程序里有多个线程,比如中断函数,在中断函数里,你改变了这个原始变量,那可能缓存并不知道你更改了,下次程序还看缓存的变量,就会造成数据更改不同步的问题,这时,我们的做法也是,读取变量定义的前面加上一个volatile,告诉编译器这个变量是易变的,每次读取你都得执行到位,要直接从内存里找,不要再用缓存优化了,所以总结-下就是,如果开启了编译器优化,在无意义加减变量,多线程更改变量,读写与硬件相关的存储器时都需要加上volatile,防止被编译器优化,如果你默认,不开编译器优化,那就无所谓了,加不加都一样

其中,读取,可以直接读,写入,需要解锁,并且执行后面的流程
 

5.1 程序存储器全擦除

第 步是读取LOCK位,看一下芯片锁没锁,下面,如果LOCK位=1,锁住了,就执行解锁过程,在KEYR寄存器,先写入KEY1,再写入KEY2,这里,如果它当前没锁住就不用解锁了,在库函数中,并没有这个判断,库函数是直接执行解锁过程,解锁之后,首先,置控制寄存器里的MER(Mass Erase)位为1,然后再置STRT(Start)位为1,其中置STRT为1是触发条件,STRT为1之后,芯片开始干活,然后芯片看到,MER位是1,它就知道,接下来要干的活就是全擦除,这样内部电路就会自动执行全擦除的过程,然后继续擦除也是需要花一段时间的,所以擦除过程开始后程序要执行等待,判断状态寄存器的BSY(BuSy)位是否为1,BSY位表示芯片是否处于忙状态,BSY为1,表示芯片忙,所以这里,如果判断BSY=1,就跳转回来,继续循环判断,直到BSY=0,跳出循环,这样全擦除过程就结束了,最后一步,这里写的是读出并验证所有页的数据,读出并验证所有页的数据,这个是测试程序才要做的,正常情况下,全擦除完成了,我们默认就是成功了

5.2 程序存储器页擦除

第一步,上面这块一样的,是解锁的流程,第二步,这个方框里的置控制寄存器的PER(Page Erase) 位为1,然后,在AR(Address Register)地址寄存器中选择要擦除的页,最后,置控制寄存器的STRT位为1,置STRT为1,也是触发条件,STRT为1,芯片开始干活,然后芯片看到,PER=1,它就知道,接下来要执行页擦除,然后闪存不止一页,页擦除,芯片就要知道要具体要擦哪一页,所以,它会继续看AR寄存器的数据,AR寄存器我们要提前写入一个页起始地址,这样芯片就会把我们指定的一页,给擦除掉,然后擦除开始之后,我们也需要等待BSY位,最后,读出并验证数据,这个就不用看了,这就是页擦除的过程

5.3 程序存储器编程

擦除之后,我们就可以执行写入的流程了,另外说明一下,STM32的闪存在写入之前会检查指定地址有没有擦除,如果没有擦除就写入,STM32则不执行写入操作,除非写入的全是0,这一个数据是例外,因为不擦除就写入,可能会写入错误,但全写入0的话,写入肯定是没问题的,写入的第一步,也是解锁,然后第二步,我们需要置控制寄存器的PG(Programming)位为1,表示我们即将写入数据,之后第三步,就是在指定的地址写入半字,这步,我们需要用到刚才说的这句代码使用指针,在指定地址写入数据,想写入什么数据在这里指定即可,另外这里注意写入操作,只能以半字的新式写入,其中字,Word,就是32位数据,半字,HalfWord,就是16位数据,字节,Byte,就是8位数据,如果你要写入32位,就分两次完成,这个就比较麻烦了,如果你想单独写入一个字节,还要保留另一个字节的原始数据的话,那就只能把整页数据都读到SRAM,再随意修改SRAM数据,修改全部完成之后再把墪页都擦除,最后再把整页都写回去,所以,如果你想像SRAM一样随心所欲的读写,那最好的加法就是先把闪存的一页读到SRAM中,读写完成后,再擦除一页,整体写回去,那回到流程图这里,写入数据这个代码,就是触发开始的条件,不需要像擦除一样,置STRT位了,写入半字之后,芯片会处于忙状态,等待BSY位清0,这样写入数据的过程就完成了,那每执行这样一个流程,只能写入一个半字,如果要写入很多数据,那就不断循环调用这个流程,就可以了

6.选项字节

首先这里是选项字节的组织和用途,图里的起始地址就是我们刚才说的选项字节的起始地址1FFFF800,这一块的这些数据就是前面的用户选项字节,里面总共只有16个字节,其中有一半的名称,前面都带了个n,比如RDP和nRDP,这个意思就是你在写入RDP数据时,要同时在nRDP写入数据的反码,这样写入操作才是有效的,如果芯片检测到这两个存储器不是反码的关系,那就代表数据无效,有错误,对应的功能就不执行,这是一个安全保障措施,当然这个写入反码的过程,硬件会自动计算,并写入

1位保护4x8=32页,总共保护32*4=128页,正好对应中容量的最大128页       

7.器件电子签名

也就是每个芯片的身份证号,这个数据存放的基地址是1FFFF7E8,每一个芯片的这96位数据都是不一样的,使用这个唯一ID号可以做一些加密的操作,比如你想写入一段程序只能在指定设备运行,那就可以在程序的多处加入ID号判断,如果不是指定设备的ID号,就不执行程序功能,,这样即使你的程序被盗,在别的设备上也难以运行,这就是STM32的电子签名

相关文章:

STM32—FLASH闪存

1.FLASH简介 STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程 我们怎么操作这些存储器呢?这就需要用到这个闪存存储器接口了,闪…...

AP上线的那些事儿(1)capwap建立过程、设备初始化以及二层上线

1、了解FITAP与AC的建立过程 之前我们已经知道了FATAP与FIT是一对双胞胎一样的兄弟,FAT哥哥能够直接独立使用当AP桥接、路由器等,而弟弟FIT则比较薄弱,独自发挥不出功效,需要一位师傅(AC)来带领&#xff0c…...

10 django管理系统 - 管理员管理 - 新建管理员(通过模态框和ajax实现)

在文章“04 django管理系统 - 部门管理 - 新增部门”中&#xff0c;我们通过传统的新增页面来实现部门的添加。 在本文中&#xff0c;我们通过模态框和ajax来实现管理员的新增。 首先在admin_list.html中新建入口&#xff0c;使用按钮 <div class"panel-heading&quo…...

Mysql中表字段VARCHAR(N)类型及长度的解释

本文将针对MySQL 中 varchar (N)类型字段的存储方式进行解释&#xff0c;主要是对字符和字节的关系的理解。 1. varchar (N) 中的 N varchar (N) 中的 N 表示字符数&#xff0c;而不是字节数。这意味着 N 表示你可以存储多少个字符。 字符数&#xff1a;指的是字符的个数&…...

git提交信息写错处理方式

在Git中&#xff0c;你可以通过使用rebase命令来合并提交记录。以下是一个简单的步骤来合并一系列提交&#xff1a; 使用git rebase -i开始交互式变基。在打开的编辑器中&#xff0c;你会看到一个提交列表。若要合并提交&#xff0c;将要合并的提交前面的pick改为squash或s。保…...

C#从零开始学习(用unity探索C#)(unity Lab1)

初次使用Unity 本章所有的代码都放在 https://github.com/hikinazimi/head-first-Csharp Unity的下载与安装 从 unity官网下载Unity Hub Unity的使用 安装后,注册账号,下载unity版本,然后创建3d项目 设置窗口界面布局 3D对象的创建 点击对象,然后点击Move Guzmo,就可以拖动…...

【SpringBoot】15 Echarts+Thymeleaf 绘制各种图表

Gitee仓库 https://gitee.com/Lin_DH/system 介绍 ECharts是百度开源的一个前端组件。它是一个使用 JavaScript 实现的开源可视化库&#xff0c;可以流畅的运行在 PC 和移动设备上&#xff0c;兼容当前绝大部分浏览器&#xff08;IE8/9/10/11&#xff0c;Chrome&#xff0c;…...

网络学习笔记

一、网络的结构与功能 网络的鲁棒性与抗毁性 如果在移走少量节点后网络中的绝大部分节点仍然是连通的&#xff0c;那么就该网络的连通性对节点故障具有鲁棒性 网络上的动力学 动力系统&#xff1a;自旋、振子或混沌的同步、可激发系统 传播过程&#xff1a;信息传播与拥堵…...

[论文笔记]HERMES 3 TECHNICAL REPORT

引言 今天带来论文HERMES 3 TECHNICAL REPORT&#xff0c;这篇论文提出了一个强大的工具调用模型&#xff0c;包含了训练方案介绍。同时提出了一个函数调用标准。 为了简单&#xff0c;下文中以翻译的口吻记录&#xff0c;比如替换"作者"为"我们"。 聊天模…...

MySQL-19.多表设计-一对多-外键

一.多表问题分析 二.添加外键 三.外键约束的问题...

MySQL程序介绍<一>

目录 MySQL程序简介 mysqld - MySQL 服务器 ​编辑 mysql - MySQL 命令⾏客⼾端 MySQL程序简介 1.MySQL安装完成通常会包含如下程序&#xff1a; Linux系统程序⼀般在 /usr/bin⽬录下&#xff0c;可以通过命令查看 windows系统⽬录&#xff1a; 你的安装路径\MySQL Server…...

Leetcode 第 419 场周赛题解

Leetcode 第 419 场周赛题解 Leetcode 第 419 场周赛题解题目1&#xff1a;3318. 计算子数组的 x-sum I思路代码复杂度分析 题目2&#xff1a;3319. 第 K 大的完美二叉子树的大小思路代码复杂度分析 题目3&#xff1a;思路代码复杂度分析 题目4&#xff1a;3321. 计算子数组的 …...

那些年 我们说走就走

那些年 我们说走就走 —— 2022-03-20 二月十八 春分 我总是钟情于原生景色&#xff0c;犹如那句 “落霞与孤鹜齐飞&#xff0c;秋水共长天一色。” 所绘。 我热爱骑行&#xff0c;向往自然&#xff0c;对有着 “中国人的景观大道” 之称的 318 国道川藏线憧憬已久。 17 年暑…...

MySQL初识

在了解什么是MySQL前&#xff0c;我们先了解一下什么是数据库&#xff1f;&#xff1f; 1. 数据库简介 1.1 什么是数据库 数据库是20世纪60年代末发展起来的⼀项重要技术&#xff0c;已经成为计算机科学与技术的⼀个重要分⽀。数据库技术主要是⽤来解决数据处理的⾮数值计算问…...

基于Java微信小程序的的儿童阅读系统的详细设计和实现(源码+lw+部署文档+讲解等)

详细视频演示 请联系我获取更详细的演示视频 项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不…...

利用 OBS 推送 WEBRTC 流到 smart rtmpd

webrtc whip 推流 & whep 拉流简介 RFC 定义 通用的 webrtc 对于 SDP 协议的交换已经有对应的 RFC 草案出炉了。这就是 WHIP( push stream ) & WHEP ( pull stream ) . WHIP RFC Link: https://www.ietf.org/archive/id/draft-ietf-wish-whip-01.html WHEP RFC Link:…...

【python】极简教程3-函数

函数是将代码组织到可重用块中的一种方法。 函数调用 Python提供了许多内置函数,例如print: print(Hello, World!)函数调用通常包含函数名,后跟圆括号,括号内是参数列表。参数是传递给函数的数据,函数会基于这些数据执行操作。 数学函数 使用math函数前需要先导入mat…...

Python案例小练习——小计算器

文章目录 前言一、代码展示二、运行展示 前言 这是用python实现一个简单的计器。 一、代码展示 def calculate(num1, op, num2):if op "":return float(num1) float(num2)elif op "-":return float(num1) - float(num2)elif op "*":return…...

仓储数字化蓝图

1、仓储能力建设 2、仓储数字化建设...

【数字图像处理】第5章 图像空域增强方法

上理考研周导师的哔哩哔哩频道 我在频道里讲课哦 目录 5.1 图像噪声 相关概念 ①图像噪声的产生 ② 图像噪声分类 ③ 图像噪声特点 5.2 图像增强方法分类 ①图像增强概念 ②图像增强目的 ③图像增强技术方法: 5.3 基于灰度变换的图像增强 1. 概述: 2. 灰度变换…...

23-Oracle 23 ai 区块链表(Blockchain Table)

小伙伴有没有在金融强合规的领域中遇见&#xff0c;必须要保持数据不可变&#xff0c;管理员都无法修改和留痕的要求。比如医疗的电子病历中&#xff0c;影像检查检验结果不可篡改行的&#xff0c;药品追溯过程中数据只可插入无法删除的特性需求&#xff1b;登录日志、修改日志…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

嵌入式学习笔记DAY33(网络编程——TCP)

一、网络架构 C/S &#xff08;client/server 客户端/服务器&#xff09;&#xff1a;由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序&#xff0c;负责提供用户界面和交互逻辑 &#xff0c;接收用户输入&#xff0c;向服务器发送请求&#xff0c;并展示服务…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

python打卡第47天

昨天代码中注意力热图的部分顺移至今天 知识点回顾&#xff1a; 热力图 作业&#xff1a;对比不同卷积层热图可视化的结果 def visualize_attention_map(model, test_loader, device, class_names, num_samples3):"""可视化模型的注意力热力图&#xff0c;展示模…...