[米联客-安路飞龙DR1-FPSOC] FPGA基础篇连载-18 I2C MASTER控制器驱动设计

软件版本:Anlogic -TD5.9.1-DR1_ES1.1

操作系统:WIN10 64bit

硬件平台:适用安路(Anlogic)FPGA

实验平台:米联客-MLK-L1-CZ06-DR1M90G开发板

板卡获取平台:https://milianke.tmall.com/

登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!

目录

1 系统框图

1.1  程序设计

1.1.1 空闲控制忙指示模块

1.1.2 SCL时钟分频器:

1.1.3 开始条件/停止条件

1.1.4 发送移位模块

1.1.5 接收移位模块

1.1.6 总线错误指示模块

2 状态机设计

3 程序源码


1 系统框图

I2C Master控制器主要包含I2C收发数据状态机,SCL时钟分频器、发送移位模块、接收移位模块、空闲控制忙指示模块。SCL和SDA的输出逻辑和时序通过SCL和I2C状态机控制。

重点介绍下其中的关键信号:

IO_sda为I2C双向数据总线

O_scl为I2C时钟

I_wr_cnt写数据字节长度,包含了器件地址,发送I_iic_req前,预设该值

I_rd_cnt读数据字节长度,仅包含读回有效部分,发送I_iic_req前,预设该值

I_wr_data写入的数据

O_rd_data读出的数据,如果是读请求,当O_iic_busy从高变低代表数据读回有效

I_iic_req I2C操作请求,根据I_rd_cnt是否大于0决定是否有读请求

I_iic_mode是否支持随机读写,发送I_iic_req前,预设该值

O_iic_busy总线忙

1.1  程序设计

1.1.1 空闲控制忙指示模块

为了方便使用该驱动程序,和前面学习的驱动一样,IIC master也使用I_iic_req以及O_iic_busy用于信号的握手。用户程序通过设置I_iic_req为高,请求读/写数据;设置O_iic_busy为1,表示IIC总线正忙,这时用户程序需要等待非忙的时候,请求读/写下一个数据。

reg scl_clk    = 1'b0;       //I2C控制器内部SCL时钟,与外部时钟存在OFFSET参数设置的相位偏移
reg  rd_req = 1'b0;          //读请求,当判断到需要读数据,内部状态机中设置1


