✅ 操作成功!

i2s协议

发布时间:2023-06-06 作者:admin 来源:文学

i2s协议

i2s协议

嗜血综合征-pn10

2023年2月20日发(作者:世界十大珍稀动物)

STM32---SPI通信的总结(库函数操作)

本文主要由7项内容介绍SPI并会在最后附上测试源码供参考:

的通信协议

通信初始化(以STM32为从机,LPC1114为主机介绍)

的读写函数

的中断配置

的SMA操作

6.测试源码

7.易出现的问题及原因和解决方法

一、SPI的通信协议

SPI(SerialPeripheralInterfac)是一种串行同步通讯协议,由一个主设备

和一个或多个从设备组成,主设备启动一个与从设备的同步通讯,从而完成数据的

交换。SPI接口一般由4根线组成,CS片选信号(有的单片机上也称为NSS),

SCLK时钟信号线,MISO数据线(主机输入从机输出),MOSI数据线(主机输

出从机输入),CS决定了唯一的与主设备通信的从设备,如没有CS信号,则

只能存在一个从设备,主设备通过产生移位时钟信号来发起通讯。通讯时主机的数

据由MISO输入,由

MOSI输出,输入的数据在时钟的上升或下降沿被采样,输出数据在紧接着的下降

或上升沿被发出(具体由SPI的时钟相位和极性的设置而决

定)

二、以STM32为例介绍SPI通信

32f103带有3个SPI模块其特性如下:

2221SPI特征

■3线全双工冋步棲输

•帯或不帯第二根双向数按线的职线鴨工同步性输

•8或16位传输帧格式选提

■主或从操种

•支持多主模式

•8个丄模式波特率预分频系数(最大为fpcix/2)

•从摸戒频最大为fpcLx/2)

•主橈式和从模式的快連通缶:摄大SPI速度达到18MHz

•卜:模式和从模式卜均可以由牧件或硬什进行NSS管理]I/从操作摸式的动态改变

•可编程的吋钟极性和相位

•可编程的数据顺斥・MSB在刑或LSB在前

•可触竝中凝的专用笈送和接收标蛊

•SPIS线忙状态様志

•龙持可靠通陆的硬fICRC

2SPI初始化

初始化SPI主要是对SPI要使用到的引脚以及SPI通信协议中时钟相位

和极性进行设置,其实STM32的工程师已经帮我们做好了这写工作,调

用库函数,根据自己的需要来修改其中的参量来完成自己的配置即可,

主要的配置是如下几项:

引脚的配置

SPI1的SCLK,MISO,MOSI分别是PA5,PA6,PA7弓I脚,这几个弓I脚的模

式都配置成GPIO_Mode_AF_PP复用推挽输出(关于GPIO的8种工作模式如不清

楚请自己百度,在此不解释),如果是单主单从,CS引脚可以不配置,都设置成软

件模式即可。

/*ConfigureSPI1pins:SCK,MISOandMOSI*/

GP10_InitStructure=GPI0_P^n_5IGPI0_Pin_6IGPIO_Pin_7;

GPIo2IrLitstructure-GPIOZMode=GPIO_Mode_AF_PP?

GPIO^InitS七匸口^Speed=GPIO_Speed_50MHz;

