首页 > 编程语言 >14 SPI MASET发送程序设计

14 SPI MASET发送程序设计

时间:2023-12-29 11:46:13浏览次数:30  
标签:发送 MASET 14 tx clk spi SPI 时钟

软件版本:VIVADO2021.1

操作系统:WIN10 64bit

硬件平台:适用XILINX A7/K7/Z7/ZU/KU系列FPGA

登录米联客(MILIANKE)FPGA社区-www.uisrc.com观看免费视频课程、在线答疑解惑!

1 概述

SPI的发送器驱动程序主要围绕SPI_MOSI以及SPI_SCLK来设计。通过前面的SPI协议学习,我们这里设计的SPI驱动程序需要支持CPHA=0 CPOL=0; CPHA=1 CPOL=0; CPHA=0 CPOL=1; CPHA=1 CPOL=1四种情况。CPHA用于控制SPI接收器的采样时钟位置,CPOL用于设置SPI_SCLK的初始电平是高电平还是低电平。

SPI发送器的SPI_MOSI的设计:

可以通过设置一个stroble信号用于控制数据是在哪一个SPI_SCLK更新。

当CPHA=0的时候,当stroble有效,在SPI_SCK的第2个时钟沿更新数据;当CPHA=1的时候,当stroble有效,在SPI_SCK的第1个时钟沿更新数据。

SPI发送器的SPI_SCLK的设计:

SPI_SCLK通过CPOL设置,可以设置一个内部参考时钟,这个时钟默认的时钟极性为CPOL=0的情况,如果设置CPOL=1,只需要对该时钟取反。

SPI发送器的分频器设计:

SPI的SPI_SCLK可以通过分频系数设置分频参数,这样可以调整SPI发送驱动的数据发送速度。

2 SPI Master发送驱动器设计

SPI Master驱动程序包含基本的时钟分频器、数据控制器、并串移位模块、CPOL控制、CPHA控制。

为了方便SPI Master主控制器可以方便使用该驱动程序,设计了spi_tx_req以及spi_busy用于信号的握手。

-设置spi_tx_req为1,通知SPI发送驱动程序种的数据控制器,请求数据发送。

-SPI发送驱动的数据控制器保存spi_tx_data数据,并且设置O_spi_busy代表开始发送数据,spi总线忙;

以下给出握手时序。在以后米联客的代码种,接口之间的握手也会采用类似信号和时序。

3 SPI Master发送控制器设计

发送控制器设计核心部分在于状态机的设计。我们米联客设计的驱动接口,一般将接口驱动程序和驱动控制程序分开编写,这样的好处可以让代码层次更加清晰,实用维护更加方便。

在SPI Master发送控制器的设计种,核心状态机部分首先设置spi_tx_req=1启动一次SPI传输,之后等待spi_busy为高电平,之后设置spi_tx_req=0,并且等待spi_busy变为低电平。之后可以进行下一次的数据传输。

4 SPI Master发送驱动程序设计

我们先给出spi-master的驱动程序源码,然后对源码的设计做一些分析。

/*******************************SPI Master发送驱动器*********************

--以下是米联客设计的SPI Master发送驱动器

--1.代码简洁,占用极少逻辑资源,代码结构清晰,逻辑设计严谨

--2.I_spi_tx_req,用户程序通过设置I_spi_tx_req为高,请求SPI Master发送驱动器,发送数据,SPI Master发送驱动器把I_spi_tx_data数据通过 O_spi_mosi总线发送出去

--3.通过CPOL以及CPHA自由的控制SPI Master发送驱动器的时钟极性一级时钟相位,并且在时序上做出相应的匹配。

--4.O_spi_busy,表示当前*SPI Master发送总线整忙,这时用户程序需要等待非忙的时候,请求发送数据。*********************************************************************/

