a7105可以使用四线spi,或者3线spi, 但是之前都是使用3线的软件模拟的三线spi的,所以不想改其它代码了,就想可以提高一个spi的读写速度,原来软件方式的读写速度,在48Mhz的03x下面,大约速度是1.6mbs,使用硬件spi之后,最终大约速度为12mbs.
为了令它可以稳定工作,这个还是花了不了时间了。主要的挑战是因为是使用3线spi,情况有些特殊,我们需要使用MOSI一条数据线实现主和从的双向模式通信。根据文档,我们使用进行以下设置
1. SPIx->CR1 的 BIDIMODE 设置为1,表示启用双向模式
2. 使用SPIX->CR1的BIDIOE 来控制方向,0表示当前为Master读数据,1表示当前为Master写数据
3. 更换这个方向时需要把SPIx禁用, SPIX->CR1的SPE,来控制。
这个总体思路是这样的,我实际编写完代码,发现是完全不工作的,网上也比较少这样的使用,有少数几个文章有说这个。
根据文档,在BIDIMODE 为1,BIDIOE为0, 时钟CLK在SPE马上开启输出,同时它只会在SPE为零时才会停,这个意味着,我们要依赖于Slave的处理速度,当时也依赖而自己准确开关这个CLK信号,否则这个读出来的数据就会错乱。
这时需要一个比较慢的速度才可以稳定,最开始我并没有注意到这个,在 spiInit.SPI_BaudRatePrescaler 小于 SPI_BaudRatePrescaler_128时,要不读取全是时,要不出现一开始是对的,读着读着,数据就是错的,最后读出来全零的情况。只能不断的降低读写速度。最后才能稳定无误。但是128分频后,明显比软件模拟的方式还慢,这个就没有价值了。这个代码如下:
- void spi_init()
- {
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
-
-
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
-
-
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;
-
-
- GPIO_Init(GPIOB, &GPIO_InitStructure);
-
- GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_0);
- GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_0);
-
- SPI_I2S_DeInit(SPI1);
- spiInit.SPI_Direction = SPI_Direction_1Line_Tx;
- spiInit.SPI_Mode = SPI_Mode_Master;
- spiInit.SPI_DataSize = SPI_DataSize_8b;
- spiInit.SPI_CPOL = SPI_CPOL_Low;
- spiInit.SPI_CPHA = SPI_CPHA_1Edge;
- spiInit.SPI_NSS = SPI_NSS_Soft;
- spiInit.SPI_BaudRatePrescaler= SPI_BaudRatePrescaler_128;
- spiInit.SPI_FirstBit = SPI_FirstBit_MSB;
- spiInit.SPI_CRCPolynomial = 7;
- SPI_Init(SPI1, &spiInit);
-
- SPI_SSOutputCmd(SPI1,DISABLE);
-
- //NSS
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
- GPIO_Init(GPIOA, &GPIO_InitStructure);
-
- SPI_TIModeCmd(SPI1,DISABLE);
- SPI_NSSPulseModeCmd(SPI1,DISABLE);
- SPI_Cmd(SPI1,ENABLE);
- }
-
- void Rf_Spi_Write_Byte(uint8_t dat)
- {
- if(!g_bisTx)
- {
-
- SPI1->CR1 &= (uint16_t)~((uint16_t)SPI_CR1_SPE);//DISABLE SPI
- SPI1->CR1 |= SPI_Direction_Tx;
- g_bisTx=1;
- }
-
- *(uint8_t*)&SPI1->DR = dat;
- SPI1->CR1 |= SPI_CR1_SPE; //ENABLE SPI
-
-
- // while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) != RESET){}
- //SPI_SendData8(SPI1,dat);
-
- while(!(SPI1->SR & SPI_I2S_FLAG_TXE)){}
- while (SPI1->SR & SPI_I2S_FLAG_BSY){}
-
- SPI1->CR1 &= (uint16_t)~((uint16_t)SPI_CR1_SPE);
-
-
-
- }
-
- uint8_t Rf_Spi_Read_Byte(void)
- {
- #if USE_HARDWARE_SPI
-
- uint8_t dat =0;
-
- if(g_bisTx)
- {
- SPI1->CR1 &= (uint16_t)~((uint16_t)SPI_CR1_SPE);//DISABLE SPI
- SPI1->CR1 &= SPI_Direction_Rx;
- /* Clear FRXTH bit */
- SPI1->CR2 &= (uint16_t)~((uint16_t)SPI_CR2_FRXTH);
- /* Set new FRXTH bit value */
- SPI1->CR2 |= SPI_RxFIFOThreshold_QF; //SET IT 8 BIT per READ, THAT is QUETER OF 32BIT
-
- }
- SPI1->CR1 |= SPI_CR1_SPE; //ENABLE SPI
- g_bisTx=0;
-
- while (!(SPI1->SR & SPI_I2S_FLAG_RXNE) ) ; // wait data received
- SPI1->CR1 &= (uint16_t)~((uint16_t)SPI_CR1_SPE);//DISABLE SPI
- dat = *(uint8_t*)&SPI1->DR;
-
- return dat;
-
- }
这个根据工作原理分析了一下,为什么在这样,主要是因为我们的读的时候,要是这个速度高,我们的mcu还没有去禁用SPI停时钟这个太慢了,上面代码已经在判断到RXNE时马上disable SPI了,但是明显,这个还是不够快。怎么办? 如何能提高速度? 这个折腾了很久,终于发现一个不合理的操作可以直接把速度成 DIV4,即12MB。这个就是先禁用SPI, 再判断RXNE...发现竟然是可以很稳定的工作。
-
- SPI1->CR1 |= SPI_CR1_SPE; //ENABLE SPI
- g_bisTx=0;
- SPI1->CR1 &= (uint16_t)~((uint16_t)SPI_CR1_SPE);//DISABLE SPI
-
- while (!(SPI1->SR & SPI_I2S_FLAG_RXNE) ) ; // wait data received
- dat = *(uint8_t*)&SPI1->DR;
这个确实也是神奇,特别写这个文章记录一下。
归纳的原理应该是SPI ENABLE就开始读了,这ENABLE和DISALBE的时间刚好就是12MB的速度,多一条指令都不行了。
同时也尝试了使用DMA去加速,但是同样因为这个时钟刹车停止问题,使用DMA时实际测试需要更慢才能稳定传输,也要去到SPI_BaudRatePrescaler_128。究其原因应该也是DMA停止时操作太多,无法简化,导致时钟无法及时停止,引起数据错误。
以下是三线SPI的DMA传输的代码,供参考。
- void Rfchip_Spi_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- DMA_InitTypeDef DMA_InitStructure={0};
-
-
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
-
-
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
-
-
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;
-
-
- GPIO_Init(GPIOB, &GPIO_InitStructure);
-
- GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_0);
- GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_0);
-
- SPI_I2S_DeInit(SPI1);
- spiInit.SPI_Direction = SPI_Direction_1Line_Tx;
- spiInit.SPI_Mode = SPI_Mode_Master;
- spiInit.SPI_DataSize = SPI_DataSize_8b;
- spiInit.SPI_CPOL = SPI_CPOL_Low;
- spiInit.SPI_CPHA = SPI_CPHA_1Edge;
- spiInit.SPI_NSS = SPI_NSS_Soft;
- spiInit.SPI_BaudRatePrescaler= SPI_BaudRatePrescaler_128;
- spiInit.SPI_FirstBit = SPI_FirstBit_MSB;
- spiInit.SPI_CRCPolynomial = 7;
- SPI_Init(SPI1, &spiInit);
-
- SPI_SSOutputCmd(SPI1,DISABLE);
-
- //NSS
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
- GPIO_Init(GPIOA, &GPIO_InitStructure);
-
- SPI_TIModeCmd(SPI1,DISABLE);
- SPI_NSSPulseModeCmd(SPI1,DISABLE);
-
-
- DMA_DeInit(DMA1_Channel2);
- DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
- DMA_InitStructure.DMA_MemoryBaseAddr = 0;
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
- DMA_InitStructure.DMA_BufferSize = 0;
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
- DMA_InitStructure.DMA_Priority = DMA_Priority_High;
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
- DMA_Init(DMA1_Channel2, &DMA_InitStructure);
-
-
- DMA_DeInit(DMA1_Channel3);
- DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
- DMA_InitStructure.DMA_MemoryBaseAddr = 0;
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
- DMA_InitStructure.DMA_BufferSize = 0;
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
- DMA_InitStructure.DMA_Priority = DMA_Priority_High;
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
- DMA_Init(DMA1_Channel3, &DMA_InitStructure);
-
-
-
-
- SPI_Cmd(SPI1,ENABLE);
-
-
-
- //GIO
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
- GPIO_Init(GPIOA, &GPIO_InitStructure);
- RFChip_Disable;
- }
-
-
-
- void Rf_Spi_Write_Bytes(uint8_t *pbuf, uint32_t len)
- {
-
- if(!g_bisTx)
- {
-
- SPI1->CR1 &= (uint16_t)~((uint16_t)SPI_CR1_SPE);//DISABLE SPI
- SPI1->CR1 |= SPI_Direction_Tx;
- g_bisTx=1;
- }
-
- DMA_Cmd(DMA1_Channel3,DISABLE);
-
- DMA1_Channel3->CMAR = (uint32_t)pbuf;
- DMA1_Channel3->CNDTR = len;
- DMA_Cmd(DMA1_Channel3,ENABLE);
- SPI_I2S_DMACmd(SPI1,SPI_I2S_DMAReq_Tx,ENABLE);
- SPI1->CR1 |= SPI_CR1_SPE; //ENABLE SPI
- while(DMA_GetFlagStatus(DMA1_FLAG_TC3)==RESET);
- DMA_ClearFlag(DMA1_FLAG_TC3);
- SPI_I2S_DMACmd(SPI1,SPI_I2S_DMAReq_Tx,DISABLE);
-
- while (SPI1->SR & SPI_I2S_FLAG_BSY){}
-
- SPI1->CR1 &= (uint16_t)~((uint16_t)SPI_CR1_SPE);
- DMA_Cmd(DMA1_Channel3,DISABLE);
-
-
-
-
- }
-
- void Rf_Spi_Read_Bytes(uint8_t *pbuf, uint32_t len)
- {
-
- uint16_t Dis_SPI_CR1_SPE=(uint16_t)~((uint16_t)SPI_CR1_SPE);
- uint16_t dMA_DISABLE = (uint16_t)(~DMA_CCR_EN);
- if(g_bisTx)
- {
- SPI1->CR1 &= (uint16_t)~((uint16_t)SPI_CR1_SPE);//DISABLE SPI
- SPI1->CR1 &= SPI_Direction_Rx;
- /* Clear FRXTH bit */
- SPI1->CR2 &= (uint16_t)~((uint16_t)SPI_CR2_FRXTH);
- /* Set new FRXTH bit value */
- SPI1->CR2 |= SPI_RxFIFOThreshold_QF; //SET IT 8 BIT per READ, THAT is QUETER OF 32BIT
-
- }
- g_bisTx=0;
- DMA1_Channel2->CCR &= dMA_DISABLE; //DISABLE DMA CHANNEL2
- DMA1_Channel2->CMAR = (uint32_t)pbuf;
- DMA1_Channel2->CNDTR = len;
- SPI1->CR2 |= SPI_I2S_DMAReq_Rx; //DMA REQUEST
- SPI1->CR1 |= SPI_CR1_SPE; //ENABLE SPI
- DMA1_Channel2->CCR |= DMA_CCR_EN; //ENABLE DMA CHANNEL2
-
- while(!(DMA1->ISR & DMA1_FLAG_TC2));
- DMA1_Channel2->CCR &=dMA_DISABLE; //DISABLE DMA CHANNEL2
-
- SPI1->CR1 &= Dis_SPI_CR1_SPE;//DISABLE SPI
-
- SPI1->CR2 &= (uint16_t)~SPI_I2S_DMAReq_Rx;
-
- DMA_ClearFlag(DMA1_FLAG_TC2);
-
-
- }