I²C即Inter-Integrated Circuit(集成电路总线),它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代设计出来的一种简单、双向、二线制总线标准。多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。主机启动总线,并产生时钟用于传送数据,此时任何接收数据的器件均被认为是从机。I²C总线由数据线SDA和时钟线SCL构成通信线路,既可用于发送数据,也可接收数据。在主控与被控IC之间可进行双向数据传送,数据的传输速率在标准模式下可达100kbit/s,在快速模式下可达400kbit/s,在高速模式下可达3.4Mbit/s,各种被控器件均并联在总线上,通过器件地址(SLAVE ADDR,具体可查器件手册)识别。I²C总线物理拓扑结构图如下所示:

  图中的IIC_SCL是串行时钟线,IIC_SDA是串行数据线,由于I2C器件一般采用开漏结构与总线相连,所以IIC_SCL和IIC_SDA均需接上拉电阻,也正因此,当总线空闲时,这两条线路都处于高电平状态,当连到总线上的任一器件输出低电平,都将使总线拉低,即各器件的SDA及SCL都是“线与”关系。IIC总线支持多主和主从两种工作方式,通常工作在主从工作方式,我们的开发板就采用主从工作方式。在主从工作方式中,系统中只有一个主机,其它器件都是具有I2C总线的外围从机。在主从工作方式中,主机启动数据的发送(发出启动信号)并产生时钟信号,数据发送完成后,发出停止信号。I2C总线结构虽然简单,使用两线传输,然而要实现器件间的通信,需要通过控制SCL和SDA的时序,使其满足I2C的总线传输协议,方可实现器件间的数据传输。
 
 
一、起始和结束
  在I2C器件开始通信(传输数据)之前,串行时钟线SCL和串行数据线SDA线由于上拉的原因处于高电平状态,此时I2C总线处于空闲状态。
  如果主机(此处指FPGA)想开始传输数据,只需在SCL为高电平时将SDA线拉低,产生一个起始信号,从机检测到起始信号后,准备接收数据,当数据传输完成,主机只需产生一个停止信号,告诉从机数据传输结束,停止信号的产生是在SCL为高电平时,SDA从低电平跳变到高电平,从机检测到停止信号后,停止接收数据。起始信号之前为空闲状态,起始信号之后到停止信号之前的这一段为数据传输状态,主机可以向从机写数据,也可以读取从机输出的数据,数据的传输由双向数据线(SDA)完成。停止信号产生后,总线再次处于空闲状态。
  起始:SCL为高时,SDA由高拉低。
  停止:SCL为高时,SDA由低拉高
 
 
二、数据传输和应答期
  先看看数据是怎么通过两根线传过来的,除了起始和结束比较特殊,中间的数据传输所遵循的规律如下所示:
  
1.数据传输  
  SCL为低时,SDA运行变化。
  SCL为高时,SDA数据锁存。
2.应答期,SDA总线是三态门
  ①在第8个时钟周期末,主机释放SDA以使从机应答
  ②在第9个时钟周期,从机将SDA拉低以应答
  ③若第9个时钟周期,SCL为高电平时,SDA未被检测到为低电平,视为非应答,表明此次数据传输失败。
  ④在第9个时钟周期末,从机释放SDA以使主机继续传输数据,如果主机发送停止信号,此次传输结束。
 
 
三、器件地址
  每个I2C器件都有一个器件地址,有些I2C器件的器件地址是固定的,而有些I2C器件的器件地址由一个固定部分和一个可编程的部分构成,这是因为很可能在一个系统中有几个同样的器件,器件地址的可编程部分能最大数量的使这些器件连接到I2C总线上,例如为了增加系统的EEPROM容量,可能需要多个EEPROM。器件可编程地址位的数量由它可使用的管脚决定,比如EEPROM器件一般会留下3个管脚用于可编程地址位,当硬件电路上分别将这3个管脚连接到GND或VCC时,就可以设置不同的可编程地址。但有些I2C器件在出厂时器件地址就设置好了,用户不可以更改(如实时时钟PCF8563的器件地址为固定的7’h51)。所以当主机想给某个器件发送数据时,只需向总线上发送接收器件的器件地址即可。
  进行数据传输时,主机首先向总线上发出开始信号,对应开始位S,然后按照从高到低的位序发送器件地址,一般为7bit,第8bit位为读写控制位R/W,该位为0时表示主机对从机进行写操作,当该位为1时表示主机对从机进行读操作,然后接收从机响应。对于AT24C64来说,其传输器件地址格式如下图所示。
 
 
