【FPGA】IIC协议通用主机接口的设计与实现详解
一、认识IIC
IIC(I2C)协议是一种串行通信协议,用于连接微控制器和外围设备。IIC协议只需要两根信号线(时钟线SCL和数据线SDA)就能完成设备之间的通信;支持多主机和多从机通信,通过设备地址区分不同的设备;标准模式下可达100Kbit/s,快速模式下可达400Kbit/s,高速模式下可达3.4Mbit/s;具有应答机制,可以检测数据的正确性和设备的存在性。
二、协议详解
在闲置状态时,时钟线和数据线都会保持高电平。IIC协议的具体传输过程如下:
1. 主机发送起始信号,即在时钟线SCL保持高电平的情况下,数据线SDA由高电平向低电平跳变。
2. 主机发送从机设备地址和读写控制位,从机的设备地址一般为7位,读写控制位为1位,0表示写操作,1表示读操作。主机在发送完8位数据后,释放数据线SDA,等待从机的应答信号。
3. 从机接收到地址和控制位后,进行地址识别,如果匹配,则在下一个时钟周期内,将数据线SDA拉低,表示应答信号。如果不匹配,则保持数据线SDA为高电平,等待主机发送停止信号或新的起始信号。
4. 如果主机发送的是写操作,那么主机继续发送数据字节,每发送一个字节后,释放数据线SDA,等待从机的应答信号。如果从机接收到数据字节后,将数据线SDA拉低,表示应答信号。如果从机无法接收数据字节或者发生错误,那么保持数据线SDA为高电平,等待主机发送停止信号或新的起始信号。
5. 如果主机发送的是读操作,那么主机释放数据线SDA,由从机发送数据字节。每接收一个字节后,主机根据需要发送应答信号或非应答信号。应答信号表示主机需要继续接收数据字节,非应答信号表示主机已经接收完毕或者发生错误。非应答信号由主机在最后一个时钟周期内将数据线SDA拉高实现。
6. 主机发送停止信号,即在时钟线SCL保持高电平的情况下,数据线SDA由低电平向高电平跳变。停止信号表示一次IIC通信的结束。
重要的起始位和结束位如下图所示,且在IIC协议中,控制数据线的设备会在SCL为低电平时控制SDA数据线,在SCL为高电平时对SDA数据线进行采样:


三、具体读写时序
具体读写时序图类似下图,这是我从一个支持IIC协议的设备的手册上截取下来的,值得一提的是,在下图中,灰色部分表示的是数据总线SDA由主机操纵,白色部分表示数据总线SDA由从机操纵。
写数据时(如下图的I2C Register Wr):首先是主机发送一个起始位,接着是七位宽的设备地址加上一位宽的写控制位,从机响应一次;然后主机发送一字节的寄存器地址,从机响应一次;然后由主机发送一字节以上的数据,从机响应;最后发送一个停止位。
读数据时(如下图的I2C Register Rd):首先是主机发送一个起始位,接着是七位宽的设备地址加上一位宽的写控制位;然后是主机发送寄存器地址,从机应答;再重新发送一次起始位,设备地址和读控制位,用于表示接下来是要进行读操作,从机应答;从机发送数据,主机应答,在得到最后一字节数据后,由主机发送一个非应答信号;最后发送一个停止位。

四、设计部分
网站上大家对于iic的设计已经有非常详细的讲解了,这里就不再做太简单的说明,以下主要是我对于IIC的个人方面的一些理解和设计。
1.接口设计
作为被广泛使用的协议,我们在设计时应该尽可能的设计成通用模块,以便下一次能够直接使用。因此,模块的接口定义应为如图所示:

