【数字IC】从零开始的Verilog SPI设计

2023-11-20

从零开始的Verilog SPI协议设计

一、写在前面

上一节中,我们详细讨论了SPI的协议内容并从设计组件的角度给出了SPI协议中所需要的诸多内容,以供读者参考
这一节中,我们自定义如下标准的SPI主从设备进行设计,需要注意的是,本篇文章中所涉及的SPI仅供初学者学习参考并没有采用实际工业开发中所涉及到的代码标准和思考,也并未进行综合与后仿。

1.1 协议标准

1.2 数字IC组件代码

1.3 设计要求

  • 一个主设备,一个从设备的SPI全双工通信,从设备是一个寄存器组
  • 全局时钟100Mhz
  • CPOL = 0,CPHA = 1
  • 可支持波特率2,4,8,16分频四种状态
  • 数据传输为MSB(高位优先)
  • 数据位宽固定8位

1.4 其他协议解读

1.4.1 UART协议
1.4.2 SPI协议
1.4.3 I2C协议
1.4.4 AXI协议

二、设计思想

2.1 从设备读写时序(reg_array)

我们想要在本篇博客中用SPI的方式操控从设备:一个位宽为8bit,深度为16位的寄存器组,寄存器组在数字IC设计中颇为常见,也颇为基础,非常适合在本篇博客中用以展示SPI串行总线的奥妙,CPU的内部就需要寄存器组作为最低level的存储层级,因为SPI协议的数据通信单方向只有一条线,因此我们需要使用这一根线来控制寄存器组的读写时序,本寄存器组的读写逻辑由作者自行定义。

2.1.1 写时序

需要两拍才能执行完毕,第一拍从data_in发送地址和模式,其中地址为前四位模式为后四位,若需要往寄存器组中写数据,第一拍的后四位需要为“1111”,写模式第二拍从data_in发送需要写入的发送数据
(需要注意的是,受限于SPI的串型接口,一拍的相当于SCK的八倍频时钟信号,即需要八个时钟周期接收到一个字节,为一拍)
在这里插入图片描述

2.1.2 读时序

需要两拍才能执行完毕,第一拍从data_in发送地址和模式,其中读地址为前四位模式为后四位,若需要从寄存器组中读数据,第一拍的后四位需要为“0000”,读模式第二拍从data_out发送需要读出的发送数据
(需要注意的是,受限于SPI的串型接口,一拍的相当于是SCK的八倍频时钟信号,即需要八个时钟周期接收到一个字节,为一拍)

在这里插入图片描述

2.2 从设备的控制器设计思想(SPI_slave_controller)

单独的reg_array没有办法按照SPI协议直接相连,因为SPI是一款串行总线,而我们上面提到的reg_array使用的是并行接口,因此我们需要一个SPI的控制器来与reg_array连接,根据协议的内容,这个控制器的功能

  • 串并转换:即将接收到的来自主设备的MOSI串行信号翻译成reg_array可懂的并行信号,以此供从设备采集
  • 并串转换:即将来自从设备的输出值(并行量)翻译为串型量,发送到MISO位置处,传递给主设备
  • 输出从设备的控制信号:比如上文读写时序中,寄存器组状态跳变的就需要enable信号的控制,enable信号什么时候输出,这个是来自SPI_slave的控制的

2.3 主设备设计思想

2.3.1 波特率分频器设计思想(BaudratePrescaler)

【数字IC】深入浅出理解SPI协议,我们探讨了为什么要对SPI时钟信号进行分频,并确定了2,4,8,16分频的四个参数,按照偶数分频的思想,我们可以采用寄存器级联法或计数器法的方法进行分频,二者均可满足目的,作者选择了更为简单的寄存器级联法来进行分频操作

2.3.2 数据发送的控制器(SPI_master_contorller)

在主设备端,我们同样需要一个控制器来发送数据,这个控制器首先需要对MCU/CPU来的数据进行串并转换,其次,它也需要告诉SPI的四条线,什么时候nss拉低,什么时候发送数据,因此我们肯定需要按照状态对其进行划分,区分状态机的跳转。

三、从设备(reg_array)

3.1 状态机跳变

