【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、添加分区释放…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...

搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...

Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

计算机基础知识解析:从应用到架构的全面拆解
目录 前言 1、 计算机的应用领域:无处不在的数字助手 2、 计算机的进化史:从算盘到量子计算 3、计算机的分类:不止 “台式机和笔记本” 4、计算机的组件:硬件与软件的协同 4.1 硬件:五大核心部件 4.2 软件&#…...

C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...

Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...