STM32之SPI读写W25Q128芯片

news2024/9/22 13:38:41
  1. SPI简介

    STM32的SPI是一个串行外设接口。它允许STM32微控制器与其他设备(如传感器、存储器等)进行高速、全双工、同步的串行通信。通常包含SCLK(串行时钟)、MOSI(主设备输出/从设备输入Master Output Slave Input)、MISO(主设备输入/从设备输出Master Input Slave Output)和NSS/CS片选信号Chip Select)这4条线,支持多个从设备连接到一个主设备上。

SPI,是一种高速的,全双工同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROMFLASH实时时钟AD转换器,还有数字信号处理器和数字信号解码器之间。

2. SPI使用步骤

我们将利用 STM32 的 SPI 来读取外部 SPI FLASH 芯片(W25Q128)为例,学习SPI。

2.1 SPI时钟SCLK

   SPI时钟特点主要包括:时钟速率时钟极性时钟相位三方面。

   时钟速率

SPI总线上的主设备必须在通信开始时候配置并生成相应的时钟信号。从理论上讲,只要实际可行,时钟速率就可以是你想要的任何速率,当然这个速率受限于每个系统能提供多大的系统时钟频率,以及最大的SPI传输速率。

时钟极性

根据硬件制造商的命名规则不同,时钟极性通常写为CKPCPOL。时钟极性和相位共同决定读取数据的方式,比如信号上升沿读取数据还是信号下降沿读取数据。

CKP可以配置为1或0,这意味着可根据需要将时钟的默认状态(IDLE)设置为高或低。极性反转可以通过简单的逻辑逆变器实现。须参考设备的数据手册才能正确设置CKPCKE

CKP = 0:时钟空闲IDLE为低电平0;

CKP = 1:时钟空闲IDLE为高电平1。

时钟相位

根据硬件制造商的不同,时钟相位通常写为CKECPHA。顾名思义,时钟相位/边沿,也就是采集数据时是在时钟信号的具体相位或者边沿;

CKE = 0:在时钟信号SCK的第一个跳变沿采样

CKE = 1:在时钟信号SCK的第二个跳变沿采样

2.2四种操作根据

SPI的时钟极性时钟相位特性可以设置4不同的SPI通信操作模式,它们的区别是定义了在时钟脉冲的哪条边沿转换(toggles)输出信号,哪条边沿采样输入信号,还有时钟脉冲的稳定电平值(就是时钟信号无效时是高还是低),详情如下所示:

Mode0:CKP=0,CKE=0当空闲态时,SCK处于低电平,数据采样是在第1个边沿,也就是SCK由低电平到高电平的跳变,所以数据采样是在上升沿(准备数据),(发送数据)数据发送是在下降沿。

Mode1:CKP=0,CKE=1当空闲态时,SCK处于低电平,数据发送是在第2个边沿,也就是SCK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。

Mode2:CKP=1,CKE=0当空闲态时,SCK处于高电平,数据采集是在第1个边沿,也就是SCK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。

Mode3:CKP=1,CKE=1当空闲态时,SCK处于高电平,数据发送是在第2个边沿,也就是SCK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。

图中黑线采样数据的时刻蓝线SCK时钟信号

举个例子,下图是SPI Mode0读/写时序,可以看出SCK空闲状态为低电平,主机输出数据在第一个跳变沿被从机采样,主机输入数据同理。

3.STM32相关的SPI

    STM32的SPI外设可用作通讯的主机及从机, 支持最高的SCK时钟频率为fpclk/2 (STM32F103型号的芯片默认fpclk1为36MHz, fpclk2为72MHz),完全支持SPI协议的4种模式,数据帧长度可设置为8位或16位, 可设置数据MSB先行或LSB先行。它还支持双线全双工、双线单向以及单线模式。 其中双线单向模式可以同时使用MOSI及MISO数据线向一个方向传输数据,可以加快一倍的传输速度。而单线模式则可以减少硬件接线, 当然这样速率会受到影响。我们只讲解双线全双工模式