//总线忙状态
always @(posedge scl_clk or negedge I_rstn )begin
    if(I_rstn == 1'b0)
        O_iic_busy <= 1'b0;
    else begin
        if((I_iic_req == 1'b1 || rd_req == 1'b1 || O_iic_bus_error))//I_iic_req == 1'b1 || rd_req == 1'b1总线进入忙状态
            O_iic_busy <= 1'b1;
        else if(IIC_S == IDLE)
            O_iic_busy <= 1'b0;
    end        
end
1.1.2 SCL时钟分频器

下面我们以简单写1个字节来说明关键的时序顺序设计。

所有的控制逻辑以IIC_S状态机的状态,以及内部时钟scl_clk为主要时序来控制。写操作内部同步时序全部以scl_clk的上升沿进行,为了满足数据Tsu和Thd,设O_iic_scl延迟于scl_r半周期的四分之三 OFFSET = CLK_DIV - CLK_DIV/4。这样对于SLAVE接收来说具有足够的Tsu和Thd。对于读操作,每个scl_sck的下降沿采集总线,由于scl完成了相位调整,也是非常容易满足Tsu和Thd。

localparam SCL_DIV = CLK_DIV/2;
localparam OFFSET = SCL_DIV - SCL_DIV/4;//设置I2C总线的SCL时钟的偏移,以满足SCL和SDA的时序要求,外部的SCL延迟内部的半周期的四分之三

reg [15:0] clkdiv = 16'd0;   //I2C 时钟分频寄存器
reg scl_r      = 1'b1;       //I2C控制器的SCL内部时钟
reg scl_clk    = 1'b0;       //I2C控制器内部SCL时钟,与外部时钟存在OFFSET参数设置的相位偏移

wire scl_offset;             //scl 时钟偏移控制

//scl 时钟分频器
always@(posedge I_clk)
    if(clkdiv < SCL_DIV)    
        clkdiv <= clkdiv + 1'b1;
    else begin
        clkdiv <= 16'd0;
        scl_clk <= !scl_clk;
    end

assign  scl_offset  = (clkdiv == OFFSET);//设置scl_offset的时间参数

always @(posedge I_clk) O_iic_scl <=  scl_offset ?  scl_r : O_iic_scl; //O_iic_scl延迟scl_offset时间的scl_r
1.1.3 开始条件/停止条件

SDA:总线空闲状态sda_o保持高电平,sda_o拉低,代表启动传输。

//当进入IIC_S状态为启动、停止设置sda=0,结合scl产生起始位,或者(IIC_S == R_ACK && (rcnt != I_rd_cnt) sda=0,用于产生读操作的ACK

always @(*)
    if(IIC_S == START || IIC_S == STOP1 || (IIC_S == R_ACK && (rcnt != I_rd_cnt)))
        sda_o <= 1'b0;
    else if(IIC_S == W_WAIT)
        sda_o <= sda_r[7];
    else  sda_o <= 1'b1; //否则其他状态都为1,当(IIC_S == R_ACK && (rcnt == I_rd_cnt) 产生一个NACK

SCL:设置scl_r开始和结束的条件为高电平,数据传输过程scl_r为内部时钟scl_clk

//当IIC_S状态机处于,同时空闲状态,设置SCL为高电平,同时也是空闲,停止状态,用于产生起始位和停止位时序,否则寄存scl_clk时钟

always @(*)
    if(IIC_S == IDLE || IIC_S == STOP1 || IIC_S == STOP2)
        scl_r <= 1'b1;
    else 
        scl_r <= scl_clk;
1.1.4 发送移位模块

当总线进行写操作时,将I_wr_data的数据通过发送移位模块传送到IIC总线sda_r上。

localparam START   = 4'd1;//I2C 总线启动
localparam W_WAIT  = 4'd2;//I2C 总线等待写完成
localparam W_ACK   = 4'd3;//I2C 总线等待写WACK

reg [2:0] IIC_S = 4'd0; //I2C 状态机
reg [7:0] wcnt = 8'd0;       //发送数据计数器,以byte为单位


//I2C数据发送模块,所有的写数据都通过此模块发送
always @(posedge scl_clk)
    if(IIC_S == W_ACK || IIC_S == START)begin//IIC_S=START和W_ACK,把需要发送的数据,寄存到sda_r
        sda_r <= I_wr_data[(wcnt*8) +: 8];//寄存需要发发送的数据到sda_r
        if( rd_req ) sda_r <= {I_wr_data[7:1],1'b1};//对于读操作,rd_req由内部代码产生,当写完第一个数据(器件地址),后通过判断I_rd_cnt,确认是否数据需要读
    end
    else if(IIC_S == W_WAIT)//当W_WAT状态,通过移位操作,把数据发送到数据总线
        sda_r <= {sda_r[6:0],1'b1};//移位操作
    else 
        sda_r <= sda_r;
1.1.5 接收移位模块

当总线进行读操作时,将IIC总线上的数据sda_i_r通过接收移位模块传送到O_rd_data上。

localparam IDLE    = 4'd0;//I2C 总线空闲状态
localparam R_WAIT  = 4'd4;//I2C 总线等待读完成
localparam R_ACK   = 4'd5;//I2C 总线等待读RACK

reg [7:0] sda_i_r = 8'd0;    //接收寄存器
reg [7:0] rcnt = 8'd0;       //接收数据计数器,以byte为单位

//I2C数据接收模块,I2C读期间,把数据通过移位操作,移入O_rd_data
always @(negedge scl_clk)begin
    if(IIC_S == R_WAIT ) //当IIC_S == R_WAIT ||IIC_S == W_ACK(如果读操作,第1个BIT是W_ACK这个状态读)启动移位操作
        sda_i_r <= {sda_i_r[6:0],sda_i};
    else if(IIC_S == R_ACK)//当IIC_S == R_ACK,完成一个BYTE读,把数据保存到O_rd_data
        O_rd_data[((rcnt-1'b1)*8) +: 8] <= sda_i_r[7:0];
    else if(IIC_S == IDLE)//空闲状态,重置sda_i_r
        sda_i_r <= 8'd0;
end
1.1.6 总线错误指示模块

进行写操作时,I2C总线无法读到正确ACK,拉高O_iic_bus_error

//总线错误指示状态

always @(negedge scl_clk or negedge I_rstn )begin
    if(I_rstn == 1'b0)
        O_iic_bus_error <= 1'b0;    
    else begin
        if(IIC_S  == W_ACK && sda_i == 1'b1)//I_iic_req == 1'b1 || rd_req == 1'b1总线进入错误状态
            O_iic_bus_error <= 1'b1;
        else if(I_iic_req == 0)
            O_iic_bus_error <= 1'b0;
    end        
end

2 状态机设计

所有的SDA和SCL控制依据状态机设计。

IDLE:在IDLE状态,当iic_req请求有效代表一次全新的传输进入启动I2C Start启动传输阶段,如果rd_req有效代表目前要进行repeated start,也是进去START状态。

START:在START状态对bcnt进行初始化,设置需要发送的bit数量,因为不管是写操作,还是随机读操作,I2C总线协议要求,都要发送器件地址。因此在START后发送7Bit的器件地址和1bit的读/写位。

W_WAIT:在此阶段发送一个完整的8bit数据,发送移位模块也会在此阶段对数据移位。

W_ACK:对于写操作,SLAVE设备响应ACK,如果还有数据需要些,则回到W_WAIT;如果还要进行读操作,则回到IDLE产生一次Repeated Start;如果已经完成所有数据发送,也没有数据需要读,则进入STOP1

R_WAIT:在此阶段完成8bits数据接收,接收移位模块工作,之后进入R_ACK

R_ACK:响应NACK,如果还有数据需要接收,则再次进入R_WAIT,否则进入STOP1,完成本次传输。

STOP1:产生停止位,SDA=0 SDA=1,进入STOP1

SOTP2:产生停止位,SDA=1 SDA=1,回到IDLE

程序源码

`timescale 1ns / 1ns //仿真刻度/精度

module uii2c#
(
parameter integer WMEN_LEN = 8'd0,//写长度,以字节为单位,包含器件地址
parameter integer RMEN_LEN = 8'd0,//读长度,以字节为单位,不包含器件地址
parameter integer CLK_DIV  = 16'd499// I2C时钟分频系数
)
(
input  wire I_clk,//系统时钟输入
input  wire I_rstn,//系统复位,低电平有效
output reg  O_iic_scl = 1'b0,//I2C时钟SCL
inout  wire IO_iic_sda,//I2C 数据总线
input  wire [WMEN_LEN*8-1'b1:0]I_wr_data,//写数据寄存器,其中WMEN_LEN设置了最大支持的数据字节数,越大占用的FPGA资源越多
input  wire [7:0]I_wr_cnt,//写数据计数器,代表写了多少个字节
output reg  [RMEN_LEN*8-1'b1:0]O_rd_data = 0,//读数据寄存器,其中RMEN_LEN设置了最大支持的数据字节数,越大占用的FPGA资源越多
input  wire [7:0]I_rd_cnt,//读数据计数器
input  wire I_iic_req,//I_iic_req == 1 使能I2C传输
input  wire I_iic_mode,//I_iic_mode = 1 随机读   I_iic_mode = 0 读当前寄存器或者页读
output reg  O_iic_busy = 1'b0,//I2C控制器忙
output reg  O_iic_bus_error, //I2C总线,无法读到正确ACK出错
output reg  IO_iic_sda_dg
);

localparam IDLE    = 4'd0;//I2C 总线空闲状态
localparam START   = 4'd1;//I2C 总线启动
localparam W_WAIT  = 4'd2;//I2C 总线等待写完成
localparam W_ACK   = 4'd3;//I2C 总线等待写WACK
localparam R_WAIT  = 4'd4;//I2C 总线等待读完成 
localparam R_ACK   = 4'd5;//I2C 总线等待读RACK 
localparam STOP1   = 4'd6;//I2C 总线产生停止位
localparam STOP2   = 4'd7;//I2C 总线产生停止位   

localparam SCL_DIV = CLK_DIV/2;

localparam OFFSET = SCL_DIV - SCL_DIV/4;//设置I2C总线的SCL时钟的偏移,以满足SCL和SDA的时序要求,外部的SCL延迟内部的半周期的四分之三

reg [2:0] IIC_S = 4'd0; //I2C 状态机
//generate  scl
reg [15:0] clkdiv = 16'd0;   //I2C 时钟分频寄存器
reg scl_r 	   = 1'b1;       //I2C控制器的SCL内部时钟
reg sda_o 	   = 1'b0;       //I2C控制器的SDA
reg scl_clk    = 1'b0;       //I2C控制器内部SCL时钟,与外部时钟存在OFFSET参数设置的相位偏移
reg [7:0] sda_r = 8'd0;      //发送寄存器
reg [7:0] sda_i_r = 8'd0;    //接收寄存器
reg [7:0] wcnt = 8'd0;       //发送数据计数器,以byte为单位
reg [7:0] rcnt = 8'd0;       //接收数据计数器,以byte为单位
reg [2:0] bcnt = 3'd0;       //bit计数器
reg  rd_req = 1'b0;          //读请求,当判断到需要读数据,内部状态机中设置1
wire sda_i;                  //sda 输入
wire scl_offset;             //scl 时钟偏移控制

assign  sda_i   = (IO_iic_sda == 1'b0) ?  1'b0 : 1'b1;  //读总线 
assign  IO_iic_sda = (sda_o == 1'b0) ?  1'b0 : 1'bz;    //写总线,1'bz代表高阻,I2C外部通过上拉电阻,实现总线的高电平

//scl 时钟分频器
always@(posedge I_clk)
    if(clkdiv < SCL_DIV)    
        clkdiv <= clkdiv + 1'b1;
    else begin
        clkdiv <= 16'd0; 
        scl_clk <= !scl_clk;
    end


assign  scl_offset  = (clkdiv == OFFSET);//设置scl_offset的时间参数
always @(posedge I_clk) O_iic_scl <=  scl_offset ?  scl_r : O_iic_scl; //O_iic_scl延迟scl_offset时间的scl_r

//采集I2C 数据总线sda
always @(posedge I_clk) IO_iic_sda_dg <= sda_i;  

//当IIC_S状态机处于,同时空闲状态,设置SCL为高电平,同时也是空闲,停止状态,用于产生起始位和停止位时序,否则寄存scl_clk时钟
always @(*) 
    if(IIC_S == IDLE || IIC_S == STOP1 || IIC_S == STOP2)
        scl_r <= 1'b1;
    else 
        scl_r <= scl_clk;
  

//当进入IIC_S状态为启动、停止设置sda=0,结合scl产生起始位,或者(IIC_S == R_ACK && (rcnt != I_rd_cnt) sda=0,用于产生读操作的ACK
always @(*) 
    if(IIC_S == START || IIC_S == STOP1 || (IIC_S == R_ACK && (rcnt != I_rd_cnt)))
        sda_o <= 1'b0;
    else if(IIC_S == W_WAIT)
        sda_o <= sda_r[7]; 
    else  sda_o <= 1'b1; //否则其他状态都为1,当(IIC_S == R_ACK && (rcnt == I_rd_cnt) 产生一个NACK 

//I2C数据发送模块,所有的写数据都通过此模块发送
always @(posedge scl_clk) 
    if(IIC_S == W_ACK || IIC_S == START)begin//IIC_S=START和W_ACK,把需要发送的数据,寄存到sda_r
        sda_r <= I_wr_data[(wcnt*8) +: 8];//寄存需要发发送的数据到sda_r
        if( rd_req ) sda_r <= {I_wr_data[7:1],1'b1};//对于读操作,rd_req由内部代码产生,当写完第一个数据(器件地址),后通过判断I_rd_cnt,确认是否数据需要读
    end
    else if(IIC_S == W_WAIT)//当W_WAT状态,通过移位操作,把数据发送到数据总线
        sda_r <= {sda_r[6:0],1'b1};//移位操作
    else 
        sda_r <= sda_r;

//sda data bus read and hold data to O_rd_data register when IIC_S=R_ACK
//I2C数据接收模块,I2C读期间,把数据通过移位操作,移入O_rd_data
always @(negedge scl_clk)begin
    if(IIC_S == R_WAIT ) //当IIC_S == R_WAIT ||IIC_S == W_ACK(如果读操作,第1个BIT是W_ACK这个状态读)启动移位操作
        sda_i_r <= {sda_i_r[6:0],sda_i};
    else if(IIC_S == R_ACK)//当IIC_S == R_ACK,完成一个BYTE读,把数据保存到O_rd_data
        O_rd_data[((rcnt-1'b1)*8) +: 8] <= sda_i_r[7:0];
    else if(IIC_S == IDLE)//空闲状态,重置sda_i_r
        sda_i_r <= 8'd0;
end

//总线忙状态
always @(posedge scl_clk or negedge I_rstn )begin
	if(I_rstn == 1'b0)
    	O_iic_busy <= 1'b0; 
	else begin
    	if((I_iic_req == 1'b1 || rd_req == 1'b1 || O_iic_bus_error))//I_iic_req == 1'b1 || rd_req == 1'b1总线进入忙状态
    	    O_iic_busy <= 1'b1; 
        else if(IIC_S == IDLE)
        	O_iic_busy <= 1'b0;
    end 		
end

//总线忙状态
always @(negedge scl_clk or negedge I_rstn )begin
	if(I_rstn == 1'b0)
    	O_iic_bus_error <= 1'b0;  	
	else begin
    	if(IIC_S  == W_ACK && sda_i == 1'b1)//I_iic_req == 1'b1 || rd_req == 1'b1总线进入忙状态
    	    O_iic_bus_error <= 1'b1; 
        else if(I_iic_req == 0)
        	O_iic_bus_error <= 1'b0;
    end 		
end

//I2C Master控制器状态机
always @(posedge scl_clk or negedge I_rstn )begin
		if(I_rstn == 1'b0)begin //异步复位,复位相关寄存器
           wcnt 	<= 8'd0;
           rcnt 	<= 8'd0;
           rd_req 	<= 1'b0;   
           IIC_S    <= IDLE;
        end
        else begin
        case(IIC_S) //sda = 1 scl =1
        IDLE:begin//在空闲状态,sda=1 scl=1 
           if(I_iic_req == 1'b1 || rd_req == 1'b1) //当I_iic_req == 1'b1代表启动传输 当 rd_req == 1'b1 代表读操作需要产生repeated start 重复启动  
              IIC_S  <= START; //进入START状态
           else begin
              wcnt <= 8'd0; //复位计数器
              rcnt <= 8'd0; //复位计数器
           end
        end
        START:begin //这个状态,前面的代码,先设置sda = 0,scl_offset参数设置了scl_clk时钟的偏移,之后 scl_clk =0 即scl =0 产生起始位或者重复起始位
           bcnt <= 3'd7; //设置bcnt的初值         
           IIC_S  <= W_WAIT;//进入发送等待
        end           
        W_WAIT://等待发送完成,这里发送8bits 数据,写器件地址,写寄存器地址,写数据,都在这个状态完成
        begin
           if(bcnt > 3'd0)//如果8bits没发送完,直到发送完
               bcnt  <= bcnt - 1'b1; //bcnt计数器,每发送1bit减1
           else begin //8bits发送完毕
               wcnt <= wcnt + 1'b1; //wcnt计数器,用于记录已经写了多少字节
               IIC_S  <= W_ACK;//进入W_ACK状态
           end
        end 
        W_ACK://等待WACK,此阶段,也判断是否有读操作
        begin 
           if(wcnt < I_wr_cnt)begin //判断是否所有数据发送(写)完成
              bcnt <= 3'd7; //如果没有写完,重置bcnt
              IIC_S <= W_WAIT;//继续回到W_WAIT等待数据发送(写)完成
           end
           else if(I_rd_cnt > 3'd0)begin//I_rd_cnt > 0代表有数据需要读,I_rd_cnt决定了有多少数据需要读
              if(rd_req == 1'b0 && I_iic_mode == 1'b1)begin //对于第一次写完器件地址,如果I_iic_mode==1代表支持随机读
                  rd_req <= 1'b1;//设置rd_req=1,请求读操作
                  IIC_S <= IDLE; //设置状态进入IDLE,根据rd_req的值会重新产生一次为读操作进行的repeated重复start
              end
              else //如果之前已经完成了repeated重复start,那么读操作进入读数据阶段
                  IIC_S <= R_WAIT;//进入读等待
                  bcnt <= 3'd7;//设置bcnt的初值  
           end
           else //如果所有的发送完成,也没数据需要读,进入停止状态
              IIC_S <= STOP1; 
        end  
        R_WAIT://等待读操作完成
        begin
           rd_req <= 1'b0;//重置读请求rd_req=0
           bcnt  <= bcnt - 1'b1; //bit 计数器
           if(bcnt == 3'd0)begin //当8bits数据读完
              rcnt <= (rcnt < I_rd_cnt) ? (rcnt + 1'b1) : rcnt;//判断是否还有数据需要读
              IIC_S  <= R_ACK;//进入R_ACK
           end
        end
        R_ACK://R_ACK状态产生NACK
        begin
           bcnt <= 3'd7;//重置读请求bcnt计数器
           IIC_S <= (rcnt < I_rd_cnt) ? R_WAIT : STOP1; //如果所有数据读完,进入停止状态
        end  
        STOP1:begin//产生停止位 sda = 0 scl = 1
            rd_req 	<= 1'b0;              
            IIC_S <= STOP2;
        end
        STOP2://产生停止位  sda = 1 scl = 1
            IIC_S <= IDLE;          
        default:
            IIC_S <= IDLE;
    	endcase
    end
end

endmodule

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

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

相关文章

【5G Sub-6GHz模块】专为IoT/eMBB应用而设计的RG520NNA、RG520FEB、RG530FNA、RG500LEU 5G模组

推出全新的5G系列模组&#xff1a; RG520NNADB-M28-SGASA RG520NNADA-M20-SGASA RG520FEBDE-M28-TA0AA RG530FNAEA-M28-SGASA RG530FNAEA-M28-TA0AA RG500LEUAA-M28-TA0AA ——明佳达 1、5G RG520N 系列——专为IoT/eMBB应用而设计的LGA封装模块 RG520N 系列是一款专为 IoT…

Ghost Browser指纹浏览器集成IPXProxy代理IP:解锁Twitch直播新体验

​Twitch 是一个实时视频流平台&#xff0c;允许人们实时播放各自的内容&#xff0c;无论是游戏、娱乐、体育、音乐还是其他内容。不少人的人都想要在Twitch直播来吸引更多的粉丝&#xff0c;然而有时候会面临无法成功使用Twitch的问题。本文将带来Ghost Browser指纹浏览器集成…

网络概念: 互联网和局域网、 OSI七层网络互联模型、数据封装、应用端口、地址解析、网络设备、网络配置

文章目录 引言I 网络概念1.1 互联网和局域网1.2 OSI七层网络互联模型1.3 数据封装1.4 TCP/IP协议1.5 应用端口II 地址解析III 网络设备3.1 集线器 HUB3.2 交换机 swich3.3 路由器 router3.4 防火墙 firewallIV 网络配置4.1 网络安全域(你住哪里?)4.2 地址转换(NAT,你名字叫…

Go 1.19.4 函数-Day 08

1. 函数概念和调用原理 1.1 基本介绍 函数是基本的代码块&#xff0c;用于执行一个任务。 Go 语言最少有个 main() 函数。 你可以通过函数来划分不同功能&#xff0c;逻辑上每个函数执行的是指定的任务。 函数声明告诉了编译器函数的名称&#xff0c;返回类型&#xff0c;和参…

Windows与Ubuntu安装ffmpeg

文章目录 前言ffmpeg的简介安装ffmpegWindows下载设置环境变量 Ubuntu 总结 前言 FFmpeg是一款非常强大的开源音视频处理工具&#xff0c;它包含了众多的音视频编解码库&#xff0c;可以用于音视频的采集、编解码、转码、流化、过滤和播放等复杂的处理。在Windows系统上安装FF…

一五六、Node+Vue 使用七牛上传图片,并配置个人域名

1. 七牛云ak/sk获取 点击注册&#x1f517;开通七牛开发者帐号如果已有账号&#xff0c;直接登录七牛开发者后台&#xff0c;点击这里&#x1f517;查看 Access Key 和 Secret Key 2. Node.js获取七牛token 安装qiniu npm install qiniu创建空间 Node获取token const qi…

Redis中数据分片与分片策略

概述 数据分片是一种将数据分割并存储在多个节点上的技术&#xff0c;可以有效提高系统的扩展性和性能。在Redis中&#xff0c;数据分片主要用于解决单个实例存储容量和性能瓶颈的问题。通过将数据分散存储到多个Redis节点中&#xff0c;可以将负载均衡到不同的服务器上&#…

如何使用在线工具将手机相册中的图片转换为JPG格式

我们经常在手机相册中保存大量的图片&#xff0c;无论是家庭聚会的照片还是旅行的瞬间&#xff0c;每一幅图像都承载着珍贵的记忆。然而&#xff0c;有时候我们会遇到图片格式不兼容的问题&#xff0c;尤其是在需要将图片分享到特定平台或编辑时。 例如&#xff0c;某些社交平台…

手机操作系统的沉浮往事

手机操作系统的沉浮往事&#xff08;上&#xff09; 移动终端操作系统&#xff0c;也就是指手机、平板电脑等设备所使用的操作系统。 在移动互联网高度发达的今天&#xff0c;我们使用移动终端操作系统的时长&#xff0c;可能已经远远超过了 Windows 等桌面操作系统。 那么&…

Windows 11预览补丁KB5040527影响火绒驱动加载的解决办法

7 月 11 日&#xff0c;微软更新Windows 11 预览版本补丁 KB5040527&#xff0c;补丁安装后会影响火绒驱动加载导致火绒安全软件服务异常&#xff0c;补丁相关信息如下&#xff1a; https://blogs.windows.com/windows-insider/2024/07/11/releasing-windows-11-builds-22621-…

Apache trino的ldap认证开启

作者&#xff1a;櫰木 1、背景 由于trino 默认没有开启用户认证体系&#xff0c;需要ldap用户进行认证。开启tls和ldap用户认证&#xff0c;提高安全性。 2、配置 前置条件。 trino 集群已经部署完成 ldap 服务 openjdk 版本大于11.0.17 生成证书 keytool -genkeypair…

Python(集合)

集合中只能存储不可变类型&#xff0c;使用{}定义&#xff0c;元素之间用逗号分隔 #集合的使用 #方法1 s{10,20,30,40} #整数是不可变数据类型,不能存储列表 print(s) #方法2 sset()#创建了一个空集合 print(s) s{}#创建了一个字典 print(s,type(s))sset(helloworld) print(s)…

UNiapp微信小程序Ucharts

效果图如下 以上为加载接口所得数据的玫瑰图与折线图 具体步骤如下 1&#xff0c;将插件导入Hbuiler 所需要的项目中&#xff08;插件地址&#xff1a;秋云 ucharts echarts 高性能跨全端图表组件 - DCloud 插件市场&#xff09; 2&#xff0c;导入成功是这样的 3&#xff0c…

镜舟科技荣获优秀数字化服务商奖,助力企业用数智技术重塑新消费

7 月 13 日&#xff0c;由 ITShare智享会和 BT商业科技观察主办的2024 第八届 FMCG 零售消费品数字化峰会于上海落幕。在现场&#xff0c;镜舟科技凭借在多家零售企业构建与实施智能数据中台解决方案的成功经验&#xff0c;荣获优秀数字化服务商奖项。 在会上&#xff0c;麦当劳…

软件测试——web单功能测试

工作职责&#xff1a; 1.负责产品系统测试&#xff0c;包括功能测试、性能测试、稳定性测试、用户场景测试、可靠性测试等。 2.负责测试相关文档的编写&#xff0c;包括测试计划、测试用例、测试报告等。 3.负责自动化测试框架、用例的维护。 岗位要求&#xff1a; 1.熟练…

无编码器多模态大模型EVE:原生多模态新方案

近期&#xff0c;关于多模态大模型的研究如火如荼&#xff0c;工业界的投入也愈发高涨。国外相继推出了炙手可热的模型&#xff0c;例如 GPT-4o &#xff08;OpenAI&#xff09;、Gemini&#xff08;Google&#xff09;、Phi-3V &#xff08;Microsoft&#xff09;、Claude-3V&…

SpringMVC框架--个人笔记步骤总结

一、步骤 1.创建工程 2.加入springmvc依赖--pom.xml <!--springmvc依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </depend…

jenkins系列-07.轻易级jpom安装

jpom是一个容器化服务管理工具&#xff1a;在线构建&#xff0c;自动部署&#xff0c;日常运维, 比jenkins轻量多了。 本篇介绍mac m1安装jpom: #下载&#xff1a;https://jpom.top/pages/all-downloads/ 解压&#xff1a;/Users/jelex/Documents/work/jpom-2.10.40 启动前修…

C语言 ——— 编写代码,判断 整型数组 是否 有序

目录 题目要求 代码实现 题目要求 判断 整型数组 是否有序 如果 整型数组 有序输出 sorted&#xff1b;否则输出 unsorted 代码实现 #include<stdio.h> int main() {int arr[10] { 0 };int sz sizeof(arr) / sizeof(arr[0]);//输入for (int i 0; i < sz; i){s…

Large Language Model系列之一:语言模型与表征学习(Language Models and Representation Learning)

语言模型与表征学习&#xff08;Language Models and Representation Learning&#xff09; 1 语言模型 N-Gram模型 from collections import defaultdictsentences [The swift fox jumps over the lazy dog.,The swift river flows under the ancient bridge.,The swift br…