【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协议的起始位与结束位

数据的变化与采集

三、具体读写时序

        具体读写时序图类似下图,这是我从一个支持IIC协议的设备的手册上截取下来的,值得一提的是,在下图中,灰色部分表示的是数据总线SDA由主机操纵,白色部分表示数据总线SDA由从机操纵。

        写数据时(如下图的I2C Register Wr):首先是主机发送一个起始位,接着是七位宽的设备地址加上一位宽的写控制位,从机响应一次;然后主机发送一字节的寄存器地址,从机响应一次;然后由主机发送一字节以上的数据,从机响应;最后发送一个停止位

       读数据时(如下图的I2C Register Rd):首先是主机发送一个起始位,接着是七位宽的设备地址加上一位宽的写控制位;然后是主机发送寄存器地址,从机应答;再重新发送一次起始位设备地址读控制位,用于表示接下来是要进行读操作,从机应答;从机发送数据,主机应答,在得到最后一字节数据后,由主机发送一个非应答信号;最后发送一个停止位

IIC协议

四、设计部分

        网站上大家对于iic的设计已经有非常详细的讲解了,这里就不再做太简单的说明,以下主要是我对于IIC的个人方面的一些理解和设计。

        1.接口设计

        作为被广泛使用的协议,我们在设计时应该尽可能的设计成通用模块,以便下一次能够直接使用。因此,模块的接口定义应为如图所示:

IIC模块接口

        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时,都会将每一个操作都分为一个状态,例如,起始位是一个状态,发送设备地址是一个状态,读写控制位是一个状态,接收应答和发送非应答分别为一个状态等等,这样的划分十分的细致,每一个操作都能一目了然,但我认为这样做的同时会使得状态的跳转更加的复杂,使得整个状态机变得十分的臃肿,如下图。

以前的IIC状态框图设计
我以前的IIC状态框图设计

        于是,我便重新构思了一下设计方案:以9位(9bit)为一个进行周期设计,即每9位或1位为一个状态。具体状态如下:

        状态一:IDLE  表示闲置状态;

        状态二:START  表示起始位;

        状态三:DEV_CMD  表示设备地址位、读写控制位宽和从机的应答位;

        状态四:ADDR  表示发送寄存器地址位和接收从机的应答位;

        状态五:WDATA  表示写数据部分;

        状态六:RDATA  表示读数据部分;

        状态七:STOP  表示停止位;

重新设计的IIC状态框图
重新设计的IIC状态框图

        3.时序设计

        写数据时序:

写时序1

写时序2

        读数据时序:

读时序1

读时序2

        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信号的赋值逻辑。

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

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

相关文章

C++栈、队列、优先级队列模拟+仿函数

目录 一、栈的模拟和deque容器 1.deque 1.1deque结构 1.2deque优缺点 2.stack模拟 二、队列的模拟 三、priority_queue优先级队列 1.优先级队列模拟 2.添加仿函数 一、栈的模拟和deque容器 在之前&#xff0c;我们学过了C语言版本的栈&#xff0c;可以看这篇文章 栈和…

java智慧工地云平台源码,以物联网、移动互联网技术为基础,结合大数据、云计算等,实现工程管理绿色化、数字化、精细化、智能化的效果

智慧工地将更多人工智能、传感技术、虚拟现实等高科技技术植入到建筑、机械、人员穿戴设施、场地进出关口等各类物体中&#xff0c;围绕人、机、料、法、环等各方面关键因素&#xff0c;彻底改变传统建筑施工现场参建各方现场管理的交互方式、工作方式和管理模式&#xff0c;智…

香飘飘的想象空间:全面创新驱动外延与内涵双增长,未来可期

面对时常会发生代际更替的消费者主力群体&#xff0c;创新已经成为一种商业上的必须。 日前&#xff0c;香飘飘发布了三季报&#xff0c;数据显示&#xff0c;第三季度&#xff0c;香飘飘营业收入8.08亿元&#xff0c;同比增长20.41%。前三季度&#xff0c;香飘飘营收19.79亿元…

视频号视频提取小程序,快速下载视频号视频

​视频号提取小程序可以帮助用户方便地从视频号视频平台获取到自己喜欢的视频号内容。通过这个小程序&#xff0c;你可以快速搜索并提取出视频号&#xff0c;并进行相关的操作。 据悉视频下载bot小程序目前已经更名为【提取下载小助手】 使用视频号提取小程序有以下几个步骤&…

Kubernetes 通过 Deployment 部署Jupyterlab

概要 在Kubernetes上部署jupyterlab服务&#xff0c;链接Kubernetes集群内的MySQL&#xff0c;实现简单的数据开发功能。 前置条件 镜像准备&#xff1a;自定义Docker镜像--Jupyterlab-CSDN博客 MySQL-Statefulset准备&#xff1a;StatefulSet 简单实践 Kubernetes-CSDN博客…

学习vue3

一、入门 1.引入外部库 ①直接将所有的js都通过script标签引入到html文件中&#xff0c;所有的js资源在web页面中都能通用。 ②使用js引用js&#xff08;ES6&#xff09;&#xff0c;模块导入与导出 2.模块是只读引用 这段话是在解释 Vue.js 中的概念和用法。在 Vue.js 中&a…

从实时数据库转战时序数据库,他陪伴 TDengine 从 1.0 走到 3.0

