文档库 最新最全的文档下载
当前位置:文档库 › STM32----8---SPI

STM32----8---SPI

STM32----8---SPI
STM32----8---SPI

4月25日---- 4月27日SPI总线

经过两天的学习,哈哈SPI终于搞定,用STM32控制IIC EEPORM,将EEPROM中的数据读到SPI的FLASH中。

---------------------------------------------------------------------------------------------------------------------- SPI(Serial Peripheral Interface)总线:串行外围总线接口

一、SPI总线的简介:

SPI是由摩托罗拉公司开发的全双工同步串行总线,没有总线仲裁机制,所以工作在单主机系统中。主设备启动与从设备的同步通讯,从而完成数据的交换。对于只能做从机的器件接口来说,SPI接口由SDI(串行数据输入),SDO(串行数据输出),SCK(串行移位时钟)和CS(从使能信号)。通讯时,数据由SDO输出,SDI输入,数据在时钟的上升沿或者下降沿由SDO输出,在紧接着的下降沿或者上升沿由SDI读入,这样经过8/16帧,以完成8/16位数据的传输。

对于有的微控制器来说,它的硬件SPI即可做主机,也可以做从机,即可实现主从配置,MOSI:主出从入、MISO:主入从出、SCK:串行时钟、SS:从属选择。(当做主机输出时,该信号用于选中需要访问的从机,SS输入高电平,外部决定其为主机;当做从机时,SS为输入或者一般的IO口使用)。常用的SPI接法:在软件配置的条件下SS不使用。

1、SPI总线的通信时序

(1)在SPI通信中,在全双工模式下,发送和接收是同事进行的

(2)数据传输的时钟基来自主控制器的时钟脉冲;摩托罗拉没有定义任何通用的SPI 时钟的规范,最常用的时钟设置是基于时钟极性CPOL和时钟相位CPHA两个参数。

a)CPOL=0,表示时钟的空闲状态为低电平

b)CPOL=1,表示时钟的空闲状态为高电平

c)CPHA=0,表示同步始终的第一个边沿(上升或者下降)数据被采样

d)CPHA=1,表示同步始终的第二个边沿(上升或者下降)数据被采样

即CPOL和CPHA的设置决定了数据采样的时钟沿。

(3)在多个从设备的系统中,每个设备需要独立的使能信号,硬件比I2C系统复杂(4)没有应答机制确定是否收到数据,没有I2C总线系统安全。

(5)SPI主机与之通信的从机的时钟极性和相位应该一致

i.主设备SPI时钟和极性的配置由从机来决定

ii.主设备的SDO、SDI和从设备的SDO、SDI一致

iii.主从设备是在SCLK的控制下,同时发送和接受数据,并通过两个双向移位寄存器来交换数据。

iv.工作原理如下:上升沿输出数据,下降沿输入数据

(6)SPI的四种工作模式

SPI通信是串行发送或接收数据的,即一位一位的发送和接收、且传输一般是高位MSB 在前,低位在后LSB。

2、SPI总线通信的软件模拟

(1)根据从机来选择SPI的工作模式(SPI0、SPI1、SPI2、SPI3),本例选择SPI0 (2)上升沿输入数据,下降沿输出数据(AT45DB161D)

a)主机发送数据,即从机接收数据

SCK=0 ;

MOSI=n(1或0); //接从机的SDI引脚

Asm("NOP"); //也许有适当的延时以使数据稳定

SCK=1; //产生一个上升沿,也就是在上升沿来时把数据读走

b)主机接收数据,即从机发送数据(接收一位数据)

SCK=1;//

SCK=0;//产生一个下降沿

Bit=MISO; //连接从机的SDO引脚

c)主机向从机写入一个字节的数据

Void SPI_Write(unsigned char data)

{

Uchar i;

Bit temp;

For(i=0;i<8;i++)

{

Temp=(bit)(dat&0x80);

Sck=0;

MOSI=temp;

Asm("NOP");

SCK=1; //产生一个上升沿

Data=data<<1;//传输下一位数据

}

}

d):从机向主机发送一个字节的数据

Uchar spi _read(void)

{

Uchar i,data =0;

For(i=0;i<8;i++)

{

Data=data<<1;

Sck=1;

SCK=0;

Data|=MISO;//连接从机的SDO引脚

}

Return data;

}

SPI3

SPI1

SPI2

SPI0

二、STM32中的硬件SPI

1、特点:

(1)2个SPI接口:SPI1位于告诉总线的APB2总线上、SPI2位于低俗的APB1总线上。

(2)硬件接口有:3根弦的全双工同步通讯;2根线的单双工同步通讯;1根线的双向数据线(另外一根时钟线)

(3)数据帧宽度可编程:支持8位和16位数据传输格式。(SPI_CR1的DFF位)(4)可编程的数据传输顺序:高位在前或者低位在前(SPI_CR1寄存器中的

LSBFIRST位)

(5)可作为主设备也可作从设备(SPI_CR1寄存器的MSTR位)