四、存储器地址(字地址)
  一般而言,每个兼容I2C协议的器件,内部总会有可供读写的寄存器或存储器,对于我们本次实验用到的EEPROM存储器,内部就是一系列顺序编址的存储单元。所以,当我们对一个器件中的存储单元(包括寄存器)进行读写时,首先要指定存储单元的地址即字地址,然后再向该地址写入内容。该地址为一个或两个字节长度,具体长度由器件内部的存储单元的数量决定,当存储单元数量不超过一个字节所能表示的最大数量(2^8=256)时,用一个字节表示,超过一个字节所能表示的最大数量时,就需要用两个字节来表示。
1.单字节的字地址
2.双字节的字地址
 
 
五、IIC写
  主机发送完字地址,从机正确应答后就把内部的存储单元地址指针指向该单元。如果读写控制位R/W位为“0”即写命令,从机就处于接收数据的状态,此时,主机就开始写数据了。写数据分为单字节写(对于EEPROM而言,称为字节写)和连续写(对于EEPROM而言,称为页写)。
  不管单字节写和连续写,都可概括为:start + 器件地址 + 写命令(0) + 字地址 + 数据 + stop
1.单字节写
  单字节写:发送完一字节数据后发送结束信号。
2.连续写
  连续写:发送完一字节数据后继续发送下一字节数据,最后发送的是结束信号。
 
 
六、IIC读
  主机发送完字地址,从机正确应答后就把内部的存储单元地址指针指向该单元。如果读写控制位R/W位为“1”即读命令,主机就处于接收数据的状态,从机从该地址单元输出数据。读数据分为当前地址读、单字节读和连续读。
  不管是单字节读还是连续读,都可以概括为:start + 器件地址 + 写命令(0) + 字地址 + start + 器件地址 + 读命令(1) + 接收从机的数据 + 主机非应答(1) + stop
1.单字节读
  发送完器件地址和字地址后又发送起始信号和器件地址,而且第一次发送器件地址时后面的读写控制位为“0”,也就是写命令,第二次发送器件地址时后面的读写控制位为“1”,也就是读。为什么会有这样奇怪的操作呢?这是因为我们需要使从机内的存储单元地址指针指向我们想要读取的存储单元地址处,所以首先发送了一次Dummy Write也就是虚写操作,只所以称为虚写,是因为我们并不是真的要写数据,而是通过这种虚写操作使地址指针指向虚写操作中字地址的位置,等从机应答后,就可以按当前地址读的方式读数据了。因此也可以理解为:没有发送数据的单次写操作 + 当前地址的读操作
    单字节读:读取完一字节数据后,主机发送非应答信号。
 
2.连续读
  连续读:读取完一字节数据后,主机发送应答信号,读取完最后一个字节数据后,主机发送非应答信号。
 
 
 
 
七、IIC控制器Verilog代码设计(修改自正点原子FPGA)
 //**************************************************************************
