016 - STM32学习笔记 - SPI读写FLASH(一)

news2024/9/22 11:26:39

016 - STM32学习笔记 - SPI访问Flash(一)

之前csdn的名称是宥小稚,后来改成放学校门口见了,所以前面内容看到图片水印不要在意,都是自己学习过程中整理的,不涉及版权啥的。

1、什么是SPI?

SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设备接口,是一种高速全双工的通信总线。

在这里插入图片描述

在SPI总线中,一共有四条线:

SCLK:同步时钟信号线,用于通讯数据同步。由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如 STM32 的 SPI 时钟频率最大为fpclk/2,两个设备之间通讯时,通讯速率由最低速率设备决定。

CS:片选信号线,或设备选择线,也称为SS、NSS,当多个SPI从机设备与SPI主机相连时,设备的其他信号线SCLK、MOSI、MISO同时并联到相同的SPI总线上,无论多少个从机设备,这三条总线都是公用的,唯独CS线都是从机设备与主机设备的一对一连接,SPI总线上有多少从机设备,就有多少根CS线。I2C中主机通过设备地址来寻址进行通讯;SPI中没有设备地址,只使用CS信号线进行片选,当主机要与某个从机设备进行通信时,则将该从机设备的CS信号线设置为低电平,就表示该设备被选中,接着就可以开始通讯了,当通讯结束后,CS线被拉高,则表示结束信号。

MOSI:Master Output,Slave Input,主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,数据方向为主机到从机

MISO:Master Input,Slave Output,主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,数据方向为从机到主机

2、SPI协议

在这里插入图片描述

SPI通讯过程

在SPI通讯过程中,CS、SCK、MOSI信号都是由主机产生,MISO信号是由从机产生,其中MOSI和MISO信号只有在CS片选信号为低电平时有效,在SCLK每个信号周期,MOSI和MISO都可以传输一位数据,因此SPI总线可以同时收发数据。

a、通讯起始与结束信号:

根据SPI通讯过程的图来看,当SPI总线无通信时,CS片选信号线一直保持高电平,当开始通信时,CS信号线跳变为低电平,此时SCLK、MOSI、MISO信号线上的信号才有效,通信结束后,CS跳变为高电平,表示此次通信结束了。

b、有效数据:

在SPI中,当CS片选信号低电平时,MOSI和MISO数据线在SCLK的每个时钟周期都会传输一位数据,并且MOSI和MISO上的信号时同时进行传输的,MSB(高位)先行或LSB(地位)先行并没有硬性规定,但要保证两个SPI设备之间使用同样的协定,就跟上图一样,采用MSB先行模式了。

c、CPOL/CPHA

下面要学习关于SPI的四种通讯模式,上图中的时序时SPI的其中一种通讯模式,四种通讯模式的主要区别在于总线空闲时,SCLK的时钟状态以及数据采样时刻。在此之前先看一下CPOL(时钟极性)和CPHA(时钟相位)。

CPOL:是指SPI通讯设备处于空闲状态时,SCK信号线的的电平信号(就是CS片选信号是高电平时,SCLK的状态),当CS信号线为高电平时,CPOL = 0,SCLK则为低电平;CPOL = 1,SCLK则为高电平;

CPHA:时钟相位实际指的就是数据采样的时间,当CPHA = 0的时候,采样信号在SCLK时钟线的奇数边沿,当CPHA = 1的时候,采样信号则是在SCLK时钟线的偶数边沿。

在这里插入图片描述

CPHA = 0

在这里插入图片描述

CPHA = 1

所以根据CPOL和CPHA的不同状态可以组合出四种模式,如下表

模式CPOLCPHA空闲时SCLK采样时刻
000低电平奇数边沿
101低电平偶数边沿
210高电平奇数边沿
311高电平偶数边沿

在实际应用中,模式0模式3 用的比较多。

3、SPI框图

在这里插入图片描述

a、SPI引脚

前面已经介绍过SPI的四个引脚了,这里不多做赘述,主要看一下四个引脚在F429里面的分布,F429中,SPI一共由6组,其中SPI1、SPI4、SPI5、SPI6挂载在APB2总线上,最高通信速率可以达到45MBtis/s,SPI2、SPI3是挂载在APB1上,最高通信速率为22.5MBtis/s。

在这里插入图片描述