3.1时钟控制逻辑

    时钟由寄存器控制。SCK线的时钟信号,由波特率发生器根据“控制寄存器CR1”中的BR[0:2]位控制,该位是对fpclk时钟的分频因子, 对fpclk的分频结果就是SCK引脚的输出时钟频率,计算方法见表 BR位对fpclk的分频。

其中的fpclk频率是指SPI所在的APB总线频率, APB1为fpclk1,APB2为fpckl2。

通过配置“控制寄存器CR”的“CPOL位”及“CPHA”位可以把SPI设置成前面分析的4种SPI模式。

实际应用中,我们一般不使用STM32 SPI外设的标准NSS信号线,而是更简单地使用普通的GPIO,软件控制它的电平输出,从而产生通讯起始和停止信号。

3.2通讯过程

STM32使用SPI外设通讯时,在通讯的不同阶段它会对“状态寄存器SR”的不同数据位写入参数,我们通过读取这些寄存器标志来了解通讯状态。主发送器通讯过程中的是“主模式”流程,即STM32作为SPI通讯的主机端时的数据收发过程。

3.3从代码层面理解SPI

我们将利用 STM32 的 SPI 来读取外部 SPI FLASH 芯片(W25Q128),实现类似IIC的功能。

    使能 SPI2 的时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE );//PORTB 时钟使能

RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE );//SPI2 时钟使能

GPIOB端口挂在STM32的APB2时钟线上。SPI2是挂在STM32的APB1时钟线上。

配置相关引脚的复用功能

这里使用 PB13、14、15 这 3 个(SCK.、MISO、MOSI,CS 使用软件管理方式),所以设置这三个为复用 IO。

GPIO_InitTypeDef GPIO_InitStructure;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15 复用推挽输出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化 GPIOB

SPI2的引脚在PB上,可以参考W25Q128硬件连接图。

初始化 SPI2, 设置 SPI2 工作模式

库函数中是通过 SPI_Init 函数来实现。

void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct)

SPI_InitTypeDef 的定义:

typedef struct

{

uint16_t SPI_Direction;

uint16_t SPI_Mode;

uint16_t SPI_DataSize;

uint16_t SPI_CPOL;

uint16_t SPI_CPHA;

uint16_t SPI_NSS;

uint16_t SPI_BaudRatePrescaler;

uint16_t SPI_FirstBit;

uint16_t SPI_CRCPolynomial;

}SPI_InitTypeDef;

SPI_BaudRatePrescaler设置 SPI 波特率预分频值决定 SPI 的时钟的参数,从不分频道 256 分频 8 个可选值

SPI_BaudRatePrescaler_256 //256 分频值

传输速度为 72M/256=281.25KHz。

初始化代码:

SPI_InitTypeDef SPI_InitStructure;

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线双向全双工

SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主 SPI

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // SPI 发送接收 8 位帧结构

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串行同步时钟的空闲状态为高电平

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//第二个跳变沿数据被采样

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS 信号由软件控制

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //预分频 256

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 位开始

SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值计算的多项式

SPI_Init(SPI2, &SPI_InitStructure); //根据指定的参数初始化外设 SPIx 寄存器

使能SPI2

SPI_Cmd(SPI2, ENABLE); //使能 SPI 外设

SPI2_ReadWriteByte(0xff); //④启动传输,主机发一个字节,进行一次传输,可以启动传输

SPI传输数据

发送数据函数

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data)

接收数据函数

uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)

查看 SPI 传输状态函数

判断数据是否传输完成,发送区是否为空
判断接收是否完成,接收区是否空

接收

SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE)

发送

SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)

设置SPI2速度函数

单独的设置分频系数的函数

//SPI 速度设置函数

//SpeedSet://SPI_BaudRatePrescaler_256 256 分频 (SPI 281.25K@sys 72M)

void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)

