libftdi1学习笔记 7 - MPSSE I2C

目录

1. 初始化

2. 原理

3. i2cStart

4. i2cStop

5. i2cRecvByte

6. i2cSendByte

7. i2cRead

8. i2cWrite

9. 验证

9.1 初始化i2c

9.2 初始化gpio

9.3 写10个字节到EEPROM

9.4 读回10字节数据

9.5 运行结果


I2C(主)采用2个或3个GPIO模拟的方式实现,如果sdao等于sdai相等,表示SDA是双向的,否则一个GPIO作为SDA输出,一个GPIO作为SDA输入。 如果电平不想转换方向则选择3个GPIO的方式。注意,scl和sda必须在同一组内,比如scl在ACBUS0~7,那么sda也必须是这组内的,不能是ADBUS0~ADBUS7。

typedef struct 
{
    struct ftdi_context *ftdi;
    
    uint8_t scl;
    uint8_t sdao;
    uint8_t sdai;

    int freq;
    uint8_t *pCommand;
    int iCommand;
}mpsse_i2c_s;

freq表示I2C的频率,0表示最快速度,值越大越慢。

一般I2C可以接多个从设备,所以这里只定义了2组I2C,如果需要更多,修改数组大小即可。

typedef enum
{
    I2C_PORT_0 = 0,
    I2C_PORT_1,
    I2C_PORT_MAX,
}mpsse_i2c_port_e;

mpsse_i2c_s i2c[I2C_PORT_MAX];

1. 初始化

void i2cInit(uint8_t port, mpsse_i2c_s init)
{
    if(port >= I2C_PORT_MAX)
        return;
    i2c[port] = init;
    i2c[port].freq += 1;
}

pCommand和iCommand这里不需要初始化。其他参数按照硬件配置即可。这里freq加1是为了后面循环时直接使用。

2. 原理

从gpio写可知,通过命令0x80或0x81写入3个字节命令,FTDI设备会执行一次IO操作,如果同样的命令写freq次,那么GPIO的操作频率就会降低。比如0x80 level dir 0x80 level dir,写了2次0x80,设备就是连续设置一样GPIO两次,频率就是写一次的一半。

注意设定I2C的GPIO必须是同一组,定义好读写命令

const uint8_t gpioWirteCommand[2] = {0x80, 0x82};
const uint8_t gpioReadCommand[2] = {0x81, 0x83};

然后根据I2C的GPIO中任意一个计算应该使用哪个命令。

#define gpioCommand(port) (i2c[port].scl / 8)

例如scl选择ACBUS0,对应IO编号为8,gpioCommand = 1, 对应的读写命令为0x83和0x82。

将命令更新到命令buffer中,注意,整个I2C的操作命令是最后才真正发送给FTDI设备。

#define i2cCommandWrite(port, n)    do{\
    for (int i = 0; i < (int)i2c[port].freq * n; i++)\
    {\
        i2c[port].pCommand[i2c[port].iCommand++] = gpioWriteCommand[gpioCommand(port)];\
        i2c[port].pCommand[i2c[port].iCommand++] = (gpio.level >> (gpioCommand(port) * 8)) & 0xff;\
        i2c[port].pCommand[i2c[port].iCommand++] = (gpio.dir >> (gpioCommand(port) * 8)) & 0xff;\
    }\
}while(0)

定义SDA的输入输出:

#define i2cSDAOut(port)     do{\
    gpio.dir |= ((uint16_t)1 << i2c[port].sdao);\
}while(0)

#define i2cSDAIn(port)      do{\
    gpio.dir &= (uint16_t)(~((uint16_t)1 << i2c[port].sdai));\
}while(0)

定义SDA输出高低电平:

#define i2cSDAHigh(port)    do{\
    gpio.level |= ((uint16_t)1 << i2c[port].sdao);\
}while(0)

#define i2cSDALow(port)     do{\
    gpio.level &= (uint16_t)(~((uint16_t)1 << i2c[port].sdao));\
}while(0)

定义读SDA的命令:

#define i2cSDARead(port)    do{\
    i2c[port].pCommand[i2c[port].iCommand++] = gpioReadCommand[gpioCommand(port)];\
}while(0)

由于SCL一直为输出,不需要单独设置方向,只需要定义SCL的输出高低电平:

#define i2cSCLHigh(port)    do{\
    gpio.level |= ((uint16_t)1 << i2c[port].scl);\
}while(0)

