SPI驱动代码

1.概念

        SPI协议,Serial Peripheral Interface,串行外围设备接口),是一种同步传输的协议;所谓的同步,是指发送方和接收方使用的是同一个时钟;上一章写的uart的接受始终是自己产生的,两方没使用同一个时钟,因此是异步通信;SPI协议使用较为广泛,比较知名的就有flash;以及其他各种设备,据笔者所知,有一款热成像模块就是用的SPI协议;

2.SPI协议

        SPI协议是分不同模式的。SPI通常有四根线,分别是CS(片选),spi_clk(spi时钟),mosi(主输出从输入)以及miso(主输入从输出),这里的M代表FPGA,S代表外围SPI设备例如Flash。

        SPI的模式分为如下几种,介绍两个概念:CPOL和CPHA;CPOL叫做时钟极性,CPHA叫做时钟相位;当CPOL为0时,代表spi_clk在空闲状态为1低电平,CPOL为1时,代表spi_clk在空闲状态为1;CPHA表示当时钟来了,我在第几个边沿开始采集;CPHA为0,第一个边沿采集;CPHA为1,第二个边沿开始采集;这里以模式0举例(CPOL=0,CPHA=0),当处于空闲状态时,spi_clk为低,采集数据的边沿在时钟的第一个边沿,也就是上升沿;

        当主从设备需要通信的时候,先拉底cs信号,发送spi_clk,并且伴随着时钟在mosi的数据线上发送数据;或者在接受数据的时候,产生spi_clk接收miso上的数据;顺便一提,spi协议通常都是高位先发送,因此在接收数据时可以使用左移的操作;

3.SPI驱动模块解析

        输入输出端口定义如下:外部模块输入时钟以及复位;输入输出的SPI一组信号;用户接口有接受miso信号的o_user_data,以及发送数据的o_user_data;用户将需要发送的数据连接到这个端口,即可通过SPI发送数据;用户从o_user_read_data接受的数据就是miso传输过来的数据。

    input                               i_clk               ,
    input                               i_rst               ,

    output                              o_spi_clk           ,
    output                              o_spi_cs            ,
    output                              o_spi_mosi          ,
    input                               i_spi_miso          ,

    input   [P_DATA_WIDTH - 1 :0]       i_user_data         ,
    input                               i_user_valid        ,
    output                              o_user_ready        ,

    output  [P_READ_DATA_WIDTH - 1:0]   o_user_read_data    ,
    output                              o_user_read_valid   

4.部分代码解析

这里定义了一个运行信号,用于标志spi发送或者接收状态;以下是发送数据接受数据的代码段;

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        r_run <= 'd0;
    else if(r_spi_cnt && r_cnt == 7)
        r_run <= 'd0;
    else if(w_user_active)
        r_run <= 'd1;
    else 
        r_run <= r_run;
end

这里定义了计数器用于计数已发送的数据;spi_clk在运行状态下为输入时钟的二分频,为了表明上升沿或者下降沿,做了一个spi_cnt,当spi_cnt == 1 时,表明为spi_clk的下降沿;spi_cnt == 0 时,代表spi_clk的上升沿;以下是发送数据接受数据的代码段;

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        r_cnt <= 'd0;
    else if(r_spi_cnt && r_cnt == 7)
        r_cnt <= 'd0;
    else if(r_spi_cnt)
        r_cnt <= r_cnt + 1;
    else 
        r_cnt <= r_cnt;
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        r_spi_cnt <= 'd0;
    else if(r_run)
        r_spi_cnt <= r_spi_cnt + 1;
    else 
        r_spi_cnt <= 'd0;
end
always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        ro_spi_mosi <= 'd0;
    else if(w_user_active)
        ro_spi_mosi <= i_user_data[P_DATA_WIDTH - 1];
    else if(r_spi_cnt)
        ro_spi_mosi <= r_user_data[P_DATA_WIDTH - 2];
    else 
        ro_spi_mosi <= ro_spi_mosi;
end     

always@(posedge ro_spi_clk,posedge i_rst)
begin
    if(i_rst)
        ro_user_read_data <= 'd0;
    else
        ro_user_read_data <= {ro_user_read_data[P_DATA_WIDTH - 2 : 0],i_spi_miso};
end

5.仿真模块

        以下是仿真截图,在发发送数据期间,cs信号拉低,并且产生了spi_clk;这里发送的端口直接连接的55,也可以看到mosi的数据发送正确;tb文件做了数据回环,因此在read_valid有效期间可以看到接收到的数据也是55,该驱动模块仿真正确,上板使用ila抓取波形后也是正确的;需要注意的是,这里抓到的数据为ff,那是因为选择的管脚未连接外部信号,fpga默认的管脚都是高电平;ILA的使用可以在网上找到教程使用;

6.计划

下周可能更新Flash驱动器的代码,也可能更新DDR3的例程;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值