在这里插入图片描述

IDLE:是待机状态,当其接收到css的拉低信号时,跳转到S0
S0:S0状态代表该从设备已经被选中了
S1和S2:是读取操作的状态机
S3和S4:是写入操作的状态机

本状态机跳转图由Questasim自动生成,相关操作更新于
如何自动生成设计文件的状态机跳转图(仿真工具使用技巧)

3.2 设计文件

module reg_array(
input clk,
input rst_n,
input nss,
input enable, // to ensure that it can sample data_in
input [7:0] data_in,
output reg [7:0] data_out,
output done//读取操作的完成信号);

localparam IDLE = 3'b000;
localparam S0   = 3'b001; 
localparam S1   = 3'b010;
localparam S2   = 3'b011;
localparam S3   = 3'b100;
localparam S4   = 3'b101;

reg [2:0] state;
reg [2:0] nstate;

reg [7:0] array [0 : 15];
reg [3:0] address;

//状态机第一段
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		state <= IDLE ;
	else 
		state <= nstate;

//状态机第二段
always@(*)
begin
	case(state)
	IDLE :  nstate = !nss ? S0 :IDLE;
 	S0   :  nstate = (enable & data_in[3:0] == 4'b0000) ? S1 :  (enable & data_in[3:0] == 4'b1111) ? S3 : S0;
	S1   :  nstate = (enable) ? S2 : S1;
	S2   :  nstate = !nss ? S0 : IDLE;
	S3   :  nstate = (enable) ? S4 : S3;
	S4   :  nstate = !nss ? S0 : IDLE; 
	default: nstate = IDLE;
	endcase
end

//数据读取操作
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		begin
		array[0] <= 16'h0000;
		array[1] <= 16'h0000;
		array[2] <= 16'h0000;
		array[3] <= 16'h0000;
		array[4] <= 16'h0000;
		array[5] <= 16'h0000;
		array[6] <= 16'h0000;
		array[7] <= 16'h0000;
		array[8] <= 16'h0000;
		array[9] <= 16'h0000;
		array[10] <= 16'h0000;
		array[11] <= 16'h0000;
		array[12] <= 16'h0000;
		array[13] <= 16'h0000;
		array[14] <= 16'h0000;
		array[15] <= 16'h0000;
		end
	else if (nstate == S2)
		data_out <= array[address];
	else if (nstate == S4)
		array[address] <= data_in;
	else
		data_out <= data_out;

//地址操作
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		address <= 4'h0;
	else if(state == S0 && enable)
		address <= data_in[7:4];
	else 
		address <= address;

assign done = (state==S2 && nstate == S0) ? 1 : 0 ;

	
endmodule

3.3 仿真文件

`timescale 1ns / 1ps
module reg_array_tb ();

reg clk;
reg rst_n;
reg nss;
reg enable;
reg [7:0] data_in;
wire [7:0] data_out;
wire done;

reg_array u1 (clk,rst_n,nss,enable,data_in,data_out,done);

task reset;
begin
	clk = 0;
	rst_n = 1;
	nss = 1;
	enable = 0;
	@(negedge clk);
	rst_n = 0;
	@(negedge clk);
	rst_n = 1;
	@(negedge clk);
	@(negedge clk);
end
endtask

task write;
input [3:0] w_address;
input [7:0] w_data;
begin
	@(negedge clk);
	nss = 0;
	enable = 0;
	@(negedge clk);
	enable = 1;
  data_in = {w_address,4'b1111};
  @(negedge clk);
	data_in = w_data;
	@(negedge clk);
end
endtask

task read;
input [3:0] r_address;
begin
	@(negedge clk);
	nss = 0;
	enable = 0;
	@(negedge clk);
	enable = 1;
  data_in = {r_address,4'b0000};
  @(negedge clk);
	enable = 0;
//	data_in = r_data;
	repeat(8) @(negedge clk);
	enable = 1;
end
endtask

always #5 clk = !clk;

initial
begin
reset;
write(4'h3,8'h1a);
write(4'ha,8'ha4);
read(4'ha);
read(4'h3);
#2000 $stop;
end




endmodule

3.4 仿真结果

请添加图片描述

四、从设备控制器设计(SPI_slave_controller)

4.1 设计文件(SPI_slave_controller)

需要关注的是,SPI_slave_controller设计虽然有诸多接口,但是实际上与主设备相连的只有四个,即,sck,nss,mosi,miso,其它的的接口都是体现在从设备整体内部的互联,体现了SPI通信的四根线的原则

module SPI_slave_controller
#(parameter CPOL = 1'b0,
 parameter CPHA = 1'b0)
(input mosi,
 input nss,
 input sck,
 input rst_n,
 input done,// done信号来控制什么时候该发送读出的数据
 input [7:0] data_in, //来自reg_array的data_in(读出的数据)
 output reg enable,
 output[7:0]data_out, //送往reg_array的data_out(写入寄存器组的数据)
 output reg miso);


	reg [0:7] receive_reg;
	reg [0:7] send_reg;
 	reg [3:0] r_cnt;
	reg [3:0] s_cnt;


// 数据采样
always@(posedge sck or negedge rst_n )
	if(!rst_n)
		receive_reg <= 8'h00;
	else if(nss)
		receive_reg <= receive_reg;
	else
		receive_reg[r_cnt]<= mosi;

//采样计数器,接收满一个字节重置
always@(posedge sck or negedge rst_n)
	if(!rst_n)
		r_cnt <= 4'h0;
	else if(nss || r_cnt == 4'h7)
		r_cnt <= 4'h0;
	else 
		r_cnt <= r_cnt + 1'b1;

//reg_array的控制信号生成
always@(posedge sck or negedge rst_n)
	if(!rst_n)
		enable <= 1'b0;
	else if(!nss && r_cnt == 4'h7)
		enable <= 1'b1;
	else 	
		enable <= 1'b0;

assign data_out = enable ? receive_reg : 8'h00;

// 数据的发送
always@(negedge sck or negedge rst_n)
	if(!rst_n)
		send_reg <= 8'h00;
	else if(done)
		send_reg <= data_in;
	else if(!nss && s_cnt < 4'h8)
		 miso <= send_reg[s_cnt];
	else 
		 miso <= 1'bx;
//数据发送计数器
always@(negedge sck or negedge rst_n)
	if(!rst_n)
		s_cnt <= 4'h0;
	else if(nss || s_cnt == 4'h7)
		s_cnt <= 4'h0;
	else 
		s_cnt <= s_cnt + 1'b1;		
endmodule


4.2 仿真文件(SPI_slave)

这里按照主设备的发送时序,写了名为r_data的task,以此用来仿真,另外的的一个task是时钟复位task,此处的仿真文件,直接将控制器和寄存器组作为整体来进行仿真,没有单独区分控制器的仿真

`timescale 1ns / 1ps
module SPI_slave_tb ();
reg mosi;
reg nss;
reg sck;
reg rst_n;
wire done;
wire [7:0] data_in;
wire enable;
wire [7:0]data_out;
wire miso;

reg_array u1 (.clk(sck),.rst_n(rst_n),.nss(nss),.enable(enable),.data_in(data_out),.data_out(data_in),.done(done));
SPI_slave_controller  #(0,0)u2 (.mosi(mosi),.nss(nss),.sck(sck),.rst_n(rst_n),.done(done),
										 .data_in(data_in),.enable(enable),.data_out(data_out),.miso(miso));

