一、SPI  简介

  SPI是 Serial Peripheral interface 的缩写,就是串行外围设备接口。SPI 接口主要应用在  EEPROM, FLASH,实时时钟,AD 转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为 PCB 的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,STM32 有 SPI 接口。下面是 SPI 的内部简明图:

图1 SPI 内部结构简明图

  SPI 接口一般使用 4 条线通信:

    MISO  主设备数据输入,从设备数据输出。

    MOSI  主设备数据输出,从设备数据输入。

    SCLK 时钟信号,由主设备产生。

    CS 从设备片选信号,由主设备控制。

  从图中可以看出,主机和从机都有一个串行移位寄存器,主机通过向它的 SPI 串行寄存器写入一个字节来发起一次传输。寄存器通过 MOSI 信号线将字节传送给从机,从机也将自己的移位寄存器中的内容通过 MISO 信号线返回给主机。这样,两个移位寄存器中的内容就被交换。外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。

二、

  SPI 主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。

  SPI 总线四种工作方式  SPI  模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果CPOL=0,串行同步时钟的空闲状态为低电平;如果 CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果CPHA=0,在串行同步时钟的第一个跳变沿(CPOL位为0时就是下降沿,CPOL位为’1’时就是上升沿)数据被采样;如果 CPHA=1,在串行同步时钟的第二个跳变沿(CPOL位为0时就是下降沿,CPOL位为’1’时就是上升沿)数据被采样。SPI 主模块和与之通信的外设备时钟相位和极性应该一致。

  不同时钟相位下的总线数据传输时序如图 2 所示:

图2  不同时钟相位下的总线传输时序(CPHA=0/1)

  STM32 的 SPI 功能很强大,SPI 时钟最多可以到 18Mhz,支持 DMA,可以配置为 SPI 协议或者 I2S 协议(仅大容量型号支持) 。

  使用 STM32 的 SPI2 的主模式,下面就来看看 SPI2 部分的设置步骤吧。SPI 相关的库函数和定义分布在文件 stm32f10x_spi.c 以及头文件 stm32f10x_spi.h 中。

三、STM32 的主模式配置步骤如下:

  1)配置相关引脚的复用功能,使能 SPI2 时钟

  用 SPI2,第一步就要使能 SPI2 的时钟。其次要设置 SPI2 的相关引脚为复用输出,这样才会连接到 SPI2 上否则这些 IO 口还是默认的状态,也就是标准输入输出口。这里使用的是 PB13、14、15 这 3 个(SCK.、MISO、MOSI,CS 使用软件管理方式),所以设置这三个为复用 IO。

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(  RCC_APB2Periph_GPIOB, ENABLE );        //PORTB 时钟使能

RCC_APB1PeriphClockCmd(  RCC_APB1Periph_SPI2, ENABLE );        //SPI2 时钟使能  

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;             //PB13/14/15 复用推挽输出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);                  //初始化 GPIOB

  2)初始化 SPI2,设置 SPI2 工作模式

  接下来要初始化 SPI2,设置 SPI2 为主机模式,设置数据格式为 8 位,然设置 SCK 时钟极性及采样方式。并设置 SPI2 的时钟频率(最大 18Mhz),以及数据的格式(MSB 在前还是LSB 在前)。这在库函数中是通过 SPI_Init 函数来实现的。

    void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);

  第一个参数是 SPI 标号,这里我们是使用的 SPI2。下面我们来看看第二个参数结构体类型 SPI_InitTypeDef 的定义:

typedef struct

{

uint16_t SPI_Direction;

uint16_t SPI_Mode;

uint16_t SPI_DataSize;

uint16_t SPI_CPOL;

uint16_t SPI_CPHA;

uint16_t SPI_NSS;

uint16_t SPI_BaudRatePrescaler;

uint16_t SPI_FirstBit;

uint16_t SPI_CRCPolynomial;

}SPI_InitTypeDef;