`timescale 1ns / 1ps                                            //定义仿真时间刻度/精度

 

module ui_mspi_tx#

(

parameter CLK_DIV = 100,

parameter CPOL = 1'b0,                                            //时钟极性参数设置

parameter CPHA = 1'b1                                             //时钟相位参数设置

)

(

input       I_clk,                                                 //系统时钟输入

input       I_rstn,                                                //系统复位输入

output      O_spi_mosi,                                            //发送SPI数据

output      O_spi_sclk,                                            //发送SPI时钟

input       I_spi_tx_req,                                         //发送数据请求

input [7:0] I_spi_tx_data,                                        //发送数据  

output      O_spi_busy                                            //发送状态忙,代表正在发送数据

);

 

localparam [9:0] SPI_DIV     = CLK_DIV;                         //第二时钟边沿计数器

localparam [9:0] SPI_DIV1    = SPI_DIV/2;                       //第一时钟边沿计数器

 

reg [9:0]   clk_div  = 10'd0;  

reg         spi_en   = 1'b0;

reg         spi_clk  = 1'b0;

reg [3:0]   tx_cnt   = 4'd0;

reg [7:0]   spi_tx_data_r=8'd0;

wire        clk_end;

wire        clk_en1;                                           //第一内部时钟边沿使能

wire        clk_en2;                                           //第二内部时钟边沿使能

reg         spi_strobe_en;

wire        spi_strobe;                   //CPHA=0数据在第一时钟边沿上传输,CPHA=1数据在第二时钟边沿上发送

 

assign      clk_en1     = (clk_div == SPI_DIV1);//第一内部时钟边沿使能

assign      clk_en2     = (clk_div == SPI_DIV);//第二内部时钟边沿使能

assign      clk_end     = (clk_div == SPI_DIV1)&&(tx_cnt==4'd8);

//计数器发送第一个内部时钟0到7次,当计数达到最后8时,不发送时钟//当CPHA=0时,数据的第一个SCLK转换边缘被采样,因此数据更新在第二个转换边缘上

//当CPHA=1时,数据的第二个SCLK转换边缘被采样,因此数据更新在第一个转换边缘上

assign      spi_strobe  = CPHA ? clk_en1&spi_strobe_en : clk_en2&spi_strobe_en ;

assign      O_spi_sclk  = (CPOL == 1'b1) ? ~spi_clk : spi_clk;//设置SPI时钟的初始电平

assign      O_spi_mosi  = spi_tx_data_r[7];

assign      O_spi_busy  = spi_en;

 

always@(posedge I_clk)begin                                   //时钟分频器

    if(spi_en == 1'b0)

        clk_div <= 10'd0;

    else if(clk_div < SPI_DIV)

        clk_div <= clk_div + 1'b1;

    else

        clk_div <= 0;

end

always@(posedge I_clk)begin                                   //生成spi内部时钟

        if(spi_en == 1'b0)

            spi_clk <= 1'b0;

    else if(clk_en2)

            spi_clk <= 1'b0;                                   //第二时钟边沿

        else if(clk_en1&&(tx_cnt<4'd8))                       //第一时钟边沿

            spi_clk <= 1'b1;

    else

        spi_clk <= spi_clk;

end

always@(posedge I_clk)begin  

          if(I_rstn == 1'b0)

             spi_strobe_en <= 1'b0;

          else if(tx_cnt < 4'd8)begin

               if(clk_en1) spi_strobe_en <= 1'b1;    

          end

          else

               spi_strobe_en <= 1'b0;        

end

always@(posedge I_clk)begin  

          if((I_rstn == 1'b0)||(spi_en == 1'b0))

             tx_cnt <= 4'd0;

          else if(clk_en1)

             tx_cnt <= tx_cnt + 1'b1;      

end

always@(posedge I_clk)begin                                           //spi发送模块

    if(I_rstn == 1'b0 || clk_end)begin

        spi_en <= 1'b0;

        spi_tx_data_r <= 8'h00;

    end

    else if(I_spi_tx_req&&(spi_en == 1'b0)) begin                    //启用传输

            spi_en <= 1'b1;

            spi_tx_data_r <= I_spi_tx_data;

    end

    else if(spi_en)begin

         spi_tx_data_r[7:0] <= (spi_strobe) ? {spi_tx_data_r[6:0],1'b1} : spi_tx_data_r;

    end

   

end  

endmodule

我们看下驱动器的驱动程序部分核心模块设计分析

4.1 分频器设计

系统时钟一般运行于较高速度,而SPI的SCLK需要基于系统时钟分频后产生,所以首先需要设计一个分频器,用于对SCLK分频

localparam [9:0] SPI_DIV     = CLK_DIV; //第二时钟边沿计数器

localparam [9:0] SPI_DIV1    = SPI_DIV/2; //第一时钟边沿计数器

 

always@(posedge I_clk)begin //时钟分频器

    if(spi_en == 1'b0)

        clk_div <= 10'd0;

    else if(clk_div < SPI_DIV)

        clk_div <= clk_div + 1'b1;

    else

        clk_div <= 0;

end

4.2 SCLK时钟设计

SCLK可以支持CPOL=0(空闲状态输出低电平)和CPOL=1(空闲状态输出高电平)

首先我们设计内部SCLK,这个SCLK的周期和我们实际输出的SCLK周期一致,当我们设置CPOL=0或者CPOL=1的时候我们只要对时钟采取取反或者不取反操作。

内部的SCLK通过clk_en1和clk_en2的触发时刻来实现电平的输出和切换。

assign      clk_en1     = (clk_div == SPI_DIV1); //第一内部时钟边沿使能

assign      clk_en2     = (clk_div == SPI_DIV); //第二内部时钟边沿使能

assign      o_spi_sclk  = (CPOL == 1'b1) ? ~spi_clk : spi_clk;//设置SPI时钟的初始电平

 

always@(posedge I_clk)begin //生成spi内部时钟

        if(spi_en == 1'b0)

            spi_clk <= 1'b0;

    else if(clk_en2)

            spi_clk <= 1'b0; //第二时钟边沿

        else if(clk_en1&&(tx_cnt<4'd8)) //第一时钟边沿

            spi_clk <= 1'b1;

    else

        spi_clk <= spi_clk;

end

4.3 数据控制器设计

数据控制器是SPI-Master驱动器设计中最关键的部分,数据控制器包括驱动控制接口,也包含了SPI数据部分的并串移位模块。当SPI的控制器部分发送了spi_tx_req为高电平后,下一个系统时钟周期数据会被寄存到spi_tx_data_r并且设置spi_en为高电平,之后时钟分频模块、spi_clk模块等开始工作。同时设置spi_busy信号为高电平,通知SPI控制器SPI驱动器已经处于工作状态。

移位数据的更新通过spi_stroble控制,spi_stroble根据CPHA的设置决定是clk_en1更新数据还是clk_en2更新数据。clk_en1和SCLK的第1个跳变沿同步,clk_en2和SCLK的第2个跳变沿同步。

 

//当CPHA=0时,数据的第一个SCLK转换边缘被采样,因此数据更新在第二个转换边缘上

//当CPHA=1时,数据的第二个SCLK转换边缘被采样,因此数据更新在第一个转换边缘上

assign      spi_strobe  = CPHA ? clk_en1&spi_strobe_en : clk_en2&spi_strobe_en ;

 

assign      o_spi_mosi  = spi_tx_data_r[7];

assign      o_spi_busy  = spi_en;

……

……

//spi发送模块

always@(posedge I_clk)begin

    if(I_rstn == 1'b0 || clk_end)begin

        spi_en <= 1'b0;

        spi_tx_data_r <= 8'h00;

    end

    else if(I_spi_tx_req&&(spi_en == 1'b0)) begin //启用传输

            spi_en <= 1'b1;

            spi_tx_data_r <= I_spi_tx_data; //将信号进行缓存

    end

    else if(spi_en)begin

         spi_tx_data_r[7:0] <= (spi_strobe) ? {spi_tx_data_r[6:0],1'b1} : spi_tx_data_r;

    end

   

end  

5 SPI Master发送控制器设计

SPI Master的发送控制器根据不同的实际应用需要一次或者多次把一个或者多个数据发送出去,在本实验中,演示了发送连续的加计数器数据的方法。

/************************ SPI Master发送控制器*********************

--以下是米联客设计的SPI Master发送控制器

--1.spi_tx_req,用户程序通过设置spi_tx_req为高,请求SPI Master发送驱动器,发送数据,SPI Master发送驱动器把spi_tx_data测试数据通过O_spi_mosi总线发送出去

--2.O_spi_busy,表示当前SPI发送总线整忙,这时用户程序需要等待非忙的时候,请求发送数据。

--3.演示发送spi_tx_data测试数据,通过ila观察发送数据时许

*********************************************************************/

`timescale 1ns / 1ps

 