引脚SPI1 SPI2 SPI3 SPI4 SPI5 SPI6
MOSIPA7/PB5PB15/PC3/PI3PB5/PC12/PD6PE6/PE14PF9/PF11PG14
MISOPA6/PB4PB14/PC2/PI2PB4/PC11PE5/PE13PF8/PH7PG12
SCKPA5/PB3PB10/PB13/PD3PB3/PC10PE2/PE12PF7/PH6PG13
NSSPA4/PA15PB9/PB12/PI0PA4/PA15PE4/PE11PF6/PH5PG8
SPI引脚表

b、时钟控制逻辑

SPI的时钟信号由SCLK线发出,内部通过波特率发生器根据控制寄存器CR1中的BR[2:0]位控制,该位是对fpclk时钟的分频因子,对fpclk的分频结果就是SCLK引脚输出的频率。

在这里插入图片描述

BR[2:0]分频结果(SCLK频率)BR[2:0]分频结果(SCLK频率)
000fpclk/2100fpclk/32
001fpclk/4101fpclk/64
010fpclk/8110fpclk/128
011fpclk/16111fpclk/256

c、数据控制逻辑

在SPI框图中可以看到,SPI的MOSI和MISO都是连接到数据移位寄存器上,而数据移位寄存器中的数据,来源于接收缓冲区、发送缓冲区以及MOSI和MISO线,向外发送数据时,数据向外发送时,数据移位寄存器将“发送缓冲区”的数据作为数据源,将数据按位发送出去;接收数据时,数据移位寄存器从MISO数据线上将数据按位通过“数据移位寄存器”采集到“接收缓冲区”。

此处需要注意,数据帧的长度可以通过控制寄存器CR1DFF[11]位配置成8位或16位模式,通过配置“LSBFIRST [7]位可选择 MSB 先行还是 LSB 先行。

4、SPI结构体

SPI结构体声明在stm32f4xx_spi.h中,内容如下:

typedef struct
{
    uint16_t SPI_Direction;           /* SPI单、双向模式 */
    uint16_t SPI_Mode;                /* SPI主/从机模式 */
    uint16_t SPI_DataSize;            /* SPI数据帧长度 */
    uint16_t SPI_CPOL;                /* 时钟极性CPOL设置,可选高/低电平 */
    uint16_t SPI_CPHA;                /* 时钟相位CPHA设置,可选奇/偶边沿采样 */
    uint16_t SPI_NSS;                 /* 设置CS引脚由SPI硬件控制还是软件控制 */
    uint16_t SPI_BaudRatePrescaler;   /* 设置时钟分频因子,fpclk/分频=fsck */
    uint16_t SPI_FirstBit;            /* 设置MSB/LSB先行 */
    uint16_t SPI_CRCPolynomial;       /* 设置CRC校验的表达式 */
}SPI_InitTypeDef;

a、SPI单双向选择(SPI_Direction):

在固件库中提供了以下四种模式

  • 双线全双工(SPI_Direction_2Lines_FullDuplex)
  • 双线只接收(SPI_Direction_2Lines_RxOnly)
  • 单线只接收(SPI_Direction_1Line_Rx)
  • 单线只发送模式(SPI_Direction_1Line_Tx)

b、SPI片选信号控制选择(SPI_NSS)

可选有两种模式:

  • 硬件模式(SPI_NSS_Hard )
  • 软件模式(SPI_NSS_Soft )

其中硬件模式中的片选信号是由SPI硬件自动产生,而软件模式则是由用户将GPIO的引脚电平拉高或者拉低产生信号,常用的为软件模式。

5、编程测试

在这里插入图片描述

在F429的核心板上可以看到,PF6、7、8、9引脚分别对应SPI的CS(片选)、CLK(时钟)、MOSI(输入)、MISO(输出)功能,另外Flash还有两个引脚WP(写保护控制)和HOLD(暂停通讯控制),当WP为低电平时,禁止数据写入,HOLD为低电平时,通讯暂停,MISO输出为高阻态,时钟和输入无效,这里用不到,所以直接接电源拉成高电平。

相关宏定义,在bsp_spi_flash.h中