#define i2cSCLLow(port)     do{\
    gpio.level &= (uint16_t)(~((uint16_t)1 << i2c[port].scl));\
}while(0)

接下来根据i2c协议完成各个状态函数即可。 

3. i2cStart

SCL为高电平,SDA产生一个下降沿即表示I2C的Start信号

void i2cStart(uint8_t port)
{
    //Set SDA output
    //SCL outputs high, SDA outputs high
    i2cSDAOut(port);
    i2cSCLHigh(port);
    i2cSDAHigh(port);
    i2cCommandWrite(port, 1);

    //SDA outputs low
    i2cSDALow(port);
    i2cCommandWrite(port, 2);
}

计算command缓存区使用情况:9 * freq

#define I2C_START_CMD_LEN           (3 * 3 * i2c[port].freq)

4. i2cStop

SCL为高电平,SDA产生一个下降沿即表示I2C的Stop信号

void i2cStop(uint8_t port)
{
    //SDA outpus low
    i2cSDALow(port);
    i2cCommandWrite(port, 1);
    
    //SCL outputs high
    i2cSCLHigh(port);
    i2cCommandWrite(port, 1);
    
    //SDA outputs high
    i2cSDAHigh(port);
    i2cCommandWrite(port, 1);
}

计算command缓存区使用情况:9 * freq

#define I2C_STOP_CMD_LEN           (3 * 3 * i2c[port].freq )

5. i2cRecvByte

SCL产生一个上升沿,然后SDA读入1个位数据,同样的操作连续读入8个位,然后根据参数ack SDA是否输出ack信号,SDA输出低电平且SCL产生一个上升沿即为ack信号,否则SDA输出高电平。ack完成后写入0x87命令,这个命令是将FTDI设备缓冲区中的数据立刻回传到PC。

void i2cRecvByte(uint8_t port, bool ack)
{
    //SCL outputs low, SDA outputs low
    //Set SDA as input
    i2cSCLLow(port);
    i2cSDALow(port);
    i2cSDAIn(port);
    i2cCommandWrite(port, 1);

    uint8_t loop = 8;
    while (loop-- > 0)
    {
        //SCL output low
        i2cSCLLow(port);
        i2cCommandWrite(port, 1);

        //SCL output high
        i2cSCLHigh(port);
        i2cCommandWrite(port, 1);

        //Read SDA
        i2cSDARead(port);
    }

    //SCL outputs low, SDA outputs low
    //Set SDA as output
    i2cSCLLow(port);
    i2cSDALow(port);
    i2cSDAOut(port);
    i2cCommandWrite(port, 1);

    if (ack == true)
    {
        i2cSDALow(port);
    }
    else
    {
        i2cSDAHigh(port);
    }
    i2cCommandWrite(port, 1);

    //SCL output high
    i2cSCLHigh(port);
    i2cCommandWrite(port, 1);

    i2c[port].pCommand[i2c[port].iCommand++] = 0x87;

    //SCL output low
    i2cSCLLow(port);
    i2cCommandWrite(port, 1);
}

注意,读入的数据此时并没有处理,等全部完成再读入数据。

计算command缓存区使用情况:

#define I2C_RECV_CMD_LEN           (5 * 3 * i2c[port].freq + 8 * (2 * 3 * i2c[port].freq + 1) + 1)

6. i2cSendByte

SCL上升沿发送SDA的一个位信息,同样的操作连续发送8个位。发送完成后同样一个SCL上升沿读入ack信息。

void i2cSendByte(uint8_t port, uint8_t dat)
{
    for(uint8_t j = 0; j < 8; j++)
    {
        //SCL output low
        i2cSCLLow(port);
        i2cCommandWrite(port, 1);
        //SDA output dat
        if ((dat & 0x80) == 0x80)
            i2cSDAHigh(port);
        else
            i2cSDALow(port);
        dat <<= 1;
        //Set SDA as output
        i2cSDAOut(port);
        i2cCommandWrite(port, 1);
        //SCL output high
        i2cSCLHigh(port);
        i2cCommandWrite(port, 1);
    }

    //SCL outputs low, SDA outputs low
    //Set SDA as input
    i2cSCLLow(port);
    i2cSDALow(port);
    i2cSDAIn(port);
    i2cCommandWrite(port, 1);

    //SCL output low
    //i2cSCLLow(port);
    //i2cCommandWrite(port, 1);
    
    //SCL output high
    i2cSCLHigh(port);
    i2cCommandWrite(port, 10);
    //Read SDA
    i2cSDARead(port);

    //SDA outputs high
    //Set SDA as output
    i2cSDAHigh(port);
    i2cSDAOut(port);
    i2cCommandWrite(port, 1);

    //SCL output low
    i2cSCLLow(port);
    i2cCommandWrite(port, 1);
}

