本文主要是为了针对 SPI 通讯的深入了解,加强基本工的练习。希望对大家有用!联系方式:QQ:279894340
想要原代码的可以到我的博客下载。谢谢
一、SPI总线协议及SPI时序图详解:
SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议。
SPI是一个环形总线结构,由ss(cs)、sck、sdi、sdo构成,其时序其实很简单,主要是在sck的控制下,两个双向移位寄存器进行数据交换。
上升沿发送、下降沿接收、高位先发送。
上升沿到来的时候,sdo上的电平将被发送到从设备的寄存器中。
下降沿到来的时候,sdi上的电平将被接收到主设备的寄存器中。
假设主机和从机初始化就绪:并且主机的sbuff=0xaa (10101010),从机的sbuff=0x55 (01010101),下面将分步对spi的8个时钟周期的数据情况演示一遍(假设上升沿发送数据)。
---------------------------------------------------
脉冲 主机sbuff 从机sbuff sdi sdo(到从设备)
---------------------------------------------------
0 00-0 10101010 01010101 0 0
---------------------------------------------------
1 0--1 0101010x 10101011 0 1
1 1--0 01010100 10101011 0 1
---------------------------------------------------
2 0--1 1010100x 01010110 1 0
2 1--0 10101001 01010110 1 0
---------------------------------------------------
3 0--1 0101001x 10101101 0 1
3 1--0 01010010 10101101 0 1
---------------------------------------------------
4 0--1 1010010x 01011010 1 0
4 1--0 10100101 01011010 1 0
---------------------------------------------------
5 0--1 0100101x 10110101 0 1
5 1--0 01001010 10110101 0 1
---------------------------------------------------
6 0--1 1001010x 01101010 1 0
6 1--0 10010101 01101010 1 0
---------------------------------------------------
7 0--1 0010101x 11010101 0 1
7 1--0 00101010 11010101 0 1
---------------------------------------------------
8 0--1 0101010x 10101010 1 0
8 1--0 01010101 10101010 1 0全双工通讯,一次传2个字节
---------------------------------------------------
这样就完成了两个寄存器8位的交换,上面的0--1表示上升沿、1--0表示下降沿,sdi、 sdo相对于主机而言的。根据以上分析,一个完整的传送周期是16位,即两个字节,因为,首先主机要发送命令过去,然后从机根据主机的名准备数据,主机在下一个8位时钟周期才把数据读回来。
SPI总线是Motorola公司推出的三线同步接口,同步串行3线方式进行通信:一条时钟线SCK,一条数据输入线MOSI,一条数据输出线MISO;用于 CPU与各种外围器件进行全双工、同步串行通讯。SPI主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。
/*********************************************************************************************************
** 宏定义#define SPI_IN GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6) //MISO----PA6
/**********************************************************************
* Function: SPI_SendData
* Description:
*
* Input: void
* Output:
* Return: RecevieData :接收到的数据
* Others:
* Modify Date: Version: Author: Modification:
* -----------------------------------------------
* 2017-06-22 V1.0 Hu Weiping
**********************************************************************/
static uint8_t SPI_SendData(uint8_t data)
{
uint8_t RecevieData=0,i;
for(i=0;i<8;i++)
{
if(data&0x80)
{
SPI_OUTHIGH;
}
else
{
SPI_OUTLOW;
}
SPI_SCKHIGH; //我这里是选择下降沿采集数据
data<<=1;
__NOP();__NOP();__NOP();__NOP();
RecevieData <<= 1;
if(SPI_IN)
{
RecevieData |= 1; //Wait SDO to go Hi
}
SPI_SCKLOW;
}
return RecevieData;
}
/**********************************************************************
* Function: MYSPI_Init
* Description: 初始化GPIO口
*
* Input: void
* Output:
* Return: void
* Others:
* Modify Date: Version: Author: Modification:
* -----------------------------------------------
* 2017-06-22 V1.0 Hu Weiping
**********************************************************************/
static void MYSPI_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOA ,ENABLE);
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7; //SPI_CS and SPI_SCK and SPI_OUT
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //SPI_IN
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //SPI_CS and SPI_SCK and SPI_OUT
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB,&GPIO_InitStructure);
SPI_CSHIGH;
SPI_SCKLOW;
SPI_OUTLOW;
}
/**********************************************************************
* Function: SPI_Delay
* Description:
*
* Input: void
* Output:
* Return: void
* Others:
* Modify Date: Version: Author: Modification:
* -----------------------------------------------
* 2017-06-22 V1.0 Hu Weiping
**********************************************************************/
static void SPI_Delay(void)
{
uint16_t i;
for(i=0;i<72;i++);
}
/**********************************************************************
* Function: W25x16_WriteEnable
* Description:
*
* Input: void
* Output:
* Return: void
* Others:
* Modify Date: Version: Author: Modification:
* -----------------------------------------------
* 2017-06-22 V1.0 Hu Weiping
**********************************************************************/
void W25x16_WriteEnable(void)
{
SPI_CSLOW;
SPI_SendData(0x06);
SPI_CSHIGH;
SPI_Delay();
}
/**********************************************************************
* Function: W25x16_BlockErase
* Description: SectorErase 64K Byte
*
* Input: void
* Output:
* Return: void
* Others:
* Modify Date: Version: Author: Modification:
* -----------------------------------------------
* 2017-06-22 V1.0 Hu Weiping
**********************************************************************/
void W25x16_BlockErase(uint16_t addr)
{
W25x16_WriteEnable();
SPI_CSLOW;
SPI_SendData(0xD8);
SPI_SendData((addr>>16)&0xFF);
SPI_SendData((addr>>8)&0xFF);
SPI_SendData(addr&0xFF);
SPI_CSHIGH;
}
/**********************************************************************
* Function: W25x16_ReadDeviceID
* Description: 读取设备ID
*
* Input: void
* Output:
* Return: void
* Others:
* Modify Date: Version: Author: Modification:
* -----------------------------------------------
* 2017-06-22 V1.0 Hu Weiping
**********************************************************************/
uint32_t W25x16_NewReadDeviceID(void)
{
uint8_t ret;
u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
SPI_CSLOW;
SPI_SendData(0x9F);
Temp0 = SPI_SendData(0xff);
Temp1 = SPI_SendData(0xff);
Temp2 = SPI_SendData(0xff);
SPI_CSHIGH;
Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
return Temp;
}
uint8_t WriteBuf[10] = {0xAA, 0xBB, 0x45, 0x78, 0x34, 0x78, 0x89,0xAC, 0xBC, 0xDD};
uint8_t RreadBuf[10] = {0};
void W25x16_test(void)
{
W25x16_SectorErase(0x0000);
W25x16_WritePage(0x0000, WriteBuf, 10);
W25x16_ReadData(0x0000, RreadBuf, 10);
}
/**********************************************************************
* Function: W25x16_Init
* Description: 初始化设备ID
*
* Input: void
* Output:
* Return: void
* Others:
* Modify Date: Version: Author: Modification:
* -----------------------------------------------
* 2017-06-22 V1.0 Hu Weiping
**********************************************************************/
__IO uint32_t numID = 0;
void W25x16_Init(void)
{
MYSPI_Init();
numID = W25x16_NewReadDeviceID(); //这里可以查看到W25X16 ID
W25x16_test();
while(1);
}