{

    assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));

    SPI2->CR1&=0XFFC7;

    SPI2->CR1|=SPI_BaudRatePrescaler; //设置 SPI2 速度

    SPI_Cmd(SPI2,ENABLE);

}

读写一个字节

u8 SPI2_ReadWriteByte(u8 TxData)

{

    u8 retry=0;

    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //等待发送区空

    {

       retry++;//重试

       if(retry>200)return 0;

    } //读取两百次还没有值,说明无效,返回

    SPI_I2S_SendData(SPI2, TxData); //通过外设 SPIx 发送一个数据

    retry=0;

    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //等待接收完一个 byte

    {

       retry++;

       if(retry>200)return 0;

    }

    return SPI_I2S_ReceiveData(SPI2); //返回通过 SPIx 最近接收的数据

}

W25Q128

• W25Q128 是华邦公司推出的大容量 SPI FLASH 产品,W25Q128 的容量为 128Mb,该系列还有 W25Q80/16/32/64等。

擦除

W25Q128 的最小擦除单位为一个扇区,也就是每次必须擦除 4K 个字节。
    这样要求芯片必须有 4K 以上 SRAM 才能很好的操作。

W25QXX驱动解读

W25QXX.h

W25QXX_CS片选,值0选定,1取消

初始化SPI

读取状态寄存器

写状态寄存器

擦除一个扇区

读取 SPI FLASH

//在指定地址开始读取指定长度的数据

//pBuffer:数据存储区

//ReadAddr:开始读取的地址(24bit)

//NumByteToRead:要读取的字节数(最大 65535)

void W25QXX_Read (u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)

{

u16 i;

SPI_FLASH_CS=0; //使能器件

SPI2_ReadWriteByte(W25X_ReadData); //发送读取命令

SPI2_ReadWriteByte((u8)((ReadAddr)>>16)); //发送 24bit 地址

SPI2_ReadWriteByte((u8)((ReadAddr)>>8));

SPI2_ReadWriteByte((u8)ReadAddr);

for(i=0;i<NumByteToRead;i++)

{

pBuffer[i]=SPI2_ReadWriteByte(0XFF); //循环读数

}

SPI_FLASH_CS=1;

}

无检查写函数

//在指定地址开始写入指定长度的数据,但是要确保地址不越界!

//pBuffer:数据存储区

//WriteAddr:开始写入的地址(24bit)

//NumByteToWrite:要写入的字节数(最大65535)

//CHECK OK

void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)  

{                    

    u16 pageremain;   

    pageremain=256-WriteAddr%256; //单页剩余的字节数              

    if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节,这也是结束标识

    while(1)

    {     

        W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);

        if(NumByteToWrite==pageremain)break;//写入结束了

        else //NumByteToWrite>pageremain

        {

            pBuffer+=pageremain;

            WriteAddr+=pageremain; 

            NumByteToWrite-=pageremain;          //减去已经写入了的字节数

            if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节

            else pageremain=NumByteToWrite;       //不够256个字节了

        }

        //按照页剩余写一次,然后256个字节的写,然后写最后一页多出来的。

    };     

}

NoCheck是说可以跨扇区的写
下方表示写了一个扇区

W25QXX_Write函数

作用与 W25QXX_Flash_Read 的作用类似,不过是用来写数据到 W25Q128 里面的,其代码如下:

u8 W25QXX_BUFFER[4096];

void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)

