实现 利用SPI 接口 发送 显示屏 初始化代码
`timescale 1ns/1ns module lcd_spi_top_tb(); reg clk_x2_i; reg rst_i; wire spi_cs; wire spi_scl; wire spi_mosi; wire spi_miso; wire spi_init_done; initial begin clk_x2_i=0; rst_i=0; #1000; rst_i=1; #1000; @(posedge spi_init_done) $stop; end always #125 clk_x2_i<=~clk_x2_i; lcd_spi_top #( .SPI_IN_WIDTH (6'd16),//spi 输入位数 .SPI_OUT_WIDTH (6'd16),//SPI 输出位数 .SPI_CPOL (1'b0),//空闲状态SCL电平 0:SCL=0 1:SCL=1 .SPI_CPHA (1'b0) //SPI数据在哪个SCL边沿有效 0:数据在SCL第一个边沿有效,1:数据在SCL第二个边沿有效, ) lcd_spi_top_inst ( .clk_x2_i (clk_x2_i ), .rst_i (rst_i ), .spi_cs_o (spi_cs ), .spi_scl_o (spi_scl ), .spi_mosi (spi_mosi ), .spi_miso (spi_miso ), .spi_init_done(spi_init_done) ); endmodule
module lcd_spi_top #( parameter [5:0]SPI_IN_WIDTH =6'd16,//spi 输入位数 parameter [5:0]SPI_OUT_WIDTH =6'd16,//SPI 输出位数 parameter [0:0]SPI_CPOL=1'b0,//空闲状态SCL电平 0:SCL=0 1:SCL=1 parameter [0:0]SPI_CPHA=1'b0, //SPI数据在哪个SCL边沿有效 0:数据在SCL第一个边沿有效,1:数据在SCL第二个边沿有效, parameter [5:0]CS_F_DELAY=6'd1, parameter [5:0]CS_B_DELAY=6'd1 ) ( input wire [0:0] clk_x2_i, //SPI输出时钟的2倍时钟输入 input wire [0:0] rst_i, //复位信号输入 output reg [0:0] lcd_rst, //显示屏复位 output wire [0:0] spi_cs_o, // output wire [0:0] spi_scl_o, output wire [0:0] spi_mosi, input wire [0:0] spi_miso, output reg [0:0] spi_init_done ); localparam [7:0] SPI_CODE_NUM=8'D207 ; reg [15:0] rom [SPI_CODE_NUM-1:0]; initial begin rom[0 ]=16'h0003; rom[1 ]=16'h0121; rom[2 ]=16'h0227; rom[3 ]=16'h031B; rom[4 ]=16'h04F4; rom[5 ]=16'h0507; rom[6 ]=16'h0790; rom[7 ]=16'h081B; rom[8 ]=16'h0903; rom[9 ]=16'h0A25; rom[10 ]=16'h0B20; rom[11 ]=16'h0C20; rom[12 ]=16'h0D0D; rom[13 ]=16'h0E20; rom[14 ]=16'h0F20; rom[15 ]=16'h1040; rom[16 ]=16'h1140; rom[17 ]=16'h1240; rom[18 ]=16'h1340; rom[19 ]=16'h1408; rom[20 ]=16'h1506; rom[21 ]=16'h16B4; rom[22 ]=16'h1700; rom[23 ]=16'h1888; rom[24 ]=16'h19FF; rom[25 ]=16'h1A00; rom[26 ]=16'h1B80; rom[27 ]=16'h1C00; rom[28 ]=16'h1D00; rom[29 ]=16'h1E00; rom[30 ]=16'h1F3F; rom[31 ]=16'h2085; rom[32 ]=16'h2189; rom[33 ]=16'h2287; rom[34 ]=16'h2387; rom[35 ]=16'h2487; rom[36 ]=16'h258C; rom[37 ]=16'h2683; rom[38 ]=16'h270C; rom[39 ]=16'h282B; rom[40 ]=16'h2923; rom[41 ]=16'h2A28; rom[42 ]=16'h2B28; rom[43 ]=16'h2C30; rom[44 ]=16'h2D3B; rom[45 ]=16'h2E00; rom[46 ]=16'h2F08; rom[47 ]=16'h3000; rom[48 ]=16'h3100; rom[49 ]=16'h3200; rom[50 ]=16'h3300; rom[51 ]=16'h3400; rom[52 ]=16'h3500; rom[53 ]=16'h3600; rom[54 ]=16'h3700; rom[55 ]=16'h3800; rom[56 ]=16'h3900; rom[57 ]=16'h3A00; rom[58 ]=16'h3B00; rom[59 ]=16'h3C00; rom[60 ]=16'h3D00; rom[61 ]=16'h3E00; rom[62 ]=16'h3F00; rom[63 ]=16'h4000; rom[64 ]=16'h418E; rom[65 ]=16'h42A0; rom[66 ]=16'h4300; rom[67 ]=16'h4429; rom[68 ]=16'h0121; rom[69 ]=16'h0227; rom[70 ]=16'h0790; rom[71 ]=16'h0A25; rom[72 ]=16'h0B20; rom[73 ]=16'h0C20; rom[74 ]=16'h0D0D; rom[75 ]=16'h0E20; rom[76 ]=16'h0F20; rom[77 ]=16'h1040; rom[78 ]=16'h113A; rom[79 ]=16'h122C; rom[80 ]=16'h1340; rom[81 ]=16'h2085; rom[82 ]=16'h2189; rom[83 ]=16'h2287; rom[84 ]=16'h2387; rom[85 ]=16'h2487; rom[86 ]=16'h258C; rom[87 ]=16'h2683; rom[88 ]=16'h270C; rom[89 ]=16'h282B; rom[90 ]=16'h2923; rom[91 ]=16'h2A28; rom[92 ]=16'h2B28; rom[93 ]=16'h2C30; rom[94 ]=16'h2D3B; rom[95 ]=16'h2E00; rom[96 ]=16'h2F08; rom[97 ]=16'h4000; rom[98 ]=16'h418E; rom[99 ]=16'h42A0; rom[100]=16'h4300; rom[101]=16'h4429; rom[102]=16'h0600; rom[103]=16'h0000; rom[104]=16'h0003; rom[105]=16'h0121; rom[106]=16'h0227; rom[107]=16'h031B; rom[108]=16'h04F4; rom[109]=16'h0507; rom[110]=16'h0790; rom[111]=16'h081B; rom[112]=16'h0903; rom[113]=16'h0A25; rom[114]=16'h0B20; rom[115]=16'h0C20; rom[116]=16'h0D0D; rom[117]=16'h0E20; rom[118]=16'h0F20; rom[119]=16'h1040; rom[120]=16'h1140; rom[121]=16'h1240; rom[122]=16'h1340; rom[123]=16'h1408; rom[124]=16'h1506; rom[125]=16'h16B4; rom[126]=16'h1700; rom[127]=16'h1888; rom[128]=16'h19FF; rom[129]=16'h1A00; rom[130]=16'h1B80; rom[131]=16'h1C00; rom[132]=16'h1D00; rom[133]=16'h1E00; rom[134]=16'h1F3F; rom[135]=16'h2085; rom[136]=16'h2189; rom[137]=16'h2287; rom[138]=16'h2387; rom[139]=16'h2487; rom[140]=16'h258C; rom[141]=16'h2683; rom[142]=16'h270C; rom[143]=16'h282B; rom[144]=16'h2923; rom[145]=16'h2A28; rom[146]=16'h2B28; rom[147]=16'h2C30; rom[148]=16'h2D3B; rom[149]=16'h2E00; rom[150]=16'h2F08; rom[151]=16'h3000; rom[152]=16'h3100; rom[153]=16'h3200; rom[154]=16'h3300; rom[155]=16'h3400; rom[156]=16'h3500; rom[157]=16'h3600; rom[158]=16'h3700; rom[159]=16'h3800; rom[160]=16'h3900; rom[161]=16'h3A00; rom[162]=16'h3B00; rom[163]=16'h3C00; rom[164]=16'h3D00; rom[165]=16'h3E00; rom[166]=16'h3F00; rom[167]=16'h4000; rom[168]=16'h418E; rom[169]=16'h42A0; rom[170]=16'h4300; rom[171]=16'h4429; rom[172]=16'h0121; rom[173]=16'h0227; rom[174]=16'h0790; rom[175]=16'h0A25; rom[176]=16'h0B20; rom[177]=16'h0C20; rom[178]=16'h0D0D; rom[179]=16'h0E20; rom[180]=16'h0F20; rom[181]=16'h1040; rom[182]=16'h113A; rom[183]=16'h122C; rom[184]=16'h1340; rom[185]=16'h2085; rom[186]=16'h2189; rom[187]=16'h2287; rom[188]=16'h2387; rom[189]=16'h2487; rom[190]=16'h258C; rom[191]=16'h2683; rom[192]=16'h270C; rom[193]=16'h282B; rom[194]=16'h2923; rom[195]=16'h2A28; rom[196]=16'h2B28; rom[197]=16'h2C30; rom[198]=16'h2D3B; rom[199]=16'h2E00; rom[200]=16'h2F08; rom[201]=16'h4000; rom[202]=16'h418E; rom[203]=16'h42A0; rom[204]=16'h4300; rom[205]=16'h4429; rom[206]=16'h0600; end localparam [3:0]SEND_IDLE =0000; localparam [3:0]LCD_RESET =0001; localparam [3:0]SEND_CMD =0010; localparam [3:0]SEND_CMD1 =0110; localparam [3:0]SEND_DELAY =0111; localparam [3:0]SEND_END =0101; reg [31:0] spi_data_i; reg spi_start; wire spi_done; wire spi_busy; wire [31:0] spi_data_o; reg [5:0] state; reg [10:0] reg_cnt; reg [20:0] delay_cnt; parameter [20:0] DELAY_50MS=21'd200_000; //50MS*1000*1000/250NS=21'd200_000 parameter [20:0] DELAY_120MS=21'd480_000; //120MS*1000*1000/250NS=21'd480_000 always @(negedge clk_x2_i or negedge rst_i ) begin if(rst_i==1'b0) begin state<=SEND_IDLE; spi_data_i<=32'B0; spi_start<=1'B0; reg_cnt<=11'd0; delay_cnt<=20'd0; spi_init_done<=1'b0; lcd_rst<=1'b0; end else begin case(state) SEND_IDLE : begin if(delay_cnt<DELAY_50MS) begin state<=SEND_IDLE; lcd_rst<=1'b0; delay_cnt<=delay_cnt+1'd1; end else begin state<=LCD_RESET; lcd_rst<=1'b1; delay_cnt<=21'd0; end end LCD_RESET : begin if(delay_cnt<DELAY_120MS) begin state<=LCD_RESET; lcd_rst<=1'b1; delay_cnt<=delay_cnt+1'd1; end else begin state<=SEND_CMD; delay_cnt<=20'd0; end end SEND_CMD :begin if(spi_busy==1'b0) begin if(reg_cnt<SPI_CODE_NUM ) begin spi_data_i<=rom[reg_cnt]; spi_start<=1'b1; state<=SEND_CMD1; end else begin state<=SEND_END; spi_init_done<=1'b1; reg_cnt<=11'd0; spi_start<=1'b0; end end else begin state<=state; reg_cnt<=reg_cnt; spi_start<=1'B0; end end SEND_CMD1 :begin if(spi_busy==1'b1) begin reg_cnt<=reg_cnt+1'd1; spi_start<=1'b0; state<=SEND_CMD; end else begin state<=SEND_CMD; end end SEND_DELAY :begin if(delay_cnt<DELAY_120MS) begin delay_cnt<=delay_cnt+1'd1; reg_cnt<=reg_cnt; end else begin state<=SEND_CMD; delay_cnt<=10'd0; reg_cnt<=reg_cnt; end spi_start<=1'b0; end SEND_END :begin spi_data_i<=32'B0; spi_start<=1'B0; reg_cnt<=11'd0; spi_init_done<=1'b1; state<=SEND_END; end default : begin spi_data_i<=32'B0; spi_start<=1'B0; reg_cnt<=8'd0; state<=SEND_END; end endcase end end lcd_spi_m #( .SPI_IN_WIDTH (SPI_IN_WIDTH),//spi 输入位数 .SPI_OUT_WIDTH (SPI_OUT_WIDTH),//SPI 输出位数 .SPI_CPOL (SPI_CPOL),//空闲状态SCL电平 0:SCL=0 1:SCL=1 .SPI_CPHA (SPI_CPHA), //SPI数据在哪个SCL边沿有效 0:数据在SCL第一个边沿有效,1:数据在SCL第二个边沿有效, .CS_F_DELAY (6'd1), .CS_B_DELAY (6'd1) ) lcd_spi_m_inst ( .rst_n_i (rst_i) , //复位输入,低电平复位 .spi_x2clk_i(clk_x2_i) ,//SPI系统时钟 为SCL输出时钟的两倍 .spi_data_i (spi_data_i),//输入32位要从MOSI发送出去的数据 .spi_start (spi_start) , //单次发送开始,把数据送到spi_data_i 并把spi_start维技一个周期的高电平 .spi_miso_i (spi_miso) ,//主机接收从机输出引脚 .spi_done (spi_done) ,//SPI完成一次传输并从spi_data_o输出读到的数据 .spi_busy (spi_busy) ,//SPI忙信号输出,在忙状态时不接收外部数据,高表示忙 .spi_cs_o (spi_cs_o) ,//SPI片选信号输出低有效 .spi_scl_o (spi_scl_o) ,//SPI 时钟信号输出,请结合CPOL CPHA分析有效性 .spi_mosi_o (spi_mosi),//SPI主机输出从机输入接口 .spi_data_o (spi_data_o) //从从机读到的数据在SPI_DONE为高时为有效数据 ); endmodule
1 //SPI主程序 2 //功能:完成32位以内SPI接口的数据双向通信 3 module lcd_spi_m 4 #( 5 parameter [5:0]SPI_IN_WIDTH =6'd9,//spi 输入位数 6 parameter [5:0]SPI_OUT_WIDTH =6'd9,//SPI 输出位数 7 parameter [0:0]SPI_CPOL=1'b0,//空闲状态SCL电平 0:SCL=0 1:SCL=1 8 parameter [0:0]SPI_CPHA=1'b1, //SPI数据在哪个SCL边沿有效 0:数据在SCL第一个边沿有效,1:数据在SCL第二个边沿有效, 9 parameter [5:0]CS_F_DELAY=6'D1, //CS前面延时 10 parameter [5:0]CS_B_DELAY=6'D1 //CS后面延时 11 12 13 ) 14 ( 15 input wire [0:0] rst_n_i, //复位输入,低电平复位 16 input wire [0:0] spi_x2clk_i,//SPI系统时钟 为SCL输出时钟的两??? 17 input wire [31:0] spi_data_i,//输入32位要从MOSI发送出去的数据 18 input wire [0:0] spi_start, //单次发送开始,把数据送到spi_data_i 并把spi_start维技一个周期的高电??? 19 input wire [0:0] spi_miso_i,//主机接收从机输出引脚 20 output reg [0:0] spi_done,//SPI完成一次传输并从spi_data_o输出读到的数??? 21 output reg [0:0] spi_busy,//SPI忙信号输出,在忙状态时不接收外部数据,高表示忙 22 output reg [0:0] spi_cs_o,//SPI片选信号输出低有效 23 output reg [0:0] spi_scl_o,//SPI 时钟信号输出,请结合CPOL CPHA分析有效??? 24 output reg [0:0] spi_mosi_o,//SPI主机输出从机输入接口 25 output reg [31:0] spi_data_o//从从机读到的数据在SPI_DONE为高时为有效数据 26 27 28 ); 29 30 31 localparam [7:0] CS_F_CNT=CS_F_DELAY+CS_F_DELAY; //CS前面延时 32 localparam [7:0] CS_B_CNT=CS_B_DELAY+CS_B_DELAY; //CS后面延时 33 localparam [7:0] WIDTH_MAX=(SPI_IN_WIDTH>SPI_OUT_WIDTH)? (SPI_IN_WIDTH*2'D2+CS_F_CNT+CS_B_CNT) :(SPI_OUT_WIDTH*2'D2+CS_F_CNT+CS_B_CNT); 34 35 36 37 38 39 //产生spi_x2clk_i时钟计数 40 reg [6:0] clk_cnt; 41 always @(posedge spi_x2clk_i or negedge rst_n_i) 42 begin 43 if(rst_n_i==1'b0) 44 clk_cnt<=6'd0; 45 else if(spi_busy==1'b1 ) 46 begin 47 if(clk_cnt<WIDTH_MAX) 48 clk_cnt<=clk_cnt+1'd1; 49 else 50 clk_cnt<=7'd0; 51 end 52 else 53 clk_cnt<=7'd0; 54 end 55 56 //输出SPI_CS信号 57 always @(posedge spi_x2clk_i or negedge rst_n_i) 58 begin 59 if(rst_n_i==1'b0) 60 spi_cs_o<=1'b1; 61 else if(spi_start==1'b1 && spi_busy==1'b0) 62 spi_cs_o<=1'b0; 63 else if(clk_cnt<(WIDTH_MAX-1'd1)) 64 spi_cs_o<=spi_cs_o; 65 else 66 spi_cs_o<=1'b1; 67 end 68 69 70 71 72 //输出spi_scl信号 73 always @(posedge spi_x2clk_i or negedge rst_n_i) 74 begin 75 if(rst_n_i==1'b0) 76 begin 77 if(SPI_CPOL==1'b0) 78 begin spi_scl_o<=1'b0; end 79 else 80 begin spi_scl_o<=1'b1; end 81 spi_done<=1'b0; 82 end 83 else if(clk_cnt>=CS_F_CNT-1'd1 && clk_cnt<(WIDTH_MAX-CS_B_CNT-1'd1)) 84 begin 85 spi_scl_o<=~spi_scl_o; 86 end 87 else if(clk_cnt>=WIDTH_MAX-1'd1) 88 begin 89 spi_done<=1'b1; 90 91 end 92 else 93 begin 94 if(SPI_CPOL==1'b0) 95 spi_scl_o<=1'b0; 96 else 97 spi_scl_o<=1'b1; 98 spi_done<=1'b0; 99 100 end 101 end 102 //在spi_start???时捕获数??? 103 reg [31:0] temp_data_i; 104 //输出SPI_MOSI信号 105 always @(posedge spi_x2clk_i or negedge rst_n_i) 106 begin 107 if(rst_n_i==1'b0) 108 begin 109 spi_mosi_o<=1'b0; 110 spi_busy<=1'b0; 111 temp_data_i<=32'b0; 112 end 113 else if(spi_start==1'b1 && spi_busy==1'b0) //在spi_start???时捕获数??? 114 begin 115 temp_data_i<=spi_data_i; 116 spi_busy<=1'b1; 117 end 118 else if(spi_done==1'b1) 119 spi_busy<=1'b0; 120 else if(SPI_CPHA==1'b0) 121 begin 122 if((clk_cnt>=(CS_F_CNT-8'd2)) && (clk_cnt<(WIDTH_MAX-CS_B_CNT-CS_F_CNT)) && (clk_cnt%2==0) ) 123 begin 124 spi_mosi_o<=temp_data_i[SPI_OUT_WIDTH-1'd1]; 125 temp_data_i<={temp_data_i[(SPI_OUT_WIDTH-2'd2):0],temp_data_i[SPI_OUT_WIDTH-1'd1]}; 126 end 127 else 128 begin 129 spi_mosi_o<=spi_mosi_o; 130 end 131 132 end 133 else if(SPI_CPHA==1'b1) 134 begin 135 if((clk_cnt>=(CS_F_CNT-8'd1)) && clk_cnt<(WIDTH_MAX-CS_B_CNT-CS_F_CNT) && (clk_cnt%2==1) ) 136 begin 137 spi_mosi_o<=temp_data_i[SPI_OUT_WIDTH-1'd1]; 138 temp_data_i<={temp_data_i[SPI_OUT_WIDTH-2'd2:0],temp_data_i[SPI_OUT_WIDTH-1'd0]}; 139 end 140 else 141 begin 142 spi_mosi_o<=spi_mosi_o; 143 end 144 end 145 else 146 begin 147 spi_mosi_o<=1'b1; 148 end 149 150 end 151 152 //接收SPI_MISO信号 153 always @(posedge spi_x2clk_i or negedge rst_n_i) 154 begin 155 if(rst_n_i==1'b0) 156 spi_data_o<=32'b0; 157 else if(SPI_CPHA==1'b0) 158 begin 159 if((clk_cnt>=CS_F_CNT-8'D1) && (clk_cnt<(WIDTH_MAX-CS_B_CNT)) && (clk_cnt%2==1) ) 160 begin 161 spi_data_o<={spi_data_o[30:0],spi_miso_i}; 162 end 163 else 164 begin 165 spi_data_o<=spi_data_o; 166 end 167 end 168 else if(SPI_CPHA==1'b1) 169 begin 170 if((clk_cnt>=CS_F_CNT) && (clk_cnt<(WIDTH_MAX-CS_B_CNT)) && (clk_cnt%2==1) ) 171 begin 172 spi_data_o<={spi_data_o[30:0],spi_miso_i}; 173 end 174 else 175 begin 176 spi_data_o<=spi_data_o; 177 end 178 end 179 else 180 begin 181 spi_data_o<=spi_data_o; 182 end 183 184 end 185 186 187 endmodule
1 //testbench 2 `timescale 1ns/1ns 3 module lcd_spi_m_tb(); 4 reg rst_n_i; 5 reg spi_x2clk_i; 6 reg [31:0] spi_data_i; 7 reg spi_start; 8 reg spi_miso_i; 9 wire [0:0] spi_done; 10 wire [0:0] spi_busy; 11 wire [0:0] spi_cs_o; 12 wire [0:0] spi_scl_o; 13 wire [0:0] spi_mosi_o; 14 wire [31:0] spi_data_o; 15 16 17 18 always #50 spi_x2clk_i<=~spi_x2clk_i; 19 initial begin 20 rst_n_i=0; 21 spi_x2clk_i=0; 22 #200; 23 spi_start=0; 24 rst_n_i=1; 25 @(posedge spi_x2clk_i) 26 spi_data_i=16'h121; 27 spi_start=1; 28 @(posedge spi_x2clk_i) 29 spi_start=0; 30 @(posedge spi_done) 31 #200; 32 @(posedge spi_x2clk_i) 33 spi_data_i=16'h0003; 34 spi_start=1; 35 @(posedge spi_x2clk_i) 36 spi_start=0; 37 @(posedge spi_done) 38 #200; 39 @(posedge spi_x2clk_i) 40 spi_data_i=16'hF227; 41 spi_start=1; 42 @(posedge spi_x2clk_i) 43 spi_start=0; 44 @(posedge spi_done) 45 #200; 46 @(posedge spi_x2clk_i) 47 spi_data_i=16'hA31B; 48 spi_start=1; 49 @(posedge spi_x2clk_i) 50 spi_start=0; 51 @(posedge spi_done) 52 $stop; 53 end 54 55 always @(negedge spi_scl_o or negedge rst_n_i )//要根据CPOL CPHA配置 spi_scl_o采样极性 56 begin 57 if(rst_n_i==1'b0) 58 spi_miso_i<=1'b0; 59 else if (spi_cs_o==1'b0 ) 60 spi_miso_i<=~spi_miso_i; 61 else 62 spi_miso_i<=1'b0; 63 end 64 65 lcd_spi_m 66 #( 67 .SPI_IN_WIDTH(6'd16),//spi 输入位数 68 .SPI_OUT_WIDTH (6'd16),//SPI 输出位数 69 .SPI_CPOL(1'b1), 70 .SPI_CPHA(1'b1), 71 .CS_F_DELAY(6'd1), 72 .CS_B_DELAY(6'd1) 73 74 75 76 ) 77 lcd_spi_m_inst 78 ( 79 .rst_n_i (rst_n_i ) , 80 .spi_x2clk_i (spi_x2clk_i ) , 81 .spi_data_i (spi_data_i ) , 82 .spi_start (spi_start ) , 83 .spi_miso_i (spi_miso_i ) , 84 .spi_done (spi_done ) , 85 .spi_busy (spi_busy ) , 86 .spi_cs_o (spi_cs_o ) , 87 .spi_scl_o (spi_scl_o ) , 88 .spi_mosi_o (spi_mosi_o ) , 89 .spi_data_o (spi_data_o ) 90 91 ); 92 93 endmodule