clk 为输入时钟;
rst 为复位信号(高有效);
txdat 表示需要通过IIC输出的数据;
txget 表示已输出该字节的信号;
rxdat 表示从sda获得的数据;
rxvld 表示数据有效;
dev_addr 表示设备地址;
reg_addr 表示读写地址(寄存器地址);
cmd_op 表示读写指令;
op_byte_num 表示读写字节数;
iic_work_en 表示模块开始工作信号;
busy 表示工作状态信号;
scl 表示IIC时钟;
sda 表示IIC数据;
2.状态机设计
因为要做一个IIC协议会比较复杂,所以说这里采用状态机的形式,以理清每一个操作。但是一般我们看到的IIC状态设计、包括在之前我设计IIC时,都会将每一个操作都分为一个状态,例如,起始位是一个状态,发送设备地址是一个状态,读写控制位是一个状态,接收应答和发送非应答分别为一个状态等等,这样的划分十分的细致,每一个操作都能一目了然,但我认为这样做的同时会使得状态的跳转更加的复杂,使得整个状态机变得十分的臃肿,如下图。
于是,我便重新构思了一下设计方案:以9位(9bit)为一个进行周期设计,即每9位或1位为一个状态。具体状态如下:
状态一:IDLE 表示闲置状态;
状态二:START 表示起始位;
状态三:DEV_CMD 表示设备地址位、读写控制位宽和从机的应答位;
状态四:ADDR 表示发送寄存器地址位和接收从机的应答位;
状态五:WDATA 表示写数据部分;
状态六:RDATA 表示读数据部分;
状态七:STOP 表示停止位;
3.时序设计
写数据时序:


读数据时序:


4.代码设计
首先,定义模块
其中,我使用的时钟为100Mhz,复位信号为高电平有效:
module i2c_master_interface #(parameter ADDR_LEN = 1,parameter IIC_CLOCK = 400000) (input i_clk ,input i_rst ,input [7:0] i_txdat ,output o_txget ,output [7:0] o_rxdat ,output o_rxvld ,input [6:0] i_dev_addr ,input [ADDR_LEN*8-1:0] i_reg_addr,input i_cmd_op ,input [3:0] i_op_byte_num ,input i_iic_work_en ,output o_busy ,//output o_output_en,inout io_scl ,inout io_sda
);endmodule
第二步,进行参数的定义
localparam SCL_PERIOD = 1000/(IIC_CLOCK/100000) , // in_clk = 100MHzSCL_HALF = SCL_PERIOD >> 1 ,LOW_HALF = SCL_HALF >> 1 ,HIGH_HALF = (SCL_PERIOD+SCL_HALF)>>1;localparam IDLE = 7'b000_0001,START = 7'b000_0010,DEV_CMD = 7'b000_0100,ADDR = 7'b000_1000,WDATA = 7'b001_0000,RDATA = 7'b010_0000,STOP = 7'b100_0000;localparam WRITE_BIT = 1'b0,READ_BIT = 1'b1,OP_WRITE = 1'b0,OP_READ = 1'b1;
第三步,定义需要用到的信号
reg [6:0] state_c ;
reg [6:0] state_n ;reg [7:0] cnt_scl ;
reg reg_scl ;
reg [3:0] cnt_bit ;
wire end_cnt_bit;
reg [3:0] bit_num ;
reg rw_bit ;reg [3:0] cnt_byte;
reg [3:0] op_byte_num ;reg [ADDR_LEN*8-1:0] reg_addr_1;
reg [7:0] reg_addr_2 ;reg [7:0] reg_txdat ;
reg txget ;reg reg_sda ;
reg reg_i_sda ;
reg [7:0] rx_data ;
reg reg_ack ;
reg rxvld ;
reg busy ;wire o_sda;
wire i_sda;
wire o_scl;
wire i_scl;
wire output_en;
第四步,构造有限状态机
依照逻辑,状态机的跳转条件如下:

// FMS
always @(posedge i_clk or posedge i_rst) beginif (i_rst) beginstate_c <= IDLE;endelse beginstate_c <= state_n;end
endalways @(*) begincase (state_c)IDLE : beginif (i_iic_work_en) beginstate_n = START;endelse beginstate_n = state_c;endendSTART : beginif (end_cnt_bit) beginstate_n = DEV_CMD;endelse beginstate_n = state_c;endendDEV_CMD : beginif (end_cnt_bit && reg_ack) begin // NO ACKstate_n = IDLE;endelse if ((rw_bit == WRITE_BIT) && end_cnt_bit && (reg_ack == 0)) begin //state_n = ADDR;endelse if ((rw_bit == READ_BIT) && end_cnt_bit && (reg_ack == 0)) begin //state_n = RDATA;endelse beginstate_n = state_c;endendADDR : beginif (end_cnt_bit && reg_ack) beginstate_n = IDLE;endelse if ((i_cmd_op == OP_WRITE) && (cnt_byte == (ADDR_LEN - 1)) && end_cnt_bit && (reg_ack == 0)) begin //state_n = WDATA;endelse if ((i_cmd_op == OP_READ) && (cnt_byte == (ADDR_LEN - 1)) && end_cnt_bit && (reg_ack == 0)) begin //state_n = START;endelse beginstate_n = state_c;endendWDATA : beginif (end_cnt_bit && reg_ack) beginstate_n = IDLE;endelse if ((cnt_byte == (op_byte_num - 1)) && end_cnt_bit && (reg_ack == 0)) begin //state_n = STOP;endelse beginstate_n = state_c;endendRDATA : beginif ((cnt_byte == (op_byte_num - 1)) && end_cnt_bit) begin //state_n = STOP;endelse beginstate_n = state_c;endendSTOP : beginif (end_cnt_bit) beginstate_n = IDLE;endelse beginstate_n = state_c;endenddefault: state_n = IDLE;endcase
end
第五步,构造中间逻辑
// cnt_bit bit_num
always @(posedge i_clk or posedge i_rst) beginif (i_rst) begincnt_bit <= 0;endelse if ((state_c != IDLE) && (cnt_scl == (SCL_PERIOD - 1))) beginif (end_cnt_bit) begincnt_bit <= 0;endelse begincnt_bit <= cnt_bit + 1;endend
end
assign end_cnt_bit = (state_c != IDLE) && (cnt_scl == (SCL_PERIOD - 1)) && (cnt_bit == (bit_num - 1));always @(*) begincase (state_c)IDLE : bit_num = 0;START : bit_num = 1;DEV_CMD : bit_num = 9;ADDR : bit_num = 9;WDATA : bit_num = 9;RDATA : bit_num = 9;STOP : bit_num = 1;default : bit_num = 0;endcase
end// rw_bit
always @(posedge i_clk or posedge i_rst) beginif (i_rst) beginrw_bit <= WRITE_BIT;endelse begincase (i_cmd_op)OP_WRITE : rw_bit <= WRITE_BIT;OP_READ : beginif ((state_c == STOP) && end_cnt_bit) beginrw_bit <= WRITE_BIT;endelse if ((state_c == DEV_CMD) && (rw_bit == WRITE_BIT) && end_cnt_bit && (reg_ack == 0)) begin //rw_bit <= READ_BIT;endenddefault : rw_bit <= rw_bit;endcaseend
end// cnt_byte op_byte_num
always @(posedge i_clk or posedge i_rst) beginif (i_rst) begincnt_byte <= 0;endelse if (state_c == IDLE) begincnt_byte <= 0;endelse if ((state_c == ADDR) && end_cnt_bit && (reg_ack == 0)) beginif (cnt_byte == (ADDR_LEN - 1)) begincnt_byte <= 0;endelse begincnt_byte <= cnt_byte + 1;endendelse if ((state_c == RDATA || state_c == WDATA) && end_cnt_bit && (reg_ack == 0)) beginif (cnt_byte == (op_byte_num - 1)) begincnt_byte <= 0;endelse begincnt_byte <= cnt_byte + 1;endend
endalways @(posedge i_clk or posedge i_rst) beginif (i_rst) beginop_byte_num <= 1;endelse if ((state_c == START) && end_cnt_bit) beginop_byte_num <= i_op_byte_num;end
end// reg_addr_1 reg_addr_2
always @(posedge i_clk or posedge i_rst) beginif (i_rst) beginreg_addr_1 <= 0;endelse if ((state_c == START) && end_cnt_bit) beginreg_addr_1 <= i_reg_addr;end
endalways @(posedge i_clk or posedge i_rst) beginif (i_rst) beginreg_addr_2 <= 0;endelse if (state_c == ADDR) beginreg_addr_2 <= reg_addr_1[((ADDR_LEN - cnt_byte)*8-1) -: 8]; //end
end// reg_txdat txget
always @(posedge i_clk or posedge i_rst) beginif (i_rst) beginreg_txdat <= 8'hff;endelse if (state_c == WDATA) beginreg_txdat <= i_txdat;end
endalways @(posedge i_clk or posedge i_rst) beginif (i_rst) begintxget <= 1'b0;endelse if ((state_c == WDATA) && end_cnt_bit) begintxget <= 1'b1;endelse begintxget <= 1'b0;end
end
第六步,构建SCL上的逻辑
// cnt_scl reg_scl
always @(posedge i_clk or posedge i_rst) beginif (i_rst) begincnt_scl <= 0;endelse if (state_c != IDLE) beginif (cnt_scl == (SCL_PERIOD - 1)) begincnt_scl <= 0;endelse begincnt_scl <= cnt_scl + 1;endend
endalways @(posedge i_clk or posedge i_rst) beginif (i_rst) beginreg_scl <= 0;endelse if (cnt_scl == SCL_HALF - 1) beginreg_scl <= 1;endelse if (cnt_scl == SCL_PERIOD - 1) beginreg_scl <= 0;endelse beginreg_scl <= reg_scl;end
end
第七步,构建SDA上的逻辑
// reg_sda
always @(posedge i_clk or posedge i_rst) beginif (i_rst) beginreg_sda <= 1'b1;endelse begincase (state_c)START : beginif (cnt_scl == LOW_HALF - 1) beginreg_sda <= 1'b1;endelse if (cnt_scl == HIGH_HALF -1) beginreg_sda <= 1'b0;endendDEV_CMD : beginif (cnt_scl == LOW_HALF - 1) begincase (cnt_bit)0 : reg_sda <= i_dev_addr[6];1 : reg_sda <= i_dev_addr[5];2 : reg_sda <= i_dev_addr[4];3 : reg_sda <= i_dev_addr[3];4 : reg_sda <= i_dev_addr[2];5 : reg_sda <= i_dev_addr[1];6 : reg_sda <= i_dev_addr[0];7 : reg_sda <= rw_bit;default: reg_sda <= reg_sda;endcaseendendADDR : beginif (cnt_scl == LOW_HALF - 1) begincase (cnt_bit)0 : reg_sda <= reg_addr_2[7];1 : reg_sda <= reg_addr_2[6];2 : reg_sda <= reg_addr_2[5];3 : reg_sda <= reg_addr_2[4];4 : reg_sda <= reg_addr_2[3];5 : reg_sda <= reg_addr_2[2];6 : reg_sda <= reg_addr_2[1];7 : reg_sda <= reg_addr_2[0];default: reg_sda <= reg_sda;endcaseendendWDATA : beginif (cnt_scl == LOW_HALF - 1) begincase (cnt_bit)0 : reg_sda <= reg_txdat[7];1 : reg_sda <= reg_txdat[6];2 : reg_sda <= reg_txdat[5];3 : reg_sda <= reg_txdat[4];4 : reg_sda <= reg_txdat[3];5 : reg_sda <= reg_txdat[2];6 : reg_sda <= reg_txdat[1];7 : reg_sda <= reg_txdat[0];default: reg_sda <= reg_sda;endcaseendendRDATA : begin // SACKif ((cnt_scl == LOW_HALF - 1) && (cnt_byte == op_byte_num - 1) && (cnt_bit == 8)) begin //reg_sda <= 1'b1; // NACKendelse if ((cnt_scl == LOW_HALF - 1) && (cnt_bit == 8)) beginreg_sda <= 1'b0; // ACKendendSTOP : beginif (cnt_scl == LOW_HALF - 1) beginreg_sda <= 1'b0;endelse if (cnt_scl == HIGH_HALF -1) beginreg_sda <= 1'b1;endenddefault : reg_sda <= reg_sda;endcaseend
end// reg_i_sda rx_data reg_ack rxvld
always @(posedge i_clk or posedge i_rst) beginif (i_rst) beginreg_i_sda <= 1'b1;endelse beginreg_i_sda <= i_sda;end
endalways @(posedge i_clk or posedge i_rst) beginif (i_rst) beginrx_data <= 0;endelse if ((state_c == RDATA) && (cnt_scl == HIGH_HALF - 1)) begincase (cnt_bit)0 : rx_data[7] <= reg_i_sda;1 : rx_data[6] <= reg_i_sda;2 : rx_data[5] <= reg_i_sda;3 : rx_data[4] <= reg_i_sda;4 : rx_data[3] <= reg_i_sda;5 : rx_data[2] <= reg_i_sda;6 : rx_data[1] <= reg_i_sda;7 : rx_data[0] <= reg_i_sda;default: rx_data <= rx_data;endcaseend
endalways @(posedge i_clk or posedge i_rst) beginif (i_rst) beginreg_ack <= 1'b1;endelse if ((state_c == DEV_CMD || state_c == ADDR || state_c == WDATA) && (cnt_bit == 8) && (cnt_scl == HIGH_HALF -1)) beginreg_ack <= reg_i_sda;end
endalways @(posedge i_clk or posedge i_rst) beginif (i_rst) beginrxvld <= 1'b0;endelse if ((state_c == RDATA) && end_cnt_bit) beginrxvld <= 1'b1;endelse beginrxvld <= 1'b0;end
end
第八步,构造busy信号
// busy
always @(posedge i_clk or posedge i_rst) beginif (i_rst) beginbusy <= 1'b0;endelse if (i_iic_work_en) beginbusy <= 1'b1;end// else if ((state_c == STOP) && end_cnt_bit) begin// busy <= 1'b0;// endelse if (state_c == IDLE) beginbusy <= 1'b0;endelse beginbusy <= 1'b1;end
end
第九步,赋值输出
// output
assign o_scl = (state_c == IDLE)? 1 : reg_scl;
assign o_txget = txget;
assign o_rxdat = rx_data;
assign o_rxvld = rxvld;
assign o_busy = busy;
assign o_sda = reg_sda;
assign output_en = (state_c == RDATA)? ((cnt_bit == 8)? 1:0) : ((cnt_bit == 8)? 0:1);//assign o_output_en = output_en;
最后一步,构建三态门
这里使用vivado中的原语IO_BUF来构建:
IOBUF #(.DRIVE(12), // Specify the output drive strength.IBUF_LOW_PWR("TRUE"), // Low Power - "TRUE", High Performance = "FALSE" .IOSTANDARD("DEFAULT"), // Specify the I/O standard.SLEW("SLOW") // Specify the output slew rate
) iobuf_inst_sda (.O ( i_sda ), // Buffer output.IO ( io_sda ), // Buffer inout port (connect directly to top-level port).I ( o_sda ), // Buffer input.T (~output_en ) // 3-state enable input, high=input, low=output
);IOBUF #(.DRIVE(12), // Specify the output drive strength.IBUF_LOW_PWR("TRUE"), // Low Power - "TRUE", High Performance = "FALSE" .IOSTANDARD("DEFAULT"), // Specify the I/O standard.SLEW("SLOW") // Specify the output slew rate
) iobuf_inst_scl (.O ( i_scl ), // Buffer output.IO ( io_scl ), // Buffer inout port (connect directly to top-level port).I ( o_scl ), // Buffer input.T ( 0 ) // 3-state enable input, high=input, low=output
);
5.仿真验证
写时序