{

    u32 secpos;

    u16 secoff;

    u16 secremain;

    u16 i;

    u8 * W25QXX_BUF;

    W25QXX_BUF=W25QXX_BUFFER;

    secpos=WriteAddr/4096;//扇区地址,每个扇区是4096,所以除以4096得到的整数就是扇区的地址标号

    secoff=WriteAddr%4096;//在扇区内的偏移

    secremain=4096-secoff;//扇区剩余空间大小

    //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用

    if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于 4096 个字节

    while(1)

    {

        W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容

        //secpos*4096是该扇区的起始地址

        for(i=0;i<secremain;i++)//校验数据

        {

            if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除,偏移地址内有数据

//擦除后的默认值是0xFFF

        }

        if(i<secremain)//需要擦除

        {

            W25QXX_Erase_Sector(secpos); //擦除这个扇区

            for(i=0;i<secremain;i++) //复制

            {

                W25QXX_BUF[i+secoff]=pBuffer[i];

            }

            W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区

        }

        else

            W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);

        //如果扇区剩余空间足够,直接写入扇区剩余区间.

        //是否需要写入下一个扇区

        if(NumByteToWrite==secremain)break;//写入结束了

        else//写入未结束

        {

            secpos++;//扇区地址增 1

            secoff=0;//偏移位置为 0

            pBuffer+=secremain; //指针偏移

            WriteAddr+=secremain; //写地址偏移

            NumByteToWrite-=secremain; //字节数递减

            if(NumByteToWrite>4096)secremain=4096;//下一个扇区还是写不完

            else secremain=NumByteToWrite; //下一个扇区可以写完了

        }

    };

}

//跟无检查页写入的逻辑一致。

该函数可以在 W25Q128 的任意地址开始写入任意长度(必须不超过 W25Q128 的容量)的数据。

先获得首地址(WriteAddr)所在的扇区,并计算在扇区内的偏移,然后判断要写入的数据长度是否超过本扇区所剩下的长度,如果不超过,再先看看是否要擦除,如果不要,则直接写入数据即可,如果要则读出整个扇区,在偏移处开始写入指定长度的数据,然后擦除这个扇区,再一次性写入。

擦除的最小单位是扇区,也就是4K。所以在擦除之前我们先将这个扇区的数据读取出来,保存在缓存区。在缓存中将对应的地址更新之后,一次性将数据写到对应的sector之中。

当所需要写入的数据长度超过一个扇区的长度的时候,我们先按照前面的步骤把扇区剩余部分写完,再在新扇区内执行同样的操作,如此循环,直到写入结束。

    我理解,整个STM32读写W25Q128是不断封装SPI_I2S_SendData和ReadData的一个过程,先封装到读写一个字节ReadWriteByte,再封装到读写一个扇区W25QXX_Write_NoCheck,W25QXX_Write。整个过程比较标准,无需太多改动。

4.STM32之SPI实战

    在实现STM32的SPI通讯之前,先做个小实验,实现USART串口通讯。因为调试STM32的SPI通讯可以把结果通过串口打印到电脑上显示,方便观察结果。

STM32CubeMX学习笔记(6)——USART串口使用_unused(huart)-CSDN博客

5.Keil调试代码

    今天学了Keil的debug功能,刚开始程序卡在了HAL_INIT这里,这是个很奇怪的问题。CubeMx生成的代码段,什么都没有做就是无法调试。原来要在CubeMx里面勾选一个调试功能。

在KEIL中勾选Use-MicroLib库

因为调用了printf,这是一个C++里面的功能,需要重映射。代码如下。

新建一个retarget.c文件。

#include "stdio.h"

#include "stm32f1xx_hal.h"

#include "usart.h"

#pragma import(__use_no_semihosting_swi)

#pragma import(__use_no_semihosting)

void _sys_exit(int x) {

    x = x;

}

struct __FILE  {

    int handle;

    /* Whatever you require here. If the only file you are using is */

    /* standard output using printf() for debugging, no file handling */

    /* is required. */

};

/* FILE is typedef’ d in stdio.h. */

FILE __stdout;

void _ttywrch(int ch){};

   

int fputc(int ch, FILE *f)

{

    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);

    return ch;

}

这段代码也是调了好久,网站上的或多或少不太对。

有了上面的代码,main函数就可以调用printf函数,打印到串口显示出来。

Stm32 debug停留在"BKPT 0xAB"或者"SWI 0xAB"的解决办法。

