libftdi1学习笔记 5 - SPI Nor Flash

目录

1.  初始化

2. CS控制例子

3. 读ID

3.1 制造商

3.2 容量大小

3.3 设置IO类型

3.3.1 setQSPIWinbond

3.3.2 setQSPIMxic

3.3.3 setQSPIMicrochip

3.3.4 setQSPIMicron

4. 写保护

5. 等待空闲

6. 擦除扇区

7. 页编程

8. 页读

9. 写

10. 读

11. 验证


基于MPSSE SPI实现Nor Flash的读写。也定义两组flash。

typedef enum
{
    SFLASH_PORT_0 = 0,
    SFLASH_PORT_1,
    SFLASH_PORT_MAX,
}sflash_port_e;

定义一个结构体记录flash的属性

typedef struct 
{
    sflash_io_e ioType;
    uint8_t spiPort;
    void (*pCSEnable)(uint8_t port, bool enable);

    sflash_manufactor_e manufactor;
    sflash_size size;
    sflash_addr_size_e addrSize;
}sflash_s;

ioType:表示flash的IO类型,一般支持3种接口方式:

typedef enum
{
    SFLASH_SPI = 0,
    SFLASH_DSPI,
    SFLASH_QSPI,
}sflash_io_e;

spiPort:表示该flash使用哪组SPI

pCSEnable:对应CS脚控制的接口函数

manufactor:该flash的生产商

typedef enum
{
    SFLASH_Winbond = 0,
    SFLASH_Cypress,
    SFLASH_ESMT,
    SFLASH_GigaDevice,
    SFLASH_MXIC,
    SFLASH_Micron,
    SFLASH_ISIS,
    SFLASH_Microchip,
    SFLASH_ZBit,
    SFLASH_FuDan,
    SFLASH_BOYA,
    SFLASH_Unknown,
}sflash_manufactor_e;

size:该flash的大小

typedef enum
{
    SFLASH_SIZE_256KB = 0,
    SFLASH_SIZE_512KB,
    SFLASH_SIZE_1MB,
    SFLASH_SIZE_2MB,
    SFLASH_SIZE_4MB,
    SFLASH_SIZE_8MB,
    SFLASH_SIZE_16MB,
    SFLASH_SIZE_32MB,
}sflash_size;

addrSize:该flash的地址宽度

typedef enum
{
    SFLASH_ADDR_24BIT = 3,
    SFLASH_ADDR_32BIT,
}sflash_addr_size_e;

1.  初始化

void sflashInit(uint8_t port, sflash_s init)
{
    if(port >= SFLASH_PORT_MAX)
        return;
    sflash[port] = init;
}

2. CS控制例子

初始化spi要根据实际情况配置CS的控制函数,例如:

#define SFLASH_CS_PIN       3
void sflash0CS(uint8_t port, bool enable)
{
    spiCPOL(port);
    mpsseGpioWrite(SFLASH_CS_PIN, !enable);
}

3. 读ID

通过命令0x9F读取flash的Jedec ID,其函数原型为:

uint32_t sflashReadJedecID(uint8_t port)

返回值即是id。

    uint8_t cmd[4] = {CMD_READ_ID, 0xff, 0xff, 0xff};
    uint32_t id;

    sflash[port].pCSEnable(port, true);
    spiWriteBytes(sflash[port].spiPort, cmd, 1);
    spiReadBytes(sflash[port].spiPort, cmd + 1, 3);
    sflash[port].pCSEnable(port, false);

    id = ((uint32_t)cmd[1] << 16) | (uint32_t)(cmd[2] << 8) | (uint32_t)(cmd[3]);

由于flash的QSPI方式和品牌商有关,这里根据ID做判断(注意,这套规则并不是所有厂商都符合)

3.1 制造商