//SPI 号及时钟初始化函数
#define FLASH_SPI                       SPI5									//根据SPI引脚表可以看到PF6-9都是挂载在SPI5下
#define FLASH_SPI_CLK                   RCC_APB2Periph_SPI5						 //SPI5时钟
//SCK 引脚PF7
#define FLASH_SPI_SCK_PIN               GPIO_Pin_7
#define FLASH_SPI_SCK_GPIO_PORT         GPIOF
#define FLASH_SPI_SCK_GPIO_CLK          RCC_AHB1Periph_GPIOF
#define FLASH_SPI_SCK_PINSOURCE         GPIO_PinSource7
#define FLASH_SPI_SCK_AF                GPIO_AF_SPI5
//MISO 引脚PF8
#define FLASH_SPI_MISO_PIN              GPIO_Pin_8
#define FLASH_SPI_MISO_GPIO_PORT        GPIOF
#define FLASH_SPI_MISO_GPIO_CLK         RCC_AHB1Periph_GPIOF
#define FLASH_SPI_MISO_PINSOURCE        GPIO_PinSource8
#define FLASH_SPI_MISO_AF               GPIO_AF_SPI5
//MOSI 引脚PF9
#define FLASH_SPI_MOSI_PIN              GPIO_Pin_9
#define FLASH_SPI_MOSI_GPIO_PORT        GPIOF
#define FLASH_SPI_MOSI_GPIO_CLK         RCC_AHB1Periph_GPIOF
#define FLASH_SPI_MOSI_PINSOURCE        GPIO_PinSource9
#define FLASH_SPI_MOSI_AF               GPIO_AF_SPI5
//CS(NSS)引脚PF6
#define FLASH_CS_PIN                    GPIO_Pin_6
#define FLASH_CS_GPIO_PORT              GPIOF
#define FLASH_CS_GPIO_CLK               RCC_AHB1Periph_GPIOF

#define SPI_FLASH_CS_LOW()              {FLASH_CS_GPIO_PORT->BSRRH=FLASH_CS_PIN;} 	/*控制CS片选信号输出低电平*/
#define SPI_FLASH_CS_HIGH()             {FLASH_CS_GPIO_PORT->BSRRL=FLASH_CS_PIN;}	/*控制CS片选信号输出高电平*/

相关GPIO配置及SPI的配置和初始化

void SPI_GPIO_Config(void)
{
    GPIO_InitTypeDef SPI_GPIO_Initstructure;
    SPI_InitTypeDef SPI_InitStructure;
    /*  SPI四个引脚时钟使能,只要是外设,第一步一定是开启时钟!!! */
    RCC_AHB1PeriphClockCmd(FLASH_SPI_SCK_GPIO_CLK | FLASH_SPI_MISO_GPIO_CLK|FLASH_SPI_MOSI_GPIO_CLK|FLASH_CS_GPIO_CLK, ENABLE);
    /* 将SCLK、MISO、MOSI引脚都连接到复用功能,CS片选信号由软件控制,不需要 */
    GPIO_PinAFConfig(FLASH_SPI_SCK_GPIO_PORT,FLASH_SPI_SCK_PINSOURCE,FLASH_SPI_SCK_AF);
    GPIO_PinAFConfig(FLASH_SPI_MISO_GPIO_PORT,FLASH_SPI_MISO_PINSOURCE,FLASH_SPI_MISO_AF);
    GPIO_PinAFConfig(FLASH_SPI_MOSI_GPIO_PORT,FLASH_SPI_MOSI_PINSOURCE,FLASH_SPI_MOSI_AF);
    /* 配置SCLK信号引脚 */
    SPI_GPIO_Initstructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
    SPI_GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;
    SPI_GPIO_Initstructure.GPIO_Mode = GPIO_Mode_AF;
    SPI_GPIO_Initstructure.GPIO_OType = GPIO_OType_PP;
    SPI_GPIO_Initstructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    /* 初始化SCLK引脚 */
    GPIO_Init(FLASH_SPI_SCK_GPIO_PORT, &SPI_GPIO_Initstructure);
    /* 配置并初始化MISO引脚 */
    SPI_GPIO_Initstructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
    GPIO_Init(FLASH_SPI_MISO_GPIO_PORT, &SPI_GPIO_Initstructure);
    /* 配置并初始化MOSI引脚 */
    SPI_GPIO_Initstructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
    GPIO_Init(FLASH_SPI_MOSI_GPIO_PORT, &SPI_GPIO_Initstructure);
    /* 配置CS引脚为输出模式并初始化,这里片选由软件自行控制,因此此处模式选择为输出 */
    SPI_GPIO_Initstructure.GPIO_Pin = FLASH_CS_PIN;
    SPI_GPIO_Initstructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_Init(FLASH_CS_GPIO_PORT, &SPI_GPIO_Initstructure);
    /* 将CS片选信号置位高电平,非选中状态 */
    SPI_FLASH_CS_HIGH();
    /*------------------------以下为SPI配置内容------------------------*/
    /* SPI时钟使能,只要是外设,第一步一定是开启时钟!!! */
    /* PF6-9是挂载在SPI5上,因此使能SPI5的时钟 */
    RCC_APB2PeriphClockCmd(FLASH_SPI_CLK, ENABLE);
    /* 配置SPI为双线全双工 */
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    /* 配置SPI为主机模式 */
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    /* 设置数据帧为8bit */
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    /* 设置时钟极性为高电平 */
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    /* 设置时钟相位为偶采样 */
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    /* 设置片选信号由软件控制 */
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    /* 设置为2分频 */
    /*F429的主频是180MHz,那么APB2的时钟fpclk1就是90MHz,而SP1、4、5、6最高频率为45MHz,因此需要2分频;
    同理如果要用到SPI2、3,最高频率为22.5MHz,	而SPI2、3挂载在AP1,总线时钟为45MHz,如果要达到最高速度,同样要2分频
    需要注意的是,此处SPI的最大速度取决于总线中设备通讯速率最低的设备 */
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
    /* 设置MSB先行 */
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    /* 设置CRC校验多项式,保证通信质量,大于1就行 */
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    /* 初始化SPI */
    SPI_Init(FLASH_SPI, &SPI_InitStructure);
    /* 使能 FLASH_SPI */
    SPI_Cmd(FLASH_SPI, ENABLE);
}