计算command缓存区使用情况:

#define I2C_SEND_CMD_LEN    (8 * 3 * (4 * i2c[port].freq) + 12 * 3 * i2c[port].freq + 1)

7. i2cRead

int i2cRead(uint8_t port, uint8_t slaveAddr, uint8_t addrbit, int addr, uint8_t* dat, int len)

首先判断参数合法性

    if(port >= I2C_PORT_MAX)
        return -1;
    if(len <= 0)
        return -1;

在读数据前先把USB缓存读空,可以通过变量ftdi->readbuffer_remaining获取当前缓存里面有多少有效数据,大于0的情况就把数据读出来丢弃。

    if(i2c[port].ftdi->readbuffer_remaining > 0)
    {
        uint8_t *pDummy = (uint8_t *)malloc(i2c[port].ftdi->readbuffer_remaining);
        if(pDummy)
        {
            ftdi_read_data(i2c[port].ftdi, pDummy, i2c[port].ftdi->readbuffer_remaining);
            free(pDummy);
        }
    }

初始化Command缓存,按照发送的字节数计算需要多少个字节保存命令。

//2 * start + 4 * sendbyte + len * recvbyte + 1 * stop
int commandlength = (I2C_START_CMD_LEN * 2 
    + I2C_SEND_CMD_LEN * 4
    + I2C_RECV_CMD_LEN * len
    + I2C_STOP_CMD_LEN);
i2c[port].pCommand = (uint8_t *)malloc(commandlength);
i2c[port].iCommand = 0;

按照I2C设备的协议配置好Command缓存

    i2cStart(port);
    if(addrbit > 0)
        i2cSendByte(port, slaveAddr);
    if(addrbit > 8)
        i2cSendByte(port, (uint8_t)(addr >> 8));
    if(addrbit > 0)
    {
        i2cSendByte(port, (uint8_t)addr);
        i2cStart(port);
    }
    i2cSendByte(port, slaveAddr | 0x01);
    int sendLen = len;
    while(--sendLen)
    {
        i2cRecvByte(port, true);
    }
    i2cRecvByte(port, false);
    i2cStop(port);

然后写到FTDI设备

    if(i2c[port].iCommand > commandlength)
    {
        printf("i2cRead error: command buffer is overflow %d:%d\n", 
            i2c[port].iCommand, commandlength);
        if(i2c[port].pCommand)
            free(i2c[port].pCommand);
        return -2;
    }
    ftdi_write_data(i2c[port].ftdi, i2c[port].pCommand, i2c[port].iCommand);
    if(i2c[port].pCommand)
        free(i2c[port].pCommand);

发送完这些数据后,就是等待FTDI设备执行完数据并把读入的数据更新到USB驱动层。读入的数据是8个位的GPIO数据,要计算一下一共读入了多少个字节数据,每个i2cSendByte读入包含ack的一个字节,最多4个,然后在读入len个8位数据(i2cRecvByte),所以一共读入字节数为

int rdLen = ((addrbit > 0) ? 2 : 0) + 
    ((addrbit > 8) ? 1 : 0) + 1 +
    len * 8;
uint8_t *pReadBuf = (uint8_t *)malloc(rdLen);

当ftdi_read_data时,实际是有一个超时的问题,如果读入的数据在设定的时间内没读回则返回超时错误,所以在调用前根据数据长度设置一个合适的超时时间:

int readtimeout = i2c[port].ftdi->usb_read_timeout;
i2c[port].ftdi->usb_read_timeout = 2 * i2c[port].iCommand + 2 * rdLen * i2c[port].freq;
int ret = ftdi_read_data(i2c[port].ftdi, pReadBuf, rdLen);