task reset;
begin
	sck = 0;
	rst_n = 1;
	nss = 1;
	@(negedge sck);
	rst_n = 0;
	@(negedge sck);
	rst_n = 1;
	@(negedge sck);
	@(negedge sck);
end
endtask

task r_data;
input [7:0] r_data;
begin
	@(negedge sck);
	nss = 0;
  #0.5
	mosi = r_data[7];

	@(negedge sck);
	mosi = r_data[6];

	@(negedge sck);
	mosi = r_data[5];

	@(negedge sck);
	mosi = r_data[4];

	@(negedge sck);
	mosi = r_data[3];

	@(negedge sck);
	mosi = r_data[2];

	@(negedge sck);
	mosi = r_data[1];

	@(negedge sck);
	mosi = r_data[0];
end
endtask

always #5 sck = !sck;


initial
	begin
		reset;
		r_data(8'haf);
		r_data(8'h13);
		r_data(8'ha0);
		r_data(8'h00);
		#1000
		$stop;
	end



endmodule

4.3 仿真结果

请添加图片描述
拉低nss后,我们按照slave的读写时序,先写后读,先向地址a中写入13,再从a地址中读出写入的数据,发现二者相符,需要注意的是,此slave的读取操作的第二拍,主设备依旧会发送数据,体现了【数字IC】深入浅出理解SPI协议所说SPI协议的本质是两个移位寄存器不断地互相交换数据,不过读操作第二拍发送的数据不会对寄存器的存储值产生影响

四、波特率分频器(BaudratePrescaler)

4.1 设计文件

module BaudratePrescaler #(parameter Prescaler = 2'b01)
( input clk,
	input rst_n,
	output reg clk_scaler 
);

	reg scaler_2;
	reg scaler_4;
	reg scaler_8;
	reg scaler_16;

always@(posedge clk or negedge rst_n)
	if(!rst_n)
		scaler_2 <= 1'b0;
	else
		scaler_2 <= !scaler_2;

always@(posedge scaler_2 or negedge rst_n)
	if(!rst_n)
		scaler_4 <= 1'b0;
	else
		scaler_4 <= !scaler_4;

always@(posedge scaler_4 or negedge rst_n)
	if(!rst_n)
		scaler_8 <= 1'b0;
	else
		scaler_8 <= !scaler_8;

always@(posedge scaler_8 or negedge rst_n)
	if(!rst_n)
		scaler_16 <= 1'b0;
	else
		scaler_16 <= !scaler_16;

always@(*)
	case(Prescaler)
	2'b00: clk_scaler = scaler_2;
	2'b01: clk_scaler = scaler_4;
	2'b10: clk_scaler = scaler_8;
	2'b11: clk_scaler = scaler_16;
	default: clk_scaler = 1'b0;
	endcase

endmodule

4.2 仿真文件

`timescale 1ns / 1ps
module BaudratePrescaler_tb ();
reg clk;
reg rst_n;
wire clk_scaler;

BaudratePrescaler #(0) u0 (clk,rst_n,clk_scaler);
BaudratePrescaler #(1) u1 (clk,rst_n,clk_scaler);
BaudratePrescaler #(2) u2 (clk,rst_n,clk_scaler);
BaudratePrescaler #(3) u3 (clk,rst_n,clk_scaler);
BaudratePrescaler #(4) u4 (clk,rst_n,clk_scaler);


task reset;
	begin
		@(negedge clk);
		rst_n = 0;
		@(negedge clk);
		@(negedge clk);
		rst_n = 1;
	end
endtask


initial
	begin
		clk = 0;
		rst_n = 1;
		reset;
		#1000
		$stop;
	end
	
always #5 clk = !clk;


endmodule

4.3 仿真结果

在这里插入图片描述
我们按照Prescaler的值分别例化了五个module,分别是二分频,四分频,八分频,十六分频和错误分频,观察输出的值,符合预期,设计成立。

五、SPI主设备控制器(SPI_master_controller)

5.1 状态机跳变(SPI_master_controller)

在这里插入图片描述

w_IDLE是复位以后的状态,当接收到enable信号后跳转到w_S1状态
w_S1是发送前7bit的状态
w_S2是发送最后1bit的状态
为什么需要用两个状态来表示同样的一个发送行为呢?跳转到5.2.1即可寻找到答案。

5.2 设计文件(SPI_master_controller)

module SPI_master 
	#(parameter Prescaler = 2'b00,
	 parameter CPOL = 0,
	 parameter CPHA = 1)
	(input clk,
	 input rst_n,
	 input enable,
	 input [7:0] data_in,
   input  miso,
	 output sck_o,
	 output reg mosi,
	 output nss);

localparam w_IDLE = 2'b00;
localparam w_S1   = 2'b01;
localparam w_S2   = 2'b10;

reg [1:0] w_state;
reg [1:0] w_nstate;


reg [7:0] send_reg_1;
reg			  send_reg_2;
reg [0:7] receive_reg;


reg [2:0] w_cnt;
reg [2:0] r_cnt;

wire [7:0] data_out;

reg nss_r;

always@(negedge clk or negedge rst_n)
	if(!rst_n)
		w_state <= w_IDLE;
	else
		w_state <= w_nstate; 

always@(*)
	case(w_state)
		w_IDLE : w_nstate = enable ? w_S1 : w_IDLE;
		w_S1   : w_nstate = (w_cnt == 3'h6) ? w_S2 : w_S1;
		w_S2	 : w_nstate = enable ? w_S1 : w_IDLE;
	default: 	 w_nstate = w_IDLE;
	endcase

always@(negedge clk or negedge rst_n)
	if(!rst_n)
		send_reg_1 <= 8'h00;
	else if(w_state == w_IDLE && enable || w_state == w_S1 || w_state == w_S2)
		send_reg_1 <= data_in;
	else
		send_reg_1 <= 8'h00;

always@(negedge clk or negedge rst_n)
	if(!rst_n)
		send_reg_2 <= 1'h0;
	else if(w_state == w_S1 && w_cnt == 3'h1)
		send_reg_2 <= send_reg_1[1];
	else
		send_reg_2 <= 1'h0;

always@(negedge clk or negedge rst_n)
	if(!rst_n)
		w_cnt <= 3'h0;
	else if(w_state == w_S1 || w_state == w_S2 )
		w_cnt <= w_cnt + 1'b1;
	else if (w_cnt == 3'h7)
		w_cnt <= 3'h0;
	else
		w_cnt <= w_cnt;

always@(*)
	if(w_state == w_S1)
		mosi = send_reg_1[w_cnt];
	else if(w_state == w_S2)
		mosi = send_reg_2;
	else
		mosi = 1'bx;

always@(negedge clk or negedge rst_n)
	if(!rst_n )
		nss_r <= 1'b1;
	else if(w_state == w_IDLE && enable || w_state == w_S1 || w_state == w_S2)
		nss_r <= 1'b0;
	else if(w_state == w_IDLE || !enable)
		nss_r <= 1'b1;
	else
		nss_r <= nss_r;

assign nss = nss_r;

// read_statement
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		r_cnt <= 3'h0;
	else if(r_cnt < 3'h7 && enable)
		r_cnt <= r_cnt + 1'b1;
	else if(r_cnt == 3'h7)
		r_cnt <= 3'h0;
	else
		r_cnt <= r_cnt;

always@(posedge clk or negedge rst_n)
	if(!rst_n)
		receive_reg <= 8'h00;
	else if(!nss_r)
		receive_reg[r_cnt] <= miso;
	else
		receive_reg <= receive_reg;

assign data_out = r_cnt == 3'h7 ? receive_reg : 8'hx; 

assign sck_o = (!rst_n) ? CPOL : clk ;



endmodule
5.2.1如何让SPI pipeline起来

当8bit的数据传输进来后,依次按从高到低的顺序进行发送,存储这8bit的寄存器是send_reg_1,但是这种形式没有办法pipeline起来,因为8bit寄存器内部数值的更新会暂停整个数据的发送(相当于需要暂停一个时钟周期),因此使用了一个send_reg_2,来保留最后1bit的数据,通过增大面积的方式减少了暂停产生的SPI带宽损失。

5.2 仿真文件

这里的仿真也同样是将波特率分频器和主设备控制器共同进行仿真,第一段代码为二者连接的.v文件,第二段代码为二者的仿真文件。

module top_module
(input clk,
input rst_n,
input enable,
input [7:0] data_in_top,
input miso,
output nss,
output sck_o,
output mosi
);


wire clk_sacler;
wire [7:0] data_in;

BaudratePrescaler  u1 (.clk(clk),.rst_n(rst_n),.clk_scaler(clk_scaler));
SPI_master      	 u2 (.clk(clk_scaler),.rst_n(rst_n),.enable(enable),
											.data_in(data_in_top),.miso(miso),.sck_o(sck_o),.mosi(mosi),.nss(nss));

endmodule

`timescale 1ns / 1ps
module top_module_tb();
reg clk;
reg rst_n;
reg enable;
reg [7:0] data_in;
reg miso;
wire sck_o;
wire mosi;
wire nss;

top_module u1 (.clk(clk),.rst_n(rst_n),.enable(enable),.data_in_top(data_in),.miso(miso));

task reset;
	begin
		clk = 0;
		rst_n=1;
		enable = 0;
		data_in = 8'h00;
		miso = 1;
	@(negedge clk);
		rst_n = 0;
	@(negedge clk);
		rst_n = 1;
		enable =1;
  #0.5
		data_in = 8'h1f;
	end
endtask

always #5 clk = !clk;

initial
begin 
reset;
#100;
enable = 0;
#1000;
enable = 1;
#2000;
$stop;
end

endmodule

5.3 仿真结果

请添加图片描述

六、本设计与工业级SPI的差距

  • 输入输出处使用FIFO做缓冲

感兴趣的读者可以参考 【数字IC手撕代码】Verilog同步FIFO,增加相对应的模块和控制信号,使整个SPI的设计更为完善

  • CPOL与CPHA的更多状态

这里的案例只引入了CPOL和CPHA的一种状态,如果想要添加更多的状态可以使用条件编译的ifdef的方式来进行

  • 从设备的复杂程度

我们案例的从设备只支持读写,其实以EEPROM——常用于SPI控制的内存芯片为例,读写的命令可以非常复杂,如顺序读写,随机读写,错误检测,错误矫正等诸多内容可以进行添加

  • 等等等等

实际上,本设计仅为学习参考使用,配合【数字IC】深入浅出理解SPI协议使读者对于协议和电路实现有基本的认识才是本篇博文的目的所在。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【数字IC】从零开始的Verilog SPI设计 的相关文章

  • qtdesigner界面美化

    文章目录 前言 一 QSS 1 1 QSS选择器介绍 2 2 使用 二 自定义属性 2 1 添加自定义属性 前言 pyqt5能快速构建界面 但是你会发现构建出来的界面没有像我们平常用的客户端界面一样美观 现在 就让我学习一下如何美化界面 本

随机推荐

  • 如何防止过拟合和欠拟合

    过拟合和欠拟合是模型训练过程中经常出现的问题 两种情况正好相反 现将两者的定义及如何防止进行简要总结 1 过拟合 1 1 定义 是指模型对于训练数据拟合呈现过当的情况 反映到评估指标上就是模型在训练集上的表现很好 但是在测试集上的表现较差
  • 如何终止java线程

    终止线程的三种方法 有三种方法可以使终止线程 1 使用退出标志 使线程正常退出 也就是当run方法完成后线程终止 2 使用stop方法强行终止线程 这个方法不推荐使用 因为stop和suspend resume一样 也可能发生不可预料的结果
  • n行Python代码系列:两行代码去除抖音快手短视频尾部Logo

    老猿Python博文目录 https blog csdn net LaoYuanPython 一 引言 最近看到好几篇类似 n行Python代码 的博文 看起来还挺不错 简洁 实用 传播了知识 带来了阅读量 撩动了老猿的心 决定跟风一把 推
  • 基于Springboot+Vue图书商城系统

    目录 1 系统架构 2 系统介绍 3 运行环境 4 系统演示 5 前端搭建 6 功能展示 7 代码展示 1 系统架构 后台 SpringBoot Mybatis plus Mybatis Hutool工具包 lombok插件 前台 Vue
  • UE4 制作导出Content目录下某个文件夹内所有模型的六视图并将模型资源文件复制到指定文件夹的插件

    一 新建空白插件 在Bulid cs内加入两个模块 EditorSubsystem UnrealEd PublicDependencyModuleNames AddRange new string Core EditorSubsystem
  • 用vue写一个学校官网

    首先 你需要准备一个文本编辑器 如 Sublime Text VSCode 等 和浏览器 然后 你需要安装 Vue 你可以通过使用 npm 命令来安装 npminstall g vue
  • Ubuntu Server 22.04 安装桌面

    Ubuntu Server 22 04 安装桌面 sudo apt get update sudo apt get upgrade apt get install y ubuntu desktop 如果你不想安装一些附加的程序 可用以下命令
  • Tomcat startup.bat Using CATALINA_OPTS: ““,启动闪退

    Tomcat 控制台打开startup bat 发现Tomcat终端窗口闪退打不开 在startup bat最后加一个pause 会弹出Using CATALINA OPTS 重新设置了JAVA HOME 没用 在startup bat最前
  • python对csv文档进行读,写,追加操作(csv,pandas)

    python处理csv文档的两种方法 csv pandas python处理csv一般采用两种方法一种是import pandas 另一种是 import csv 本文将介绍这两种方法对csv进行读 写 追加的操作 1 import pan
  • mysql unique key使用_mysql unique key在查询中的使用与相关问题

    1 建表语句 CREATE TABLE employees emp no int 11 NOT NULL birth date date NOT NULL first name varchar 14 NOT NULL last name v
  • WordPress多站点伪静态的设置方式(WP站群必备)

    apache的设置方式 正常参考wordpress自带的设置方式即可 本文只介绍Ngnix及IIS下Wordpress多站点伪静态设置方式 Ngnix的wordpress多站点伪静态设置方式 wordpress固定链接设置 try file
  • Arduino for ESP8266&ESP32适用库ESPAsyncWebServer:快速入门

    文章目录 目的 特征 安装 快速体验 注意事项 总结 目的 Arduino for ESP8266 和 Arduino for ESP32 中默认就有WebServer 不过这些WebServer都是同步的 不支持同时处理多个连接 这在很多
  • AngularJs单元测试

    这篇文章主要介绍了angularJS中的单元测试实例 本文主要介绍利用Karma和Jasmine来进行ng模块的单元测试 并用Istanbul 来生成代码覆盖率测试报告 需要的朋友们可以参考下 以下可全都是干货哦 当ng项目越来越大的时候
  • 如何在Insert插入操作之后,获取自增主键的ID值

    背景说明 MyBatis中 在大多数情况下 我们向数据库中插入一条数据之后 并不需要关注这条新插入数据的主键ID 我们也知道 正常在DAO中的插入语句虽然可以返回一个int类型的值 但是这个值表示的是插入影响的行数 而不是新插入数据的主键I
  • Unity3D如何修改Button显示的文字以及深入了解Button组件

    在创建了一个Button后 结构如图 先仔细观察一下Button的Inspector视图 发现其中竟然有一个叫Button的脚本组件 新建脚本 代码如下 并将该脚本绑定给Canvas组件 using UnityEngine UI using
  • winform记录

    SpeechSynthesizer 下边代码多次调用 会导致内存溢出outofmemory SpeechSynthesizer 需要改为全局静态 private void button Click object sender EventAr
  • 【MySQL笔记】MySQL8新特性 — 计算列

    什么叫计算列呢 简单来说就是某一列的值是通过别的列计算得来的 例如 a 列值为 1 b 列值为 2 c 列不需要手动插入 定义 a b 的结果为 c 的值 那么 c 就是计算列 是通过别的列计算得来的 在 MySQL 8 中 CREATE
  • Pytorch Torch.utils.data.Sampler

    对于 可迭代样式的数据集 数据加载顺序完全由用户定义的可迭代样式控制 这样可以更轻松地实现块读取和动态批处理大小 例如 通过每次生成一个批处理的样本 为了从数据集中读取数据 pytorch提供了Sampler基类与多个子类实现不同方式的数据
  • 天刀服务器维护4月19,5月19日服务器例行维护公告

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 亲爱的玩家 青龙乱舞区 大地飞鹰区 沧海云帆区 把酒邀月区全部服务器将在5月19日6 00 10 00停机维护更新 维护完成后上述各服务器客户端版本更新至2 1 47 18 由于机房网络波动 云
  • 【数字IC】从零开始的Verilog SPI设计

    从零开始的Verilog SPI协议设计 一 写在前面 1 1 协议标准 1 2 数字IC组件代码 1 3 设计要求 1 4 其他协议解读 1 4 1 UART协议 1 4 2 SPI协议 1 4 3 I2C协议 1 4 4 AXI协议 二