由ID的16~23位确定品牌商

    switch (id & 0xff0000)
    {
        case 0xEF0000:
            sflash[port].manufactor = SFLASH_Winbond;
            printf("sflash Winbond\n");
            break;
        case 0x010000:
            sflash[port].manufactor = SFLASH_Cypress;
            printf("sflash Cypress\n");
            break;
        case 0x8C0000:
            sflash[port].manufactor = SFLASH_ESMT;
            printf("sflash ESMT\n");
            break;
        case 0xC80000:
            sflash[port].manufactor = SFLASH_GigaDevice;
            printf("sflash GigaDevice\n");
            break;
        case 0xC20000:
            sflash[port].manufactor = SFLASH_MXIC;
            printf("sflash MXIC\n");
            break;
        case 0x200000:
            sflash[port].manufactor = SFLASH_Micron;
            printf("sflash Micron\n");
            break;
        case 0x5E0000:
            sflash[port].manufactor = SFLASH_ZBit;
            printf("sflash ZBit\n");
            break;
        case 0x9D0000:
            sflash[port].manufactor = SFLASH_ISIS;
            printf("sflash ISIS\n");
            break;
        case 0xA10000:
            sflash[port].manufactor = SFLASH_FuDan;
            printf("sflash FuDan\n");
            break;
        case 0xBF0000:
            sflash[port].manufactor = SFLASH_Microchip;
            printf("sflash Microchip\n");
            break;
        case 0x680000:
            sflash[port].manufactor = SFLASH_BOYA;
            printf("sflash BOYA\n");
            break;
        default:
            sflash[port].manufactor = SFLASH_Unknown;
            printf("sflash Unknown\n");
            break;
    }

3.2 容量大小

根据ID的位0~7确定容量大小

    switch(id & 0xff)
    {
        case 0x12:
            sflash[port].size = SFLASH_SIZE_256KB;
            sflash[port].addrSize = SFLASH_ADDR_24BIT;
            printf("sflash size 256KB, 24bits address\n");
            break;
        case 0x13:
            sflash[port].size = SFLASH_SIZE_512KB;
            sflash[port].addrSize = SFLASH_ADDR_24BIT;
            printf("sflash size 512KB, 24bits address\n");
            break;
        case 0x14:
            sflash[port].size = SFLASH_SIZE_1MB;
            sflash[port].addrSize = SFLASH_ADDR_24BIT;
            printf("sflash size 1MB, 24bits address\n");
            break;
        case 0x15:
            sflash[port].size = SFLASH_SIZE_2MB;
            sflash[port].addrSize = SFLASH_ADDR_24BIT;
            printf("sflash size 2MB, 24bits address\n");
            break;
        case 0x16:
            sflash[port].size = SFLASH_SIZE_4MB;
            sflash[port].addrSize = SFLASH_ADDR_24BIT;
            printf("sflash size 4MB, 24bits address\n");
            break;
        case 0x17:
            sflash[port].size = SFLASH_SIZE_8MB;
            sflash[port].addrSize = SFLASH_ADDR_24BIT;
            printf("sflash size 8MB, 24bits address\n");
            break;
        case 0x18:
            sflash[port].size = SFLASH_SIZE_16MB;
            sflash[port].addrSize = SFLASH_ADDR_24BIT;
            printf("sflash size 16MB, 24bits address\n");
            break;
        case 0x19:
            sflash[port].size = SFLASH_SIZE_32MB;
            sflash[port].addrSize = SFLASH_ADDR_32BIT;
            printf("sflash size 32MB, 32bits address\n");
            break;
    }

3.3 设置IO类型

    if (sflash[port].ioType == SFLASH_QSPI)
    {
        sflashSetQSPI(true);
    }
    else
    {
        sflashSetQSPI(false);
    }

根据不同制造商设置flash的IO类型,这里只实现SPI和QSPI的方式,部分制造商的设定方式是一样的。

bool sflashSetQSPI(uint8_t port, bool enable)
{
    bool ret = false;
    switch(sflash[port].manufactor)
    {
        case SFLASH_Winbond:
        case SFLASH_GigaDevice:
        case SFLASH_Cypress:
        case SFLASH_ZBit:
        case SFLASH_BOYA:
        case SFLASH_FuDan:
            ret = setQSPIWinbond(port, enable);
            break;
        case SFLASH_MXIC:
        case SFLASH_ESMT:
        case SFLASH_ISIS:
            ret = setQSPIMxic(port, enable);
            break;
        case SFLASH_Microchip:
            ret = setQSPIMicrochip(port, enable);
            break;
        case SFLASH_Micron:
            ret = setQSPIMicron(port, enable);
            break;
        default:
            return false;
    }
    return ret;
}

3.3.1 setQSPIWinbond

第一种方式以Winbond为代表,QSPI的使能位在Status2寄存器中。

通过命令0x35读入该寄存器值,通过0x31写即可。

