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 highi2cSDAOut(port);i2cSCLHigh(port);i2cSDAHigh(port);i2cCommandWrite(port, 1);//SDA outputs lowi2cSDALow(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 lowi2cSDALow(port);i2cCommandWrite(port, 1);//SCL outputs highi2cSCLHigh(port);i2cCommandWrite(port, 1);//SDA outputs highi2cSDAHigh(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 inputi2cSCLLow(port);i2cSDALow(port);i2cSDAIn(port);i2cCommandWrite(port, 1);uint8_t loop = 8;while (loop-- > 0){//SCL output lowi2cSCLLow(port);i2cCommandWrite(port, 1);//SCL output highi2cSCLHigh(port);i2cCommandWrite(port, 1);//Read SDAi2cSDARead(port);}//SCL outputs low, SDA outputs low//Set SDA as outputi2cSCLLow(port);i2cSDALow(port);i2cSDAOut(port);i2cCommandWrite(port, 1);if (ack == true){i2cSDALow(port);}else{i2cSDAHigh(port);}i2cCommandWrite(port, 1);//SCL output highi2cSCLHigh(port);i2cCommandWrite(port, 1);i2c[port].pCommand[i2c[port].iCommand++] = 0x87;//SCL output lowi2cSCLLow(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 lowi2cSCLLow(port);i2cCommandWrite(port, 1);//SDA output datif ((dat & 0x80) == 0x80)i2cSDAHigh(port);elsei2cSDALow(port);dat <<= 1;//Set SDA as outputi2cSDAOut(port);i2cCommandWrite(port, 1);//SCL output highi2cSCLHigh(port);i2cCommandWrite(port, 1);}//SCL outputs low, SDA outputs low//Set SDA as inputi2cSCLLow(port);i2cSDALow(port);i2cSDAIn(port);i2cCommandWrite(port, 1);//SCL output low//i2cSCLLow(port);//i2cCommandWrite(port, 1);//SCL output highi2cSCLHigh(port);i2cCommandWrite(port, 10);//Read SDAi2cSDARead(port);//SDA outputs high//Set SDA as outputi2cSDAHigh(port);i2cSDAOut(port);i2cCommandWrite(port, 1);//SCL output lowi2cSCLLow(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 againint 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 * stopint 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 againint 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 inputgpioSetting.dir |= ((uint16_t)GPIO_DIR_OUT << i2cSetting.scl) | ((uint16_t)GPIO_DIR_OUT << i2cSetting.sdao);    //scl output, sdao outputgpioSetting.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: FT8NZV77Open 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.mzph.cn/diannao/586.shtml

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

相关文章

QTableView获取可见的行数

场景 当我们需要实时刷新QTableView时&#xff0c;而此时tableView的数据量较大&#xff0c;如果全部刷新显然不合理&#xff0c;如果可以只对用户看的到的数据进行刷新那就最好了&#xff0c;经过一番摸索找到了几种方式&#xff0c;可供参考 代码 方法1 QVector<int>…

64B/66B编码 自定义PHY层设计

一、前言 之前的一篇文章讲解了64B/66B的基本原理&#xff0c;本篇在基于64B/66B GT Transceiver的基础之上设计自定义PHY。基本框图如下。 二、GT Mdule GT Module就按照4个GT CHannel共享一个GT COMMON进行设置&#xff0c;如下图。要将例子工程中的GT COMMON取出&#xff…

docker环境搭建

项目环境搭建 1、安装 Linux 虚拟机 &#xff08;1&#xff09;下载安装&#xff1a; VM VirtualBox 下载安装&#xff1a;Downloads – Oracle VM VirtualBox&#xff0c;要先开启CPU虚拟化 &#xff08;2&#xff09;通过vagrant&#xff0c;在VirtualBox中安装虚拟机 下…

STM32学习和实践笔记(15):STM32中断系统

中断概念 CPU执行程序时&#xff0c;由于发生了某种随机的事件(外部或内部)&#xff0c;引起CPU暂 时中断正在运行的程序&#xff0c;转去执行一段特殊的服务程序(中断服务子程序 或中断处理程序)&#xff0c;以处理该事件&#xff0c;该事件处理完后又返回被中断的程序 继…

修改Ubuntu的镜像源为清华镜像源

修改Ubuntu的镜像源为清华镜像源 1、首先使用以下命令备份现有的镜像源&#xff1a; cd /etc/apt sudo cp sources.list sources.list.bak 2、使用以下命令打开镜像源文件&#xff1a; sudo vim /etc/apt/sources.list 3、在vim插入模式下使用以下内容替换掉原镜像源…

MySQL基础-----约束详解

目录 一. 概述: 二.约束演示&#xff1a; 三.外键约束&#xff1a; 3.1介绍&#xff1a; 3.2外键约束语法&#xff1a; 3.3删除&#xff0c;更新行为&#xff1a; 一. 概述: &#x1f9d0;&#x1f9d0;概念&#xff1a;约束是作用于表中字段上的规则&#xff0c;用于限制…

css面试题整理