module spi_master_tx#

(

parameter CLK_DIV = 100        

)

(

input  I_sysclk,                                         //输入时钟

input  I_rstn,                                         //系统复位

output O_spi_sclk,                                     //SPI发送时钟

output O_spi_mosi                                      //SPI发送数据

);

 

wire        spi_busy;                                     //SPI忙信号

reg         spi_tx_req;                                   //SPI发送req信号,有发送需求时拉高

reg [7:0]   spi_tx_data;                                  //待发送数据存储

reg [1:0]   M_S;                                           //状态机

 

//spi send state machine

always @(posedge I_sysclk) begin

    if(!I_rstn) begin                                      //拉低复位

        spi_tx_req  <= 1'b0;

        spi_tx_data <= 8'd0;

        M_S <= 2'd0;

    end

    else begin

        case(M_S)

        0:if(!spi_busy)begin                            //总线不忙启动传输

           spi_tx_req  <= 1'b1;                         //req信号拉高,开始传输

           spi_tx_data <= spi_tx_data + 1'b1;          //测试数据

           M_S <= 2'd1;

        end

        1:if(spi_busy)begin                             //如果spi总线忙,清除spi_tx_req

           spi_tx_req  <= 1'b0;

           M_S <= 2'd0;

        end

        default:M_S <= 2'd0;

        endcase

    end