关于采访嘉宾 在关胜亮的学生时代&#xff0c;“神童”这个称号如影随形&#xff0c;很多人初听时会觉得这个称谓略显夸张&#xff0c;有些人还会认为这是不是就是一种调侃&#xff0c;但是如果你听说过他的经历&#xff0c;就会理解这一称号的意义所在了。 受到教师母亲的影…

React之render

一、原理 首先&#xff0c;render函数在react中有两种形式&#xff1a; 在类组件中&#xff0c;指的是render方法&#xff1a; class Foo extends React.Component {render() {return <h1> Foo </h1>;} }在函数组件中&#xff0c;指的是函数组件本身&#xff1a…

【Linux进阶之路】进程(中)—— 进程地址空间

文章目录 一、 进程地址空间1.概念引入2.基本概念3.深入概念3.1 初识信息交互3.2 区域划分3.3 进程地址空间3.4 再识页表缺页中断进程挂起 总结 一、 进程地址空间 1.概念引入 指针指向的地址是内存中的地址吗&#xff1f;下面我们用一个实验来证明一下。 先来写程序看一下程…

C# Onnx Yolov8 Detect 戴安全帽检测

效果 项目 代码 using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using OpenCvSharp; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms;namespace Onnx…

pd19虚拟机软件 Parallels Desktop 19 mac中文停用功能

Parallels Desktop 19 mac是一款功能强大的虚拟机软件&#xff0c;它允许用户在Mac电脑上同时运行Windows、Linux和其他操作系统。Parallels Desktop提供了直观易用的界面&#xff0c;使用户可以轻松创建、配置和管理虚拟机。 Parallels Desktop 19 for Mac停用功能 从Paralle…

python+unittest+requests+HTMLRunner编写接口自动化测试集

问题描述&#xff1a; 搭建接口测试框架&#xff0c;执行用例请求多个不同请求方式的接口 实现步骤&#xff1a; ① 创建配置文件config.ini&#xff0c;写入部分公用参数&#xff0c;如接口的基本url、测试报告文件路径、测试数据文件路径等配置项 1 [DATABASE] 2 data_addre…

xcode15一直显示正在连接iOS17真机问题解决

前言 更新xcode15之后&#xff0c;出现了各种报错问题&#xff0c;可谓是一路打怪啊&#xff0c;解决一个报错问题又来一个。没想到到了最后还能出现一个一直显示正在连接iOS17真机的问题 一直显示正在连接iOS17真机的问题 问题截图如下&#xff1a; 解决方法 1. 打开De…

使用canvas做了一个最简单的网页版画板,5分钟学会

画板实现的效果&#xff1a;可以切换画笔的粗细&#xff0c;颜色&#xff0c;还可以使用橡皮擦&#xff0c;还可以清除画布&#xff0c;然后将画的内容保存下载成一张图片&#xff1a; 具体用到的canvas功能有&#xff1a;画笔的粗细调整lineWidth&#xff0c;开始一个新的画笔…

紫光同创FPGA实现HSSTLP高速接口通信,8b/10b编解码数据回环,提供PDS工程源码和技术支持

目录 1、前言免责声明 2、我这里已有的 GT 高速接口解决方案3、设计思路框架HSSTLP详解HSSTLP基本了解HSSTLP之时钟HSSTLP之PCSHSSTLP之PMAHSSTLP之接口说明 硬件设计HSSTLP IP调用和配置 4、PDS工程详解5、上板调试验证并演示6、福利&#xff1a;工程代码的获取 紫光同创FPGA实…

js实现在报表参数界面获取body中控件的值

要在报表参数界面获取body中控件的值&#xff0c;你可以使用JavaScript来实现。下面是一个详细的介绍&#xff1a; 1. DOM&#xff08;文档对象模型&#xff09;&#xff1a; - DOM是用于操作HTML文档的API&#xff0c;它允许你通过JavaScript访问和操作文档中的元素。 - 在报…

网络通信和tcp协议

一、计算机网络架构模型 1、OSI七层模型 2、TCP/IP模型 3、TCP/IP协议族 无论是什么网络模型&#xff0c;都是为上一层提供服务&#xff0c;抽象层建立在低一层提供的服务上&#xff0c;每层都对应不同的协议 4、地址和端口号 1&#xff09;MAC地址 MAC 地址共 48 位&#…

ARM映像文件组成

引言 ARM编译器将各种源文件&#xff08;汇编文件、C语言程序文件、C语言程序文件&#xff09;编译生成ELF格式的目标文件&#xff08;后缀为.o文件&#xff0c;以下将目标文件简称为.o文件&#xff09;&#xff0c;.o文件经过连接器&#xff0c;和C/C运行时库一起编译生成ELF格…

visual studio Qt 开发环境中手动添加 Q_OBJECT 导致编译时出错的问题

问题简述 创建项目的时候&#xff0c;已经添加了类文件&#xff0c;前期认为不需要信号槽&#xff0c;就没有添加宏Q_OBJECT,后面项目需要&#xff0c;又加入了宏Q_OBJECT&#xff0c;但是发现只是添加了一个宏Q_OBJECT&#xff0c;除此之外没有改动其它的代码&#xff0c;原本…

十四天学会C++之第八天:文件操作

1. 文件的打开和关闭 文件操作的基本概念。打开文件&#xff1a;使用fstream库打开文件以供读写。关闭文件&#xff1a;确保文件在使用完毕后正确关闭。 文件的打开和关闭&#xff1a;C 文件操作入门 在C编程中&#xff0c;文件操作是一项重要的任务&#xff0c;可以读取和写…