首先测试一下通过SPI读取FALSH的Device_ID号,根据FALSH手册,可以看到设备ID是0xEF4018,如下图

在这里插入图片描述

另外在看一下SPI的指令表,在指令表中,可以看到查看FALSH的指令为0x9F:

在这里插入图片描述

如此根据指令表编写读取FALSH的设备ID,可以看到,读取设备ID时,需要下发指令0x9F,之后Byte2-Byte4中的内容组合则为设备ID,时序如下图:

在这里插入图片描述

u32 SPI_FLASH_ReadID(void)
{
    u8 temp[3] = {0x00,0x00,0x00};
    /* 开始通讯:拉低片选信号CS */
    SPI_FLASH_CS_LOW();
    /* 发送 JEDEC 指令,读取 ID */
    SPI_FLASH_SendByte(0x9F);
    /* 读取一个字节数据 */
    temp[0] = SPI_FLASH_SendByte(Dummy_Byte);
    /* 读取一个字节数据 */
    temp[1] = SPI_FLASH_SendByte(Dummy_Byte);
    /* 读取一个字节数据 */
    temp[2] = SPI_FLASH_SendByte(Dummy_Byte);
    /* 停止通讯:拉高片选信号CS */
    SPI_FLASH_CS_HIGH();
    /* 组合数据返回 */
    return (temp[0] << 16) | (temp[1] << 8) | temp[2];
}

这段程序中,如果直接使用库函数SPI_I2S_ReceiveData去读取数据的话,发现读取回来的数据不是我们要的0xEF4018,因为在每次数据发送完成后,需要等待发送缓冲区为空时,才可以发送下一个要发送的数据,因此跟I2C的一样,我们需要去检测一下发送缓冲区是否为空,当发送缓冲区为空时,硬件会将TXE标志置1,因此需要调用SPI_GetFlagStatus去获取TXE状态,为此在这里实现SPI_FLASH_SendByte来尝试读取设备ID:

u8 SPI_FLASH_SendByte(u8 byte)
{
    SPITimeout = SPIT_FLAG_TIMEOUT;
    /* 等待发送缓冲区为空, TXE 事件 */
    while (SPI_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_TXE) == RESET)
    {
        if ((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);
    }
    /* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
    SPI_I2S_SendData(FLASH_SPI, byte);
    SPITimeout = SPIT_FLAG_TIMEOUT;
    /* 等待接收缓冲区非空, RXNE 事件 */
    while (SPI_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_RXNE) == RESET)
    {
        if ((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1);
    }
    /* 读取数据寄存器,获取接收缓冲区数据 */
    return SPI_I2S_ReceiveData(FLASH_SPI);
}

实现方法有点类似I2C,代码大家看一下I2C那节的内容就能理解,关于超时检测和SPI_TIMEOUT_UserCallback回调函数的实现也可以看一下I2C那节的内容。

到这里在到main函数中测试一下:

#include "stm32f4xx.h"
#include "bsp_led.h"
#include "bsp_systick.h"
#include "bsp_usart_dma.h"
#include "bsp_spi_flash.h"
#include <stdio.h>