读入数据后如果出错就再试一次,如果再出错就返回错误退出。

    if(ret < rdLen)
    {//try again
        int remain;
        if(ret < 0)
        {
            remain = rdLen;
            ret = ftdi_read_data(i2c[port].ftdi, pReadBuf, rdLen);
        } 
        else
        {
            remain = rdLen - ret;
            ret = ftdi_read_data(i2c[port].ftdi, pReadBuf + ret, remain);
        }
        
        if(ret + remain < rdLen)
        {
            i2c[port].ftdi->usb_read_timeout = readtimeout;
            if(pReadBuf)
                free(pReadBuf);
            return -3;
        }
    }
    i2c[port].ftdi->usb_read_timeout = readtimeout;

此时读入的数据中包含了ack信息和I2C读回的数据信息。先判断一下ack是不是正确。

    int ackLen = ((addrbit > 0) ? 2 : 0) + 
        ((addrbit > 8) ? 1 : 0) + 1;
    for(int i = 0; i < ackLen; i++)
    {
        if ((pReadBuf[i] & ((uint8_t)(1 << (i2c[port].sdai % 8)))) 
            == (uint8_t)(1 << (i2c[port].sdai % 8))) //Check ACK
        {
            printf("check ack fail %d, 0x%x\n", i, pReadBuf[i]);
            if(pReadBuf)
                free(pReadBuf);
            return -4;
        }
    }

然后把每个位信息组合成字节数据保存在返回指针中。

    int j = 0;
    for(int i = ackLen; i < rdLen; i++)
    {
        int max = i + 8;
        uint8_t tmp = 0;
        for(; i < max; i++)
        {
            tmp <<= 1;
            if ((pReadBuf[i] & ((uint8_t)(1 << (i2c[port].sdai % 8)))) 
                == (uint8_t)(1 << (i2c[port].sdai % 8)))
            {
                tmp |= 0x01;
            }
        }
        dat[j++] = tmp;
        i--;
    }

最后释放malloc的数据,且返回0.

    if(pReadBuf)
        free(pReadBuf);
    printf("i2cRead OK\n");
    return 0;

8. i2cWrite

int i2cWrite(uint8_t port, uint8_t slaveAddr, uint8_t addrbit, int addr, uint8_t* dat, int len)

类似i2cRead。

首先判断参数合法性

    if(port >= I2C_PORT_MAX)
        return -1;
    if(len <= 0)
        return -1;

写数据前也读空一下缓存。

    if(i2c[port].ftdi->readbuffer_remaining > 0)
    {
        uint8_t *pDummy = (uint8_t *)malloc(i2c[port].ftdi->readbuffer_remaining);
        if(pDummy)
        {
            ftdi_read_data(i2c[port].ftdi, pDummy, i2c[port].ftdi->readbuffer_remaining);
            free(pDummy);
        }
    }

初始化Command缓存

    //1 * start + 3 * sendbyte + len * sendbyte + 1 * stop
    int commandlength = (I2C_START_CMD_LEN * 1 
        + I2C_SEND_CMD_LEN * 3
        + I2C_SEND_CMD_LEN * len
        + I2C_STOP_CMD_LEN);
    i2c[port].pCommand = (uint8_t *)malloc(commandlength);
    i2c[port].iCommand = 0;

按照I2C设备的协议配置好Command缓存

    i2cStart(port);
    i2cSendByte(port, slaveAddr);
    if(addrbit > 8)
        i2cSendByte(port, (uint8_t)(addr >> 8));
    if(addrbit > 0)
        i2cSendByte(port, (uint8_t)addr);
    int n = 0;
    int sendLen = len;
    while(sendLen--)
    {
        i2cSendByte(port, dat[n++]);
    }
    i2cStop(port);

将Command缓存一次发送出去

    if(i2c[port].iCommand > commandlength)
    {
        printf("i2cWrite error: command buffer is overflow %d:%d\n", 
            i2c[port].iCommand, commandlength);
        if(i2c[port].pCommand)
            free(i2c[port].pCommand);
        return -2;
    }
    int writetimeout = i2c[port].ftdi->usb_write_timeout;
    i2c[port].ftdi->usb_write_timeout = i2c[port].iCommand + len * i2c[port].freq;
    int ret = ftdi_write_data(i2c[port].ftdi, i2c[port].pCommand, i2c[port].iCommand);
    if(i2c[port].pCommand)
        free(i2c[port].pCommand);
    i2c[port].ftdi->usb_write_timeout = writetimeout;
    if(ret < 0)
    {
        printf("usb write fail %d\n", ret);
        return -5;
    }