结构体成员变量比较多,这里我们挑取几个重要的成员变量讲解一下:

  第一个参数 SPI_Direction 是用来设置 SPI 的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式,这里我们选择全双工模式 SPI_Direction_2Lines_FullDuplex。

  第二个参数 SPI_Mode 用来设置 SPI 的主从模式,这里我们设置为主机模式 SPI_Mode_Master,当然有需要你也可以选择为从机模式 SPI_Mode_Slave。

  第三个参数 SPI_DataSiz 为 8 位还是 16 位帧格式选择项,这里我们是 8 位传输,选择 SPI_DataSize_8b。

  第四个参数 SPI_CPOL 用来设置时钟极性,我们设置串行同步时钟的空闲状态为高电平所以我们选择 SPI_CPOL_High。

  第五个参数 SPI_CPHA 用来设置时钟相位,也就是选择在串行同步时钟的第几个跳变沿(上升或下降)数据被采样,可以为第一个或者第二个条边沿采集,这里我们选择第二个跳变沿,所以选择 SPI_CPHA_2Edge

  第六个参数 SPI_NSS 设置 NSS 信号由硬件(NSS 管脚)还是软件控制,这里我们通过软件控制 NSS 关键,而不是硬件自动控制,所以选择 SPI_NSS_Soft。

  第七个参数 SPI_BaudRatePrescaler 很关键,就是设置 SPI 波特率预分频值也就是决定 SPI 的时钟的参数 , 从不分频道 256 分频 8 个可选值,初始化的时候我们选择 256 分频值 SPI_BaudRatePrescaler_256,  传输速度为 36M/256=140.625KHz。

  第八个参数 SPI_FirstBit 设置数据传输顺序是 MSB 位在前还是 LSB 位在前, ,这里我们选择SPI_FirstBit_MSB 高位在前。

  第九个参数 SPI_CRCPolynomial 是用来设置 CRC 校验多项式,提高通信可靠性,大于 1 即可。

设置好上面 9 个参数,就可以初始化 SPI 外设了。初始化的范例格式为:

SPI_InitTypeDef SPI_InitStructure;

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;     //双线双向全双工

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                  //主 SPI

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;               // SPI 发送接收 8 位帧结构

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                 //串行同步时钟的空闲状态为高电平

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;                //第二个跳变沿数据被采样

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                   //NSS 信号由软件控制
  
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;  //预分频 256 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;             //数据传输从 MSB 位开始 SPI_InitStructure.SPI_CRCPolynomial = ;                   //CRC 值计算的多项式 SPI_Init(SPI2, &SPI_InitStructure);                       //根据指定的参数初始化外设 SPIx 寄存器
  3)使能 SPI2
  初始化完成之后接下来是要使能 SPI2 通信了,在使能 SPI2 之后,我们就可以开始 SPI 通讯了。使能 SPI2 的方法是:
     SPI_Cmd(SPI2, ENABLE); //使能 SPI 外设

  4)SPI 传输数据

  通信接口当然需要有发送数据和接受数据的函数,固件库提供的发送数据函数原型为:

    void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);//往 SPIx 数据寄存器写入数据  Data,从而实现发送。

  固件库提供的接受数据函数原型为:

    uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)  ;//从 SPIx 数据寄存器读出接受到的数据。

  5)查看 SPI 传输状态

  在 SPI 传输过程中,我们经常要判断数据是否传输完成,发送区是否为空等等状态,这是通过函数 SPI_I2S_GetFlagStatus 实现的,这个函数很简单就不详细讲解,判断发送是否完成的方法是:

    SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);