int main(void)
{
    u32 device_id = 0;
    LED_Config();
    DEBUG_USART1_Config();
    SysTick_Init();  
    SPI_GPIO_Config();
    printf("这是SPI读取FLASH_Device_ID的测试实验!\n");
    device_id = SPI_FLASH_ReadID();
    printf("device_id = 0x%X\n",device_id);
    while(1)
    {
        if(device_id == 0xef4018)
        {
            LED_G_TOGGLE
            Delay_ms(1000);
        }
        else
        {
            LED_R_TOGGLE
            Delay_ms(1000);
        }
    }
}

在这里插入图片描述

OK,这节内容学习完毕!

根据上述内容,总给下SPI的操作顺序:

1、配置SPI对应的四个GPIO;(只要是外设,第一步一定要先打开时钟!!!)

2、将三个GPIO连接到SPI复用功能(CS引脚我们用软件自己控制,就不需要连接了!)

3、配置SPI相关参数(模式按照模式0或者模式3来配置,速度按照SPI最大速度来执行,记得要打开SPI的时钟!)

4、编写发送和读取数据的功能函数。

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

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

相关文章

Linux项目自动化构建工具-make/Makefile以及git三板斧

目录 一、关于make/makefile的背景知识二、依赖关系和依赖方法三、make/makefile如何书写&#xff1f;四、文件的三个时间(Access、Modify、Change)五、Linux下倒计时和进度条代码的书写5.1 回车换行5.2 缓冲区5.3 倒计时代码实现5.4 进度条代码实现 六、git三板斧6.1 什么是gi…

10.15资源加载

定义&#xff1a; 1.直接属性引用 生成一个actor和声音&#xff1a; 运行时就会产生一个Myactor中设置的Actor并且播放Myactor中设置的声音。 音频&#xff1a;class USoundCue&#xff1b; 纹理&#xff1a;class UTexture&#xff1b; 材质&#xff1a; class UMaterial 模…

TCP/IP出现的背景及其历史【图解TCP/IP(笔记八)】

文章目录 TCP/IP出现的背景及其历史从军用技术的应用谈起ARPANET的诞生TCP/IP的诞生UNIX系统的普及与互联网的扩张商用互联网服务的启蒙 TCP/IP出现的背景及其历史 从军用技术的应用谈起 20世纪60年代&#xff0c;很多大学和研究机构都开始着力于新的通信技术。其中有一家以美…

jmeter列表数据断言

在jmeter接口请求中&#xff0c;通常需要根据接口data列表有无返回的数据断言是接口请求成功&#xff0c;如图1&#xff0c; 通常有这么几种方法&#xff1a; beanshell断言 json断言 响应断言 图1&#xff1a; 失败请求&#xff1a;{"code":0,"msg"…

小甲鱼- python -洗牌算法 —— Fisher-Yates

练习1 自己的原始代码 &#xff08;比较复杂&#xff09; 1.没有把字符串转为列表&#xff0c;所以不能利用pop # 打乱的次数 for i in range(1,4):s "ABCDEF"n 4list []l len(s)while l > 0:k random.randint(1, l)list.append(s[k-1])s s.replac…

电子设备电池容量与充电器功率的关系

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 目录 抛出问题 手机的工作电压 手机的工作电流 手机的电池容量 电能转换公式 充电器功率 充电时间计算 总结 抛出问题 你是否也想过&#xff0c;你的手机电池容量是5000mAh&#xff0c;手机充电器是120W快…

基于低代码平台的项目设计的一般流程及低代码平台(基于iVX)与MVC的关系

基于低代码平台的项目设计的一般流程及低代码平台&#xff08;基于iVX&#xff09;与MVC的关系 1.基于低代码平台的项目设计的一般流程a.流程图b.MVC架构应用于iVX项目的各分层排序&#xff1a;&#xff08;1&#xff09;第一步&#xff1a;写M&#xff08;2&#xff09;第二步…

LeetCode[912]排序数组

难度&#xff1a;Medium 题目&#xff1a; 给你一个整数数组 nums&#xff0c;请你将该数组升序排列。 示例 1&#xff1a; 输入&#xff1a;nums [5,2,3,1] 输出&#xff1a;[1,2,3,5]示例 2&#xff1a; 输入&#xff1a;nums [5,1,1,2,0,0] 输出&#xff1a;[0,0,1,1,2,…

Openlayers实战:显示海量数据