通过百度网盘分享的文件:SPI
链接:https://pan.baidu.com/s/1qzSFFV8-Vhrb0NzqbBAeCg?pwd=sshc 
提取码:sshc 

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

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

相关文章

【React Hooks - useState状态批量更新原理】

概述 所谓批量处理就是当在同时更新多个状态下&#xff0c;能够统一批量处理更新&#xff0c;避免了重复渲染。在React17及之前版本&#xff0c;React只会在合成事件以及生命周期内部进行批量处理&#xff0c;在setTimeout、Promise、Fetch等异步请求中&#xff0c;则不会自动…

【GH】【EXCEL】P1: Write DATA SET from GH into EXCEL

文章目录 WriteFast WriteGH data material :GH process and components instructionFast Write DataFast Write Data & Clear DataFast Write to Cell EXCEL written results Write by ColumnGH data material :Compile ColumnGH process and components instructionWrite…

三、Kafka副本

2、创建2个分区两个副本 /usr/local/kafka/bin# ./kafka-topics.sh --bootstrap-server 192.168.58.130:9092 --create --topic atguigu2 --partitions 2 --replication-factor 23、查看topic详细信息 /usr/local/kafka/bin# ./kafka-topics.sh --bootstrap-server 192.168.5…

如何理解CAPL—Test编程中的测试对象

前言&#xff1a;CAPL—Test编程中的对象&#xff0c;是一个比较复杂的概念&#xff0c;对象的作用是作为Test特定函数的参数。来执行特定的功能&#xff08;这是比较复杂的一个概念&#xff0c;下文会慢慢讲解&#xff09;。 注意&#xff1a;因为翻译的问题&#xff0c;有些…

arm:ADC模数转换器

比较器 AD&#xff1a; 精度&#xff1a;10位 转换速率&#xff1a;500 KSPS 量程&#xff1a;0~3.3v void adc_init(void) {ADCCON (1 << 14) | (49 << 6) | (1 << 1); }unsigned short adc_read(void) {unsigned short value ADCDAT0;while(~(ADCCON &am…

华为M60首次降价,消费回暖能延续?

导语 8月15日&#xff0c;华为Mate 60系列首次官宣降价&#xff01;能否带动消费电子进一步回暖&#xff1f; 在当前全球经济形势复杂多变的背景下&#xff0c;各行各业都在寻求新的增长点和突破口。 消费电子市场作为科技与日常生活紧密结合的重要领域&#xff0c;其发展态势一…

基于HarmonyOS的宠物收养系统的设计与实现(一)

基于HarmonyOS的宠物收养系统的设计与实现&#xff08;一&#xff09; 本系统是简易的宠物收养系统&#xff0c;为了更加熟练地掌握HarmonyOS相关技术的使用。 项目创建 创建一个空项目取名为PetApp 首页实现&#xff08;组件导航使用&#xff09; 官方文档&#xff1a;组…

微服务中的Sidecar模式

微服务中的Sidecar模式 什么是sidecarsidecar如何工作Sidecar 代理服务注册发现Sidecar 代理异构服务发起服务调用异构服务如何被调用 常见应用以MOSN流量接管为例使用 sidecar 模式的优势sidecar和面向切片编程AOP的关系参考 什么是sidecar sidecar是服务网络架构的产物。 S…

【网络】 arp 命令 得到网段内所有物理设备ip

我的笔记本和 NVIDIA Jetson Orin 都位于同一个 192.168.1.x 的网段内&#xff0c;我想远程访问 Orin&#xff0c;但我不知道orin的ip 方法 1: 使用 arp 命令 打开命令提示符&#xff1a; 按下 Win R 键&#xff0c;打开“运行”对话框。输入 cmd 并按 Enter 键打开命令提示符…

JAVA类加载过程/类装载的执行过程/java类加载机制/JVM加载Class文件的原理机制?