bool setQSPIWinbond(uint8_t port, bool enable)
{
    uint8_t cmd[2] = {0x35, 0xff};
    sflash[port].pCSEnable(port, true);
    if(spiWriteBytes(sflash[port].spiPort, cmd, 1) < 0)
        return false;
    if(spiReadBytes(sflash[port].spiPort, cmd + 1, 1) < 0)
        return false;
    sflash[port].pCSEnable(port, false);

    if(enable == true)
    {
        if ((cmd[1] & 0x02) == 0x02) //QE == 1
        {
            return true;
        }
        cmd[1] |= 0x02;
    }
    else
    {
        if ((cmd[1] & 0x02) == 0x00) //QE == 0
        {
            return true;
        }
        cmd[1] &= (uint8_t)0xFD;
    }

    cmd[0] = 0x31;

    sflash[port].pCSEnable(port, true);
    if(spiWriteBytes(sflash[port].spiPort, cmd, 2) < 0)
        return false;
    sflash[port].pCSEnable(port, false);
    return true;
}

3.3.2 setQSPIMxic

第二种方式以Mxic为代表,QSPI的使能位在Status寄存器的位6

通过命令0x05读入该寄存器,0x01写。

bool setQSPIMxic(uint8_t port, bool enable)
{
    uint8_t cmd[2] = {0x05, 0xff};
    sflash[port].pCSEnable(port, true);
    if(spiWriteBytes(sflash[port].spiPort, cmd, 1) < 0)
        return false;
    if(spiReadBytes(sflash[port].spiPort, cmd + 1, 1) < 0)
        return false;
    sflash[port].pCSEnable(port, false);

    if(enable == true)
    {
        if ((cmd[1] & 0x40) == 0x40) //QE == 1
        {
            return true;
        }
        cmd[1] |= 0x40;
    }
    else
    {
        if ((cmd[1] & 0x40) == 0x00) //QE == 0
        {
            return true;
        }
        cmd[1] &= (uint8_t)0xBF;
    }

    cmd[0] = 0x01;

    sflash[port].pCSEnable(port, true);
    if(spiWriteBytes(sflash[port].spiPort, cmd, 2) < 0)
        return false;
    sflash[port].pCSEnable(port, false);
    return true;
}

3.3.3 setQSPIMicrochip

Microchip的QSPI是 通过命令的方式使能(0x38)和禁止(0xFF)的。以SST26VF040A为例:

bool setQSPIMicrochip(uint8_t port, bool enable)
{
    uint8_t cmd[1] = {0x38};
    if(enable == true)
    {
        sflash[port].pCSEnable(port, true);
        if(spiWriteBytes(sflash[port].spiPort, cmd, 1) < 0)
            return false;
        sflash[port].pCSEnable(port, false);
    }
    else
    {
        cmd[0] = 0xff;
        sflash[port].pCSEnable(port, true);
        if(spiWriteBytes(sflash[port].spiPort, cmd, 1) < 0)
            return false;
        sflash[port].pCSEnable(port, false);
    }
    return true;
}

3.3.4 setQSPIMicron

Micron(以MT25QL256为例)的QSPI通过读写易失性配置寄存器的位7配置。

通过命令0x85读入该寄存器,0x81写。

bool setQSPIMicron(uint8_t port, bool enable)
{
    uint8_t cmd[2] = {0x85, 0xff};
    sflash[port].pCSEnable(port, true);
    if(spiWriteBytes(sflash[port].spiPort, cmd, 1) < 0)
        return false;
    if(spiReadBytes(sflash[port].spiPort, cmd + 1, 1) < 0)
        return false;
    sflash[port].pCSEnable(port, false);

    if(enable == true)
    {
        if ((cmd[1] & 0x80) == 0x80) //QE == 1
        {
            return true;
        }
        cmd[1] |= 0x80;
    }
    else
    {
        if ((cmd[1] & 0x80) == 0x00) //QE == 0
        {
            return true;
        }
        cmd[1] &= (uint8_t)0x7F;
    }

    cmd[0] = 0x81;

    sflash[port].pCSEnable(port, true);
    if(spiWriteBytes(sflash[port].spiPort, cmd, 2) < 0)
        return false;
    sflash[port].pCSEnable(port, false);
    return true;
}

4. 写保护

使能写的命令为:

#define CMD_WRITE_ENABLE        0x06

对应的函数:

void sflashWriteEnable(uint8_t port)
{
    uint8_t cmd[1] = {CMD_WRITE_ENABLE};
    sflash[port].pCSEnable(port, true);
    spiWriteBytes(sflash[port].spiPort, cmd, 1);
    sflash[port].pCSEnable(port, false);
}

禁止写的命令为:

#define CMD_WRITE_DISABLE       0x04

对应的函数:

void sflashWriteDisable(uint8_t port)
{
    uint8_t cmd[1] = {CMD_WRITE_DISABLE};
    sflash[port].pCSEnable(port, true);
    spiWriteBytes(sflash[port].spiPort, cmd, 1);
    sflash[port].pCSEnable(port, false);
}

5. 等待空闲

通过读状态寄存器等待设备空闲。

void sflashWaitFree(uint8_t port)
{
    while(true)
    {
        uint8_t cmd[2] = {CMD_READ_STATUS, 0xFF};
        sflash[port].pCSEnable(port, true);
        spiWriteBytes(sflash[port].spiPort, cmd, 1);
        spiReadBytes(sflash[port].spiPort, cmd + 1, 1);
        sflash[port].pCSEnable(port, false);

        if((cmd[1] & 0x01) == 0)
            break;
        usleep(1000);
    }
}

6. 擦除扇区

一般扇区大小为4096个字节,擦除扇区的命令

#define CMD_SECTOR_ERASE        0x20

命令后面接需要擦除扇区的起始地址。

在擦除命令前必须先设置写使能,擦除根据芯片不同可能需要几十ms才能完成(例如W25Q128FVxxIQ需要45ms,SST26VF032B需要25ms),等待一段时间后判断擦除是否完成,最后恢复写保护。

void sflashEraseSector(uint8_t port, uint32_t addr)
{
    uint8_t cmd[5] = {CMD_SECTOR_ERASE, 0, 0, 0, 0};
    uint8_t offset = 1;
    if(sflash[port].addrSize == SFLASH_ADDR_32BIT)
        cmd[offset++] = (uint8_t)(addr >> 24);
    cmd[offset++] = (uint8_t)(addr >> 16);
    cmd[offset++] = (uint8_t)(addr >> 8);
    cmd[offset++] = (uint8_t)(addr >> 0);
    sflashWriteEnable(port);
    sflash[port].pCSEnable(port, true);
    spiWriteBytes(sflash[port].spiPort, cmd, offset);
    sflash[port].pCSEnable(port, false);
    usleep(1000 * 20);
    sflashWaitFree(port);
    sflashWriteDisable(port);
}

7. 页编程

void sflashPageProgram(uint8_t port, uint32_t addr, uint8_t *buf, uint16_t len)

norflash的写不能按字节写,最小单位为页,一般页的大小为256字节。但是实际上页内是可以按照字节写的。比如可以在地址16的位置写10个字节数据。

地址的计算方式和擦除一样

    uint8_t cmd[5] = {CMD_PAGE_PROGRAM, 0, 0, 0, 0};
    uint8_t offset = 1;
    if(sflash[port].addrSize == SFLASH_ADDR_32BIT)
        cmd[offset++] = (uint8_t)(addr >> 24);
    cmd[offset++] = (uint8_t)(addr >> 16);
    cmd[offset++] = (uint8_t)(addr >> 8);
    cmd[offset++] = (uint8_t)(addr >> 0);

然后不同的接口有不同的命令,SPI的命令是0x02,QSPI的命令是0x32(这里有个特例,MXIC的芯片QPI的命令是0x38)。

#define CMD_PAGE_PROGRAM        0x02
#define CMD_PAGE_PROGRAM_QIO    0x32

注意,QPI的接口模式下,命令字是SPI方式发送的。 

    sflashWriteEnable(port);
    sflash[port].pCSEnable(port, true);
    if(sflash[port].ioType == SFLASH_QSPI)
    {
        cmd[0] = CMD_PAGE_PROGRAM_QIO;
        if(sflash[port].manufactor == SFLASH_MXIC)
        {
            cmd[0] = 0x38;
        }
        
        spiWriteBytes(sflash[port].spiPort, cmd, 1);
        qspiWriteBytes(sflash[port].spiPort, cmd + 1, offset);
        qspiWriteBytes(sflash[port].spiPort, buf, len);
    }
    else
    {
        cmd[0] = CMD_PAGE_PROGRAM;
        spiWriteBytes(sflash[port].spiPort, cmd, offset);
        spiWriteBytes(sflash[port].spiPort, buf, len);
    }
    sflash[port].pCSEnable(port, false);

