参考:

1.https://www.yiboard.com/thread-783-1-1.html

2.https://mansfield-devine.com/speculatrix/2018/01/avr-basics-spi-on-the-atmega-part-2/

3.https://blog.csdn.net/woshi_ziyu/article/details/79451095

第1部分中,我们在AVR ATMEGA328P微控制器上已经设置好SPI总线。现在我们就可以开始使用SPI了。

<ignore_js_op>

SPI-signals.png (11.98 KB, 下载次数: 2)

下载附件  保存到相册

2018-1-25 11:33 上传

 

设置相关引脚

在开始之前,我们需要在AVR上设置SPI总线的引脚(在主机模式下使用)。我在这里使用的是ATMEGA328P,所以需要定义一些宏来使代码变得更清晰。如果您使用不同的微控制器,则可以根据您的需要进行调整。

  1. #define SPI_SS_GPIO PB2
  2. #define SPI_SS_PORT PORTB
  3. #define SPI_SS_DDR DDRB
  4. #define SPI_MOSI_GPIO PB3
  5. #define SPI_MOSI_PORT PORTB
  6. #define SPI_MOSI_DDR DDRB
  7. #define SPI_MISO_GPIO PB4
  8. #define SPI_MISO_PORT PORTB
  9. #define SPI_MISO_DDR DDRB
  10. #define SPI_SCK_GPIO PB5
  11. #define SPI_SCK_PORT PORTB
  12. #define SPI_SCK_DDR DDRB

复制代码

现在让我们用这些宏来设置引脚。

  1. SPI_MOSI_DDR |= (1 << SPI_MOSI_GPIO);   // MOSI as output
  2. SPI_SS_DDR |= (1 << SPI_SS_GPIO);       // SS as output
  3. SPI_SCK_DDR |= (1 << SPI_SCK_GPIO);     // SCK as output
  4. // MISO should be configured automatically as input as that's default state
  5. // on GPIOs, but if you want to be emphatic
  6. SPI_MISO_DDR &= ~(1 << SPI_MISO_GPIO);  // MISO as input
  7. SPI_SS_PORT |= (1 << SPI_SS_GPIO);      // take SS high to deselect slave
  8. SPI_MISO_PORT |= (1 << SPI_MISO_GPIO);  // set pullup on MISO

复制代码

为了稍后简化事宜,我们可能还想设置一些宏来控制SS线。

  1. #define SPI_SLAVE_SELECTED SPI_SS_PORT &= ~(1 << SPI_SS_GPIO)
  2. #define SPI_SLAVE_DESELECTED SPI_SS_PORT |= (1 << SPI_SS_GPIO)

复制代码

现在我们已经设置完成,让我们看看实现这些艰苦工作的寄存器。

SPDR - SPI数据寄存器

这是关键的SPI寄存器的第三个,它像魔术一样工作。只需在该寄存器中写入一个字节,AVR就会启动SPI,并将数据的整个字节发送到电缆上,而无需您进一步操作。由于第1部分提到的移位寄存器之间的传输,当这样做时,SPDR包含传入的数据。所以主从机之间的数据交换由主机完成:

●     将SS线拉低以启用从机设备。

●     写一个字节到SPDR。

●     等待交换完成。

●     再次把SS线拉高。

●     读取SPDR中的内容。

就是这样。我们如何知道交换是否完成?很简单 - SPSR寄存器中的SPIF位被置位。所以你所需要做的就是等待。在下面的例子中,我们只是在一个while循环中停留。这是阻塞的,如果你的微控制器很难控制,你可能会发现浪费时钟周期。如果是这样,您将需要考虑启用SPI中断,SPIF被设置时会触发。在SPI数据交换发生时,您可以继续其他事情。

所以这里有一些代码可以通过SPI发送一个字节的数据,然后返回一个字节。

  1. uint8_t value = 0xFA; // randomly chosen value for demo purposes
  2. SPI_SLAVE_SELECTED;
  3. SPDR = value;                  // initiates transfer
  4. while( !(SPSR & (1 << SPIF))); // wait for SPIF bit to be set
  5. SPI_SLAVE_DESELECTED;

复制代码

而就是这样! SPDR现在包含从机发回的任何内容 - 很可能没有任何兴趣。

正是SPI的这一点可以让你头疼。主机刚刚发送给从机的字节可能是某种类型的命令。为了得到从机的响应,只需发送另一个任意值的字节,从机就会发送它的响应。

实际上,您可能必须发送和接收多个字节。下面是如何写一个值到该芯片的内存位置的方法:

1.    将SS线拉低。

2.    发送写入命令(0x02)。忽略从芯片回来的数据。

3.    发送16位内存地址的高字节。忽略从芯片回来的数据。

4.    发送16位内存地址的低字节。忽略从芯片回来的数据。

5.    发送一个字节的需要存储在该位置的值。忽略从芯片回来的数据。

6.    将SS线置位。

所以在这种情况下,所有的流量都是单向的,所有从芯片返回的数据(实际上只是零)都被丢弃了。现在让我们从内存位置读取一个值。方法是:

1.    将SS线拉低。

2.    发送读命令(0x03)。忽略从芯片回来的数据。

3.    发送16位内存地址的高字节。忽略从芯片回来的数据。

4.    发送16位内存地址的低字节。忽略从芯片回来的数据。

5.    发送您喜欢的任何单字节值,芯片将忽略这些值。然而,通过上述操作,芯片将已经存储在其移位寄存器中的存储位置的值发送回去。

6.    将SS线置位。

内存位置的值现在在SPDR中。

在示波器上显示如下:

<ignore_js_op>

SPI_successful_read.png (49.62 KB, 下载次数: 2)

下载附件  保存到相册

2018-1-25 11:33 上传

 