JAVA类加载过程/类装载的执行过程/java类加载机制/JVM加载Class文件的原理机制&#xff1f; 类加载的过程主要分为三个部分&#xff1a;&#xff08;加链初&#xff0c;验准解&#xff09; 加载链接初始化 而链接又可以细分为三个小部分&#xff1a; 验证准备解析 骚戴理解…

散点图适用于什么数据 thinkcell散点图设置不同颜色

在数据可视化的众多工具和技巧中&#xff0c;散点图是一种极为有效的方式&#xff0c;能够揭示变量之间的关系&#xff0c;尤其是在探索数据集的相关性、分布趋势、集群现象时。而在众多助力于制作高质量散点图的工具中&#xff0c;think-cell插件以其高效的操作和丰富的功能&a…

重定向与追加

1、>和>> > 【重定向】 如果文件不存在&#xff0c;则创建&#xff0c;并将内容输入到文件&#xff1b; 如果文件存在&#xff0c;则先清空文件&#xff0c;然后将内容输入到文件&#xff1b;>> 【追加】 如果文件不存在&#xff0c;则创建&…

自监督学习self-supervised learning

Tags: #tutorial #machine-leanring #self-supervised 目录&#xff1a; The Importance of Self-Supervised Learning Popular Learning Methods Introduction to Self-Supervised Learning 1. The Inportance of Self-Supervised Learning 监督学习(supervised learnin…

【面试】tomcat类加载机制

目录 1. 说明2. 主要类加载器2.1 Bootstrap类加载器2.2 Common类加载器2.3 Catalina类加载器2.4 Shared类加载器2.5 Web应用类加载器2.5 JSP类加载器 3. 图示4. 特点5. 加载顺序6. 面试题 1. 说明 1.tomcat的类加载机制是违反了双亲委派原则的&#xff0c;对于一些未加载的非基…

初识指针3の学习笔记

目录 1>>前言 2>>冒泡排序 3>>二级指针 4>>指针数组 5>>指针数组模拟二维数组 6>>结语 1>>前言 今天我会继续分享一些我做的笔记&#xff0c;以及我对指针的理解&#xff0c; 后续会持续分享指针几天&#xff0c;毕竟指针的内…

实验室安全分级分类管理系统在高校中的具体应用

盛元广通高校实验室安全分级分类管理系统的构建&#xff0c;旨在通过科学合理的管理手段&#xff0c;提高实验室的安全水平&#xff0c;保障师生的人身安全&#xff0c;防止实验事故的发生。这一系统通常包括实验室安全等级评估、分类管理、风险控制、安全教育与培训、应急响应…

[Qt][绘图][上]详细讲解

目录 0.为什么&#xff1f;1.绘图API核心类2.设置画笔3.设置画刷4.设置画家1.移动画家位置2.保存/加载画家的状态 0.为什么&#xff1f; 虽然Qt已经内置了很多的控件&#xff0c;但是不能保证现有控件就可以应对所有场景&#xff0c;很多时候&#xff0c;需要更强的"定制…

第N8周:使用Word2vec实现文本分类

本文为365天深度学习训练营 中的学习记录博客原作者&#xff1a;K同学啊 一、数据预处理 任务说明: 本次将加入Word2vec使用PyTorch实现中文文本分类&#xff0c;Word2Vec 则是其中的一种词嵌入方法&#xff0c;是一种用于生成词向量的浅层神经网络模型&#xff0c;由Tomas M…

spring boot学习第二十篇:使用minio上传下载文件获取文件路径

先安装好minio&#xff0c;参考&#xff1a;window10安装minio-CSDN博客 1、pom.xml文件添加依赖&#xff1a; <!-- 操作minio的java客户端--><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.…

C语言:ifswitch分支语句

目录 前言 一、if语句 1.1 if 1.2 else 1.3 嵌套if 1.4 悬空else问题 二、switch语句 2.1 if语句和switch语句的对比 2.2 switch语句中的default 前言 C语⾔是结构化的程序设计语言&#xff0c;这里的结构指的是顺序结构、选择结构、循环结构&#xff0c;C语言是能够实…