最后等待编程结束

    sflashWaitFree(port);
    sflashWriteDisable(port);

8. 页读

对于flash来说,没有页读的概念,这里定义页读的方式是因为FTDI设备每笔最大通信是64K字节,每次读最好设置为256B,这是因为FTDI设备的最大传输字节数为64KB,由于GPIO模拟的SPI协议特别费字节,大了会超过64KB的大小,另外,太大了有时候会出现通讯错误

对于SPI接口,使用Fast Read命令0x0B,对于QSPI,使用0xEB命令。

#define CMD_FASTREAD            0x0B
#define CMD_FASTREAD_QIO        0xEB

地址的计算等同其他

    uint8_t cmd[5] = {CMD_FASTREAD, 0, 0, 0, 0};
    uint8_t offset = 1;
    if(sflash[port].addrSize == SFLASH_ADDR_32BIT)
        cmd[offset++] = (uint8_t)(addr >> 24);
    cmd[offset++] = (uint8_t)(addr >> 16);
    cmd[offset++] = (uint8_t)(addr >> 8);
    cmd[offset++] = (uint8_t)(addr >> 0);

写完地址后需要空读1-2个字节(QSPI是空读2个字节,SPI读空1个字节)

    sflash[port].pCSEnable(port, true);
    if(sflash[port].ioType == SFLASH_QSPI)
    {
        uint8_t dummy[2];
        cmd[0] = CMD_FASTREAD_QIO;
        spiWriteBytes(sflash[port].spiPort, cmd, 1);
        qspiWriteBytes(sflash[port].spiPort, cmd + 1, offset);
        qspiReadBytes(sflash[port].spiPort, dummy, 2);
        qspiReadBytes(sflash[port].spiPort, buf, len);
    }
    else
    {
        uint8_t dummy[1];
        cmd[0] = CMD_FASTREAD;
        spiWriteBytes(sflash[port].spiPort, cmd, offset);
        spiReadBytes(sflash[port].spiPort, dummy, 1);
        spiReadBytes(sflash[port].spiPort, buf, len);
    }
    sflash[port].pCSEnable(port, false);

9. 写

void sflashWrite(uint8_t port, uint32_t addr, uint8_t *buf, uint32_t len)

在写之前要确保flash已经擦除。

首先判断一下地址是不是页对齐,不是就先把不齐的部分编程。

    uint32_t offset = 0;
    uint16_t count;
    if(port >= SFLASH_PORT_MAX)
    if(len == 0)
        return;

    if((addr % sflashPageSize) > 0)
    {
        count = (len > (sflashPageSize - (addr % sflashPageSize))) ?
                    (uint16_t)(sflashPageSize - (addr % sflashPageSize)) : (uint16_t)len;
        sflashPageProgram(port, addr, buf, count);
        offset += count;
        len -= count;
        addr += count;
    }

剩余的数据写完

    while (len > 0)
    {
        count = (len > sflashPageSize) ? (uint16_t)sflashPageSize : (uint16_t)len;

        sflashPageProgram(port, addr, buf + offset, count);
        offset += count;
        len -= count;
        addr += count;
    }

10. 读

void sflashRead(uint8_t port, uint32_t addr, uint8_t *buf, uint32_t len)

读可以任意的地址读,没有特别的处理。

    uint32_t offset = 0;
    uint32_t readPageSize = 256;
    while(len > 0)
    {
        uint16_t count;
        count = (len > readPageSize) ? (uint16_t)readPageSize : (uint16_t)len;
        sflashPageRead(port, addr, buf + offset, count);
        offset += count;
        len -= count;
        addr += count;
    } 

11. 验证

配置和SPI验证一样。这里只验证SPI、Mode0的方式。

擦除扇区->写随机数据->读入数据->比较读写的数据是否相等。

    #define EX_SFLASH_SIZE          2048
    uint8_t wrBuf[EX_SFLASH_SIZE];
    uint8_t rdBuf[EX_SFLASH_SIZE];
    int i;
    uint8_t port = 0;

然后产生随机数据

    srand(time(NULL));
    for(i = 0; i < EX_SFLASH_SIZE; i++)
    {
        wrBuf[i] = (uint8_t)rand();
        rdBuf[i] = 0;
    }

