SPI总线与IIC类似,SPI也是一种通信协议。
今天我们就以WX25X16芯片为例来介绍SPI.首先我们来看下硬件连接。
、从原理图可以看到该芯片需要单片机控制的管脚有4个,非别是CS,DO,DIO,CLK.其中CS 是片选信号,只有将该位拉低才能选中该芯片。
DO,DIO分别是输出和输入。
CLK是时钟信号。
SPI通信的步骤如下所示:1)获取地址12)获取地址23)擦除扇区4)写入数据好的,下面我们对每个步骤进行分析(1)在对芯片操作前先要对端口及SPI外设进行相应的设置:/*函数名:SPI_FLASH_Init(void)功能:对端口和SPI初始化输入:无输出:无调用:被主函数调用*/void SPI_FLASH_Init(void){SPI_InitTypeDef SPI_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;/* Enable SPI1 and GPIO clocks */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD, ENABLE);/*!< SPI_FLASH_SPI Periph clock enable */RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);/*将PA5(CLK)配置成复用推挽输出*/GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_Init(GPIOA, &GPIO_InitStructure);/*将PA6(DO)设置成浮空输入*/GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_Init(GPIOA, &GPIO_InitStructure);/将PA7(DIO)设为浮空输入/GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;GPIO_Init(GPIOA, &GPIO_InitStructure);/将PA4(CS)设为推挽输出/GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_Init(GPIOA, &GPIO_InitStructure);/拉高CS,失能芯片,该语句是宏定义,就是置高PA4/SPI_FLASH_CS_HIGH();/* SPI配置/// W25X16: data input on the DIO pin is sampled on the rising edge of the CLK.// Data on the DO and DIO pins are clocked out on the falling edge of CLK./*将SPI设为全双工模式*/SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;/*将SPI设为主模式*/SPI_InitStructure.SPI_Mode = SPI_Mode_Master;/*将SPI通信的数据大小设为8位*/SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;/*将CLK的高电平设为空闲*/SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;/*设置在第二个时钟沿捕获数据*/SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;/*指定NSS信号由软件管理*/SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;/SPI_BaudRatePrescaler用来定义波特率预分频的值,这个值用以设置发送和接收的SCK时钟/SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;/SPI_FirstBit指定了数据传输从高位还是低位开始/SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;/SPI_CRCPolynomial定义了用于CRC值计算的多项式/SPI_InitStructure.SPI_CRCPolynomial = 7;SPI_Init(SPI1, &SPI_InitStructure);/* Enable SPI1 */SPI_Cmd(SPI1, ENABLE);}(2)获取器件地址1因为SPI总线上可以挂很多的器件,所以首先要获得器件的地址。
获得器件地址的步骤是:拉低CS——>向器件发送获得地址的命令——>连续发送三个任意数据——>在发送第四个任意数据时在DO口上读出器件地址。
注意,发送完读地址后,从机会将数据自动传给主机的SPI 数据寄存器中,用户只要到该寄存器中取数就可以了。
好的,既然用到写命令那么我们写来写这两个最基础的程序。
写数据函数:/************************************************************* ******************* Function Name : SPI_FLASH_SendByte* Description : 通过SPI总线发送一字节数据,再通过SPI总线返回一字节数据* Input : byte ——要发送的数据* Output : 从机返回的数据* 调用:被SPI_FLASH_ReadDeviceID调用************************************************************** *****************/u8 SPI_FLASH_SendByte(u8 byte){/* 等待SPI发送寄存器里的数据发送结束*/while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);/* 发送数据*/SPI_I2S_SendData(SPI1, byte);/* 等待接收完一字节数据*/while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);/* 返回接收到的数据*/return SPI_I2S_ReceiveData(SPI1);}现在我们再来看看读器件地址函数:/************************************************************* ******************* Function Name : SPI_FLASH_ReadDeviceID(void)* Description : 读器件地址1* Input : 无* Output : 无* 返回: 器件地址调用:SPI_FLASH_SendByte************************************************************** *****************/u32 SPI_FLASH_ReadDeviceID(void){u32 Temp = 0;/* SPI_FLASH_CS_LOW(),是一个宏定义,就是拉低CS(PA4)*/ SPI_FLASH_CS_LOW();/*W25X_DeviceID=0XAB,是读器件地址的命令,发送完该命令后连续发三个任意数字,在发第五个任意数后可以读出地址*/SPI_FLASH_SendByte(0XAB);SPI_FLASH_SendByte(0XFF);SPI_FLASH_SendByte(0XFF);SPI_FLASH_SendByte(0XFF);/* Read a byte from the FLASH */Temp = SPI_FLASH_SendByte(0XFF);/* 失能芯片*/SPI_FLASH_CS_HIGH();/*返回器件地址*/return Temp;}像温度传感器一样,WX25X16也有很多的命令,具体的命令看下图:图5-1(3)获取器件地址2获取完地址1后,我们再来获取地址2.读地址2的命令是0X9F,从图5-1中可以看到,发送完0X9F后连续读出3个数据。
好的,我们来看看具体的程序:/******************************************************************************** Function Name : SPI_FLASH_ReadID* Description : 读器件地址2* Input : 无* Output : 无* 返回: 器件地址2调用:SPI_FLASH_SendByte********************************************************************* **********/u32 SPI_FLASH_ReadID(void){u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;/* 拉低CS来使能从机*/SPI_FLASH_CS_LOW();/*W25X_JedecDeviceID=0X9F,读取从机地址的命令,发送完命令后可以连续读三个字节的地址*/SPI_FLASH_SendByte(0X9F);/* Read a byte from the FLASH */Temp0 = SPI_FLASH_SendByte(Dummy_Byte);/* Read a byte from the FLASH */Temp1 = SPI_FLASH_SendByte(Dummy_Byte);/* Read a byte from the FLASH */Temp2 = SPI_FLASH_SendByte(Dummy_Byte);/* 拉高CS,失能从机*/SPI_FLASH_CS_HIGH();/*将三个地址合并*/Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;return Temp;}(4)擦除扇区WX25X16是2M的字节,共分为8页,每页是256个字节的大小。