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);
}
|