GPIo2Init(GPIOA

r

&GPIOTnitStructureT;

/*ConfigureI/OforFlashChipselect

GPIO__Pin=GPIO_Pin_4;

GPIO^InitStr口匚tuie=GP10_Mode_AF_PP;/7片选

GPIO_Init(GPIOA

r

&GPIO_);

通信参数的设置

SPI__Directicn=SPI_Direction_2Lines_FullDuplex;//全双工通f言

SPI__Mode=SPI_Mode_S1ave;//做从机

SPI_Iriitstructure-spi_DataSize=SPI_DataSizE_&b;//Sffl

SM^L=SEXCPOLHigh;77空闲时刻为高电平SPl'^CPHA=

SPrcPHA^2Edge;//数据住芻2个跳边沿被采集//////////SPI^~NSS=SPI_NSS_Soft;“C!

吕引廊为软件横我

SPI_lnitStructure

k

SPI_SaudRatePrescaler=SPI_BaudRatePrescaler8;"8分频

SPI^InitStructure

b

SPl2FirStBit=SPl_FirstBLtJiSB;"先传高字节

SPI__CRCPolyn0mial=7;SPI^Init(SPI1,iSPI_liiitSt工ucture);

_Direction_2Lines_FullDuplex把SPI设置成全双工通信;

2.在SPI_Mode里设置你的模式(主机或者从机),

_DataSize是来设置数据传输的帧格式的SPI_DataSize_8l是指8

位数据帧格式,也可以设置为SPI_DataSize_16即卩16位帧格式

_CPOL和SPI_CPHA是两个很重要的参数,是设置SPI通信时

钟的极性和相位的,一共有四种模式

CPOLCPOH

mode0

00

mode1

01

mode2

10

mode3

11

在库函数中CPOL有两个值SPI_CPOL_High(=1)和SPI_CPOL_Low(=0).

CPHA有两个值SPI_CPHA_1Edge(=0)禾口SPI_CPHA_2Edge(=1)

CPOL表示时钟在空闲状态的极性是高电平还是低电平,而CPHA则表示

数据是在什么时刻被采样的,手册中如下:

&1

CPOL:时钟极性

0:空闲状态时,SCK保持低也、佗

1:它闲状态时.SCK保持高电平・

注意=当通信正在进行的時候,不能修改该也・注意:Ps模式

下不梗用.

位0

CPHA:时钟相位

0:数据采样从第•个时钟边沿开始:

1:散据采样从第二个时种边沿开始匚

注意;当通信正在进行的肘候,不能條改该位.注意:Fs模

式卜•不使用.

我的程序中主、从机的这两位设置的相同都是设置成1,即空闲时时钟是高电平,数据

再第二个时钟沿被采样,实验显示数据收发都正常。

(要特别注意极性和相位的设置否则,数据传输会出现错位的现象)一般主从机的

这两个位要设置的一样,但是网上也有人说不能设置成一样的,在后文中我对主从机

极性和相位的配置的16种情况都做了测试,结果见下文。

下图很好的描述了4种模式下的时序状况

射1

引用网友的一句话:

“SPI主模块和与之通信的外设备时钟相位和极性应该一致。个人理解这句话有2层意思:其一,

主设备SPI时钟和极性的配置应该由外设的从设备来决定;其二,二者的配置应该保持一致,即主设备的SDO同从设备

的SDO配置一致,主设备的SDI同从设备的SDI配置一致。因为主从设备是在SCLK的控制下,同时发送和接收数据,并

通过2个双向移位寄存器来交换数据。”

_BaudRatePrescaler波特率的设置

这在主机模式中,这一位的设置直接决定了通信的传输速率,而从机的设

置不会影响数据传输的速率,手册中有这样一句话:

2SPI从模式

在从配置里.SCK引脚用于接收到从主设备来的串厅吋钟。SPLCR1寄%耕屮酮[2:0]的世押不影响数据传输

速率*

_FirstBit这一位是设置首先传输的高字节还是低字节

SPI_FirstBit_MSB是先传输高字节,SPI_FirstBit_LSB是先传输低字节

注意在初始化函数里还有两项重要的内容就是在初始化之前先使能SPI的

时钟和在初始化配置完成后使能SPI。

/*EnableSPI1andGPIOclocks*/

RCC_APB2Periph匚:Lockand(Rrxr—TkPBZPmriph—EPi::!.|RCC_APB2Pariph_GpIOA

7

ENABLE);

(........••初始化配置........)

/*EnableSPI1*/SPI_Cmd(SPI1/ENABLE);|

三、SPI的读写函数

SPI有一个16位的数据寄存器SPI_DR,它对应两个缓冲区,1个发送缓冲区,1个接

收缓冲区,当在控制寄存器里SPI_CR1里对DFF位设置数据帧格式为8位时,发送

和接收只用到SPI_DR[7:0这8位,15-8位被强制为0,帧格式设置成16位时全用。

读写过程在手册中是这样描述的:

数据发送过程

数拥字被并行地肓入发送缓冲器。

当从设备收到时钟信号.井且在MOSI引脚匕出现第一个数据位时,发送过程开始*第个位被发送

出上.余下的位(对于8位数据帧格式,还冇了位;对于16位数据帧格式,还冇15位)被装进移

位寄存器。勺发迖缓肝器川的数掘传输到移位寄存器时.SPISP奇存黯里的TXE标志被设置。如

果设置了API-CR2寄存器上的TXEIE位,将会产牛中断。

对于接收方.肖数据接收完成时;

•移位寄存器中的数据传送到接收缓冲器、SPI_SR寄存器屮的RXNE标盅被设蜀。

•如果设置了SPI_CR2寄存器中的RXEIE位,则产生中断。

在最后一个采样时钟边沿后.RXNEfW被置T・榕位寄存器中接收到的数据字节被传送到接收缓

冲器*当读SPI_DR寄存器吋,SPI设备返回这个值。

读寄存器吋,RX忖Efd波清除。

简而言之,

发送时,可以通过检测SPI_SR中的TXE位,当数据寄存器里有数据时,

TXE位是0,当数据全部从数据寄存器的发送缓冲区传输到移位寄存器时

TXE位被置1,这时候可以再往数据寄存器里写入数据。可以通过

while(SPI_l2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET来检测。

SPI_l2S_GetFlagStatus(SPI1SPI_I2S_FLAG_TXE是库函数可以检测SPI的一些状态位。

接收时

可以通过检测SPI_SR中的RXNE位,当数据寄存器里有数据时,RXNE位是0,当数

据全部从数据寄存器的接收缓冲区传输到移位寄存器时RXNE位被置1,这时候可以从

数据寄存器里读出数据。可以通过

while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)==RESET来;检

测。

源程序如

下,

SPI读写一个字节,读写一体

//SPI1的读写

a3SPl_ccnniuniCflition_sendByte(uSbyt&}

{-—

/*LoopwhileDRregisterinnotemplty*/

while(SPI_l2S_GetFlagStatus(SPI1,SPII2SFLAGTXE)==RESET);/*Sendbyte■through,theSPI1peripheral*/

SPI_l2S_SendData(SPI1

7

byte);

/*Waittoreceiveabyte*/

Whil&(SPI_l2S_G&tFlagEtatlls(SPIlj,SPI_I2S_FLAG_RXNE)==FLESET);Returnthebyt?rsadfromtheSPIbus*/

returnSPII2SReceiveData(SPI1);

当能成功发送和接收一个字节时,发送数组数据就变的简单了,只需要一个for循环,

和指针变量的递增即可。以下仅为参考:

(有一点特别注意,从机数据传输时要依赖主机的时钟,所以主机在接收从机发送的数

据时要往从机发送哑巴字节,这个字节可以自己定义0xff,0xfe等什么字节都可以)

读写分开的函数:

/*Description:spil通信发送数据*/

voidSPI_Ecah_Buffer_Send(u8*pBuffer,u16NumByteToRead)

{

for(inti=0;i

SPI_Conmunication_SendByte(*pBuffer);

pBuffer++;

}

}

/*Description:spil通信接收收据*/

voidSPI_Buffer_Receive(u8*pBuffer,u16NumByteToRead)

{

while(NumByteToRead--)/*whilethereisdatatoberead*/

{

/*ReadabytefromtheFLASH*/

*pBuffer=SPI_Conmunication_SendByte(Dummy_Byte);

/*Pointtothenextlocationwherethebytereadwillbesaved*/

pBuffer++;

}

}

读写一体的函数

/*Description:spi1通信发送接收读写数据*/

voidSPI_Ecah_Buffer_Send(u8*str,u8*pBuffer,u16NumByteToRead)

{

for(inti=0;i

{

*str=SPI_Conmunication_SendByte(*pBuffer);

pBuffer++;

str++;

}

}

四、SPI的中断配置

在SPI的SPI_CR2中可以配置,STM32的SPI的通信一共有8个中断其中最常用的

是如下4个。

TXEIE:发送缓冲区空中断使能在发送过程中,数据全部从数据寄存器的发送缓冲区

传输到移位寄存器时

TXE位被置1这时如果使能了TXEIE就会触发发送完成的中断请求。在中断服务函

数里可以做你想做的事情,也可以用一个标志位,在外面完成相应的操作。

(使用中断时要特别注意,及时的清除中断标志,为下一次能够触发中断做准备。)

RXNEIE:接收缓冲区非空中断使能

接收同发送。

TXDMAEN:发送缓冲区DMA使能

RXDMAEN:接收缓冲区DMA使能

位7TXEIE:发送缓冲区空屮断使能

0:集止TXE中断;

1:允许TXE中斯,当TXE标总耗位时产生中断请求.

注意:不要同时设^TXEIE和TXDMAEN,

手册中有这样一句话,“不能同时设置TXEIE和TXDMAEN”这一点要特别注意。

也就是说如果你在SPI的通信中不用DMA则使能TXEIE的中断,不使能TXDMAEN

的中断,如果在SPI中使用DMA传输,则禁能TXEIE的中断,只使能TXDMAEN的

中断。

五、SPI的DMA操作

DMA(DirectMemoryAccess)直接内存存取,直接存储器存取用来提供在外设

和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU任何干

预,通过DMA数据可以快速地移动。使用DMA最大的特点就是数据传输

不经过CPU这就节省了CPU的资源,让CPU能有更多的时间来做其他的事情。

SPI的DMA操作,就是在SPI->TXE为1时,会向对应的DMA通道发出请求,

DMA通道会发出应答信号,SPI收到应答信号后撤销请求信号,DMA撤销应答信号,

并把内存值装入SPI_DR的发送缓区,SPI的传送开始。

DMA的初始化

voidDMATXInit(void)

{

RCC_AHBPeriphclockQnd(RCC_AHEPeriph__DMAl

r

ENABLE)fDMA_InitTypeD®fDMA__InitStruetuz:总;

Ju32)SPll_DR_Addr;

Me^toryBas-eAddr=(u32}DMATx;

DIFi=DIR_?eriphera1DST;//(DMATx—>SPIi_DR_Addr

Buffersize=EpiTXEise;"发送数据的个数

Peripheryline=DMA_PeripheralInc_Disable;"外说地址奇存器不变Memjorylnc

=DMA_Main0ryIncEnable;和内存地址奇存器递增PeripheralDataSize=

DKA_PeripheralDataSize_Byte;"夕卜设数捋竞度为日位MemoryDataSise=

DMA_?etlph£ralDataSizeEytm;

Mode=DHA_Mocie_yonnal;"工作衽正世缓存樓式下

Priarity=DMA_Pri□rity_High;

M2M=EMAM2MDisable;禁止口皿矗内存到内存的传输

DMA_ITConfig(DMAl_Channel3

T

DWA_IT_TC

R

EMABLE);/7幵启DMA传締左成申断

CUfED31aCOUJiter—DMAGetCur?iter(DMA1Channel3);

DMA_Cmd!(DMAl_Chann©137ENABLE);

DMA_D«Init(DMAl_Channel3);

DMAinitstructiire,DMA^PeriphsralBasaAddf

DMA"

DMA

DMA

DMA

DMA

DMA

DMA]

DMA_InitStructure・DMADMA_InitStruet口r吕・DMA

DMA__.

DMAInit(DMAlChannels

r

ADMAInitStructure);

InitStructure・DMA.

InitStruct□匕吕「DMA

Initstrueture・DMA

InitStructure・DMA

InitStr口匕匕口工

色.DMAInitStructure

・DMAInitStmetLire,

DMA

DMA_PeripheralBaseAdd是值外设数据的地址,用SPI1故DMA外设地址

对应的是SPI1_DR_Addr,

DMA_MemoryBaseAddr是内存地址,它的值可以使,你要发送的数据所存放的数组

的名,因为数组名代表的是数组数据存放的首地址,在SPI-DMA

的发送中可以理解为把DMATX[]数组里的数据传送到SPI1_DR_Addr

DMA_DIR是指数据传输的方向,其值发送时其值为

DMA_DIR_PeripheralDST即外设是目的地,方向是DMATX—>SPI1_DR_Addr,

在接受收时其值为DMA_DIR_PeripheralSRC,即外设是数据的来源,传输方向是

SPI1_DR_Addr—>用户指定的数据存储数组。

DMA_BufferSize用来设置传输数据的个数,在STM32的DMA中其值的范围是0—

65536.

DMA_Mode指DMA的传输模式DMA_Mode_Normal为正常工作模式

DMA_Mode_Circular是循环工作模式,这里对循环模式的解释我认为有位网友解释的

很不错如下:

“循环的意思是指DMA的传输数量计数器会重置初值,由于DMA每传一个数据,

传输数量计数器减一,只有在传输数量计数器的值不为零时,才会响应请求。在循环

模式下,当传输计数器的值减为0后,会重新装载;

而内存(缓存)地址则不管循环非循环模式,都会在每次传输完成后重置为基地址。所

以,如果我们把DMA设置会正常模式,那么在下次传输前,只需对DMA的传输数量

计数器重新写入就行。循环模式一般用于数据更新,比如ADC采用需要不停更新数

据。”

在初始化完成之后要开启DMA的中断,在我的程序中开启的是DMA传输完成中

断。

DMA_ITConfig(DMAl_Channel3,DMA_ZT_TC

z

ENABLE);〃开启DMA传输完成中断=

DMA^GetCurrDataCounter(DMA1ChanneL3);

DMA_Cmd.(DMAl_Channe137ENABLE);//使自玄DMA1通薩13

DMA传输有3个中断标志位,常用的是传输完成的中断。如下:

9.3.6中断

可以在DIVIA传输过半、传输完成和传输错误时产生屮断。为应用f器的不同位来打开这

些中断◎

表39DMA中断请求

中斷事件爭件标盘位便能控制位

传输过半

HTIFHTIE

传输立成

TCIF

TCIE

传输错逞

TEIFTEIE

这样在传输完设定的数据个数之后就会触发传输完成的中断,用户可以再中断服务函

数中,进行相应的操作,有一点特别注意,就是要及时清除中断标志位,为下次能够

正常触发中断做准备。

voidDMAl_Channel2_IRQHandler(void)//当传输芫SpiRXSiEe个字节{一一_

SpiCommOn=1;//SP工通信标志位

RXCurrDataCounter=DMA_GetCurrDataCounter(DMA1Channel2};DMA

ClearlTPendingBit(Dr4Ai_IT_TC2);//Y青除中断标志位

在我的中断服务函数中有一个标志位SpiCommon被置1后再中断之外进行其他的处

理,同时调用DMA_ClearlTPendingBit(DMA1」T_TC2)来及时清除中断标志。

在进行DMA的数据传输时要先禁能DMA的通道,重置传输数据个数的值,数据的存

储位置等,再使能DMA的通道,等待DMA的传输完成。

我的操作时这样的,先往DMATX[]里写入相应的数据,然后如下

SpiTXSize=53;

DMA^Cmd(DMAl_Channel3

1

DISABLE};

DMATXInit(}

DMZ^Cmd(DMAl_Channel3,ENABLE);

这样可能有一点不好的地方,因为只改变了SpiTXSize的值,却又重新执行了

DMATXInit()函数,可能此处能够再改善一下。

六测试中出现的问题及原因和解决方法

示波器观察主机能够产生正确的时钟,主机输出引脚也能产生正确的数据,但

是从机不能接受数据。

可能原因:

1.从机的接收中断配置不正确,或者没有打开相应的中断。

2.在从机中TXEIE的中断和TXDMAEN的中断都被使能,手册中说,这两个中断只

能使能1个.

从机能接收数据,但是接收的数据乱码

可能原因:

1.主从机的时钟相位和极性的配置导致的,关于这一点想做一下说明,网上有人说,

主从机时钟的相位和极性要配置的一样,也有人说不能配置的一样,而我对于主从

机的相位和极性的16种组合情况全做了试验,结果如下:(主机LPC1114的

SPI1从机STM32的SPI1)

("表示能正常通信)

从通信

CPOLCPHACPOLCPHA

0000

V

0001乱码

(左移1位)

0010

乱码

0011

乱码

0100

乱码

0101乱码

0110

V

0111乱码

1000V

1001V

1010

乱码

1011

乱码

1100

乱码

1101

乱码

1110

V

1111

V

(当然可能上述的结果也跟测试环境有关,当对其有所怀疑时,读者不妨

自己实验看一下。)

2.乱码的第二个原因可能是两个设备没有共地而造成的,在出现问题时一定要先检查

一下硬件的连接是否正确,是否有虚焊接触不好的地方而导致通讯不正常。

从机能接收数据,但接收的数据不全,又丢字节的现象发生。

可能原因:

1.如果是通过串口打印来观察接收数据,那要看一下数据中是否有0,结

合自己的串口函数分析一下,因为打印数组或者字符串时遇0会截止

2.看一下接收的数组中,其指针是否是递增的。

3.如果使用了CS片选信号,看一下主机发出的数据是否都在

围内。

CS拉低的范

👁️ 阅读量:0