esp8266 spi发送数据缓冲器为空才能发送吗

spi在dma模式下读写sd卡==
热门型号:
&&&当前位置:
spi在dma模式下读写sd卡
用户名:jynkelan
注册时间: 15:51:00
spi在dma模式下读写sd卡
请教大家,/****************************************************************************Desripton:readblockindmamodeFuctionname:ReadBlockInDMA()Input:NoneReturn:None*****************************************************************************/u8ReadBlockInDMA(u8*pBuffer,u32ReadAddr,u16NumByteToRead){//u32i=0;DMA_InitTypeDefDMA_InitSu8rvalue=MSD_RESPONSE_FAILURE;/*MSDchipselectlow*/MSD_CS_LOW();/*initialdmachannel2*/DMA_DeInit(DMA_Channel2);DMA_InitStructure.DMA_PeripheralBaseAddr=(u32)SPI1_DR_ADMA_InitStructure.DMA_MemoryBaseAddr=(u32)pBDMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;DMA_InitStructure.DMA_BufferSize=NumByteToRDMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_DDMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_EDMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_BDMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_BDMA_InitStructure.DMA_Mode=DMA_Mode_NDMA_InitStructure.DMA_Priority=DMA_Priority_VeryHDMA_InitStructure.DMA_M2M=DMA_M2M_DDMA_Init(DMA_Channel2,&DMA_InitStructure);SPI_DMACmd(SPI1,SPI_DMAReq_Rx,ENABLE);/*SendCMD17(MSD_READ_SINGLE_BLOCK)toreadoneblock*/MSD_SendCmd(MSD_READ_SINGLE_BLOCK,ReadAddr,0xFF);if(!MSD_GetResponse(MSD_RESPONSE_NO_ERROR)){/*Nowlookforthedatatokentosignifythestartofthedata*/if(!MSD_GetResponse(MSD_START_DATA_SINGLE_BLOCK_READ)){DMA_Cmd(DMA_Channel2,ENABLE);while(!DMA_GetFlagStatus(DMA_FLAG_TC2)){}}}/*GetCRCbytes(notreallyneededbyus,butrequiredbyMSD)*/MSD_ReadByte();MSD_ReadByte();/*Setresponsevaluetosuccess*/rvalue=MSD_RESPONSE_NO_ERROR;DMA_Cmd(DMA_Channel2,DISABLE);/*MSDchipselecthigh*/MSD_CS_HIGH();/*Senddummybyte:8Clockpulsesofdelay*/MSD_WriteByte(DUMMY);/*Returnsthereponse*/}/******************************************************************************:WritebyteindmamodeFuctionname:WriteBlockInDMA(u8*pBuffer,u32WriteAddr,u16NumByteToWrite)Input:pBuffer--databufferforsendWriteAddr--addresstowriteNumByteToWrite--datalengthofthedatatowriteOutput:None*********************************************************************************/u8WriteBlockInDMA(u8*pBuffer,u32WriteAddr,u16NumByteToWrite){//u32i=0;DMA_InitTypeDefDMA_InitSu8rvalue=MSD_RESPONSE_FAILURE;/*MSDchipselectlow*/MSD_CS_LOW();/*initialthespi1inwritemode*//*initialdmachannel3*/DMA_DeInit(DMA_Channel3);DMA_InitStructure.DMA_PeripheralBaseAddr=(u32)SPI1_DR_ADMA_InitStructure.DMA_MemoryBaseAddr=(u32)pBDMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;DMA_InitStructure.DMA_BufferSize=NumByteToWDMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_DDMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_EDMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_BDMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_BDMA_InitStructure.DMA_Mode=DMA_Mode_NDMA_InitStructure.DMA_Priority=DMA_Priority_HDMA_InitStructure.DMA_M2M=DMA_M2M_DDMA_Init(DMA_Channel3,&DMA_InitStructure);SPI_DMACmd(SPI1,SPI_DMAReq_Tx,ENABLE);//SPI_Cmd(SPI1,ENABLE);MSD_SendCmd(MSD_SET_BLOCKLEN,512,0x00);MSD_ReadByte();MSD_ReadByte();/*SendCMD24(MSD_WRITE_BLOCK)towritemultipleblock*/MSD_SendCmd(MSD_WRITE_BLOCK,WriteAddr,0xFF);/*CheckiftheMSDacknowledgedthewriteblockcommand:R1response(0x00:noerrors)*/if(!MSD_GetResponse(MSD_RESPONSE_NO_ERROR)){/*Sendadummybyte*/MSD_WriteByte(DUMMY);/*Sendthedatatokentosignifythestartofthedata*/MSD_WriteByte(0xFE);/*WritetheblockdatatoMSD:writecountdatabyblock*/DMA_Cmd(DMA_Channel3,ENABLE);while(!DMA_GetFlagStatus(DMA_FLAG_TC3));/*PutCRCbytes(notreallyneededbyus,butrequiredbyMSD)*/MSD_ReadByte();MSD_ReadByte();/*Readdataresponse*/if(MSD_GetDataResponse()==MSD_DATA_OK){rvalue=MSD_RESPONSE_NO_ERROR;}}/*MSDchipselecthigh*/MSD_CS_HIGH();/*Senddummybyte:8Clockpulsesofdelay*/MSD_WriteByte(DUMMY);DMA_Cmd(DMA_Channel3,DISABLE);/*Returnsthereponse*/}这是分别用dma读写sd卡的读和写一个block的程序,在程序中调用到这俩个程序时,dma读子程序在while(!DMA_GetFlagStatus(DMA_FLAG_TC2)){}这一直等待,也就是说dma传输没有完成。而调用写子程序是完全没问题的,也就是dma时钟等问题以不是问题,不知道还有什么没考虑到的?????郁闷!!!!!
用户名:jynkelan
注册时间: 11:21:00
&帮主帮忙分析一下,是不是卡的响应时钟与DMA时钟匹配有问题????
用户名:jynkelan
注册时间: 14:40:00
&&难道是时钟同步问题?因为我们知道读sd卡时每从SPI总线上读一个字节的数据都要先进行时钟同步,这点从MSD_ReadByte()函数可以看出。也就是读一个字节数据前得向spi总线发数据0xff,这是必须的,如果省掉这一步,读数据是不能成功的。问题是不是出在这,也就是说当我们用dma来读SPI-&gtDR中的数据时,这是dma硬件并没有向spi总线发送同步信号,而在SPI-&gtDR中得不到正确的读数而导致dma读取不成功??
用户名:jynkelan
注册时间: 14:45:00
&而向sd卡写数据时并不需要额外的时钟同步,所以dma写能正确进行。
用户名:jynkelan
注册时间: 15:54:00
&看来我的理解还是有误,发0xff只是为了启动时钟clock,而实质上dma也是可以启动时钟的。。。。。。问题还是找不到啊。。。。
用户名:lidawei1
注册时间: 16:44:00
不发的话无时钟信号,怎么能收到?
用户名:jynkelan
注册时间: 12:46:00
&&&这个spi的dma应该硬件能开启时钟,也就是说DMA_Cmd(DMA_Channel2,ENABLE);后spi就会有时钟信号,但在我这事实却不是这样,开启dma通道2后spi1并没有时钟信号。????
用户名:lidawei1
注册时间: 19:09:00
SPI收发是同时进行的,共用一个时钟,主模式发时会开启时钟,收时不会,与DMA无关。DMA只是在接收缓冲区非空或发送缓冲区空时传数据。主模式SPI数据发送过程从“当一字节写进发送缓冲器时,发送过程开始”,即要启动主模式SPI传输,应有一字节写进发送缓冲器,对于发送的DMA操作,当设置好DMA后,因SPI的txe为1,故会触发DMA传一个字节到DR,此操作会开启主模式SPI的发送及接收过程,故“写子程序是完全没问题的”,但对于读操作,如果只设置读的DMA,因触发读DMA的条件是rxne为1,即必须接收缓冲区非空,此条件在只设置读DMA的情况下无法达到,因没有一个字节写进发送缓冲器,也就没有数据收到了,故出现“dma读子程序在while(!DMA_GetFlagStatus(DMA_FLAG_TC2)){}这一直等待”的情况,可见,要完成读DMA操作,也应先写数据到DR中,可通过同时开启另一个DMA通道用于SPI的发送来完成。
用户名:jynkelan
注册时间: 9:29:00
&&&&但这样的话也就是在DMA读时,如果我要连续读512字节,也得发512次0xff作为时钟信号。这样用dma还会有速度优势吗?&&
用户名:jynkelan
注册时间: 9:49:00
&&感谢10楼,程序修改如下,接受成功,但在11楼提到的问题还是可以继续讨论一下。/****************************************************************************Desripton&&&:read&block&in&dma&modeFuctionname&:ReadBlockInDMA()Input&&&&&&&&:NoneReturn&&&&&&:None*****************************************************************************/u8&ReadBlockInDMA(u8&*pBuffer,&u32&ReadAddr,&u16&NumByteToRead){&&&&u32&i&=&0;&&&&DMA_InitTypeDef&&DMA_InitS&&&&u8&rvalue&=&MSD_RESPONSE_FAILURE;&&&&&&/*&MSD&chip&select&low&*/&&&&MSD_CS_LOW();&&&&&&&&/*initial&dma&channel&2*/&&&&DMA_DeInit(DMA_Channel2);&&&&DMA_InitStructure.DMA_PeripheralBaseAddr&=&(u32)SPI1_DR_A&&&&DMA_InitStructure.DMA_MemoryBaseAddr&=&(u32)pB&&&&DMA_InitStructure.DMA_DIR&=&DMA_DIR_PeripheralSRC;&&&&DMA_InitStructure.DMA_BufferSize&=&NumByteToR&&&&DMA_InitStructure.DMA_PeripheralInc&=&DMA_PeripheralInc_D&&&&DMA_InitStructure.DMA_MemoryInc&=&DMA_MemoryInc_E&&&&DMA_InitStructure.DMA_PeripheralDataSize&=&DMA_PeripheralDataSize_B&&&&DMA_InitStructure.DMA_MemoryDataSize&=&DMA_MemoryDataSize_B&&&&DMA_InitStructure.DMA_Mode&=&DMA_Mode_N&&&&DMA_InitStructure.DMA_Priority&=&DMA_Priority_VeryH&&&&DMA_InitStructure.DMA_M2M&=&DMA_M2M_D&&&&DMA_Init(DMA_Channel2,&&DMA_InitStructure);&&&&&&&&&&&&SPI_DMACmd(SPI1,&SPI_DMAReq_Rx,&ENABLE);&&&&&&&/*&Send&CMD17&(MSD_READ_SINGLE_BLOCK)&to&read&one&block&*/&&&&&&&&&&&&MSD_SendCmd(MSD_READ_SINGLE_BLOCK,&ReadAddr,&0xFF);&&&&if&(!MSD_GetResponse(MSD_RESPONSE_NO_ERROR))&&&&{&&&&/*&Now&look&for&the&data&token&to&signify&the&start&of&the&data&*/&&&&&&&&&&&&&&&&if&(!MSD_GetResponse(MSD_START_DATA_SINGLE_BLOCK_READ))&&&&&&&&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&DMA_Cmd(DMA_Channel2,ENABLE);&&&&&&&&&&&&for(i=0&;i&ltNumByteToR&i++)&&&&&//发时钟信号,接收同时进&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&//行&&&&&&&&&&&&{&&&&&&&&&&&&&&&&SPI1-&gtDR&=&0&&&&&&&&&&&&&&&&while(_SPI_GetFlagStatus(SPI1,SPI_FLAG_TXE)&==&RESET);&&&&&&&&&&&&&}&&&&&&&&&&&&while(!DMA_GetFlagStatus(DMA_FLAG_TC2))&&&&&&&&&&&&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&}&&&&&&&&}&&&&/*&Get&CRC&bytes&(not&really&needed&by&us,&but&required&by&MSD)&*/&&&&&&MSD_ReadByte();&&&&MSD_ReadByte();&&&&/*&Set&response&value&to&success&*/&&&&rvalue&=&MSD_RESPONSE_NO_ERROR;&&&&DMA_Cmd(DMA_Channel2,&DISABLE);&&&&/*&MSD&chip&select&high&*/&&&&MSD_CS_HIGH();&&&&/*&Send&dummy&byte:&8&Clock&pulses&of&delay&*/&&&&MSD_WriteByte(DUMMY);&&&&/*&Returns&the&reponse&*/&&&&return&}
用户名:lidawei1
注册时间: 15:56:00
使用DMA并不是为了获得速度,而是充分利用等待的空闲时间,所以我前面说再开启另一个DMA通道用于SPI的发送,发送的同时接收的DMA通道也在工作,完成数据的接收,不需CPU干预,CPU可完成其他任务。要说速度,我就算不用DMA,也照样可在约18026个CPU时钟周期内将512byte数据传到SD卡,或在17234(最快曾达到16401)个CPU时钟周期内将512byte数据从SD卡读到内存,此时速度约为512/(.049(m/s)、512/(.13(m/s),已非常接近2.25m/s的理论值。顺便说一下,SD卡的读写速度,除了与此处CPU与SD卡交换数据的速度有关外,还与以下2个操作密切相关:读时:&&&&/*&Now&look&for&the&data&token&to&signify&the&start&of&the&data&*/&&&if&(!MSD_GetResponse(MSD_START_DATA_SINGLE_BLOCK_READ))写时:&&&&/*&Read&data&response&*/&&&&if&(MSD_GetDataResponse()&==&MSD_DATA_OK)此2操作花费的时间是惊人的,读时约需等10xxxCPU时钟,接近传512byte数据时间的2/3,写时约需等1xxxxx周期,是传512byte数据时间的10倍以上!仔细思考一下也是合理的,因写时会执行擦除操作,闪存的擦除时间是以ms计的。
用户名:jynkelan
注册时间: 18:19:00
&&楼上的说得很有道理,通过逻辑分析议抓数据后可以看到&&/*&Now&look&for&the&data&token&to&signify&the&start&of&the&data&*/&&&if&(!MSD_GetResponse(MSD_START_DATA_SINGLE_BLOCK_READ))写时:&&&&/*&Read&data&response&*/&&&&if&(MSD_GetDataResponse()&==&MSD_DATA_OK)确实占用较长的时间,不过接近读512字节的2/3时间还得看不同的卡,但是如果在dma开启后有dma控制器产生时钟信号好像很合理吧,因为放开速度不说,这样在dma开启后还得靠cpu发512字节的时钟信号,这样的操作好像并没有节省多少cpu时间,只有在开启dma后由dma控制器主动发时钟信号,这样在读512字节的数据时完全可以不用cpu来干预,因而才能真正地达到用cpu来处理其它任务的目的,不知道芯片设计的时候是怎么考虑的,当然如果存在其它编程算法而不是像12楼上那种方法来实现那到是有可能的?
用户名:lidawei1
注册时间: 20:45:00
SPI发送时,开DMA通道2用于将数据从内存搬到DR,SPI接收时,开DMA通道3用于将数据从DR搬到内存,再开DMA通道2用于将DUMMY搬到DR,此2个DMA一开,数据便源源不断从SPI读入内存,不用CPU干预,更不用&&&&&&&&&&&&for(i=0&;i&ltNumByteToR&i++)&&&&&//发时钟信号,接收同时进&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&//行&&&&&&&&&&&&{&&&&&&&&&&&&&&&&SPI1-&gtDR&=&0&&&&&&&&&&&&&&&&while(_SPI_GetFlagStatus(SPI1,SPI_FLAG_TXE)&==&RESET);&&&&&&&&&&&&&}
用户名:jynkelan
注册时间: 9:09:00
&&&就是收的时候并不是只开一个通道2,而是通道2和通道3一起开,发送则只开一个通道即可。
用户名:jynkelan
注册时间: 11:08:00
u8&ReadBlockInDMA(u8&*pBuffer,&u32&ReadAddr,&u16&NumByteToRead){&&&&//u32&i&=&0;&&&&//u16&j&=&0;&&&&&DMA_InitTypeDef&&DMA_InitS&&&&u8&rvalue&=&MSD_RESPONSE_FAILURE;&&&//&u16&*Enp1Addr&=&NULL;&&&/*断点1地址*/&&&&/*&MSD&chip&select&low&*/&&&&MSD_CS_LOW();&&&&&&&&/*initial&dma&channel&2*/&&&&DMA_DeInit(DMA_Channel2);&&&&DMA_DeInit(DMA_Channel3);&&&&DMA_InitStructure.DMA_PeripheralBaseAddr&=&(u32)SPI1_DR_A&&&&DMA_InitStructure.DMA_MemoryBaseAddr&=&(u32)pB&&&&DMA_InitStructure.DMA_DIR&=&DMA_DIR_PeripheralSRC;&&&&DMA_InitStructure.DMA_BufferSize&=&NumByteToR&&&&DMA_InitStructure.DMA_PeripheralInc&=&DMA_PeripheralInc_D&&&&DMA_InitStructure.DMA_MemoryInc&=&DMA_MemoryInc_E&&&&DMA_InitStructure.DMA_PeripheralDataSize&=&DMA_PeripheralDataSize_B&&&&DMA_InitStructure.DMA_MemoryDataSize&=&DMA_MemoryDataSize_B&&&&DMA_InitStructure.DMA_Mode&=&DMA_Mode_N&&&&DMA_InitStructure.DMA_Priority&=&DMA_Priority_VeryH&&&&DMA_InitStructure.DMA_M2M&=&DMA_M2M_D&&&&DMA_Init(DMA_Channel2,&&DMA_InitStructure);&&&&&&&&&&&&DMA_InitStructure.DMA_MemoryBaseAddr&=&(u32)DuumyC&&//512字节的dummy&&&&DMA_InitStructure.DMA_DIR&=&DMA_DIR_PeripheralDST;&&&&DMA_InitStructure.DMA_Priority&=&DMA_Priority_L&&&&DMA_Init(DMA_Channel3,&&DMA_InitStructure);&&&&SPI_DMACmd(SPI1,&SPI_DMAReq_Tx,&ENABLE);&&&&&&&SPI_DMACmd(SPI1,&SPI_DMAReq_Rx,&ENABLE);//&&&&DMA_ClearFlag(DMA_FLAG_TC2);&&&&&/*&Send&CMD17&(MSD_READ_SINGLE_BLOCK)&to&read&one&block&*/&&&&&&&&&&&&MSD_SendCmd(MSD_READ_SINGLE_BLOCK,&ReadAddr,&0xFF);&&&&if&(!MSD_GetResponse(MSD_RESPONSE_NO_ERROR))&&&&{&&&&/*&Now&look&for&the&data&token&to&signify&the&start&of&the&data&*/&&&&&&&&&&&&&&&&if&(!MSD_GetResponse(MSD_START_DATA_SINGLE_BLOCK_READ))&&&&&&&&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&DMA_Cmd(DMA_Channel3,ENABLE);&&&&&&&&&&&&&&&DMA_Cmd(DMA_Channel2,ENABLE);//&&&&&&&&&&&&&for(i=0;&i&ltNumByteToR&i++)//&&&&&&&&&&&&&{&&//&&&&&&&&&&&&&&&&//&&&&&&&&&&&&&&&&SPI1-&gtDR&=&DUMMY;//&&&&&&&&&&&&&&&&while((SPI1-&gtSR&SPI_FLAG_TXE)&==&(u16)RESET);&&&&&&&&&&&&&&&//&while(SPI_GetFlagStatus(SPI1,SPI_FLAG_TXE)&==&RESET);//&&&&&&&&&&&&&&&&while(SPI_GetFlagStatus(SPI1,SPI_FLAG_RXNE)&==&RESET)//&&&&&&&&&&&&&&&&{////&&&&&&&&&&&&&&&&}//&&&&&&&&&&&&&&&&*pBuffer&=&SPI1-&gtDR;//&&&&&&&&&&&&&&&&&pBuffer++;//&&&&&&&&&&&&&}//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&while(!DMA_GetFlagStatus(DMA_FLAG_TC3));&&&&&&&&&&&&&&&&&&&&&&&&while(!DMA_GetFlagStatus(DMA_FLAG_TC2));&&&&&&&&&&&&&&&&&&&&&&&&DMA_ClearFlag(DMA_FLAG_TC2);&&&&&&&&&}&&&&&&&&}&&&&/*&Get&CRC&bytes&(not&really&needed&by&us,&but&required&by&MSD)&*/&&&&&&MSD_ReadByte();&&&&MSD_ReadByte();&&&&/*&Set&response&value&to&success&*/&&&&rvalue&=&MSD_RESPONSE_NO_ERROR;&&&&DMA_Cmd(DMA_Channel2,&DISABLE);&&&&DMA_Cmd(DMA_Channel3,&DISABLE);&&&&/*&MSD&chip&select&high&*/&&&&MSD_CS_HIGH();&&&&/*&Send&dummy&byte:&8&Clock&pulses&of&delay&*/&&&&MSD_WriteByte(DUMMY);&&&&/*&Returns&the&reponse&*/&&&&return&}&优先级很重要,收应高于发。
用户名:lidawei1
注册时间: 12:04:00
无需512字节,设置为地址不增加
用户名:jynkelan
注册时间: 9:58:00
&&&对,这样简单一点,发送512次就行了,谢谢提醒!
用户名:ifree64
注册时间: 22:15:00
有一个问题请教:在代码中有while(!DMA_GetFlagStatus(DMA_FLAG_TC3));while(!DMA_GetFlagStatus(DMA_FLAG_TC2));两句,意思是DMA传输如果没有完成就继续等待。那么这样DMA的优势是否就没有体现出来呢?DMA的本意应该是把CPU从数据传输的忙等待中解放出来,可以腾出时间来做其他事情,但是如果向上面那样使用不是好像就和直接CPU忙等待读取一样了吗?因为while循环使得CPU还是在等着DMA传输完毕。不知道我说得对不对。是不是DMA要配合操作系统的任务管理加上阻塞才有它该有的性能优势呢?谢谢!
热门型号:STM32 SPI接口的简单实现
通常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脚直接连接的主或从时序图。
CPOL时钟极性和CPHA时钟相位的组合选择数据捕捉的时钟边沿。
上图显示了SPI传输的4种CPHA和CPOL位组合。此图可以解释为主设备和从设备的SCK脚、MISO脚、MOSI脚直接连接的主或从时序图。
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位,每个数据帧可以是8位或是16位。所选择的数据帧格式对发送和/或接收都有效。
配置一个SPI 这里选SPI2
SPI_InitTypeDef& SPI_InitS
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
&SPI_Cmd(SPI2, DISABLE);&&&&&&&&&&&&//必须先禁能,才能改变MODE
&&&SPI_InitStructure.SPI_Direction =SPI_Direction_2Lines_FullD&&//两线全双工
&&&SPI_InitStructure.SPI_Mode =SPI_Mode_M&&&&&&&//主
&&&SPI_InitStructure.SPI_DataSize =SPI_DataSize_8b;&&&&&&//8位
&&&SPI_InitStructure.SPI_CPOL =SPI_CPOL_H&&&&&&&&//CPOL=1时钟悬空高
&&&SPI_InitStructure.SPI_CPHA =SPI_CPHA_1E&&&&&&&//CPHA=1 数据捕获第2个
&&&SPI_InitStructure.SPI_NSS =SPI_NSS_S&&&&&&&&//软件NSS
&&&SPI_InitStructure.SPI_BaudRatePrescaler =SPI_BaudRatePrescaler_2;&&//2分频
&&&SPI_InitStructure.SPI_FirstBit =SPI_FirstBit_MSB;&&&&&&//高位在前
&&&SPI_InitStructure.SPI_CRCPolynomial =7;&&&&&&&&//CRC7
&SPI_Init(SPI2,&SPI_InitStructure);
&SPI_Cmd(SPI2, ENABLE);
//spi的配置结束了可以使用了。
也可用 函数SPI_StructInit 把SPI_InitStruct中的每一个参数按缺省值填入
_____________________________________________________________________________________
发送缓冲器空闲标志(TXE)
此标志为’1’时表明发送缓冲器为空,可以写下一个待发送的数据进入缓冲器中。当写入SPI_DR时,TXE标志被清除。
接收缓冲器非空(RXNE)
此标志为’1’时表明在接收缓冲器中包含有效的接收数据。读SPI数据寄存器可以清除此标志。
注意在2.0的库中函数 SPI_SendData SPI_ReceiveDataSPI_GetFlagStatus 等在3.0的库中 变为
SPI_I2S_SendData&&SPI_I2S_ReceiveData& SPI_I2S_GetFlagStatus
写一个发送/接受函数
static u8 SPIByte(u8 byte)
&while((SPI2-&SR &SPI_I2S_FLAG_TXE)==RESET);
&//while((SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE))==RESET);
&SPI2-&DR =
&//SPI_I2S_SendData(SPI2,byte);
&while((SPI2-&SR &SPI_I2S_FLAG_RXNE)==RESET);
//while((SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE))==RESET);
&return(SPI2-&DR);
&//returnSPI_I2S_ReceiveData(SPI2);读寄存器用硬件清除标志位。
&//SPI_I2S_ClearFlag(SPI2,SPI_I2S_FLAG_RXNE) ;直接软件清除标志位。
这里有两种写法直接操作寄存器与用库函数,相对来说直接操作寄存器应该更直观一些。
转自:http://ntn314./blog/static//
> 本站内容系网友提交或本网编辑转载,其目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及作品内容、版权和其它问题,请及时与本网联系,我们将在第一时间删除内容!
做单片机开发的时候经常需要用到模拟spi接口,这种写法不错的,网上很多类似的,我也拿来主义了. #define MOSI_H GPIO_SetBits(GPIOB, GPIO_Pin_10) #define MOSI_L GPIO_ResetBits(GPIOB, GPIO_Pin_10) #define SCLK_H GPIO_SetBits(GPIOB, ...
SPI interface SPI接口介绍
SPI是由美国摩托罗拉公司推出的一种同步串行传输规范,常作为单片机外设芯片串行扩展接 口.SPI有4个引脚:SS(从器件选择线).SDO(串行数据输出线).SDI(串行数据输入线)和SCK(同步串行时钟线).SPI可以用全双工通信 方式同时发送和接收8(16)位数据,过程如下:主机启动发送过程,送出时钟脉冲 ...
作者:马潮老师 使用的同步串行三线SPI接口,可以方便的连接采用SPI通信协议的外围或另一片AVR单片机,实现在短距离内的高速同步通信.ATmega128的SPI采用硬件方式实现面向字节的全双工3线同步通信,支持主机.从机和2种不同极性的SPI时序,通信速率有7种选择,主机方式的最高速率为1/2系统时钟,从机方式最高速率为1/4系统时钟. ATmega128 ...
SPI接口的FLASH ROM AT45DB161D的驱动方法 (1)芯片介绍 AT45DB161D是串行接口的闪存芯片,可工作在2.5V~2.7V,可广泛应用于数据语音.图像.程序代码数据存储中.AT45DB161D支持RapidS串行接口,适用于高速场合.RapidS串行接口是与SPI相兼容的,速度可达到66MHz.它包含有17,301,504个位,被组 ...
同步外设接口(SPI)是由摩托罗拉公司开发的全双工同步串行总线,该总线大量用在与EEPROM.ADC.FRAM和显示驱动器之类的慢速外设器件通信. SPI(Serial Peripheral Interface)是一种串行同步通讯协议,由一个主设备和一个或多个从设备组成,主设备启动一个与从设备的同步通讯,从而完成数据的交换.SPI 接口由SDI(串行数据输入 ...
/view/bf60ddcca184.html 关于SPI,不同的芯片具体通信方式可能会不大一样,所以要具体问题具体分析,下面是最近做LCD时碰到的两个模拟SPI协议的代码,芯片通信方式不同,代码也就不同了 SPI的工作原理不多说,网上一大把. 1.一款夏普的屏,hx8363A,和host的 ...
这几天碰到了使用SPI接口的flash,才知道flash还可以是串行的,看来以前真是井底之蛙啊,找了一些SPI接口的资料都不全,后来找到一点英文资料,翻译了一下,加上我的个人理解,凑成一篇了,希望对初学者有点帮助.
SPI接口的全称是&Serial Peripheral Interface&,意为串行外围接口,是Motorola首先在 ...
一.SPI总线是怎么一回事: 推荐文章http://blog.csdn.net/ce123_zhouwei/article/details/6897293,关于SPI与uart.i2c的比较,推荐文章/lucky-apple/archive//1234581.html,SPI最大的特点是它是它是 ...

我要回帖

更多关于 stm32 spi 从机发送 的文章

 

随机推荐