SPI试验---verilog(实用单通模式)
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(实用单通模式)的更多相关文章
- 单用户模式启动SQL Server实例总结
在SQL Server的数据库维护过程中,有时候在一些特殊情况下需要在单用户模式下启动SQL Server实例. 下面总结一下单用户模式启动SQL Server的几种方式: 1:命令模式(sqls ...
- DSDS,双模,双卡,双待,单待,双通,单通,概念及相互关系?【转】
本文转载自:https://blog.csdn.net/dirk_it/article/details/7178058?utm_source=blogxgwz9 DSDS:双卡双待 DualSimDu ...
- Linux进入单用户模式的两种方法
单用户模式的作用 在使用Linux系统中,维护人员经常会碰到一个问题,就是在拥有root账号权限和密码的用户中,总是会出现忘记root密码的情况. 遇到这种情况,一般情况下,维护人员就会通过最常用的方 ...
- Centos6进入单用户模式的两种方法
单用户模式的作用 在使用Linux系统中,维护人员经常会碰到一个问题,就是在拥有root账号权限和密码的用户中,总是会出现忘记root密码的情况.遇到这种情况,一般情况下,维护人员就会通过最常用的方法 ...
- Linux操作系统进入单用户模式的方法
单用户模式的作用 在使用Linux的过程中,维护人员经常会碰到一些问题,就是在拥有root账号权限和密码的用户中,总是会出现忘记root密码的情况. 遇到这种情况,一般情况下,维护人员就会通过最常用的 ...
- Linux学习之CentOS(二十二)--单用户模式下修改Root用户的密码
在上一篇随笔里面详细讲解了Linux系统的启动过程 (Linux学习之CentOS(二十一)--Linux系统启动详解),我们知道Linux系统的启动级别一共有6种级别,通过 /etc/inittab ...
- CentOS6.3修复模式/单用户模式修改fstab文件
今天修改LVM逻辑卷的名称时候,忘记更改fstab配置文件了,导致机器重启后找不到盘,进不了系统!立即用光盘进入修复模式进行修复! 1.修复模式操作方法: 用光盘进入Linux修复模式,插入cent ...
- centos单用户模式修改ROOT密码
首先启动的时候的时候,需要进入单用户模式(进入单用户模式的前提是系统引导器能正常工作),单用户模式是不需要输入密码,并且(进入单用户模式,没有开启网络服务,不支持远程连接 )网上说可以通过GRUB ( ...
- centos7.2进入单用户模式
1 - 在启动grub菜单,选择编辑选项启动 2 - 按键盘e键,来进入编辑界面 3 - 找到Linux 16的那一行,将ro改为rw init=/sysroot/bin/sh 4 - 现在按下 Co ...
随机推荐
- android HDMI 清晰度 分辨率
但改变分辨率时,发送广播即可: Intent intent_outputmode_change = new Intent(ACTION_OUTPUTMODE_CHANGE); intent_o ...
- 了解 Nginx 基本概念
前言 本篇是我学习 Nginx 的一些笔记,主要内容讲述了一些了解 Nginx 需要的基本概念.然后探讨一下 Nginx 的模块化的组织架构,以及各个模块的分类.工作方式.职责和提供的相关指令. 主要 ...
- 【原创】angularjs1.3.0源码解析之scope
Angular作用域 前言 之前我们探讨过Angular的执行流程,在一切准备工作就绪后(我是指所有directive和service都装载完毕),接下来其实就是编译dom(从指定的根节点开始遍历do ...
- [转载]angularjs学习总结 详细教程
http://blog.csdn.net/yy374864125/article/details/41349417#t75 目录(?)[-] 前言 AngularJS概述 AngularJS是什么 A ...
- 搜狗2013年校园招聘研发类笔试试卷之C/C++类
今天无聊在网上搜了下今年各大NB IT公司的笔试题,搜到了搜狗的,只有扫描版的试卷没有电子版也没有答案,就拿来做了做,题目非常多,涉及到C/C++.Java.数据结构.Android.IOS.Java ...
- 用纯原生js实现jquery的ready函数(两种实现)
第一种实现方式: var dom = new function() { var dom = []; dom.isReady = false; dom.isFunction = function(obj ...
- Matlab图像处理入门
1. Matlab基础 1.1 数据格式 Matlab默认的数据格式为双精度浮点数的矩阵或数组,同时支持其它数据类型.Matlab将单变量看作1´1的数组.Matlab支持的数据类型如下: 索 ...
- [Effective JavaScript 笔记]第55条:接收关键字参数的选项对象
53节建议保持参数顺序的一致约定对于帮助程序员记住每个参数在函数调用中的意义很重要.参数较少这个主意不错,但如果参数过多后,就出现麻烦了,记忆和理解起来都不太容易. 参数蔓延 如下面这些代码: var ...
- [BZOJ2502]清理雪道
[BZOJ2502]清理雪道 试题描述 滑雪场坐落在FJ省西北部的若干座山上. 从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向. 你的团队负责每周定 ...
- 从零开始写一个武侠冒险游戏-6-用GPU提升性能(1)
从零开始写一个武侠冒险游戏-6-用GPU提升性能(1) ----把帧动画的实现放在GPU上 作者:FreeBlues 修订记录 2016.06.19 初稿完成. 2016.08.05 增加对 XCod ...