读时序:

其中,我们可以清楚的看到以下关键信息:
起始位:

状态二,设备地址与写控制位:

状态三,发送寄存器地址:

写数据操作,写了5个字节:

读操作时的RESTART状态,设备地址、读控制位以及读出的1字节数据:

停止位:

五、说明
由以上设计可以看出,我设计的SCL是由低到高的,同时,在起始位的状态时,SCL也同样会由低到高,这与通常我们所理解的SCL在起始位保持高电平不符,如下图所示:

但经过我的上板实测,该方案可行。
第二点, 由以上设计可以看出,由于时序逻辑的影响,导致busy信号的输出会比state信号延迟一个周期,这意味这最好使用检测busy下降沿的方法来判断IIC接口是否还在工作,或者更改busy信号的赋值逻辑。


相关文章:
【FPGA】IIC协议通用主机接口的设计与实现详解
一、认识IIC IIC(I2C)协议是一种串行通信协议,用于连接微控制器和外围设备。IIC协议只需要两根信号线(时钟线SCL和数据线SDA)就能完成设备之间的通信;支持多主机和多从机通信,通过设备地址区分不…...
《红蓝攻防对抗实战》八.利用OpenSSL对反弹shell流量进行加密
前文推荐: 《红蓝攻防对抗实战》一. 隧道穿透技术详解《红蓝攻防对抗实战》二.内网探测协议出网之TCP/UDP协议探测出网《红蓝攻防对抗实战》三.内网探测协议出网之HTTP/HTTPS协议探测出网《红蓝攻防对抗实战》四.内网探测协议出网之ICMP协议探测出网《红蓝攻防对抗…...
手机桌面待办事项APP推荐
每天,我们每个人都面临着繁琐的事务和任务,而手机成了我们日常生活中不可或缺的伙伴。手机上的待办事项工具像一个可靠的助手,可以帮助我们更好地记录、管理和完成任务。在手机桌面上使用的待办事项APP推荐用哪一个呢? 手机是我们…...
2023NOIP A层联测18 划分
题目大意 对于一个长度为 n n n的 01 01 01字符串 S S S,请求出将其分为至少 k k k段,将每段看成二进制数求和后的最大值以及取到这个最大值的划分方案的数量。 输出最大值模 998244353 998244353 998244353后的值和划分方案的数量模 998244353 998244…...
pc与android设备进行通信
首先:根据此博客 Android模拟器调试TCP通讯_.emulator_console_auth_token-CSDN博客 思考: 只在本机电脑中: 服务器IP地址设为为0.0.0.0,并开始监听,客户端IP地址127.0.0.1,192.168.1.114都可连接。 12…...
【网安大模型专题10.19】论文6:Java漏洞自动修复+数据集 VJBench+大语言模型、APR技术+代码转换方法+LLM和DL-APR模型的挑战与机会
How Effective Are Neural Networks for Fixing Security Vulnerabilities 写在最前面摘要贡献发现 介绍背景:漏洞修复需求和Java漏洞修复方向动机方法贡献 数据集先前的数据集和Java漏洞Benchmark数据集扩展要求数据处理工作最终数据集 VJBenchVJBench 与 Vul4J 的…...
const 和 volatile 在实例成员函数的应用
const 和 volatile 的使用范围几乎没有限制 实例成员函数的参数后面可以出现 const 或 volatile,它们都用于修饰函数隐含参数 this 指向的对象 实例函数对象的参数表后面出现 const 说明this 所指向的对象是不能修改的只读对象 但是可以修改this所指向对象的非只读类…...
比Nginx测试桩更方便,ShenYu网关的Mock插件
有时候为了方便测试,我们需要模拟 HTTP 外部接口的返回结果。通常情况下,我们可以使用 Nginx 测试桩来实现这个目的。然而,Nginx 的使用门槛较高,可能对一些初级开发和测试人员来说有一定的难度。相比之下,Apache Shen…...
IDEA: 自用主题及字体搭配推荐
文章目录 1. 字体设置推荐2. 主题推荐3. Rainbow Brackets(彩虹括号)4. 设置背景图片 下面是我的 IDEA 主题和字体,它们的搭配效果如下: 1. 字体设置推荐 在使用 IntelliJ IDEA 进行编码和开发时,一个合适的字体设置可以提高你的工作效率和舒…...
Qt中的枚举变量,Q_ENUM,Q_FLAG以及Qt中自定义结构体、枚举型做信号参数传递
Qt中的枚举变量,Q_ENUM,Q_FLAG,Q_NAMESPACE,Q_ENUM_NS,Q_FLAG_NS以及其他 理论基础:一、Q_ENUM二、QMetaEnum三、Q_FLAG四、示例 Chapter1 Qt中的枚举变量,Q_ENUM,Q_FLAG,Q_NAMESPACE,Q_ENUM_NS,Q_FLAG_NS以及其他前言Q_ENUM的使用Q_FLAG的引入解决什么问题…...
【C++】priority_queue仿函数
今天我们来学习C中另一个容器适配器:优先级队列——priority_queue;和C一个重要组件仿函数: 目录 一、priority_queue 1.1 priority_queue是什么 1.2 priority_queue的接口 1.2.1 priority_queue使用举例 二、仿函数 三、关于priority…...
如何驾驭ChatGPT:掌控有效对话!
📢📢📢📣📣📣 哈喽!大家好,我是【一心同学】,一位上进心十足的【后端领域博主】!😜😜😜 ✨【一心同学】的写作风格&#x…...
LeetCode 面试题 16.03. 交点
文章目录 一、题目二、C# 题解 一、题目 给定两条线段(表示为起点 start {X1, Y1} 和终点 end {X2, Y2}),如果它们有交点,请计算其交点,没有交点则返回空值。 要求浮点型误差不超过 10^-6。若有多个交点(…...
【码银送书第九期】《ChatGPT 驱动软件开发:AI 在软件研发全流程中的革新与实践》
计算机技术的发展和互联网的普及,使信息处理和传输变得更加高效,极大地改变了金融、商业、教育、娱乐等领域的运作方式。数据分析、人工智能和云计算等新兴技术,也在不断地影响和改变着各个行业。 如今,我们正在见证人工智能技术的…...
Hadoop3.0大数据处理学习4(案例:数据清洗、数据指标统计、任务脚本封装、Sqoop导出Mysql)
案例需求分析 直播公司每日都会产生海量的直播数据,为了更好地服务主播与用户,提高直播质量与用户粘性,往往会对大量的数据进行分析与统计,从中挖掘商业价值,我们将通过一个实战案例,来使用Hadoop技术来实…...
华为机试题:HJ3 明明的随机数
目录 第一章、算法题1.1)题目描述1.2)解题思路与答案1.3)牛客链接 友情提醒: 先看文章目录,大致了解文章知识点结构,点击文章目录可直接跳转到文章指定位置。 第一章、算法题 1.1)题目描述 题目描述&…...
Python OpenCV将n×n的小图拼接成m×m的大图
Python OpenCV将nn的小图拼接成mm的大图 前言前提条件相关介绍实验环境n \times n的小图拼接成m \times m的大图代码实现 前言 由于本人水平有限,难免出现错漏,敬请批评改正。更多精彩内容,可点击进入Python日常小操作专栏、OpenCV-Python小…...
wkhtmltoimage/wkhtmltopdf 使用实践
1. 介绍 wkhtmltopdf/wkhtmltoimage 用于将简单的html页面转换为pdf或图片; 2.安装 downloads 2.1. mac os 下载64-bit 版本然后按照指示安装, 遇到 untrust developers 时,需要在 Settings -> Privacy 处信任下该安装包。 2.2. debian # 可用…...
Rclone连接Onedrive
一、Rclone介绍 Rclone是一款的命令行工具,支持在不同对象存储、网盘间同步、上传、下载数据。 我们这里连接的onedrive,其他网盘请查看官方文档。 注意: 需要先在Windows下配置好了,然后再将rclone配置文件复制到Linux的rclone配…...
RK356X/RK3588构建Ubuntu20.04根文件系统
文章目录 前言一、官网下载ubuntu-base二、挂载并构建文件系统2.1、配置构建文件系统环境2.2、编写挂载脚本mount.sh并安装相关工具2.3、轻量级的桌面环境 lubuntu-desktop2.4、卸载一些不必要的软件2.5、添加用户2.6 、允许root用户登录桌面2.7、串口自动登录2.8、添加分区释放…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...
【免费数据】2005-2019年我国272个地级市的旅游竞争力多指标数据(33个指标)
旅游业是一个城市的重要产业构成。旅游竞争力是一个城市竞争力的重要构成部分。一个城市的旅游竞争力反映了其在旅游市场竞争中的比较优势。 今日我们分享的是2005-2019年我国272个地级市的旅游竞争力多指标数据!该数据集源自2025年4月发表于《地理学报》的论文成果…...
算法刷题-回溯
今天给大家分享的还是一道关于dfs回溯的问题,对于这类问题大家还是要多刷和总结,总体难度还是偏大。 对于回溯问题有几个关键点: 1.首先对于这类回溯可以节点可以随机选择的问题,要做mian函数中循环调用dfs(i&#x…...
MLP实战二:MLP 实现图像数字多分类
任务 实战(二):MLP 实现图像多分类 基于 mnist 数据集,建立 mlp 模型,实现 0-9 数字的十分类 task: 1、实现 mnist 数据载入,可视化图形数字; 2、完成数据预处理:图像数据维度转换与…...