// *** 名称 : iic.v
// *** 作者 : xianyu_FPGA
// *** 博客 : https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2019-08-10
// *** 描述 : IIC控制器
//************************************************************************** module iic
//========================< 参数 >==========================================
#(
parameter DEVICE_ID = 'b1010000 , //器件ID
parameter CLK = 'd50_000_000 , //本模块的时钟频率
parameter SCL = 'd250_000 //输出的SCL时钟频率
)
//========================< 端口 >==========================================
(
input clk , //时钟
input rst_n , //复位,低电平有效
//IIC control ---------------------------------------
input iic_en , //IIC触发信号
input addr16_en , //16位地址使能
input addr8_en , //8位地址使能
input write_en , //IIC写使能
input read_en , //IIC读使能
input [:] iic_addr , //IIC器件内地址
input [ :] iic_data_wr , //IIC要写的数据
//IIC output ----------------------------------------
output reg [ :] iic_data_rd , //IIC读出的数据
output reg iic_done , //IIC一次操作完成
output reg iic_scl , //IIC的SCL时钟信号
inout iic_sda , //IIC的SDA数据信号
//dri_clk -------------------------------------------
output reg iic_dri_clk //驱动IIC操作的驱动时钟,1Mhz
);
//========================< 参数 >==========================================
localparam IDLE = 'b0000_0001 ; //空闲状态
localparam DEVICE = 'b0000_0010 ; //写器件地址
localparam ADDR_16 = 'b0000_0100 ; //写字地址高8位
localparam ADDR_8 = 'b0000_1000 ; //写字地址低8位
localparam DATA_WR = 'b0001_0000 ; //写数据
localparam DEVICE_RD = 'b0010_0000 ; //虚写器件地址
localparam DATA_RD = 'b0100_0000 ; //读数据
localparam STOP = 'b1000_0000 ; //结束
//========================< 信号 >==========================================
reg sda_dir ; //IIC数据(SDA)方向控制
reg sda_out ; //SDA输出信号
wire sda_in ; //SDA输入信号
reg state_done ; //状态结束
reg [ :] cnt ; //计数
reg [ :] state_c ; //状态机当前状态
reg [ :] state_n ; //状态机下一状态
reg [:] iic_addr_t ; //地址
reg [ :] iic_data_rd_t ; //读取的数据
reg [ :] iic_data_wr_t ; //IIC需写的数据的临时寄存
reg [ :] clk_cnt ; //分频时钟计数
wire [ :] clk_divide ; //模块驱动时钟的分频系数 //==========================================================================
//== sda控制
//==========================================================================
assign iic_sda = sda_dir ? sda_out : 'bz; //SDA数据输出或高阻
assign sda_in = iic_sda ; //SDA数据输入 //==========================================================================
//== 生成SCL的4倍时钟来驱动后面IIC的操作,生成1Mhz的iic_dri_clk
//==========================================================================
assign clk_divide = (CLK/SCL) >> ; // >>3即除以8 always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
iic_dri_clk <= 'b1;
clk_cnt <= 'd0;
end
else if(clk_cnt == clk_divide - 'd1) begin
clk_cnt <= 'd0;
iic_dri_clk <= ~iic_dri_clk;
end
else
clk_cnt <= clk_cnt + 'b1;
end //==========================================================================
//== 状态机
//==========================================================================
always @(posedge iic_dri_clk or negedge rst_n) begin
if(!rst_n)
state_c <= IDLE;
else
state_c <= state_n;
end always @(*) begin
case(state_c)
IDLE: begin //空闲状态
if(iic_en) begin
state_n = DEVICE;
end
else
state_n = IDLE;
end
DEVICE: begin //写器件ID
if(state_done) begin
if(addr16_en)
state_n = ADDR_16;
else if(addr8_en)
state_n = ADDR_8 ;
end
else
state_n = DEVICE;
end
ADDR_16: begin //写地址高8位
if(state_done)
state_n = ADDR_8;
else
state_n = ADDR_16;
end
ADDR_8: begin //写地址低8位
if(state_done) begin
if(write_en)
state_n = DATA_WR;
else if(read_en)
state_n = DEVICE_RD;
end
else
state_n = ADDR_8;
end
DATA_WR: begin //写数据
if(state_done)
state_n = STOP;
else
state_n = DATA_WR;
end
DEVICE_RD: begin //虚写器件ID
if(state_done)
state_n = DATA_RD;
else
state_n = DEVICE_RD;
end
DATA_RD: begin //读数据
if(state_done)
state_n = STOP;
else
state_n = DATA_RD;
end
STOP: begin //结束
if(state_done)
state_n = IDLE;
else
state_n = STOP ;
end
default:state_n= IDLE;
endcase
end //==========================================================================
//== 设计各路信号
//==========================================================================
always @(posedge iic_dri_clk or negedge rst_n) begin
if(!rst_n) begin
iic_scl <= 'b1;
sda_out <= 'b1;
sda_dir <= 'b1;
iic_done <= 'b0;
cnt <= 'b0;
state_done <= 'b0;
iic_addr_t <= 'b0;
iic_data_rd <= 'b0;
iic_data_rd_t <= 'b0;
iic_data_wr_t <= 'b0;
end
else begin
state_done <= 'b0 ;
cnt <= cnt +'b1 ;
case(state_c)
//--------------------------------------------------- 空闲状态
IDLE: begin
iic_scl <= 'b1;
sda_out <= 'b1;
sda_dir <= 'b1;
iic_done <= 'b0;
cnt <= 'b0;
if(iic_en) begin
iic_addr_t <= iic_addr;
iic_data_wr_t <= iic_data_wr;
end
end
//--------------------------------------------------- 写器件ID
DEVICE: begin
case(cnt)
'd1 : sda_out <= 1'b0;
'd3 : iic_scl <= 1'b0;
'd4 : sda_out <= DEVICE_ID[6];
'd5 : iic_scl <= 1'b1;
'd7 : iic_scl <= 1'b0;
'd8 : sda_out <= DEVICE_ID[5];
'd9 : iic_scl <= 1'b1;
'd11: iic_scl <= 1'b0;
'd12: sda_out <= DEVICE_ID[4];
'd13: iic_scl <= 1'b1;
'd15: iic_scl <= 1'b0;
'd16: sda_out <= DEVICE_ID[3];
'd17: iic_scl <= 1'b1;
'd19: iic_scl <= 1'b0;
'd20: sda_out <= DEVICE_ID[2];
'd21: iic_scl <= 1'b1;
'd23: iic_scl <= 1'b0;
'd24: sda_out <= DEVICE_ID[1];
'd25: iic_scl <= 1'b1;
'd27: iic_scl <= 1'b0;
'd28: sda_out <= DEVICE_ID[0];
'd29: iic_scl <= 1'b1;
'd31: iic_scl <= 1'b0;
'd32: sda_out <= 1'b0; //0:写
'd33: iic_scl <= 1'b1;
'd35: iic_scl <= 1'b0;
'd36: begin
sda_dir <= 'b0; //从机应答
sda_out <= 'b1;
end
'd37: iic_scl <= 1'b1;
'd38: state_done <= 1'b1; //状态结束
'd39: begin
iic_scl <= 'b0;
cnt <= 'b0;
end
default : ;
endcase
end
//--------------------------------------------------- 写字地址高8位
ADDR_16: begin
case(cnt)
'd0 : begin
sda_dir <= 'b1 ;
sda_out <= iic_addr_t[];
end
'd1 : iic_scl <= 1'b1;
'd3 : iic_scl <= 1'b0;
'd4 : sda_out <= iic_addr_t[14];
'd5 : iic_scl <= 1'b1;
'd7 : iic_scl <= 1'b0;
'd8 : sda_out <= iic_addr_t[13];
'd9 : iic_scl <= 1'b1;
'd11: iic_scl <= 1'b0;
'd12: sda_out <= iic_addr_t[12];
'd13: iic_scl <= 1'b1;
'd15: iic_scl <= 1'b0;
'd16: sda_out <= iic_addr_t[11];
'd17: iic_scl <= 1'b1;
'd19: iic_scl <= 1'b0;
'd20: sda_out <= iic_addr_t[10];
'd21: iic_scl <= 1'b1;
'd23: iic_scl <= 1'b0;
'd24: sda_out <= iic_addr_t[9];
'd25: iic_scl <= 1'b1;
'd27: iic_scl <= 1'b0;
'd28: sda_out <= iic_addr_t[8];
'd29: iic_scl <= 1'b1;
'd31: iic_scl <= 1'b0;
'd32: begin
sda_dir <= 'b0; //从机应答
sda_out <= 'b1;
end
'd33: iic_scl <= 1'b1;
'd34: state_done <= 1'b1; //状态结束
'd35: begin
iic_scl <= 'b0;
cnt <= 'b0;
end
default : ;
endcase
end
//--------------------------------------------------- 写字地址低8位
ADDR_8: begin
case(cnt)
'd0: begin
sda_dir <= 'b1 ;
sda_out <= iic_addr_t[];
end
'd1 : iic_scl <= 1'b1;
'd3 : iic_scl <= 1'b0;
'd4 : sda_out <= iic_addr_t[6];
'd5 : iic_scl <= 1'b1;
'd7 : iic_scl <= 1'b0;
'd8 : sda_out <= iic_addr_t[5];
'd9 : iic_scl <= 1'b1;
'd11: iic_scl <= 1'b0;
'd12: sda_out <= iic_addr_t[4];
'd13: iic_scl <= 1'b1;
'd15: iic_scl <= 1'b0;
'd16: sda_out <= iic_addr_t[3];
'd17: iic_scl <= 1'b1;
'd19: iic_scl <= 1'b0;
'd20: sda_out <= iic_addr_t[2];
'd21: iic_scl <= 1'b1;
'd23: iic_scl <= 1'b0;
'd24: sda_out <= iic_addr_t[1];
'd25: iic_scl <= 1'b1;
'd27: iic_scl <= 1'b0;
'd28: sda_out <= iic_addr_t[0];
'd29: iic_scl <= 1'b1;
'd31: iic_scl <= 1'b0;
'd32: begin
sda_dir <= 'b0; //从机应答
sda_out <= 'b1;
end
'd33: iic_scl <= 1'b1;
'd34: state_done <= 1'b1; //状态结束
'd35: begin
iic_scl <= 'b0;
cnt <= 'b0;
end
default : ;
endcase
end
//--------------------------------------------------- 写数据
DATA_WR: begin
case(cnt)
'd0: begin
sda_out <= iic_data_wr_t[];
sda_dir <= 'b1;
end
'd1 : iic_scl <= 1'b1;
'd3 : iic_scl <= 1'b0;
'd4 : sda_out <= iic_data_wr_t[6];
'd5 : iic_scl <= 1'b1;
'd7 : iic_scl <= 1'b0;
'd8 : sda_out <= iic_data_wr_t[5];
'd9 : iic_scl <= 1'b1;
'd11: iic_scl <= 1'b0;
'd12: sda_out <= iic_data_wr_t[4];
'd13: iic_scl <= 1'b1;
'd15: iic_scl <= 1'b0;
'd16: sda_out <= iic_data_wr_t[3];
'd17: iic_scl <= 1'b1;
'd19: iic_scl <= 1'b0;
'd20: sda_out <= iic_data_wr_t[2];
'd21: iic_scl <= 1'b1;
'd23: iic_scl <= 1'b0;
'd24: sda_out <= iic_data_wr_t[1];
'd25: iic_scl <= 1'b1;
'd27: iic_scl <= 1'b0;
'd28: sda_out <= iic_data_wr_t[0];
'd29: iic_scl <= 1'b1;
'd31: iic_scl <= 1'b0;
'd32: begin
sda_dir <= 'b0; //从机应答
sda_out <= 'b1;
end
'd33: iic_scl <= 1'b1;
'd34: state_done <= 1'b1; //状态结束
'd35: begin
iic_scl <= 'b0;
cnt <= 'b0;
end
default : ;
endcase
end
//--------------------------------------------------- 虚写器件ID
DEVICE_RD: begin
case(cnt)
'd0 : begin
sda_dir <= 'b1;
sda_out <= 'b1;
end
'd1 : iic_scl <= 1'b1;
'd2 : sda_out <= 1'b0; //重新开始
'd3 : iic_scl <= 1'b0;
'd4 : sda_out <= DEVICE_ID[6];
'd5 : iic_scl <= 1'b1;
'd7 : iic_scl <= 1'b0;
'd8 : sda_out <= DEVICE_ID[5];
'd9 : iic_scl <= 1'b1;
'd11: iic_scl <= 1'b0;
'd12: sda_out <= DEVICE_ID[4];
'd13: iic_scl <= 1'b1;
'd15: iic_scl <= 1'b0;
'd16: sda_out <= DEVICE_ID[3];
'd17: iic_scl <= 1'b1;
'd19: iic_scl <= 1'b0;
'd20: sda_out <= DEVICE_ID[2];
'd21: iic_scl <= 1'b1;
'd23: iic_scl <= 1'b0;
'd24: sda_out <= DEVICE_ID[1];
'd25: iic_scl <= 1'b1;
'd27: iic_scl <= 1'b0;
'd28: sda_out <= DEVICE_ID[0];
'd29: iic_scl <= 1'b1;
'd31: iic_scl <= 1'b0;
'd32: sda_out <= 1'b1; //1:读
'd33: iic_scl <= 1'b1;
'd35: iic_scl <= 1'b0;
'd36: begin
sda_dir <= 'b0; //从机应答
sda_out <= 'b1;
end
'd37: iic_scl <= 1'b1;
'd38: state_done <= 1'b1; //状态结束
'd39: begin
iic_scl <= 'b0;
cnt <= 'b0;
end
default : ;
endcase
end
//--------------------------------------------------- 读数据
DATA_RD: begin
case(cnt)
'd0 : sda_dir <= 1'b0;
'd1 : begin
iic_data_rd_t[] <= sda_in;
iic_scl <= 'b1;
end
'd3 : iic_scl <= 1'b0;
'd5 : begin
iic_data_rd_t[] <= sda_in;
iic_scl <= 'b1;
end
'd7 : iic_scl <= 1'b0;
'd9 : begin
iic_data_rd_t[] <= sda_in;
iic_scl <= 'b1;
end
'd11: iic_scl <= 1'b0;
'd13: begin
iic_data_rd_t[] <= sda_in;
iic_scl <= 'b1;
end
'd15: iic_scl <= 1'b0;
'd17: begin
iic_data_rd_t[] <= sda_in;
iic_scl <= 'b1;
end
'd19: iic_scl <= 1'b0;
'd21: begin
iic_data_rd_t[] <= sda_in;
iic_scl <= 'b1;
end
'd23: iic_scl <= 1'b0;
'd25: begin
iic_data_rd_t[] <= sda_in;
iic_scl <= 'b1;
end
'd27: iic_scl <= 1'b0;
'd29: begin
iic_data_rd_t[] <= sda_in;
iic_scl <= 'b1 ;
end
'd31: iic_scl <= 1'b0;
'd32: begin
sda_dir <= 'b1; //非应答
sda_out <= 'b1;
end
'd33: iic_scl <= 1'b1;
'd34: state_done <= 1'b1; //状态结束
'd35: begin
iic_scl <= 'b0;
cnt <= 'b0;
iic_data_rd <= iic_data_rd_t;
end
default : ;
endcase
end
//--------------------------------------------------- 结束
STOP: begin
case(cnt)
'd0 : begin
sda_dir <= 'b1;
sda_out <= 'b0;
end
'd1 : iic_scl <= 1'b1;
'd3 : sda_out <= 1'b1;
'd15: state_done <= 1'b1; //状态结束
'd16: begin
cnt <= 'b0;
iic_done <= 'b1; //IIC配置完成
end
default : ;
endcase
end
endcase
end
end endmodule
 参考资料:
[1]正点原子FPGA教程
[2]小梅哥FPGA教程

协议——IIC的更多相关文章

  1. 协议—IIC

    I2C总线支持任何IC生产过程NMOS CMOS双极性,两线――串行数据 SDA 和串行时钟SCL线在连接到总线的器件间传递信息,每个器件都有一个唯一的地址识别,无论是微控制器.LCD 驱动器.存储器 ...

  2. FPGA基础设计(四):IIC协议

    很多数字传感器.数字控制的芯片(DDS.串行ADC.串行DAC)都是通过IIC总线来和控制器通信的.不过IIC协议仍然是一种慢速的通信方式,标准IIC速率为100kbit/s,快速模式速率为400kb ...

  3. 九、IIC驱动原理分析

    学习目标:学习IIC驱动原理: 一.IIC总线协议 IIC串行总线包括一条数据线(SDA)和一条时钟线(SCL),支持“一主多从”和“多主机”模式:每个从机设备都有唯一的地址来识别. 图 1 IIC ...

  4. 【STM32】IIC的基本原理(实例:普通IO口模拟IIC时序读取24C02)(转载)

     版权声明:本文为博主原创文章,允许转载,但希望标注转载来源. https://blog.csdn.net/qq_38410730/article/details/80312357 IIC的基本介绍 ...

  5. IIC通信控制的AD5259------在调试过程中遇到的奇葩问题

    首先说一下的遇到的问题: 1.AD5259按照SCL是100KHz的情况下,可以正常接收上位机的数据,但是一段时间后,就不能正确的按照时序来走了 原因在于AD5259在接收到上位机的数据后需要一定的响 ...

  6. 51单片机下实现软件模拟IIC通信

    1.IIC协议简易概述 IIC全称Inter-Integrated Circuit (集成电路总线),是由PHILIPS公司在80年代开发的两线式串行总线,用于连接微控制器及其外围设备.IIC属于半双 ...

  7. 嵌入式LINUX入门到实践(一)

    MINI2440 IIC协议 IIC协议在工程中应用广泛,在我看来,此协议的优势就在于其硬件及其简单,结构清晰. 首先来解读一下S3C2440A这款芯片的IIC协议. 一.一个协议的解读从如上结构图中 ...

  8. OLED屏幕详细使用

    IC扩展-OLED屏的点亮,模拟IIC功能实现C代码点亮OLED屏,只要是可以C编程且有两个GPIO口的单片机均可更改小部分代码使用.OLED屏为像素自发光,其尺寸多为128*64,表示横轴上有128 ...

  9. 基于STM32F429的ADS1115驱动程序

    1.ADS1115中文资料:https://wenku.baidu.com/view/8bab101feef9aef8941ea76e58fafab069dc44e7.html?rec_flag=de ...

随机推荐

  1. Cogs 739. [网络流24题] 运输问题(费用流)

    [网络流24题] 运输问题 ★★ 输入文件:tran.in 输出文件:tran.out 简单对比 时间限制:1 s 内存限制:128 MB «问题描述: «编程任务: 对于给定的m 个仓库和n 个零售 ...

  2. 【loj2985】【WC2019】I君的商店

    题目 交互题: 有\(n\)个物品,每个物品的价格为0或者1; 给出为1的物品的个数奇偶性k,并保证至少有一个价格为1: 每次可以询问一个集合S的另一个集合T的价值和的大小,交互库会返回>=或者 ...

  3. javascript之数组的全部排列组合

    javascript代码如下: var arr = [1, 2, 3]; // 临时变量,用于输出 var temp = []; function n(arr) { for (var i = 0; i ...

  4. rust-vmm 学习

    V0.1.0 feature base knowledge: Architecture of the Kernel-based Virtual Machine (KVM) 用rust-vmm打造未来的 ...

  5. 前端微信小程序仿菜谱精灵

    需求描述及交互分析 设计思路和相关知识点 底部标签导航设计 幻灯片轮播效果设计 菜谱专题列表显示设计 菜谱专题详情设计 菜谱分类设计 幻灯片轮播效果动态切换展示一些美食图片 若本号内容有做得不到位的地 ...

  6. struct tcphdr

    包含在/usr/src/linux/include/linux/tcp.h struct tcphdr { __be16 source; __be16 dest; __be32 seq; __be32 ...

  7. Win7下安装VS2017、安装Qt5.10.1以及在VS2017添加qt插件

    一.安装VS2017 1.下载VS2017 进入vs下载官网https://www.visualstudio.com/zh-hans/downloads/,选择所需要的vs版本,进行在线安装. 2.安 ...

  8. 刷题记录:Shrine

    目录 刷题记录:Shrine 刷题记录:Shrine 题目复现链接:https://buuoj.cn/challenges 参考链接:Shrine 解此题总结一下flask的SSTI:CTF SSTI ...

  9. 2015-2016-2《Java程序设计》团队博客5

    一.项目进展 本周将所有的项目代码全部进行了汇总总结,归纳在了一起,进行整体的测试.虽然在编写的时候很顺利,也就是片段代码问题不大,但是汇总到一起时还是产生了冲突与不对等的问题,所以我们只能仔细地从细 ...

  10. epoll 或者 kqueue 的原理是什么?

    来自知乎:http://www.zhihu.com/question/20122137 epoll 或者 kqueue 的原理是什么? 为什么epoll和kqueue可以用基于事件的方式,单线程的实现 ...