(6)可编程的位传输速率:在主/从模式下,最大都可达18MHZ(SPI_CR1寄存器中的BR[2:0]位)

(7)可通过硬件或软件控制主从设备的NSS引脚,使用软件时,可动态改变主从模式

(8)可编程的时钟极性CPOL和时钟相位CPHA,可选择SPI四种模式中的一种

(9)带终端性能的发送和接受标志:发送缓冲区空(SPI_SR寄存器中的TXE位),接收缓冲区非空(SPI_SR中的RXNE位)。

(10)SPI总线忙状态标志位(SPI红的BSY位)

(11)带中断性能的主模式故障或溢出错误标志位和CRC错误标志:

a)主模式故障:在硬件模式下:主设备的NSS脚被拉低;在软件模式下,

SSI位被复位(SPI_SR中的MODF被硬件置位)。

b)溢出错误:从设备在接收新数据时,而前一个数据还没有被读出,即发出

溢出错误。

(12)拥有可靠通信的硬件CRC特性:在发送模式下CRC的值作为最后一个字节发送;对接收到的最后一个字节自动进行CRC校验(只使用与全双工通信,CRC8算法用于8位帧格式,CRC16算法用于16位帧格式)

2、使用硬件SPI容易出错的地方

(1)SPI主设备和与之通信的从设备的时钟极性和时钟相位应该一致,并以从设备为基准

(2)SPI仅在数据发送时才有时钟输出,一旦传输完毕,时钟就停止

(3)SPI从设备在主设备需要接收数据时才能发送数据,主设备和从设备同时进行的。

(4)SPI主设备向从设备发送数据时,直接发送即可,丢弃堵回来的数据即可。但在接收数据时,实质上也是发送数据,为接收数据提供时钟信号。

3、NSS的软硬件管理

NSS引脚可做为输入,也可作为输出引脚。(NSS作为输入模式下,才能实现多主模式)。(1)硬件模式(SPI_CR1中SSM位复位,即禁止软件模式缺点:模式固定了,灵活性差)

(2)软件模式

此时由SSMbit 位决定,SSM 位为0,禁止软件模式;SSM 为1,开启软件模式,在这种模式下外部NSS 引脚作为GPIO 用,而内部的NSS 信号电平由SPI_CR1的SSI 位来驱动。

优点:主模式和从模式下的NSS 引脚可做GPIO 用,可动态的改变主从模式,在同一

个应用中,将SPI 从从模式切换到从模式或从从模式切换到主模式,而不改变硬件。

注意:在与被控制器件连接时,只用SCK 、MOSI 、MISO 三条线即可,用软件控制NSS 使之处于主机模式,通过GPIO 选择被控器件即可。

4、SPI 接口硬件设计

(1)全双工、三线传输:以下为硬件模式

配置为:SPI _CR1:BIDIMODE=0.RXONL Y=0,SSM=0

NSS 为输入模式 NSS 为输入模式

NSS 引脚GPIO 用

NSS 引脚GPIO 用

由SSI 位决定

SPI_CR1:SSOE=0 输出使能为0

(2)单工通讯:(减少IO口的数量)

a)双向通讯:一根时钟线和一根双向数据线BIDIMODE=1(单线双向),

BIDIOE=0(只收模式)、BIDIOE=1(只发模式)

b)只接收:1根时钟线和一根单项数据线(即全双工的只接收模式,配置

BIDIMODE=0,RXONL Y=1)

5、STM32 SPI功能框图

工作原理:在接收时(MISO),接收到的数据被存放在一个内部的接收缓冲器中;在发送时(MOSI),在被发送之前,数据将首先被存放在一个内部的发送缓冲器中。对SPI_DR寄存器的读操作,将返回接收缓冲器的内容;写入SPI_DR寄存器的数据将被写入发送缓冲器中。主模式下开始传输

● 全双工模式(BIDIMODE=0并且RXONL Y=0)

─ 当写入数据到SPI_DR寄存器(发送缓冲器)后,传输开始;

─ 在传送第一位数据的同时,数据被并行地从发送缓冲器传送到8位的移位寄存器中,

然后按顺序被串行地移位送到MOSI引脚上;

─ 与此同时,在MISO引脚上接收到的数据,按顺序被串行地移位进入8位的移位寄存器中,然后被并行地传送到SPI_DR寄存器(接收缓冲器)中。

注意:也就是说,在主机模式下,发送和接收是同时进行的,所以我们发送了一个数据,也就能接收到一个数据。而STM32内部硬件是这个过程的支撑!

● 单向的只接收模式(BIDIMODE=0并且RXONL Y=1)

─ SPE=1时,传输开始;

─ 只有接收器被激活,在MISO引脚上接收到的数据,按顺序被串行地移位进入8位的移位寄存器中,然后被并行地传送到SPI_DR寄存器(接收缓冲器)中。

● 双向模式,发送时(BIDIMODE=1并且BIDIOE=1)

─ 当写入数据到SPI_DR寄存器(发送缓冲器)后,传输开始;

─ 在传送第一位数据的同时,数据被并行地从发送缓冲器传送到8位的移位寄存器中,

