Verilog自学(7) 用SPI控制GPIO输出状态
最新更新时间:2022-04-17
阅读数:
//如果是写入8bit的话,最高位就是8'h80
//如果是写入16bit的话,最高位是16'h8000
//多少bit 直接改spi_bit 即可
`define spi_highest_bits 16'h8000
`define spi_bit 16
module SPI(
input [`spi_bit-1:0]Data,
//定义SPI 8位写入数据
input sysclk,
//定义系统输入时钟
input miso,
// 定义SPI 输入引脚
output reg mosi,
output reg cs,
output reg sclk
// 定义SPI 输出引脚
);
reg [1:0]SateConter = 0 ;
reg [7:0]DataSendCounter;
// 定义计数状态
initial begin
mosi<=1'b0;
cs<=1'b1;
sclk<=1'b1;
DataSendCounter<=4'b0000;
end
//初始化GPIO
always@(posedge sysclk) begin
SateConter=SateConter+1;
//当到达计数值以后拉高Cs
cs=(DataSendCounter<(`spi_bit+1)?1'b0:1'b1);
if(SateConter==1)
sclk<=1'b0;//第一个计数状态拉低SCLK
else if(SateConter==2)
//第二个状态写入数据
begin
if (((Data<<DataSendCounter)&`spi_highest_bits)==`spi_highest_bits) begin
mosi<=1'b1;
end
else begin
mosi<=1'b0;
end
//每次写入后计数+1
DataSendCounter<=DataSendCounter+1'b1;
end
else if(SateConter>2)
//第三个计数状态拉高SCLK 计数值清0
begin
sclk<=1'b1;
SateConter=0;
end
end
endmodule
因为是各种模块组合起来,所以和之前的会有重合
//使用define 重定义memory 变量
//看起来更加直观 `符号 在键盘~的位置 ESC下方
//定义最大输入字节
//使用时候修改 spi_bit 和reg_restValue 定义就行
`define spi_bit 16
`define reg_restValue 16'h0000
module spi_reg(
//定义外部SCLK输入
input sclk,
//定义MISO输入
input miso,
//定义器件SPI使能
input nss,
//定义时钟
input clk,
//定义复位
input rst,
//定义 寄存器输出
output reg [`spi_bit-1:0] reg_data
);
//定义缓存器
reg [`spi_bit-1:0] reg_data_temp;
//定义SPI接收计数器
reg [3:0] spi_rx_counter;
initial begin
reg_data<=`reg_restValue;
reg_data_temp<=`reg_restValue;
spi_rx_counter<=4'b0000;
end
always @(posedge sclk or nss) begin
//如果nss为高 将缓存器最终输出给reg_data
if (nss) begin
reg_data<=reg_data_temp;
//清零接收计数器
spi_rx_counter<=4'b0000;
end
else begin
//reg_data_temp每次都移位一格
reg_data_temp<=reg_data_temp<<1'b1;
reg_data_temp[0]<=miso;
//计数发送到第几个字节
spi_rx_counter<=spi_rx_counter+1'b1;
//当收完地址位之后 将数据更新 否则等于其自身
reg_data[`spi_bit-1:`spi_bit-8]<=(spi_rx_counter==9?reg_data_temp[7:0]:reg_data[`spi_bit-1:`spi_bit-8]);
end
end
endmodule
// 程序功能:
// 通过SPI 写入寄存器地址 改变寄存器输出
//定义GPIO寄存器位置
`define gpioa_addr 8'h01
`define gpiob_addr 8'h02
`define gpioc_addr 8'h03
`define gpioOn_addr 8'h08
module prj(
//定义外部SCLK输入
input sclk,
//定义MISO输入
input miso,
//定义器件SPI使能
input nss,
//定义时钟
input clk,
//定义复位
input rst,
//定义 寄存器输出
output reg [7:0] gpioa,
output reg [7:0] gpiob,
output reg [7:0] gpioc
);
//定义临时寄存器
wire[15:0] reg_temp;
//当reg_temp发生改变 查询地址
always @(reg_temp) begin
//判断reg_temp[15:8]对应那个地址
case (reg_temp[15:8])
`gpioa_addr :begin
//将地址对应的寄存器赋值
gpioa<=reg_temp[7:0];
end
`gpiob_addr :begin
gpioa<=reg_temp[7:0];
end
`gpioc_addr :begin
gpioa<=reg_temp[7:0];
end
`gpioOn_addr :begin
gpioa<=8'hAA;
end
default :begin
gpioa<=8'h00;
gpiob<=8'h00;
gpioc<=8'h00;
end
endcase
end
//调用spi_reg 来写入数据
spi_reg U13(
.sclk(sclk),
.miso(miso),
.nss(nss),
.clk(clk),
.rst(rst),
.reg_data(reg_temp)
);
endmodule
1ns/1ps
module spi_reg_Tb;
reg CLK;
reg RST;
spi
wire SCLK;
wire MISO;
wire NSS;
wire MOSI;
wire [7:0]GPIOA;
wire [7:0]GPIOB;
wire [7:0]GPIOC;
reg [1:0]SclkCounter;
always #20 CLK=~CLK;
initial begin
CLK=1'b0;
1'B1; =
SCLK<=1'b0;
MISO<=1'B0;
NSS<=1'B1;
end
SPI U2(
.Data(16'h08aa),
8位写入数据
.sysclk(CLK),
//定义系统输入时钟
.miso(MISO),
定义SPI 输入引脚
.mosi(MOSI),
.cs(NSS),
.sclk(SCLK)
定义SPI 输出引脚
);
prj GPIO_OUT
(
.sclk(SCLK),
.miso(MOSI),
.nss(NSS),
.rst(RST),
.gpioa(GPIOA),
.gpiob(GPIOB),
.gpioc(GPIOC)
);
endmodule
VS Code 纯净版
简单验证如下????