STM32SPI文档格式.docx
《STM32SPI文档格式.docx》由会员分享,可在线阅读,更多相关《STM32SPI文档格式.docx(19页珍藏版)》请在冰豆网上搜索。
![STM32SPI文档格式.docx](https://file1.bdocx.com/fileroot1/2023-1/2/503080e8-299a-4bcb-8352-ef2daeb87077/503080e8-299a-4bcb-8352-ef2daeb870771.gif)
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)主机向从机写入一个字节的数据
VoidSPI_Write(unsignedchardata)
{
Uchari;
Bittemp;
For(i=0;
i<
8;
i++)
Temp=(bit)(dat&
0x80);
Sck=0;
MOSI=temp;
//产生一个上升沿
Data=data<
<
1;
//传输下一位数据
}
d):
从机向主机发送一个字节的数据
Ucharspi_read(void)
Uchari,data=0;
Sck=1;
SCK=0;
Data|=MISO;
//连接从机的SDO引脚
Returndata;
SPI3
SPI1
SPI2
SPI0
2、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.RXONLY=0,SSM=0
SPI_CR1:
SSOE=0输出使能为0
(2)单工通讯:
(减少IO口的数量)
a)双向通讯:
一根时钟线和一根双向数据线BIDIMODE=1(单线双向),BIDIOE=0(只收模式)、BIDIOE=1(只发模式)
b)只接收:
1根时钟线和一根单项数据线(即全双工的只接收模式,配置BIDIMODE=0,RXONLY=1)
5、STM32SPI功能框图
工作原理:
在接收时(MISO),接收到的数据被存放在一个内部的接收缓冲器中;
在发送时(MOSI),在被发送之前,数据将首先被存放在一个内部的发送缓冲器中。
对SPI_DR寄存器的读操作,将返回接收缓冲器的内容;
写入SPI_DR寄存器的数据将被写入发送缓冲器中。
主模式下开始传输
●全双工模式(BIDIMODE=0并且RXONLY=0)
─当写入数据到SPI_DR寄存器(发送缓冲器)后,传输开始;
─在传送第一位数据的同时,数据被并行地从发送缓冲器传送到8位的移位寄存器中,
然后按顺序被串行地移位送到MOSI引脚上;
─与此同时,在MISO引脚上接收到的数据,按顺序被串行地移位进入8位的移位寄存器
中,然后被并行地传送到SPI_DR寄存器(接收缓冲器)中。
也就是说,在主机模式下,发送和接收是同时进行的,所以我们发送了一个数据,也就能接收到一个数据。
而STM32内部硬件是这个过程的支撑!
●单向的只接收模式(BIDIMODE=0并且RXONLY=1)
─SPE=1时,传输开始;
─只有接收器被激活,在MISO引脚上接收到的数据,按顺序被串行地移位进入8位的移
位寄存器中,然后被并行地传送到SPI_DR寄存器(接收缓冲器)中。
●双向模式,发送时(BIDIMODE=1并且BIDIOE=1)
─不接收数据。
●双向模式,接收时(BIDIMODE=1并且BIDIOE=0)
─SPE=1并且BIDIOE=0时,传输开始;
─在MOSI引脚上接收到的数据,按顺序被串行地移位进入8位的移位寄存器中,然后被
并行地传送到SPI_DR寄存器(接收缓冲器)中。
─不激活发送器,没有数据被串行地送到MOSI引脚上。
三:
STM32SPI的驱动程序
------spi.h
#ifndef__SPI_H
#define__SPI_H
#include"
stm32f10x_lib.h"
voidSPIx_Init(void);
//初始化SPI口
u8SPIx_ReadWriteByte(u8TxData);
//SPI总线读写一个字节
#endif
-----spi.c
spi.h"
/***************************************************************************
-功能描述:
STM32f103SPI接口配置的初始化函数
-隶属模块:
STM32SPI操作
-函数属性:
外部,使用户使用
-参数说明:
无
-返回说明:
-函数实现步骤:
(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模块的初始化代码,配置成主机模式,访问SDCard/W25X16/24L01/JF24C
***************************************************************************/
voidSPIx_Init(void)
//
(2)
SPI_InitTypeDefSPI_InitStructure;
GPIO_InitTypeDefGPIO_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;
//SPICS
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
//通用推挽输出
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);
//启动传输
}
STM32f103SPI读写字节函数
TxData:
要写入的字节
读取到的字节
-函数说明:
由于主机SPI通信时,在发送和接受时是同时进行的,即发送完了一个字节的数据后,也应当接受到一个字节的数据
(1)stm32先等待已发送的数据是否发送完成,如果没有发送完成,并且进入循环200次,则表示发送错误,返回收到的值为0;
(2)如果发送完成,stm32从SPI1总线发送TxData
(3)stm32再等待接收的数据是否接收完成,如果没有接收完成,并且进入循环200次,则表示接收错误,则返回值0
(4)如果接收完成了,则返回STm32读取的最新的数据
stm32
------->
等待已发送的数据是否完成
OK
stm32发送数据
等待待接收的数据是否完成
stm32读取数据
u8SPIx_ReadWriteByte(u8TxData)
{
u8retry=0;
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET)//检查指定的SPI标志位设置与否:
发送缓存空标志位
{
retry++;
if(retry>
200)
return0;
}
SPI_I2S_SendData(SPI1,TxData);
//通过外设SPIx发送一个数据
retry=0;
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)==RESET);
//检查指定的SPI标志位设置与否:
接受缓存非空标志位
200)return0;
}
returnSPI_I2S_ReceiveData(SPI1);
//返回通过SPIx最近接收的数据
有了以上的STM32SPI驱动就可以去驱动SPI的任意器件,但是要注意SPI器件的操作驱动函数还要根据所用的SPI器件手册进行书写。
字符统计的函数strlen和sizeof的书写:
(1)strlen函数,不区分是数组还是字符串,读到\0为止返回长度,而且strlen是不把\0计入长度。
(2)Sizeof:
a)Char*str1="
abcdef"
;
sizeof(str1)=4是指针的字节数
b)Charstr2[]="
abcde"
sizeof(str2)=6
c)Charstr2[8]={s};
sizeof(str2)=8
d)Charss[]="
0123456789"
sizeof(ss)=11
通常SPI通过4个引脚与外部器件相连:
●MISO:
主设备输入/从设备输出引脚。
该引脚在从模式下发送数据,在主模式下接收数据。
●MOSI:
主设备输出/从设备输入引脚。
该引脚在主模式下发送数据,在从模式下接收数据。
●SCK:
串口时钟,作为主设备的输出,从设备的输入
●NSS:
从设备选择。
这是一个可选的引脚,用来选择主/从设备。
它的功能是用来作为“片选引脚”,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。
从设备的NSS引脚可以由主设备的一个标准I/O引脚来驱动。
一旦被使能(SSOE位),NSS引脚也可以作为输出引脚,并在SPI处于主模式时拉低;
此时,所有的SPI设备,如果它们的NSS引脚连接到主设备的NSS引脚,则会检测到低电平,如果它们被设置为NSS硬件模式,就会自动进入从设备状态。
当配置为主设备、NSS配置为输入引脚(MSTR=1,SSOE=0)时,如果NSS被拉低,则这个SPI设备进入主模式失败状态:
即MSTR位被自动清除,此设备进入从模式。
时钟信号的相位和极性
SPI_CR寄存器的CPOL和CPHA位,能够组合成四种可能的时序关系。
CPOL(时钟极性)位控制在没有数据传输时时钟的空闲状态电平,此位对主模式和从模式下的设备都有效。
如果CPOL被清’0’,SCK引脚在空闲状态保持低电平;
如果CPOL被置’1’,SCK引脚在空闲状态保持高电平。
如果CPHA(时钟相位)位被置’1’,SCK时钟的第二个边沿(CPOL位为0时就是下降沿,CPOL位为’1’时就是上升沿)进行数据位的采样,数据在第二个时钟边沿被锁存。
如果CPHA位被清’0’,SCK时钟的第一边沿(CPOL位为’0’时就是下降沿,CPOL位为’1’时就是上升沿)进行数据位采样,数据在第一个时钟边沿被锁存。
CPOL时钟极性和CPHA时钟相位的组合选择数据捕捉的时钟边沿。
图212显示了SPI传输的4种CPHA和CPOL位组合。
此图可以解释为主设备和从设备的SCK脚、MISO脚、MOSI脚直接连接的主或从时序图。
上图显示了SPI传输的4种CPHA和CPOL位组合。
1.在改变CPOL/CPHA位之前,必须清除SPE位将SPI禁止。
2.主和从必须配置成相同的时序模式。
3.SCK的空闲状态必须和SPI_CR1寄存器指定的极性一致(CPOL为’1’时,空闲时应上拉SCK为高电平;
CPOL为’0’时,空闲时应下拉SCK为低电平)。
4.数据帧格式(8位或16位)由SPI_CR1寄存器的DFF位选择,并且决定发送/接收的数据长度。
我只要知道主机和从机的CPOL和CPHA位要一致就够了。
有2种NSS模式:
●软件NSS模式:
可以通过设置SPI_CR1寄存器的SSM位来使能这种模式。
在这种模式下NSS引脚可以用作它用,而内部NSS信号电平可以通过写SPI_CR1的SSI位来驱动
●硬件NSS模式,分两种情况:
─NSS输出被使能:
当STM32F10xxx工作为主SPI,并且NSS输出已经通过SPI_CR2寄存器的SSOE位使能,这时NSS引脚被拉低,所有NSS引脚与这个主SPI的NSS引脚相连并配置为硬件NSS的SPI设备,将自动变成从SPI设备。
当一个SPI设备需要发送广播数据,它必须拉低NSS信号,以通知所有其它的设备它是主设备;
如果它不能拉低NSS,这意味着总线上有另外一个主设备在通信,这时将产生一个硬件失败错误(HardFault)。
─NSS输出被关闭:
允许操作于多主环境。
//我们用软件NSS主从的转换都可借助库来实现
数据帧格式
根据SPI_CR1寄存器中的LSBFIRST位,输出数据位时可以MSB在先也可以LSB在先。
根据SPI_CR1寄存器的DFF位,每