i2cWrite只需要读回所有的ack信息

    int rdLen = ((addrbit > 0) ? 1 : 0) + 
        ((addrbit > 8) ? 1 : 0) + 1 + len;

同样的方式读回所有数据

    int rdLen = ((addrbit > 0) ? 1 : 0) + 
        ((addrbit > 8) ? 1 : 0) + 1 + len;
    printf("need to read data:%d\n", rdLen);
    uint8_t *pReadBuf = (uint8_t *)malloc(rdLen);
    int readtimeout = i2c[port].ftdi->usb_read_timeout;
    i2c[port].ftdi->usb_read_timeout = i2c[port].iCommand + rdLen * i2c[port].freq;
    printf("usb read timeout:%d\n", i2c[port].ftdi->usb_read_timeout);
    ret = ftdi_read_data(i2c[port].ftdi, pReadBuf, rdLen);
    printf("read data number:%d\n", ret);
    if(ret < rdLen)
    {//try again
        int remain;
        if(ret < 0)
        {
            remain = rdLen;
            ret = ftdi_read_data(i2c[port].ftdi, pReadBuf, rdLen);
        } 
        else
        {
            remain = rdLen - ret;
            ret = ftdi_read_data(i2c[port].ftdi, pReadBuf + ret, remain);
        }
        printf("retry read %d\n", ret + remain);
        if(ret + remain < rdLen)
        {
            if(pReadBuf)
                free(pReadBuf);
            i2c[port].ftdi->usb_read_timeout = readtimeout;
            return -3;
        }
    }
    i2c[port].ftdi->usb_read_timeout = readtimeout;

判断这些读回来的数据对应SDA位的值是否符合ack正确,注意,最后一个字节并不需要判断。

    for(int i = 0; i < rdLen - 1; i++)
    {
        if ((pReadBuf[i] & ((uint8_t)(1 << (i2c[port].sdai % 8)))) 
            == (uint8_t)(1 << (i2c[port].sdai % 8))) //Check ACK
        {
            if(pReadBuf)
                free(pReadBuf);
            printf("ack fail:%d, 0x%x\n", i, pReadBuf[i]);
            return -4;
        } 
    }

最后释放malloc的数据,且返回0.

    if(pReadBuf)
        free(pReadBuf);
    printf("i2cWrite OK\n");
    return 0;

9. 验证

将I2C接口的EEPROM芯片接到FT4232H模块的ADBUS0和ADBUS1口。首先初始化mode为mpsse模式。

    int ret;
    ret = ftdi_set_bitmode(ftdi, 0, BITMODE_MPSSE);
    if(ret < 0)
    {
        printf("Set Mode Fail: %d\n", ret);
        return EXIT_FAILURE;
    }

9.1 初始化i2c

    mpsse_i2c_s i2cSetting;
    i2cSetting.ftdi = ftdi;
    i2cSetting.scl = 2;
    i2cSetting.sdao = 3;
    i2cSetting.sdai = 3;
    i2cSetting.freq = 9;
    i2cInit(0, i2cSetting);

9.2 初始化gpio

将I2C的SCL和SDA初始化为输出高电平的初始状态。

    mpsse_gpio_s gpioSetting;
    gpioSetting.ftdi = ftdi;
    gpioSetting.dir = 0x0000; //All input
    gpioSetting.dir |= ((uint16_t)GPIO_DIR_OUT << i2cSetting.scl) 
        | ((uint16_t)GPIO_DIR_OUT << i2cSetting.sdao);    //scl output, sdao output
    gpioSetting.level = 0xFFFF;
    mpsseGpioInit(gpioSetting);

9.3 写10个字节到EEPROM

    uint8_t wrBuf[10];
    printf("i2c write data: ");
    srand(time(NULL));
    for(int i = 0; i < sizeof(wrBuf); i++)
    {
        wrBuf[i] = (uint8_t)rand();
        printf("0x%2x ", wrBuf[i]);
    }
    printf("\n");
    ret = i2cWrite(0, 0xa0, 16, 0, wrBuf, sizeof(wrBuf));
    if(ret < 0)
    {
        printf("write eeprom fail\n");
        return ret;
    }