end  

 

//例化SPI Master发送驱动器

ui_mspi_tx#

(

.CLK_DIV(CLK_DIV),

.CPOL(1'b1),                                  //CPOL参数设置,可调整

.CPHA(1'b1)                                   //CPHA参数设置,可调整

)

ui_mspi_tx_inst(

.I_clk(I_sysclk),                              //系统时钟输入

.I_rstn(I_rstn),                            //系统复位输入

.O_spi_mosi(O_spi_mosi),                   //SPI发送数据串行总线

.O_spi_sclk(O_spi_sclk),                   //SPI发送时钟总线

.I_spi_tx_req(spi_tx_req),                  //SPI发送(写)数据请求

.I_spi_tx_data(spi_tx_data),                //SPI发送(写)数据

.O_spi_busy(spi_busy)                        //SPI发送驱动器忙

 );

endmodule

M_S状态机只有2个状态,M_S==0状态等待SPI-Master驱动器非忙的情况下,发送数据发送请求信号,并且在M_S==1状态等待数据确认进入忙状态后,再次回到状态0等待空闲,如果总线空闲发送下一个测试数据。

6 RTL仿真

6.1 仿真激励文件

本实验以仿真的方式演示,仿真激励信号提供一个系统时钟即可

`timescale 1ns / 1ps

 

module master_spi_tb;

localparam      SYS_TIME   =  'd20;//时钟周期,以ns为单位

reg             I_sysclk;               //系统时钟

reg I_rstn;  

wire O_spi_sclk;

wire O_spi_mosi;

 

spi_master_tx#

(

.CLK_DIV(100)                                    //设置时钟参数,可以减少仿真时间

)

spi_master_tx_inst(

.I_clk(I_sysclk),

.I_rstn(I_rstn),

.O_spi_sclk(O_spi_sclk),

.O_spi_mosi(O_spi_mosi)

);

 

initial begin

    I_sysclk  = 1'b0;                              //设置时钟基础值

    I_rstn = 1'b0;                              //低电平复位

    #100;

    I_rstn = 1'b1;                             //复位释放

 

 #2000000 $finish;

end

   

always #(SYS_TIME/2) I_sysclk = ~I_sysclk;     //产生主时钟

 

endmodule

下面展示不同CPHA、CPOL参数显示的不同的仿真结果,结果可能从图片中观察的不是很直观,各位可以自行前往工程仿真一遍。

 

 

6.2 SPI发送驱动代码仿真CPHA=0 CPOL=0

如下图所示,当CPHA=0 CPOL=0,代表SPI的SCLK默认是低电平,SPI接收器在SCLK第1个时钟沿采样。SPI发送驱动器数据在SCLK的第2个时钟沿更新,确保SPI下一个SCLK的第1个时钟沿数据有足够的建立和保持时间。下图以发送8'h02为例。

6.3 SPI发送驱动代码仿真CPHA=1 CPOL=0

如下图所示,当CPHA=1 CPOL=0,代表SPI的SCLK默认是低电平,SPI接收器在SCLK第2个时钟沿采样。SPI发送驱动器数据在下一个SCLK的第1个时钟沿更新,确保SPI下一个SCLK的第2个时钟沿数据有足够的建立和保持时间。下图以发送8'h02为例。

6.4 SPI发送驱动代码仿真CPHA=

0

CPOL=

1

和CPHA=0 CPOL=0这种设置相比,时钟SCLK取反

6.5 SPI发送驱动代码仿真CPHA=1 CPOL=1

和CPHA=1 CPOL=0这种设置相比,时钟SCLK取反

 

标签:发送,MASET,14,tx,clk,spi,SPI,时钟
From: https://www.cnblogs.com/milianke/p/17934505.html

相关文章

  • 13 SPI通信协议原理
    软件版本:VIVADO2021.1操作系统:WIN1064bit硬件平台:适用XILINXA7/K7/Z7/ZU/KU系列FPGA登录米联客(MILIANKE)FPGA社区-www.uisrc.com观看免费视频课程、在线答疑解惑!1概述SPI是一种串行总线接口,也是各类嵌入式设备以及FPGA应用开发中常用的一种串行通信接口。SPI的接口速度......
  • H5前端特殊艺术字体文件太大,可通过font-spider压缩
    原理:1.爬行本地html文档,分析所有css语句2.记录@font-face语句声明的字体,并且记录使用该字体的css选择器3.通过css选择器的规则查找当前html文档的节点,记录节点上的文本4.找到字体文件并删除没被使用的字符5.编码成跨平台使用的字体格式简而言之:就是爬出你项目中......
  • 社招面试题:说一说SPI是什么,有哪些使用场景?
    大家好,我是小米!今天在这里和大家分享一个在技术面试中常被问到的话题——SPI(ServiceProviderInterface),这是一个令人着迷的技术领域,也是很多Java开发者必须要熟悉的概念。不废话,让我们一起来揭开SPI的神秘面纱,看看它在实际开发中有哪些精彩的应用场景吧!SPI是什么?首先,我们来解释一......
  • macOS Sonoma 14.2.1 (23C71) 正式版发布,ISO、IPSW、PKG 下载 (安全更新)
    macOSSonoma14.2.1(23C71)正式版发布,ISO、IPSW、PKG下载(安全更新)本站下载的macOS软件包,既可以拖拽到Applications(应用程序)下直接安装,也可以制作启动U盘安装,或者在虚拟机中启动安装。另外也支持在Windows和Linux中创建可引导介质。作者主页:sysin.org更新摘要:macOS......
  • macOS Sonoma 14.2.1 (23C71) 正式版 Boot ISO 原版可引导镜像下载 (安全更新)
    macOSSonoma14.2.1(23C71)正式版BootISO原版可引导镜像下载(安全更新)本站下载的macOS软件包,既可以拖拽到Applications(应用程序)下直接安装,也可以制作启动U盘安装,或者在虚拟机中启动安装。另外也支持在Windows和Linux中创建可引导介质。作者主页:sysin.org更新摘要:m......
  • MacOS Sonoma14.2.1系统SSH免密登录
    摘要:MacOS下免密登录的一些注意事项。系统环境操作系统:macOSSonoma14.2.1SSH免密登录ssh免密登录的原理是在本机生成本机的ssh公钥和私钥,将公钥上传至待连接的主机,本机通过私钥进行ssh连接实现免密登录。#ssh密钥生成命令ssh-keygen#ssh上传(复制)命令#ssh-copy-i......
  • 学期:2023-2024-1 学号:20231426 《计算机基础与程序设计》第十四周学习总结
    作业信息这个作业属于哪个课程2022-2023-1-计算机基础与程序设计这个作业要求在哪里2022-2023-1计算机基础与程序设计作业这个作业的目标通过教材内容了解文件,网络作业正文https://www.cnblogs.com/hhaxx/p/17933978.html教材学习内容总结《计算科学概论......
  • 学期2023-2024-1 20231401 《计算机基础与程序设计》第十四周学习总结
    学期2023-2024-120231401《计算机基础与程序设计》第十四周学习总结作业信息这个作业属于哪个课程2023-2024-1-计算机基础与程序设计这个作业要求在哪里2023-2024-1计算机基础与程序设计第十四周作业这个作业的目标《C语言程序设计》第13章并完成云班课测试......
  • 2023-2024-1 20231329 《计算机基础与程序设计》第14周学习总结
    作业信息这个作业属于哪个课程2023-2024-1-计算机基础与程序设计https://edu.cnblogs.com/campus/besti/2023-2024-1-CFAP这个作业要求在哪里2022-2023-1计算机基础与程序设计第14周作业(https://www.cnblogs.com/rocedu/p/9577842.html)这个作业的目标《C语言程......
  • DAC转化——FPGA驱动LTC1446
    目录一、前言二、结合LTC1446芯片手册分析三、Verilog代码与仿真四、总结一、前言最近在学习利用FPGA结合DAC芯片实现数模转换,在实验中选择的LTC1446这款芯片。接下来自己将结合芯片手册进行分析,并编写Verilog代码并进行仿真验证。二、结合LTC1446芯片手册分析首先从上述第......