从内存中获取一个字节的数据似乎有很多工作要做。但是23LCV512是这些SPI器件的典型特征,一旦完成初始连接,不断地触发,它将返回更多的信息。

所以,举例来说,如果你想得到32个字节的数据,你可以使用与上面描述的相同的过程,只是发送起始地址,但是在步骤5中,为了促使从机返回数据,而不是发送一个字节的垃圾数据 - 你会发送32个。但这还不够。在每个字节之后,您需要检查SPDR中设置的内容并将其存储在某处,因为在发送下一个垃圾字节后,该值将会被覆盖。

这就是基本的SPI使用方法。

ATMEGA的SPI总线 - 第2部分的更多相关文章

  1. ATMEGA的SPI总线 - 第1部分

    转自: 1. https://www.yiboard.com/thread-782-1-1.html 2.https://mansfield-devine.com/speculatrix/2018/0 ...

  2. SPI总线

    一.概述. SPI, Serial Perripheral Interface, 串行外围设备接口, 是 Motorola 公司推出的一种同步串行接口技术. SPI 总线在物理上是通过接在外围设备微控 ...

  3. MSP430单片机的两种SPI总线实现方式

    MSP430单片机上的SPI总线的实现方式分为两种:硬件实现和软件实现. 二者的抽象层次不同,硬件实现方式下程序员只需要完成总线协议的寄存器层,即一字节(char,8位二进制)数据,而软件实现方式下程 ...

  4. SPI总线协议及SPI时序图详解

    SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口.SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚 ...

  5. SPI总线(同步)

    一.SPI总线简介 串行外围设备接口SPI(serial peripheral interface)总线技术是Motorola公司推出的一种同步串行接口.SPI 用 于CPU与各种外围器件进行全双工. ...

  6. STM32学习笔记(八) SPI总线(操作外部flash)

    1. SPI总线简介 SPI全称串行外设接口,是一种高速,全双工,同步的外设总线:它工作在主从方式,常规需要至少4根线才能够正常工作.SPI作为基本的外设接口,在FLASH,EPPROM和一些数字通讯 ...

  7. SPI总线的特点、工作方式及常见错误解答

    1.SPI总线简介 SPI(serial peripheral interface,串行外围设备接口)总线技术是Motorola公司推出的一种同步串行接口.它用于CPU与各种外围器件进行全双工.同步串 ...

  8. SPI总线通信电路设计

    数据带宽=(总线频率×数据位宽)÷8 B表示带宽,F表示存储器时钟频率,D表示存储器数据总线位数,则带宽为: B(峰值带宽)=F(时钟频率MHz)×D(总线位数bit)/8 例如,PC-100的SDR ...

  9. linux内核SPI总线驱动分析(一)(转)

    linux内核SPI总线驱动分析(一)(转) 下面有两个大的模块: 一个是SPI总线驱动的分析            (研究了具体实现的过程) 另一个是SPI总线驱动的编写(不用研究具体的实现过程) ...

随机推荐

  1. android 捕获未try的异常、抓取崩溃日志

    1.Thread.UncaughtExceptionHandler java里有很多异常如:空指针异常,越界异常,数值转换异常,除0异常,数据库异常等等.如果自己没有try / catch 那么线程就 ...

  2. Tesselation学习

    Tesselation的作用:给低片面数模型镶嵌更多片面,让低模变高模. 和法线贴图不同,法线本质是通过改变低模表面的颜色来模拟高模,比如在一个片面上普通diffuse是均匀的颜色分布(因为光照颜色一 ...

  3. 喵的Unity游戏开发之路 - 互动环境(有影响的运动)

    如图片.视频或代码格式等显示异常,请查看原文: https://mp.weixin.qq.com/s/Sv0FOxZCAHHUQPjT8rUeNw 很多童鞋没有系统的Unity3D游戏开发基础,也不知 ...

  4. linux 安装ifconfig

    一:使用yum命令下载安装wget 查看镜像中ifconfig安装包 yum search ifconfig yum install net-tools.x86_64 -y 下面按照提示一步步安装即可 ...

  5. Java中AQS基本实现原理

    一.AQS概述 AQS全名AbstractQueuedSynchronizer,意为抽象队列同步器,JUC(java.util.concurrent包)下面的Lock和其他一些并发工具类都是基于它来实 ...

  6. 深入理解xLua热更新原理

    热更新简介 热更新是指在不需要重新编译打包游戏的情况下,在线更新游戏中的一些非核心代码和资源,比如活动运营和打补丁.热更新分为资源热更新和代码热更新两种,代码热更新实际上也是把代码当成资源的一种热更新 ...

  7. d3力导图绘制节点间多条关系平行线的方法

    之前用d3做了多条线之间的绘图是曲线表示的,现在产品要求改成平行线的样式,经过在网上的调研和自己的尝试,实践出一个可用的方法,分享给大家,先展示下结果: 事先声明,本方法是在以下参考网站上进行的结合和 ...

  8. C的文件操作---笔记

    打开文件  FILE *fp = fopen(char *filename, char *mode) 关闭文件  fclose(fp) 字符形式读  char ch = fgetc(fp) 字符形式写 ...

  9. 【二叉树-所有路经系列(根->叶子)】二叉树的所有路径、路径总和 II、路径总和、求根到叶子节点数字之和(DFS)

    总述 全部用DFS来做 重点一:参数的设置:为Root,路径字符串,路径List集合. 重点二:步骤: 1 节点为null 2 所有节点的操作 3 叶子结点的操作 4 非叶节点的操作 题目257. 二 ...

  10. 写一个简单的 Linux Shell (C++)

    这里可以找到代码 github.com/z0gSh1u/expshell 支持的特性 单条指令的执行 引号引起的参数(如 $ some_program "hello, world" ...