Openlayers地图中通常的加载方式是canvas,另外还一种加载方式是webGL,在绘制海量数据时,使用GPU进行绘制可有效减少CPU的负载,提升绘制时的速度在浏览器中,可以使用WebGL的方式与GPU交互。 在本实战中,使用WebGLPoints显示海量数据。 效果图 源代码 /* * @Author: 大剑…

表单标签from

七、表单标签 form text name属性必须添加&#xff0c;否则后端不知道这个值是什么意思。且name不能重复 添加label主要是方便程序员&#xff0c;一看到用户名称这个label就是为username的&#xff0c;添加或者不添加页面效果一样 2、possword 用户密码显式出来&#xff0c;所…

与一款医疗仪器的往事

这是一款比较冷门的医疗仪器&#xff0c;SOD型多普勒脐动脉血流检测仪。 大约是20年前&#xff0c;有个朋友找到了俺&#xff0c;让俺给他写一个医疗仪器的软件。当时这个仪器是这样子的。这是唯一还能找到的当时的图片。朋友要求用C写。俺就选择了C Builder&#xff0c;比用VC…

vue源码阅读之什么是虚拟dom

前面简单说过数据响应式原理&#xff0c;大体是个怎么流程&#xff0c;数据发生变化&#xff0c;我们界面如何更新。 依赖收集收集的是watcher&#xff0c;然后当数据发生变化的时候dep通知watcher&#xff0c;然后watcher负责updateComponent。 那么更新组件过程中&#xff…

PostgreSQL查询引擎——上拉子链接SubLink

子查询是查询语句中经常出现的一种类型&#xff0c;是比较耗时的操作。优化子查询对查询效率的提升有直接的影响。从子查询出现在SQL语句的位置看&#xff0c;它可以出现在目标列、FROM子句、WHERE子句、JOIN/ON子句、GROUPBY子句、HAVING子句、ORDERBY子句等位置。子查询出现在…

c语言指针进阶(一)

大家好&#xff0c;我是c语言boom成家宝。今天为大家分享的是c语言中很重要的一个知识点------指针的深入讲解。 目录 指针 指针数组 数组指针 函数指针 什么是指针&#xff1f; 首先&#xff0c;指针的本质是一个地址&#xff0c;指针在32位机器上的大小是4个字节&a…

python_day3_str

字符串str 按索引下标查找 str Hi, world, follow, admin print(str[0]) print(str[-1])index() str Hi, world, follow, admin print(str.index(world)) #首字母下标 print(str.index(w))字符串.replace&#xff08;字符串1&#xff0c;字符串2&#xff09;&#xff1a;…

【Linux】高级IO(二)

文章目录 高级IO&#xff08;二&#xff09;I/O多路转接之pollpoll服务器 I/O多路转接之epollepoll相关函数epoll工作原理epoll回调机制epoll服务器epoll的优点 高级IO&#xff08;二&#xff09; I/O多路转接之poll poll也是系统提供的一个多路转接接口 poll系统调用也可以…

ruoyi若依 组织架构设计--[ 部门管理 ]

ruoyi若依 组织架构设计--[ 部门管理 ] 部门管理部门查询部门新增部门修改部门删除 部门管理 部门查询 需要注意的是&#xff0c;部门管理也有数据权限&#xff0c;比如A用户分配的数据权限(通过角色分配)是深圳总公司&#xff0c;那么A用户登录后看到的部门也是深圳总公司&am…

2023年前端面试题汇总-数据结构(二叉树)

对于树这个结构,最常见的就是二叉树。我们除了需要了解二叉树的基本操作之外,还需要了解一些特殊的二叉树,比如二叉搜索树、平衡二叉树等,另外还要熟悉二叉树的遍历方式,比如前序遍历、中序遍历、后序遍历、层序遍历。另外还要知道二叉树的常用遍历的方式:深度优先遍历和…

非线性优化知识

这里列下最小二乘的四种解法的优缺点&#xff0e; #mermaid-svg-CLbQz6o8j7JMq9MM {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-CLbQz6o8j7JMq9MM .error-icon{fill:#552222;}#mermaid-svg-CLbQz6o8j7JMq9MM .err…

前端开发中的单例模式

在前端开发中&#xff0c;单例模式是一种常见的设计模式&#xff0c;用于确保一个类只有一个实例&#xff0c;并提供一个全局访问点来获取该实例。 在JavaScript中&#xff0c;可以使用以下几种方式来实现单例模式&#xff1a; 字面量方式&#xff1a; const singleton {// …