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. memcache 开发版

    memcache安装,如果是用xampp,一定要下载开发版本 解压开发包,将其中的include目录复制到应用的lampp目录下 tar -zxvf xampp-linux-devel-1.7.2.t ...

  2. C标准函数库(常用部分)

  3. hdu 1261 字串数

    解题思路:ACM紫书 第十章 P319 有重复元素的全排列 答案: 所有数的和的阶乘 除以 每个数阶乘的乘积 因为给定 (26*12)! 会爆掉(long long),这里用java 的BigInte ...

  4. iptables 工具

    iptables 工具 参考文档: https://www.frozentux.net/iptables-tutorial/cn/iptables-tutorial-cn-1.1.19.html   ...

  5. 安装TFS2008最终版(转载)

    一.安装操作系统:windows server 2003 + Sp2具体步骤: 1.安装windows server 2003时选用工作组(默认为workgroup).由于在工作组环境中部署,因此使用 ...

  6. eclipse-统计代码行数

    使用Eclipse可以方便的统计工程或文件的代码行数,方法如下: 1.点击要统计的项目或许文件夹,在菜单栏点击Search,然后点击File...  2.选中正则表达式(Regular express ...

  7. delphi基本语法

    本文参考自<delphi2010语法手册> 1. 工程文件结构 源文件联系着unit单元,delphi主模块源文件格式为.dpr,其他模块为.pas,一个完整程序由一个.dpr和若干.pa ...

  8. [转] Android开发者必备的42个链接

    下面收集了42个帮助大家学习Android的内容链接,部分内容是面向初学者的,帮助大家从头开始学习Android开发,其他则面向较高级的开发者.希望推荐的这些内容对你有帮助. 官方网站 1.谷歌And ...

  9. 使用Fabric进行crash收集统计

    主要是帮助自己记一下地址. 1 申请Crashlytics服务:http://try.crashlytics.com 2 下载Fabric客户端,帮助集成Crashlytics到自己的项目中:http ...

  10. POJ 2442 Sequence

    Pro. 1 给定k个有序表,取其中前n小的数字.组成一个新表,求该表? 算法: 由于  a1[1] < a1[2] < a1[3] ... <a1[n] a2[1] < a2 ...