Arduino 与 SPI 结合使用 以及SPI 深层理解
本文主要讲解两部分内容,不做任何转发,仅个人学习记录:
一. Arduino 与 SPI 结合使用 :
二. SPI 深层理解
有价值的几个好的参考:
1. 中文版: https://blog.csdn.net/xxxxxx91116/article/details/42620413 这版本适合比较容易理解大概, 细节翻译还是要去英文版:https://www.arduino.cc/en/Tutorial/SPIEEPROM
2 .https://www.cnblogs.com/adylee/p/5399742.html 以及 https://blog.csdn.net/weixin_40117614/article/details/84070130(其中:2.2传输数据步骤如下 mode似乎弄混了,mode0与mode2在第一次采样之前已发送1bit 而mode1和mode3则是正常的发送再采样,有待考证)
一. Arduino 与 SPI 结合使用 :
1.串行外围设备接口入门(Introduction to the Serial Peripheral Interface)
2.串行EEPROM简介
3.面包板的准备
4.Arduino SPI 编程
spi_transfer函数将要传出的数据放入数据传输寄存器,然后就开始SPI传输了哈。可以通过SPI状态寄存器(SPSR)的某个位(SPIF)来查看数据传输是否结束了。关于位掩码(bit mask)可以参考这里:http://www.arduino.cc/en/Tutorial/。最后返回写入EEPROM的数据。
read_eeprom函数允许我们从EEPROM中读入数据,首先设置SLAVESELECT为低来enable设备。接下来送入一个读指定,接下来送入要读的16位地址,最高有效位有限。接下来我们发送一个假数据到EEPROM中以将数据传出。最后我们在读入一个字节后,再次设置SLAVESELECT线为高来释放设备,并返回数据,如果我们想要一次读入多个数据,那么当我们重复data=spi_transfer(0XFF)时,需要将SLAVESELECT一直设置为低,这样来回128次后读出整个页的数据:
为了方便大家CTRL+c、 CTRL+v,下面是整个手册的源码:
#define DATAOUT 11//MOSI
#define DATAIN 12//MISO
#define SPICLOCK 13//sck
#define SLAVESELECT 10//ss
//opcodes
#define WREN 6
#define WRDI 4
#define RDSR 5
#define WRSR 1
#define READ 3
#define WRITE 2
byte eeprom_output_data;
byte eeprom_input_data=0;
byte clr;
int address=0;
//data buffer
char buffer [128];
void fill_buffer()
{
for (int I=0;I<128;I++)
{
buffer[I]=I;
}
}
char spi_transfer(volatile char data)
{
SPDR = data; // Start the transmission
while (!(SPSR & (1<<SPIF))) // Wait the end of the transmission
{
};
return SPDR; // return the received byte
}
void setup()
{
Serial.begin(9600);
pinMode(DATAOUT, OUTPUT);
pinMode(DATAIN, INPUT);
pinMode(SPICLOCK,OUTPUT);
pinMode(SLAVESELECT,OUTPUT);
digitalWrite(SLAVESELECT,HIGH); //disable device
// SPCR = 01010000
//interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
//sample on leading edge of clk,system clock/4 rate (fastest)
SPCR = (1<<SPE)|(1<<MSTR);
clr=SPSR;
clr=SPDR;
delay(10);
//fill buffer with data
fill_buffer();
//fill eeprom w/ buffer
digitalWrite(SLAVESELECT,LOW);
spi_transfer(WREN); //write enable
digitalWrite(SLAVESELECT,HIGH);
delay(10);
digitalWrite(SLAVESELECT,LOW);
spi_transfer(WRITE); //write instruction
address=0;
spi_transfer((char)(address>>8)); //send MSByte address first
spi_transfer((char)(address)); //send LSByte address
//write 128 bytes
for (int I=0;I<128;I++)
{
spi_transfer(buffer[I]); //write data byte
}
digitalWrite(SLAVESELECT,HIGH); //release chip
//wait for eeprom to finish writing
delay(3000);
Serial.print('h',BYTE);
Serial.print('i',BYTE);
Serial.print('\n',BYTE);//debug
delay(1000);
}
byte read_eeprom(int EEPROM_address)
{
//READ EEPROM
int data;
digitalWrite(SLAVESELECT,LOW);
spi_transfer(READ); //transmit read opcode
spi_transfer((char)(EEPROM_address>>8)); //send MSByte address first
spi_transfer((char)(EEPROM_address)); //send LSByte address
data = spi_transfer(0xFF); //get data byte
digitalWrite(SLAVESELECT,HIGH); //release chip, signal end transfer
return data;
}
void loop()
{
eeprom_output_data = read_eeprom(address);
Serial.print(eeprom_output_data,DEC);
Serial.print('\n',BYTE);
address++;
if (address == 128)
address = 0;
delay(500); //pause for readability
}
总结:
1.这里主要以内存器EEPROM为主, 而且个人感觉这里的SPI控制进入到Arduino的开发版, 大体的方向对很多Arduino——SPI控制实用,但毕竟只是一个例子,下面一节将讲述SPI最底层的东西。
2.如果只是简单的读写,Arduino 中是有SPI.h头文件和cpp 也是大家可以研究的一个方向,现在记忆留心还是spi.transfer用法。
二. SPI 深层理解
SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议。 SPI是一个环形总线结构,由ss(cs)、sck、sdi、sdo构成,其时序其实很简单,主要是在sck的控制下,两个双向移位寄存器进行数据交换。
假设主机和从机初始化就绪:并且主机的sbuff=0xaa (10101010),从机的sbuff=0x55 (01010101),下面将分步对spi的8个时钟周期的数据情况演示一遍(假设上升沿发送数据)。
---------------------------------------------------
这样就完成了两个寄存器8位的交换,上面的0--1表示上升沿、1--0表示下降沿,sdi、 sdo相对于主机而言的。根据以上分析,一个完整的传送周期是16位,即两个字节,因为,首先主机要发送命令过去,然后从机根据主机的名准备数据,主机在下一个8位时钟周期才把数据读回来。 SPI总线是Motorola公司推出的三线同步接口,同步串行3线方式进行通信:一条时钟线SCK,一条数据输入线MOSI,一条数据输出线MISO;用于 CPU与各种外围器件进行全双工、同步串行通讯。
SPI时序图详解-SPI接口在模式0下输出第一位数据的时刻
SPI接口在模式0下输出第一位数据的时刻
SPI接口有四种不同的数据传输时序,取决于CPOL和CPHL这两位的组合。图1中表现了这四种时序, 时序与CPOL、CPHL的关系也可以从图中看出。
CPOL(时钟极性)和CPHA(时钟相位)意义
CPOL=0,表示当SCLK=0时处于空闲态,所以有效状态就是SCLK处于高电平时
CPOL=1,表示当SCLK=1时处于空闲态,所以有效状态就是SCLK处于低电平时
CPHA=0,表示数据采样是在第1个边沿,数据发送在第2个边沿
CPHA=1,表示数据采样是在第2个边沿,数据发送在第1个边沿
通过CPOL和CPHA来控制我们主设备的通信模式
发送和接收设备需要根据实际情况分析 (发送设备 ≠ 主设备)
Mode0:CPOL=0,CPHA=0
SCLK(0)空闲;
当SCLK由低到高跳变(上升沿),(接收设备)进行数据的读取;
当SCLK由高到低跳变(下降沿),(发送设备)进行数据的发送;
Mode1:CPOL=0,CPHA=1
SCLK(0)空闲;
当SCLK由高到低跳变(下降沿),(接收设备)进行数据的读取;
当SCLK由低到高跳变(上升沿),(发送设备)进行数据的发送;
Mode2:CPOL=1,CPHA=0
SCLK(1)空闲;
当SCLK由高到低跳变(下降沿),(接收设备)进行数据的读取;
当SCLK由低到高跳变(上升沿),(发送设备)进行数据的发送;
Mode3:CPOL=1,CPHA=1
SCLK(1)空闲;
当SCLK由低到高跳变(上升沿),(接收设备)进行数据的读取;
当SCLK由高到低跳变(下降沿),(发送设备)进行数据的发送;
---------------------
CPOL是用来决定SCK时钟信号空闲时的电平,CPOL=0,空闲电平为低电平,CPOL=1时,
空闲电平为高电平。CPHA是用来决定采样时刻的,CPHA=0,在每个周期的第一个时钟沿采样,
CPHA=1,在每个周期的第二个时钟沿采样。
图4中,注意看CS和MISO信号。我们可以看出,CS信号有效后,从器件立刻输出了bit1(值为1)。
通常我们进行的spi操作都是16位的。图5记录了第一个字节和第二个字节间的相互衔接的过程。 第一个字节的最后一位在SCK的上升沿被采样,随后的SCK下降沿,从器件就输出了第二个字节的第一位。
SPI总线协议介绍(接口定义,传输时序)
一、技术性能 SPI接口是Motorola 首先提出的全双工三线同步串行外围接口,采用主从模式(Master Slave)架构;支持多slave模式应用,一般仅支持单Master。 时钟由Master控制,在时钟移位脉冲下,数据按位传输,高位在前,低位在后(MSB first);SPI接口有2根单向数据线,为全双工通信,目前应用中的数据速率可达几Mbps的水平。
------------------------------------------------------- 二、接口定义 SPI接口共有4根信号线,分别是:设备选择线、时钟线、串行输出数据线、串行输入数据线。
------------------------------------------------------- 三、内部结构
四、传输时序
SPI接口在内部硬件实际上是两个简单的移位寄存器,传输的数据为8位,在主器件产生的从器件使能信号和移位脉冲下,按位传输,高位在前,低位在后。如下图所示,在SCLK的下降沿上数据改变,上升沿一位数据被存入移位寄存器。
Arduino 与 SPI 结合使用 以及SPI 深层理解的更多相关文章
- ARM与FPGA通过spi通信设计2.spi master的实现
这里主要放两个代码第一个是正常的不使用状态机的SPI主机代码:第二个是状态机SPI代码 1.不使用状态机:特权同学<深入浅出玩转FPGA>中DIY数码相框部分代码: /////////// ...
- ARM与FPGA通过spi通信设计1.spi基础知识
SPI(Serial Peripheral Interface--串行外设接口)总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息.SPI总线可直接与各个厂家生产 ...
- dubbo源码分析2——SPI机制中的SPI实现类的读取和预处理
SPI机制中的SPI实现类的读取和预处理是由ExtensionLoader类的loadFile方法来完成的 loadFile方法的作用是读取dubbo的某个SPI接口的spi描述文件,然后进行缓存,缓 ...
- Linux spi驱动分析(二)----SPI核心(bus、device_driver和device)
一.spi总线注册 这里所说的SPI核心,就是指/drivers/spi/目录下spi.c文件中提供给其他文件的函数,首先看下spi核心的初始化函数spi_init(void).程序如下: 点击(此处 ...
- 对于src路径问题,深层理解的实践。且对于输出流write()两个方法的源码阅读。
根据昨天的总结,可深层理解图片中src的路径.所以今天实现了一个想法.就是路径写入的是Controller,然后自动去本地找. 其实就是将电脑的本地图片 显示出来.通过输出流的方式. 代码如下: @R ...
- HDOJ1016 Prime Ring Problem(DFS深层理解)
Prime Ring Problem 时间限制: 200 ...
- SPI总线协议及SPI时序图详解
SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口.SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚 ...
- [SPI&I2C]I2C和SPI协议介绍
IIC vs SPI 现今,在低端数字通信应用领域,我们随处可见IIC (Inter-Integrated Circuit) 和 SPI (Serial Peripheral Interface)的身 ...
- spi数据KL25用SPI操作nor flash
最近研究spi数据,稍微总结一下,以后继续补充: KL25的SPI连接一个nor flash.该flash型号为FM25F04,支撑SPI的模式0和模式3,要求高位先发送,在上升沿采集数据. 通常,S ...
随机推荐
- Python | 详解Python中的协程,为什么说它的底层是生成器?
今天是Python专题的第26篇文章,我们来聊聊Python当中的协程. 我们曾经在golang关于goroutine的文章当中简单介绍过协程的概念,我们再来简单review一下.协程又称为是微线程, ...
- ES6--let,解构赋值,promise && ES7--async
ES-->IE10.Google.火狐 ES6 let 声明的关键字 不能重复声明 块级作用域 <input type="button" value="1&q ...
- Fitness - 05.11
倒计时234天 从本周开始改变运动模式 跑步换到每周二.四.六进行. 每周一.三.五增加瑜伽的练习,周日山地车骑行~~~~ 久违的瑜伽课,瑜伽老师居然是男的,第一次看到这么柔软的男生~! 平时感觉不到 ...
- MPI Maelstrom (Dijstra+atoi函数转换整数)
BIT has recently taken delivery of their new supercomputer, a 32 processor Apollo Odyssey distribute ...
- vueRooter的总结
这一周学习了Vue的脚手架的结构,最重要的router 该进行总结和回忆了. 1首先是router的安装,用npm命令npm install vue-router --save 2装完后,在main. ...
- 深入理解 JVM 的内存区域
深入理解运行时数据区 代码示例: 1. JVM 向操作系统申请内存: JVM 第一步就是通过配置参数或者默认配置参数向操作系统申请内存空间,根据内存大小找到具体的内存分配表,然后把内存段的起始地址和终 ...
- jenkins安装和邮件配置
一.jenkins下载 Jenkins的下载地址是https://jenkins.io/download/,下载的时候可以选择各个版本的以及对应操作系统的版本,一般你下载的时候下载通用的.war文件即 ...
- Ubuntu中的launcher
最近在ubuntu系统中下载了最新版的eclipse,在一个临时文件夹中解压了eclipse压缩包,然后打开eclipse,按平时常规做法,我在launcher里右键点击eclipse,选择“锁定到启 ...
- windows下nginx的配置
这里做的nginx的配置主要的功能是: 能够用localhost访问本地文件夹中的项目 输入ip地址访问本地文件夹中的项目 反向代理其他地址访问本地文件 1.nginx安装地址 2.解压之后的文件如下 ...
- command三国杀开发日记20200914
目前状态 一时脑热开始写的东西,计划完全使用C语言实现,尽量使用通用接口,能够在windows上直接运行 几乎是一穷二白,初步搭建了牌堆.玩家信息接口体,编写了简单的UI函数,能够将玩家信息显示在屏幕 ...