SPI通信的读写操作

一、     SPI简介:

SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是SDI(数据输入)、SDO(数据输出)、SCLK(时钟)、CS(片选)。

(1)SDO – 主设备数据输出,从设备数据输入;

(2)SDI – 主设备数据输入,从设备数据输出;

(3)SCLK – 时钟信号,由主设备产生;

(4)CS – 从设备使能信号,由主设备控制。

其中,CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效。这就允许在同一总线上连接多个SPI设备成为可能。

由SCLK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。数据输出通过 SDO线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。完成一位数据传输,输入也使用同样原理。

要注意的是,SCLK信号线只由主设备控制,从设备不能控制信号线。同样,在一个基于SPI的设备中,至少有一个主控设备。这样传输的特点:这样的传输方式有一个优点,与普通的串行通讯不同,普通的串行通讯一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停

二、     SPI的时序电路图:

SPI时钟极性CPOL = 0表示在没有数据传输时为低电平,= 1表示没有数据传输时为高电平。

SPI时钟相位CPHA,= 0表示时钟的第一个沿更新数据、第二个沿锁存数据,= 1表示时钟的第一个沿锁存数据、第二个沿更新数据。

程序代码:

    /********************************Copyright**************************************
**----------------------------File information--------------------------
** File name :spi_writeread.v
** CreateDate :2015.04
** Funtions : SPI作为主机向从机读写,读的时候要注意,总共为15个时钟,在最后一个写地址时钟的下降沿就开始读取数据,
若要在下一个时钟的下降沿读取数据则要根据需要修改程序。注:本程序先发送最高位,先接收最高位
** Operate on :M5C06N3L114C7
** Copyright :All rights reserved.
** Version :V1.0
**---------------------------Modify the file information----------------
** Modified by :
** Modified data :
** Modify Content:
*******************************************************************************/ module spi_writeread (
clk,
rst_n,
spi_re_en,
spi_wr_en,
spi_addr,
spi_send_data,
spi_read_data, spi_cs,
spi_clk,
spi_mi,
spi_mo,
spi_busy,
spi_over
);
input clk;
input rst_n;
input spi_re_en; //接收使能
input spi_wr_en; //发送使能
input [:] spi_addr; //待发送的地址
input [:] spi_send_data; //待发送的数据 output spi_cs; //片选信号
output spi_clk; //时钟信号
input spi_mi; //主机从芯片读取的数据
output spi_mo; //主机向芯片发送的数据
output reg spi_over; //spi操作完成
output reg [:] spi_read_data; //spi接收的数据,即读取的数据
output reg spi_busy; //spi忙信号 reg temp_cs;
reg temp_scl;
reg temp_mo; assign spi_cs = temp_cs;
assign spi_clk = temp_scl;
assign spi_mo = temp_mo; reg sendbit_over; //字节发送完成标志
reg resbit_over; //接收字节完成标志
reg [:] res_data; //接收的数据 //*******************状态机***************************
parameter cnt_delay = ; //CS的延时时钟的计数(根据芯片决定)
reg [:] state; //状态机
reg [:] send_data; //待发送的移位数据寄存器
reg [:] read_data; //接收数据寄存器
reg [:] delay; //发送完成,延时到可以再次发送,然后待命
reg wr_flag; //写操作标志
reg re_flag; //读操作标志
reg send_en;
reg resive_en; always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
temp_cs <= ; state <= 'd0;
send_data <= 'd0;
read_data <= 'd0;
delay <= ;
wr_flag <= ;
re_flag <= ;
spi_busy <= ;
spi_over <= ;
resive_en <= ;
send_en <=;
end
else
begin
case(state)
'd0:
begin
delay <= ;
temp_cs <= ;
send_data <= 'd0;
read_data <= 'd0;
wr_flag <= ;
re_flag <= ;
spi_busy <= ;
spi_over <= ;
resive_en <= ;
send_en <=; if(spi_wr_en) //写使能
begin
spi_busy <=;
state <= 'd1;
wr_flag <= ; //写操作标志置位高
end
else if(spi_re_en)
begin
spi_busy <=;
state <= 'd1;
re_flag <= ; //读操作标志置位高
end
end
'd1:
begin
temp_cs <= ; //拉低cs信号
state <= 'd2;
end
'd2: //拉低时钟和数据输出线
begin
if(sendbit_over)
begin
send_en <=;
if(wr_flag)
begin
state <= 'd3;
end
else if(re_flag)
begin
state <= 'd4;
resive_en <=; /* 接收使能置高 */
end
else
begin
state <= 'd0;
end
end
else
begin
send_data <= spi_addr; //将地址寄存,然后发送地址
state <= 'd2;
send_en <=;
end
end
'd3:
begin
if(sendbit_over)
begin
state <= 'd5;
send_en <=;
end
else
begin
send_data <= spi_send_data; //将地址寄存,然后发送地址
state <= 'd3;
send_en <=;
end
end
'd4:
begin
if(resbit_over)
begin
state <= 'd5;
resive_en <=;
read_data <= res_data;
end
else
begin
state <= 'd4;
resive_en <=;
end
end
'd5:
begin
temp_cs <= ;
if(delay == cnt_delay)
begin
state <= 'd6;
delay <= ;
spi_over <= ;
end
else
delay <= delay + ;
end
'd6:
begin
spi_over <= ;
spi_busy <= ;
state <= 'd0;
end
default : state <= 'd0;
endcase
end
end always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
spi_read_data <= 'd0;
end
else
begin
if(spi_over)
spi_read_data <= read_data;
else
spi_read_data <= spi_read_data;
end
end //****************发送****************
reg [:] send_state;
reg [:] shift_data;
reg [:] send_num; reg [:] resive_state;
reg [:] res_num; always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
temp_scl <= ; //模式0状态,数据、时钟都为低电平
temp_mo <= ; shift_data <= ;
send_num <= ; send_state<= ;
sendbit_over <= ; resive_state <= ;
res_data <= 'd0;
res_num <= ;
resbit_over <= ; end
else if(send_en)
begin
case(send_state)
'd0:
begin
temp_scl <= ;
temp_mo <= ;
send_num <= 'd0;
shift_data <= send_data;
send_state <= 'd1;
sendbit_over <= ;
end
'd1:
begin
temp_mo <= shift_data[] ; /* 先发最高位,再发最低位 */
send_state <= 'd2;
end
'd2:
begin
temp_scl <= ;
send_state <= 'd3;
end
'd3:
begin
if(send_num == 'd7)
begin
send_state <= 'd5;
sendbit_over <= ;
send_num <= 'd0;
end
else
begin
send_state <= 'd4;
end
end
'd4:
begin
temp_scl <= ;
shift_data <= shift_data << ;
send_num <= send_num + ;
send_state <= 'd1;
end 'd5:
begin
send_state <= 'd5;
sendbit_over <= ;
end
default: send_state <= 'd0;
endcase
end
else if(resive_en)
begin
case(resive_state)
'd0:
begin
resive_state <= 'd1;
res_num <= ;
resbit_over <= ;
end
'd1:
begin
temp_scl <= ;
res_data[] <= spi_mi; /* 接收最低位,然后左移,故实际是先接收最高位 */
resive_state <= 'd2;
end
'd2:
begin
if(res_num == 'd7)
begin
resive_state <= 'd5;
res_num <= 'd0;
end
else
begin
res_data <= res_data << ;
resive_state <= 'd3;
end
end
'd3:
begin
temp_scl <= ;
resive_state <= 'd4;
end
'd4:
begin
res_num <= res_num + ;
resive_state <= 'd1;
end
'd5:
begin
resbit_over <= ;
resive_state <= 'd6;
end
'd6:
begin
resbit_over <= ;
resive_state <= 'd6;
end
default: resive_state <= 'd0;
endcase
end
else
begin
temp_scl <= ;
temp_mo <= ; shift_data <= ;
send_num <= ;
sendbit_over <= ;
send_state<= ; resive_state <= 'd0;
res_data <= 'd0;
res_num <= ;
resbit_over <= ;
end
end endmodule

测试程序:

    /********************************Copyright**************************************
**----------------------------File information--------------------------
** File name :spi_writeread_tb.v
** CreateDate :2015.04
** Funtions : SP的测试文件
** Operate on :M5C06N3L114C7
** Copyright :All rights reserved.
** Version :V1.0
**---------------------------Modify the file information----------------
** Modified by :
** Modified data :
** Modify Content:
*******************************************************************************/ module spi_writeread_tb; reg clk;
reg rst_n;
reg spi_re_en; //接收使能
reg spi_wr_en; //发送使能
reg [:] spi_addr; //待发送的地址
reg [:] spi_send_data; //待发送的数据 wire spi_cs; //片选信号
wire spi_clk; //时钟信号
reg spi_mi; //主机从芯片读取的数据
wire spi_mo; //主机向芯片发送的数据
wire spi_over; //spi操作完成
wire [:] spi_read_data; //spi接收的数据,即读取的数据
wire spi_busy; //spi忙信号 spi_writeread spi_writeread_1(
.clk,
.rst_n,
.spi_re_en,
.spi_wr_en,
.spi_addr,
.spi_send_data,
.spi_read_data, .spi_cs,
.spi_clk,
.spi_mi,
.spi_mo,
.spi_busy,
.spi_over
); parameter tck = ;
parameter t = /tck; always
#(t/) clk = ~clk; always
#(*t) spi_mi = ~spi_mi; initial
begin
clk = ;
rst_n = ;
spi_re_en = ;
spi_wr_en = ;
spi_addr = ;
spi_send_data = ;
spi_mi = ; #(*t) rst_n = ; #(*t) spi_addr = 'h55;
spi_send_data = 'haa;
#(*t) spi_wr_en = ;
#(*t) spi_wr_en = ; #(*t) ;
#(*t) spi_addr = 'h0f;
#(*t) spi_re_en = ;
#(*t) spi_re_en = ; end endmodule

仿真图片:

 

SPI试验---verilog(实用单通模式)的更多相关文章

  1. 单用户模式启动SQL Server实例总结

      在SQL Server的数据库维护过程中,有时候在一些特殊情况下需要在单用户模式下启动SQL Server实例. 下面总结一下单用户模式启动SQL Server的几种方式: 1:命令模式(sqls ...

  2. DSDS,双模,双卡,双待,单待,双通,单通,概念及相互关系?【转】

    本文转载自:https://blog.csdn.net/dirk_it/article/details/7178058?utm_source=blogxgwz9 DSDS:双卡双待 DualSimDu ...

  3. Linux进入单用户模式的两种方法

    单用户模式的作用 在使用Linux系统中,维护人员经常会碰到一个问题,就是在拥有root账号权限和密码的用户中,总是会出现忘记root密码的情况. 遇到这种情况,一般情况下,维护人员就会通过最常用的方 ...

  4. Centos6进入单用户模式的两种方法

    单用户模式的作用 在使用Linux系统中,维护人员经常会碰到一个问题,就是在拥有root账号权限和密码的用户中,总是会出现忘记root密码的情况.遇到这种情况,一般情况下,维护人员就会通过最常用的方法 ...

  5. Linux操作系统进入单用户模式的方法

    单用户模式的作用 在使用Linux的过程中,维护人员经常会碰到一些问题,就是在拥有root账号权限和密码的用户中,总是会出现忘记root密码的情况. 遇到这种情况,一般情况下,维护人员就会通过最常用的 ...

  6. Linux学习之CentOS(二十二)--单用户模式下修改Root用户的密码

    在上一篇随笔里面详细讲解了Linux系统的启动过程 (Linux学习之CentOS(二十一)--Linux系统启动详解),我们知道Linux系统的启动级别一共有6种级别,通过 /etc/inittab ...

  7. CentOS6.3修复模式/单用户模式修改fstab文件

    今天修改LVM逻辑卷的名称时候,忘记更改fstab配置文件了,导致机器重启后找不到盘,进不了系统!立即用光盘进入修复模式进行修复!  1.修复模式操作方法: 用光盘进入Linux修复模式,插入cent ...

  8. centos单用户模式修改ROOT密码

    首先启动的时候的时候,需要进入单用户模式(进入单用户模式的前提是系统引导器能正常工作),单用户模式是不需要输入密码,并且(进入单用户模式,没有开启网络服务,不支持远程连接 )网上说可以通过GRUB ( ...

  9. centos7.2进入单用户模式

    1 - 在启动grub菜单,选择编辑选项启动 2 - 按键盘e键,来进入编辑界面 3 - 找到Linux 16的那一行,将ro改为rw init=/sysroot/bin/sh 4 - 现在按下 Co ...

随机推荐

  1. C# 抓取网页Html源码 (网络爬虫)

    http://www.cnblogs.com/wxxian001/archive/2011/09/07/2169519.html 刚刚完成一个简单的网络爬虫,因为在做的时候在网上像无头苍蝇一样找资料. ...

  2. jquery------隐式迭代

    其中Jq方法遍历内部dom数组的过程就叫做[隐式迭代] my.js $(document).ready(function(){ (function($){ $.fn.swapClass=functio ...

  3. RecyclerView的使用方法

    1.添加.jar包 File -> Project-Structure -> 左下角 “app” -> Dependencies -> 右上角“+” -> File de ...

  4. Centos目录结构详细版

    使用linux也有一年多时间了  最近也是一直在维护网站系统主机  下面是linux目录结构说明 本人使用的是centos系统,很久没有发表博文了 近期会整理自己所用所了解知识点,发表linux相关的 ...

  5. OOA/OOD/OOP(了解)

    Object-Oriented Analysis:面向对象分析方法 是在一个系统的开发过程中进行了系统业务调查以后,按照面向对象的思想来分析问题.OOA与结构化分析有较大的区别.OOA所强调的是在系统 ...

  6. 使用migrate.exe执行EF code first 迁移

    Code First 迁移可用于从 Visual Studio 内部更新数据库,但也可通过命令行工具 migrate.exe 来执行.本页简单介绍如何使用 migrate.exe 对数据库执行迁移. ...

  7. 新浪微博客户端(20)-集成MJRefresh

    HomeViewController.m /** 集成下拉刷新控件 */ - (void)setupPullToRefreshView { __unsafe_unretained UITableVie ...

  8. C++中的异常处理(二)

    C++中的异常处理(二) 标签: c++C++异常处理 2012-11-24 20:56 1713人阅读 评论(2) 收藏 举报  分类: C++编程语言(24)  版权声明:本文为博主原创文章,未经 ...

  9. C#操作XML类

    XML转换成HTML 1.//装载xsl XslCompiledTransform xslt = new XslCompiledTransform(); xslt.Load("output. ...

  10. Flume-NG(1.5版本)中SpillableMemoryChannel源码级分析

    SpillableMemoryChannel是1.5版本新增的一个channel.这个channel优先将evnet放在内存中,一旦内存达到设定的容量就使用file channel写入磁盘.然后读的时 ...