最近在研讨SPI总线,至于协议和硬件描绘就不多说了
四线包含时钟、片选、接纳、发送
初始化SP
SPI_InitStructure.SPI_DirecTIon = SPI_DirecTIon_2Lines_FullDuplex; //全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主形式
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; //16bit宽度
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; //2–18MHz; 4–9MHz; 8–4.5MHz
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPIx, &SPI_InitStructure);
SPI_Cmd(SPIx, ENABLE);
SPI不能硬件操控CS,只能软件来控,便是经过将NSS设为外部GPIO来操控。
像我所做的项目是运用STM32与FPGA通讯,而FPGA的SPI作业在这种一向状况
作为主设备的STM32,CS在传输数据的时分为低,传输结束后有必要拉高,这样FPGA能够判别出SPI的传输起止状况。
FPGA的数据传输格局是16bit地址+16bit数据
关于读16bit,完成如下
uint16_t spi_read(SPI_TypeDef* SPIx,uint32_t addr)
{
uint16_t value;
uint16_t spi_nss;
uint16_t add;
uint32_t level;
if(SPI1 == SPIx)
spi_nss = SPI1_PIN_NSS;
else if(SPI2 == SPIx)
spi_nss = SPI2_PIN_NSS;
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
GPIO_ResetBits(GPIOA, spi_nss);
SPI_I2S_SendData(SPIx, addr); //0xf014 》》 2
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPIx, 0x0);
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
SPI_I2S_ReceiveData(SPIx);
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
GPIO_SetBits(GPIOA, spi_nss);
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
value = SPI_I2S_ReceiveData(SPIx);
return value;
}
写函数
void spi_write(SPI_TypeDef* SPIx,uint32_t addr, uint16_t value)
{
uint16_t spi_nss;
uint32_t level;
if(SPI1 == SPIx)
spi_nss = SPI1_PIN_NSS;
else if(SPI2 == SPIx)
spi_nss = SPI2_PIN_NSS;
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
GPIO_ResetBits(GPIOA, spi_nss);
SPI_I2S_SendData(SPIx, addr);
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPIx, value);
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
SPI_I2S_ReceiveData(SPIx);
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
GPIO_SetBits(GPIOA, spi_nss);
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
SPI_I2S_ReceiveData(SPIx);
}
拿write函数举例
只所以这么规划是因为
假如是函数一开始就将NSS脚拉低,然后再去send,如下
GPIO_ResetBits(GPIOA, spi_nss);
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPIx, addr);
这样在CS拉低一段时刻后(时刻大概有16个时钟周期),才有CLK,这样延时就会下降SPI的传输功率
之前那种方法会在CS拉底后很快就有clk时钟出来
之所以写两次再读两次而不是读一次写一次也是考虑到功率的问题
假如先写一次再读一次,看波形每个数据之间有比较大的空地是没有clk的,便是说在传输完一个数据后再
传第二个会要等一段时刻,这个对速度要求比较高的设备是不允许的
还有值得注意的是:
假如SPI是主形式,那么GPIO设置为
NSS是GPIO_Mode_Out_PP
CLK是GPIO_Mode_AF_PP
MOSI是GPIO_Mode_AF_PP
MISO是GPIO_Mode_IN_FLOATING
假如SPI是从形式,那么GPIO设置为
NSS是GPIO_Mode_Out_PP
CLK是GPIO_Mode_IN_FLOATING
MOSI是GPIO_Mode_IN_FLOATING
MISO是GPIO_Mode_AF_PP