擦除一个扇区

 sflashEraseSector(port, 0 * 4096);

将随机数据写入flash

    sflashWrite(port, 0, wrBuf, EX_SFLASH_SIZE);

再从相同地址读回

    sflashRead(port, 0, rdBuf, EX_SFLASH_SIZE);

最后比较

    for(i = 0; i < EX_SFLASH_SIZE; i++)
    {
        if(wrBuf[i] != rdBuf[i])
        {
            printf("sflash test fail %d: %x!=%x\n", i, wrBuf[i], rdBuf[i]);
            break;
        }
    }
    printf("sflash test finish\n");

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

管理能力学习笔记五:识别团队角色,因才施用

识别团队角色&#xff0c;因才施用&#xff0c;需要做到以下三点 扬长避短 管理者要学会问自己员工能把什么做好&#xff0c;而不是想方设法改造他们的短处 。 – 彼得德鲁克 人岗匹配 将合适的人放在合适的位置 人才多样化 团队需要各式各样的人才&#xff0c;才能高效配合…

【Linux】引导过程与服务控制

目录 一、Linux操作系统引导过程 1.linux开机引导过程 2.系统初始化进程 1.init进程 2.进程启动方式 二、运行级别和Systemd单元类型 1.运行级别 2.Systemd 三、启动类故障恢复 1.修复MBR扇区故障 2.修复GRUB引导故障 3.root密码忘记的修改方式 四、系统服务控制 …

初识ansible服务及ansible主机清单配置

目录 1、什么是自动化批量管理 2、自动化工具ansible架构 3、ansible服务专用术语对照表 4、设置主机清单&#xff08;inventory&#xff09; 4.1实验环境准备 4.2配置主机清单 4.2.1分组基本格式 4.2.2指定用户名&#xff0c;密码。端口 4.2.3子组 4.3查看 4.3.1看…

基于SpringBoot+Vue的服装销售商城系统(源码+文档+包运行)

一.系统概述 顺应互联网发展的时代热潮&#xff0c;着力于服装电商&#xff0c;满足消费者的日常需求的同时解决传统服装销售的难题。商家如果还用之前的只有线下卖衣服&#xff0c;已经很落伍了&#xff0c;这样会导致了效率低下。而且&#xff0c;时间一长的话&#xff0c;积…

AIGC算法2:LLM的复读机问题

1. 什么是LLM的复读机问题 字符级别重复&#xff0c;指大模型针对一个字或一个词重复不断的生成例如在电商翻译场景上&#xff0c;会出现“steckdose steckdose steckdose steckdose steckdose steckdose steckdose steckdose…”&#xff1b;语句级别重复&#xff0c;大模型针…

不容错过的 IntelliJ IDEA 插件 Top 10

虽然 IntelliJ IDEA 功能齐全&#xff0c;您仍然可以增添一些个性化的设置。 JetBrains Marketplace 上有着大量实用插件&#xff0c;可以满足您个人或企业的特定需求。 内容库非常庞大&#xff0c;可能会让人眼花缭乱。 在这篇博文中&#xff0c;我们将分享最近和一直以来最受…

(十四)C++自制植物大战僵尸游戏windows平台视频播放实现

植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/8UFMs VLC库 在Cocos2d-x游戏开发框架中&#xff0c;没有实现windows平台视频播放的功能&#xff0c;需要自定义实现。在本项目中使用vlc库实现windows平台的视频播放功能。 vlc官网&#xff1a;网址 下载完成后&#x…

如何配置Postgres的自动扩展功能以应对数据增长

文章目录 解决方案1. 表空间管理2. 分区表3. 自动扩展配置4. 监控和告警5. 使用外部工具和服务 示例代码示例1&#xff1a;创建表空间示例2&#xff1a;创建分区表示例3&#xff1a;调整配置参数示例4&#xff1a;使用监控和告警工具 总结 在PostgreSQL中&#xff0c;随着数据的…

Spring Boot:Web应用开发之登录与退出的实现

Spring Boot 前言实现登录功能配置拦截器 实现退出功能 前言 登录与退出功能作为 Web 应用中的基础且重要的组成部分&#xff0c;直接关系到用户的安全和隐私保护。通过实现登录与退出功能&#xff0c;可以对用户的身份进行验证和授权&#xff0c;确保只有合法的用户才能访问特…

吃鸡游戏msvcp140.dll丢失的解决方法