9.4 读回10字节数据

    uint8_t rdBuf[10];
    ret = i2cRead(0, 0xa0, 16, 0, rdBuf, sizeof(rdBuf));
    if(ret < 0)
    {
        printf("read eeprom fail\n");
        return ret;
    }
    printf("i2c read data: ");
    for(int i = 0; i < sizeof(wrBuf); i++)
    {
        printf("0x%2x ", rdBuf[i]);
        if(wrBuf[i] != rdBuf[i])
        {
            printf("\neeprom verify fail\n");
            return 0;
        }
    }
    printf("\n");

9.5 运行结果

libftdi-example$ sudo ./libftdi1-example 
version:1.5.0, 1.5
Number of FTDI devices found: 1
Manufacturer: FTDI, Description: FT4232H MiniModule, Serial: FT8NZV77

Open device OK: 0
i2c write data: 0x8c 0x8d 0xc4 0xf4 0xc2 0x 4 0xd8 0x88 0x26 0xf0 
need to read data:13
usb read timeout:7383
read data number:13
i2cWrite OK
read data number:84
i2cRead OK
i2c read data: 0x8c 0x8d 0xc4 0xf4 0xc2 0x 4 0xd8 0x88 0x26 0xf0 

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

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

相关文章

爬虫机试题-爬取新闻网站

之前投简历时遇到了这样的一个笔试。本以为会是数据结构算法之类的没想到直接发了一个word直接提需求&#xff0c;感觉挺有意思就写了这篇文章&#xff0c;感兴趣的朋友可以看看。 拿到urllist 通过分析页面结构我们得以知道&#xff0c;这个页面本身没有新闻信息&#xff0c;是…

计算机软考流程介绍

笔者来介绍一下软考流程 1、考试简介 计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试&#xff1a;简称 计算机软考 认证&#xff1a; 国家人力资源和社会保障部 国家工业和信息化部 目的&#xff1a; 科学、公正地对全国计算机与软件专业技术人员进行职业资格…

Hotcoin 热门资产上新速报:以太坊互操作性基础设施Omni Network(OMNI)

Hotcoin持续为全球600万用户发掘优质潜力资产&#xff0c;热门币种交易上热币。一文快速了解今日上新资产:Omni Network&#xff08;OMNI&#xff09; 推荐指数 8.4 交易对 OMNI/USDT 交易时间 4月17日 GMT8 20&#xff1a;30 资产赛道 Layer1 项目简介 Omni 是以太坊…

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台级联时,下级平台未发流是什么原因?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

黑洞路由、 DDoS 攻击 、 环路

黑洞路由 DDoS 攻击 DDoS 攻击是一种针对服务器、服务或网络的恶意行为。DDoS 攻击通过向目标发送大量流量&#xff0c;使其不堪重负&#xff0c;导致资源和带宽被耗尽。因此&#xff0c;目标可能会变慢或崩溃&#xff0c;无法正常处理合法的流量。DDoS 攻击通常是由僵尸网络…

Jmeter 性能-内存溢出问题定位分析

1、堆内存溢出 ①稳定性压测一段时间后&#xff0c;Jmeter报错&#xff0c;日志报&#xff1a; java.lang.OutOfMemoryError.Java heap space ②用jmap -histo pid命令dump堆内存使用情况&#xff0c;查看堆内存排名前20个对象。 看是否有自己应用程序的方法&#xff0c;从…

CentOS7下安装mysql8或者mysql5.7

mysql8 1、下载 访问mysql官网下载mysql8软件包 https://dev.mysql.com/downloads/mysql/ 选择相应的版本如&#xff1a;RPM Bundle mysql-8.0.33-1.el7.x86_64.rpm-bundle.tar RPM Bundle 8.0.33 下载地址&#xff1a;https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.…

电脑桌面便签软件哪个好?好用的电脑桌面便签

电脑作为我们日常工作的重要工具&#xff0c;承载着大量的任务和项目。当工作任务繁重时&#xff0c;如何在电脑桌面上高效管理这些任务就显得尤为重要。这时&#xff0c;选择一款优秀的桌面便签软件&#xff0c;无疑会给我们带来极大的便利。 一款好的桌面便签软件&#xff0…

【React】Ant Design自定义主题风格及主题切换

Ant Design 的自定义主题&#xff0c;对于刚入手的时候感觉真是一脸蒙圈&#xff0c;那今天给它梳理倒腾下&#xff1b; 1、自定义主题要点 整体样式变化&#xff0c;主要两个部分&#xff1a; 1.1、Design Token https://ant.design/docs/react/customize-theme-cn#theme 官…