然后按顺序被串行地移位送到MOSI引脚上;

─ 不接收数据。

● 双向模式,接收时(BIDIMODE=1并且BIDIOE=0)

─ SPE=1并且BIDIOE=0时,传输开始;

─ 在MOSI引脚上接收到的数据,按顺序被串行地移位进入8位的移位寄存器中,然后被并行地传送到SPI_DR寄存器(接收缓冲器)中。

─ 不激活发送器,没有数据被串行地送到MOSI引脚上。

三:STM32 SPI的驱动程序

------spi.h

#ifndef __SPI_H

#define __SPI_H

#include "stm32f10x_lib.h"

void SPIx_Init(void); //初始化SPI口

u8 SPIx_ReadWriteByte(u8 TxData);//SPI总线读写一个字节

#endif

-----spi.c

#include "spi.h"

/*************************************************************************** - 功能描述:STM32f103 SPI接口配置的初始化函数

- 隶属模块:STM32 SPI操作

- 函数属性:外部,使用户使用

- 参数说明:无

- 返回说明:无

- 函数实现步骤:

(1)SPI1在没有重映射的条件下NSS->PA4、SCK->PA5、MISO->PA6、MOSI->MOSI,由于STM32要处于主机模式且用软件模式,所以NSS不用

(2)初始化GPIO管脚和SPI的参数设置:建立SPI和GPIO的初始化结构体

★(3)在配置GPIO的PA5、PA6、PA7时将其配置为复用输出,在复用功能下面,输入输出的方向,完全由内部控制.不需要程序处理.

(4)配置FLASH的片选信号线PA2,并设为高电平,也就是不选中FLASH

(5)打开GPIO和SPI1的时钟

(6)配置SPI1的参数SPI的方向、工作模式、数据帧格式、CPOL、CPHA、NSS软件还是硬件、SPI时钟、数据的传输位、以及CRC

(7)利用SPI结构体初始化函数初始化SPI结构体、并使能SPI1

(8)最后启动传输发送一个0xff,其实也可以不发

以下是SPI模块的初始化代码,配置成主机模式,访问SD Card/W25X16/24L01/JF24C

***************************************************************************/ void SPIx_Init(void)

{

//(2)

SPI_InitTypeDef SPI_InitStructure;

GPIO_InitTypeDef GPIO_InitStructure;

//(3)

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;

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

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOA, &GPIO_InitStructure);

//(4)

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //SPI CS

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //通用推挽输出

GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_SetBits(GPIOA,GPIO_Pin_2);

//(5)

RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE );

//(6)

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工

SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //选择了串行时钟的稳态:时钟悬空高

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //数据捕获于第二个时钟沿

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定义波特率预分频的值:波特率预分频值为256

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB 位开始:数据传输从MSB位开始

SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式

SPI_Init(SPI1, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx 寄存器

//(7)

SPI_Cmd(SPI1, ENABLE); //使能SPI外设

SPIx_ReadWriteByte(0xff);//启动传输

}

/*************************************************************************** - 功能描述:STM32f103 SPI读写字节函数

- 隶属模块:STM32 SPI操作

- 函数属性:外部,使用户使用

- 参数说明:TxData:要写入的字节

- 返回说明:读取到的字节

- 函数说明:由于主机SPI通信时,在发送和接受时是同时进行的,即发送完了一个字节的数据后,也应当接受到一个字节的数据

(1)stm32先等待已发送的数据是否发送完成,如果没有发送完成,并且进入循环200次,则表示发送错误,返回收到的值为0;

(2)如果发送完成,stm32从SPI1总线发送TxData

(3)stm32再等待接收的数据是否接收完成,如果没有接收完成,并且进入循环200次,则表示接收错误,则返回值0

(4)如果接收完成了,则返回STm32读取的最新的数据

stm32

------->等待已发送的数据是否完成

OK

------->

stm32发送数据

------->等待待接收的数据是否完成

OK

------->

stm32读取数据

***************************************************************************/

u8 SPIx_ReadWriteByte(u8 TxData)

{

u8 retry=0;

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI 标志位设置与否:发送缓存空标志位

{

retry++;

if(retry>200)

return 0;

}

SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个数据

retry=0;

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); //检查指定的SPI标志位设置与否:接受缓存非空标志位

{

retry++;

if(retry>200)return 0;

}

return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据

}

有了以上的STM32 SPI驱动就可以去驱动SPI的任意器件,但是要注意SPI器件的操作驱动函数还要根据所用的SPI器件手册进行书写。

字符统计的函数strlen 和sizeof的书写:

(1)strlen函数,不区分是数组还是字符串,读到\0为止返回长度,而且strlen是不把\0计入长度。

(2)Sizeof:

a)Char *str1="abcdef"; sizeof(str1)= 4是指针的字节数

b)Char str2[]="abcde"; sizeof(str2)= 6

c)Char str2[8]={s}; sizeof(str2)= 8

d)Char ss[]="0123456789"; sizeof(ss)= 11

相关文档