msvcp140.dll 是一个与 Microsoft Visual C Redistributable 相关的动态链接库&#xff08;DLL&#xff09;文件&#xff0c;是 Windows 操作系统中众多应用程序正常运行所必需的关键组件之一。以下是对 msvcp140.dll 文件的总体介绍和msvcp140.dll丢失的多个解决方案分享。 *…

Java项目实现Excel导出(Hutool)

官网&#xff1a; Excel生成-ExcelWriter (hutool.cn) 1.使用Hutool工具实现Excel导出&#xff08;.xlsx格式&#xff09; 业务场景&#xff1a; 使用SpringCloudmysqlmybatis-plus需要将数据库中的数据导出到Excel文件中 前端为Vue2 第零步&#xff1a;导入依赖 <!-…

NPL预训练模型-GPT-3

简介及特点 GPT-3是一个由OpenAI开发的自然语言处理&#xff08;NLP&#xff09;预训练模型&#xff0c;它是生成式预训练变换器&#xff08;Generative Pretrained Transformer&#xff09;系列的第三代模型。GPT-3以其巨大的规模和强大的语言处理能力而闻名&#xff0c;具有…

快速上手Linux核心命令

Linux 的重要性不用我多说了吧&#xff0c;大多数互联网公司&#xff0c;服务器都是采用的Linux操作系统 Linux是一个主要通过命令行来进行管理的操作系统。 只有熟练掌握Linux核心命令&#xff0c;在使用起来我们才会得心应手 这里给大家整理了Linux一些核心命令&#xff0…

游戏、app抓包

文章目录 协议app抓包游戏抓包 协议 在抓包之前&#xff0c;首先我们要对每个程序使用什么协议有个大致的了解&#xff0c;比如网页这种就是走的http协议。 在一些app中我们通过发送一个请求&#xff0c;然后服务器接受&#xff0c;响应&#xff0c;返回一个数据包&#xff0c…

数字人解决方案——EMAGE面部加肢体动画实现从音频生成数字人表情与动作

概述 AI数字人面部与肢体的驱动算法是数字人研发中至关重要的一环&#xff0c;它能够有效降低VR Chat、虚拟直播和游戏NPC等应用场景中的成本。随着技术的发展&#xff0c;基于语音的面部、肢体和手部动作生成模型已经逐步成熟并得到广泛应用。然而&#xff0c;当尝试将这些独…

反激电源——TL431及光耦反馈电路计算(不涉及环路补偿)

一、TL431及光耦反馈电路 TL431以及光耦电路是反激的副边反馈类型电路中的常见应用。 其反馈工作原理为&#xff1a;当副边的输出电压升高时&#xff0c;TL431的REF点采样电压也会升高&#xff0c;使得TL431的导通量增加&#xff0c;同时光耦内部的发光二极管流过的电流也增大&…

C++11 数据结构3 线性表的循环链式存储,实现,测试

上一节课&#xff0c;我们学了线性表 单向存储结构&#xff08;也就是单链表&#xff09;&#xff0c;这个是企业常用的技术&#xff0c;且是后面各种的基本&#xff0c;一定要牢牢掌握&#xff0c;如果没有掌握&#xff0c;下面的课程会云里雾里。 一 &#xff0c;循环链表 1…

遥测终端赋能水库泄洪监测预警,筑牢度汛安全防线!

4月10日&#xff0c;水利部召开水库安全度汛视频会议。会议要求着力强化水库防洪“四预”措施&#xff0c;加快构建雨水情监测预报“三道防线”&#xff0c;完善预警信息发布机制&#xff0c;推进数字孪生水利工程建设&#xff0c;为科学调度指挥决策提供支持。强调坚决牢牢守住…

基于3D点云的散货库存体积计算

首先&#xff0c;你需要散货库存的点云。 我将使用 IntelRealSense 捕获的散货库存的 .ply文件。 然而&#xff0c;任何其他产生点云的成像技术都同样有效。 点击这里查看本教程的 Github 上的代码。 NSDT工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - …

二叉树的中序遍历 - LeetCode 热题 36

大家好&#xff01;我是曾续缘&#x1f603; 今天是《LeetCode 热题 100》系列 发车第 36 天 二叉树第 1 题 ❤️点赞 &#x1f44d; 收藏 ⭐再看&#xff0c;养成习惯 二叉树的中序遍历 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输…
最新文章