css面试题 一、HTML语义化标签二、块级元素、内联元素、 行内块元素三、盒模型 一、HTML语义化标签 什么是HTML语义化标签&#xff1f;语义化标签的优势&#xff1f; HTML语义化标签顾名思义就是赋予标签含义&#xff0c;比如说<p>标签代表的是段落&#xff0c;还有<…

springboot结合vue实现文件上传下载功能

紧接着上一次的博客&#xff0c;这次来实现一下文件(主要是图片)的上传和下载功能&#xff0c;上一次的博客如下所示&#xff1a; Springboot集成JWT token实现权限验证-CSDN博客 其实文件的上传和下载功能(后端的部分)&#xff0c;在我之前的博客就已经有写了&#xff0c;所以…

区块链知识总结——智能合约

定义&#xff1a; 智能合约是一种基于区块链技术的自动化合约&#xff0c;它能够在没有第三方介入的情况下执行、管理和执行合约条款。智能合约是由代码编写的&#xff0c;存储在区块链上&#xff0c;并在满足特定条件时自动执行。 特点&#xff1a; 智能合约具有以下几个特…

LD-Pruner、EdgeFusion(On-Device T2I)、FreeDiff、TextCenGen、MemLLM

本文首发于公众号&#xff1a;机器感知 https://mp.weixin.qq.com/s/KiyNfwYWU-wBiCO-hE9qkA 苏 The devil is in the object boundary: towards annotation-free instance segmentation using Foundation Models Foundation models, pre-trained on a large amount of data…

AI技术与反诈的挑战

Sora的出现意味着今后利用AI升级技术实施欺诈行为门槛将变低&#xff0c;反欺诈产品服务类科技公司在面对更强大的欺诈攻击时&#xff0c;将面临更加严峻的挑战。其中&#xff0c;可预见最大的变化会来自“深度伪造”。“深度伪造”的危险不仅在于生成虚假视频和图片&#xff0…

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

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

Docker使用教程及docker部署Vue项目

什么是Docker及其工作原理 虚拟化技术Docker是什么&#xff1f;三大基本术语核心算法原理和具体操作步骤 Docker和传统虚拟化技术区别为什么使用Docker&#xff1f;Docker有什么作用&#xff1f;1.解决应用部署的环境问题遇到问题达到效果 2.容器化 docker的各种命令解释运行机…

6.GodotCanvasItem、Node2D及自定义节点

CanvasItem节点 CanvasItem节点&#xff0c;CanvasItem -> Node&#xff0c;所以CanvasItem继承了Node的所有功能Canvas是画布的意思&#xff0c;所以CanvasItem代表了就是可以被绘制的节点&#xff0c;可以设置可视化界面和材质的颜色所有的2D节点和GUI节点都继承于CanvasI…

集成电路测试学习-2

3、电源电流&#xff08;IDD&#xff09; 目的 电源电流测试是指在额定电压条件下&#xff0c;通过电源消耗的电流来反映被测器件的功耗。 功耗对于一些电池驱动的设备&#xff0c;尤为重要&#xff0c;会直接影响产品的续航时间。 同时电源电流测试也是快速分辨被测器件好坏的…

网络行为分析与异常检测

构建防火墙和使用简单的安全解决方案不足以保护网络免受网络异常或攻击&#xff0c;因为DDoS攻击、未知恶意软件和其他安全威胁一直在上升&#xff0c;改变了网络安全格局。网络管理员必须积极主动地分析网络&#xff0c;获得对网络的完全控制&#xff0c;并全面了解网络流量活…

访问云平台中linux系统图形化界面,登录就出现黑屏的问题解决(ubuntu图形界面)

目录 一、问题-图形化界面访问黑屏 二、系统环境 &#xff08;一&#xff09;网络结构示意图 &#xff08;二&#xff09;内部机器版本 三、分析 四、解决过程 &#xff08;一&#xff09;通过MobaXterm远程访问图形化界面(未成功) 1、连接方法 2、连接结果 &#xf…

acwing-y总基础课算法笔记整理

技巧 vector, 变长数组&#xff0c;倍增的思想size() 返回元素个数 capacity() 容量empty() 返回是否为空clear() 清空front()/back()push_back()/pop_back()begin()/end()[]支持比较运算&#xff0c;按字典序pair<int, int>first, 第一个元素second, 第二个元素支持…

SQL查询中正确使用圆括号以明确逻辑条件和操作符优先级的重要性,where后括号、and、or使用

SQL查询中的条件和逻辑操作符 SQL&#xff08;结构化查询语言&#xff09;是用于管理和操作关系数据库的标准编程语言。在SQL查询中&#xff0c;我们经常需要根据特定的条件来筛选和检索数据。这些条件通常使用逻辑操作符来组合&#xff0c;例如AND、OR和NOT。正确使用这些操作…

pt格式文件转engine小记【yolov5-6.0版本】

背景 项目是使用yolov5-6.0的版本&#xff0c;需要加一个新模型进去&#xff0c;yolov5提供的类别有很多&#xff0c;我这里使用chair椅子。第一步就是先把提供的pt文件转化为tensorrt所需要的engine格式的文件&#xff0c;在官网上有提供转换方法。&#xff08;似乎高版本的y…