ffmpeg入门

ffmpeg入——安装 Fmpeg地址 FFmpeg源码地址&#xff1a;https://github.com/FFmpeg/FFmpeg FFmpeg可执行文件地址&#xff1a;https://ffmpeg.org/download.html Windows平台 Windows平台下载解压后如图所示&#xff08;文件名称以-share结尾的是开发库&#xff09; FFmpeg…

Eagle for Mac v1.9.13注册版:强大的图片管理工具

Eagle for Mac是一款专为Mac用户设计的图片管理工具&#xff0c;旨在帮助用户更高效、有序地管理和查找图片资源。 Eagle for Mac v1.9.13注册版下载 Eagle支持多种图片格式&#xff0c;包括JPG、PNG、GIF、SVG、PSD、AI等&#xff0c;无论是矢量图还是位图&#xff0c;都能以清…

AndroidStudio AGP 7+, 编译aar并输出到本地仓库

1 编写构建gradle脚本代码 1.1 配置publication和repository 在指定moudle目录下新建名为"maven-publish.gradle"文件&#xff0c;其声明的publication和repository如下所示&#xff1a; apply plugin: maven-publish// This creates a task called publishReleas…

《星光对话》系列直播:带你入门数据要素

2020年12月9日&#xff0c;财政部提出企业数据资源可作为资产列入财务报表&#xff0c;打响数据要素“1N”的第一枪&#xff1b; 2022年12月2日&#xff0c;《关于构建数据基础制度更好发挥数据要素作用的意见》“数据二十条”通过提出构建数据产权、流通交易、收益分配、安全治…

维护SQLite的私有分支(二十六)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLite、MySQL 和 PostgreSQL 数据库速度比较&#xff08;本文阐述时间很早比较&#xff0c;不具有最新参考性&#xff09;&#xff08;二十五&#xff09; 下一篇&#xff1a;SQLite数据库中JSON 函数和运算符 1…

# 从浅入深 学习 SpringCloud 微服务架构(三)注册中心 Eureka(1)

从浅入深 学习 SpringCloud 微服务架构&#xff08;三&#xff09;注册中心 Eureka&#xff08;1&#xff09; 段子手168 1、微服务的注册中心 注册中心可以说是微服务架构中的”通讯录”&#xff0c;它记录了服务和服务地址的映射关系。 在分布式架构中服务会注册到这里&am…

美易官方:美债美元黄金继续涨?

全球金融市场波动加剧&#xff0c;投资者对避险资产的需求不断升温。在这一背景下&#xff0c;“投行老将”们纷纷发表观点&#xff0c;认为避险情绪尚未结束&#xff0c;美债、美元和黄金等避险资产有望继续上涨。 巴克莱一位资深投资银行家表示&#xff0c;由于担心中东冲突升…

在Linux系统中搜索当前路径及其子目录下所有PDF文件中是否包含特定字符串

目录标题 方法一&#xff1a;pdfgrep方法二&#xff1a;使用find和xargs与pdftotext&#xff08;将PDF转换为文本&#xff09;组合&#xff0c;然后用grep搜索 方法一&#xff1a;pdfgrep pdfgrep -ri "rockchip" .方法二&#xff1a;使用find和xargs与pdftotext&am…

动手学深度学习11 权重衰退

动手学深度学习11 权重衰退 1. 权重衰退2. 代码实现3. QA 视频&#xff1a; https://www.bilibili.com/video/BV1UK4y1o7dy/?spm_id_fromautoNext&vd_sourceeb04c9a33e87ceba9c9a2e5f09752ef8 电子书&#xff1a; ttps://zh-v2.d2l.ai/chapter_multilayer-perceptrons/wei…

Mamba 学习

Vision Mamba U-Mamba 以后的趋势&#xff1a; 1.Mamba模型机机制上和transform一样&#xff0c;但是参数量上做了改进&#xff0c;可以直接替代 2.vision上可以实时处理

视频太大怎么压缩变小?8种方法随时压缩视频大小

视频太大怎么压缩变小&#xff1f;视频压缩方式分为两种&#xff0c;有损压缩和无损压缩&#xff0c;什么是有损什么是无损压缩&#xff0c;什么时候视频用无损压缩更好&#xff1f;什么时候用有损压缩更好&#xff1f;如何调整视频参数实现基本无损压缩&#xff1f; 今天就借助…