STM32学习笔记——SPI串行通讯(向原子哥学习)的更多相关文章

  1. STM32学习笔记——新建工程模板步骤(向原子哥学习)

    1.  在创建工程之前,先在电脑的某个目录下面建立一个文件夹,我们先把它命名为Template,后面建立的工程可以放在这个文件夹下.在 Template 工程目录下面,新建 3 个文件夹USER , ...

  2. 串行通讯之Qt

    目录 第1章 Qt 串行通讯    1 1.1 配置.pro文件    1 1.2 查询串口信息    1 1.3 配置.打开串口    3 1.4 setRequestToSend在Windows上 ...

  3. 【Arduino】使用C#实现Arduino与电脑进行串行通讯

    在给Arduino编程的时候,因为没有调试工具,经常要通过使用串口通讯的方式调用Serial.print和Serial.println输出Arduino运行过程中的相关信息,然后在电脑上用Arduin ...

  4. 串行通讯之UARTLoopback

    目录 第1章串行通讯之UARTLoopback    2 1 USB转串口    2 2 USB Accessory    2 3 连入手机    3 4 代码改进    4 5 打开串口    4 ...

  5. 串行通讯之.NET SerialPort

    第1章串行通讯之.NET SerialPort    2 1 枚举串口    2 2 打开/关闭串口    2 3 写数据    3 3.1 写二进制数据    3 3.2 写文本数据    4 4 ...

  6. COM口,串行通讯端口,RS-232接口 基础知识

    COM口即串行通讯端口. COM口的接口标准规范和总线标准规范是RS-232,有时候也叫做RS-232口.电脑上的com口多为9针,最大速率115200bps.通常用于连接鼠标(串口)及通讯设备(如连 ...

  7. MOOS学习笔记3——命令行

    MOOS学习笔记3--命令行 例程 /** * @code A simple example showing how to use a comms client问问怎么样 */ #include &q ...

  8. STM32L476应用开发之三:串行通讯实验

    在我们的项目需求中,有两个串口应用需求,一个是与炭氢传感器的通讯,另一个是与显示屏的通讯.鉴于此,我们需要实验串行通讯. 1.硬件设计 串行通讯一个采用RS232接口,另一个直接采用TTL方式.我们在 ...

  9. 基于51的串行通讯原理及协议详解(uart)

    串行与并行通讯方式并行:控制简单,传输速度快.线多,长距离成本较高且同时接受困难.串行:将数据字节分成一位一位的行驶在一条传输线上进行传输.如图:   同步与异步串行通讯方式同步串行通讯方式:同步通讯 ...

随机推荐

  1. hdoj 4883 TIANKENG’s restaurant【贪心区间覆盖】

    TIANKENG’s restaurant Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/O ...

  2. zoj 1671 Walking Ant【简单bfs】

    Walking Ant Time Limit: 2 Seconds      Memory Limit: 65536 KB Ants are quite diligent. They sometime ...

  3. (三)phpcms之文件目录

    刚刚接触phpcms,先从它的目录结构说起. 如下图所示,是phpcms的主目录结构: 其中api是接口目录,这个接口不是很明白.大概其是把别的内容加入进来,比如论坛啊什么的. caches是缓存文件 ...

  4. POJ 1001 Exponentiation 无限大数的指数乘法 题解

    POJ做的非常好,本题就是要求一个无限位大的指数乘法结果. 要求基础:无限大数位相乘 额外要求:处理特殊情况的能力 -- 关键是考这个能力了. 所以本题的用例特别重要,再聪明的人也会疏忽某些用例的. ...

  5. UNIX环境高级编程第二版代码笔记

    1. 第一个程序 gcc 1.1.c  /tmp/ccbnJqcB.o: In function `main': 1.1.c:(.text+0x17): undefined reference to ...

  6. Apple-Watch开发1

    Communicating between the iOS app and the Watch Extension There are four scenarios where an app and ...

  7. Java基础知识强化之集合框架笔记33:Arrays工具类中asList()方法的使用

    1. Arrays工具类中asList()方法的使用 public static <T> List<T> asList(T... a): 把数组转成集合 注意事项: 虽然可以把 ...

  8. svn 树冲突

    情况类似如下:(其中 removed_directory 是你工作的某个目录)  代码如下 复制代码 $svn statusD     C removed_directory>   local ...

  9. RPM工具

    一.RMP工具管理RPM包 rpm命令是RPM软件包的管理工具.rpm原本是Red Hat Linux发行版专门用来管理Linux各项套件的程序,由于它遵循GPL规则且功能强大方便,因而广受欢迎.逐渐 ...

  10. Android图表引擎AChartEngine之折线图使用

    最近在帮老师做一个课题,其中app端需要显示折线图以便直观地看数据波动,上网查了些资料后发现了这款图标引擎,另外感谢李坤老师的博客,帮助很大. 废话不多说,下面写代码. 一.AChartEngine是 ...