STM32_SPI协议编程

有关IIC协议编程:IIC协议

目录

一、SPI介绍                                                                                   

二、硬件配置                                                                                 

三、代码配置                                                                                 

1、SPI子函数                                                                                          

SPI读写一个字节                                                                                                             

测试SPI串口通信                                                                                                             

2、W25Q128子函数                                                                                

读取芯片ID                                                                                                                       

读状态寄存器                                                                                                                   

擦除扇区                                                                                                                          

写数据                                                                                                                              

读数据                                                                                                                              

测试读取芯片ID                                                                                                                

测试W25Q128读写数据                                                                                                   


一、SPI介绍                                                                                   

4 条线通信:                                                                                                                                      

  • MISO 主设备数据输入,从设备数据输出。
  • MOSI 主设备数据输出,从设备数据输入。
  • SCLK 时钟信号,由主设备产生。
  • CS 从设备片选信号,由主设备控制。

四种工作模式                                                                                                                                     

时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。

时钟极性(CPOL)是用来配置SCLK的电平出于空闲态或者有效态。

  • CPOL=0,串行时钟空闲状态为低电平。
  • CPOL=1,串行时钟空闲状态为高电平
  • CPHA=0,串行时钟的第一个跳变沿(上升沿或下降沿)采集数据。
  • CPHA=1,串行时钟的第二个跳变沿(上升沿或下降沿)采集数据。
  1. CPOL= 0,CPHA=0。SCK串行时钟线空闲是为低电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿切换
  2. CPOL= 0,CPHA=1。SCK串行时钟线空闲是为低电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿切换
  3. CPOL= 1,CPHA=0。SCK串行时钟线空闲是为高电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿切换
  4. CPOL= 1,CPHA=1。SCK串行时钟线空闲是为高电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿切换

主要特点                                                                                                                                            

  • 可以同时发出和接收串行数据; 可以当作主机或从机工作; 提供频率可 编程时钟; 发送结束中断标志; 写冲突保护; 总线竞争保护等。

注意!!!!!                                                                                                                               

        时钟是由主机提供的,所以在从机向主机读、写数据的时候,都需要有主机发送一个字节数据,以提供时钟信号,以便之后的从机向主机读、写数据。

总之,读/写一个数据,就要写/读一个数据。(该数据为虚拟数据,无需管该数据

二、硬件配置                                                                                 

以下为STM32F407设计思路

        W25Q128 与 STM32F4 的连接,板上的 W25Q128 是直接连在 STM32F4 的 SPI1上的, 我们的 F_CS 是连接在 PB14 上面的,另外要特别注意: W25Q128 和 NRF24L01 共 用 SPI1,所以这两个器件在使用的时候,必须分时复用(通过片选控制)才行

1)配置相关引脚的复用功能,使能 SPI1 时钟。

        使用 SPI1,第一步就要使能 SPI1 的时钟, SPI1 的时钟通过 APB2ENR 的第 12 位来设置。其次要设置 SPI1 的相关引脚为复用(AF5)输出,这样才会连接到 SPI1 上。这里我们使用的是 PB3、 4、 5 这 3 个(SCK.、 MISO、 MOSI, CS 使用软件管理方式),所以设置这三个为复用IO,复用功能为 AF5。

2) 设置 SPI1 工作模式。

        这一步全部是通过 SPI1_CR1 来设置,我们设置 SPI1 为主机模式,设置数据格式为 8 位,然后通过 CPOL 和 CPHA 位来设置 SCK 时钟极性及采样方式。并设置 SPI1 的时钟频率(最大37.5Mhz),以及数据的格式(MSB 在前还是 LSB 在前)。

3) 使能 SPI1。

        SPI1_CR1 的 bit6 来设置,以启动 SPI1,在启动之后,我们就可以开始 SPI 通讯了。

三、代码配置                                                                                 

1、SPI子函数                                                                                          

void SPI1_Init(void);			 //初始化SPI1口
void SPI1_SetSpeed(u8 SpeedSet); //设置SPI1速度   
u8 SPI1_ReadWriteByte(u8 TxData);//SPI1总线读写一个字节

SPI读写一个字节                                                                                                              

//SPI1 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI1_ReadWriteByte(u8 TxData)
{		 			 
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);//等待发送区空  
    SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte数据 SPI1->DR = TxData;		
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);//等待接收完一个byte  
    return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据		    
}

测试SPI串口通信                                                                                                              

//在main函数实现
while(1)
{
    //SPI串口通信 
	tmp = SPI1_ReadWriteByte(0xAA);//写一个数据0XAA,并读取
    printf("tmp:%X\r\n",tmp);
	delay_ms(500);
}

/*注意:*********************需要用杜邦线将MISO MOSI 连到一起*********************/
/*本文章配置的MISO MOSI为PB4,PB5,故需要用杜邦线将PB4,PB5连接*/

2、W25Q128子函数                                                                                

void W25Q128_Write_Enable(void);  		//写使能 
void W25Q128_Write_Disable(void);		//写禁止

u8 W25Q128_ReadSR(void);   
void W25Q128_WriteSR(u8 sr);
void W25Q128_Wait_Busy(void);   //等待空闲

void W25Q128_Init(void);	//初始化
u16 W25Q128_ReadID(void);	//读取芯片ID

void W25Q128_Erase_Sector(u32 Dst_Addr); //擦除扇区
void W25Q128_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//页内写数据
void W25Q128_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//无检验写SPI FLASH   
void W25Q128_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);//读取数据
void W25Q128_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//写数据

读取芯片ID                                                                                                                       

读取芯片ID

        总结:写低电平驱动/CS引脚,并将指令代码移位90h,随后是000000h的24位地址(A23-A0)来启动,最后通过驱动/CS高来完成。

//读取芯片ID
//返回值如下:				   
//0XEF17,表示芯片型号为W25Q128
u16 W25Q128_ReadID(void)//读芯片ID
{
	u16 temp;
	u16 tmp1,tmp2;
	W25Q128_CS = 0;
	SPI1_ReadWriteByte(0x90);//发送读ID命令
	SPI1_ReadWriteByte(0x00);//发送读ID地址
	SPI1_ReadWriteByte(0x00);
	SPI1_ReadWriteByte(0x00);//如果24位地址最初设置为000001h,则首先读取设备ID,然后再读取制造商ID。制造商id和设备id可以连续读取,交替从一个到另一个
	/*
	 *发送一个虚拟数据,才能再读取数据 
	 */
	tmp1 = SPI1_ReadWriteByte(0xFF);//Manufacturer ID 
    printf("Manufacturer ID:%X  ",tmp1);
	tmp2 = SPI1_ReadWriteByte(0xFF);//Device ID
	printf("Device ID:%X  ",tmp2);
	temp = (tmp1<<8)|tmp2;	
	
	W25Q128_CS=1;		
	return temp;
}

读状态寄存器                                                                                                                    

读状态寄存器   

        

        总结:通过驱动/CS低电平并写状态寄存器-1的指令代码“05h”,最后通过驱动/CS高来完成

u8 W25Q128_ReadSR(void)   
{  
	u8 byte=0;   
	W25Q128_CS=0;                           //使能器件   
	SPI1_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令 W25X_ReadStatusReg为0X05  
	byte=SPI1_ReadWriteByte(0Xff);          //读取一个字节  
	W25Q128_CS=1;                           //取消片选     
	return byte;   
} 

擦除扇区                                                                                                                           

擦除扇区

        总结:在设备接受扇区擦除指令之前,必须执行一个Write Enable指令(状态寄存器位WEL必须等于1)。该指令通过低驱动/CS引脚并写指令代码“20h”跟随一个24位扇区地址(A23-A0)来启动。在扇区擦除周期中,BUSY位是1,当周期结束,设备准备再次接受其他指令时,BUSY位变为0。在扇区擦除周期完成后,状态寄存器中的写使能锁存(WEL)位被清除为0。

//Dst_Addr:扇区地址 根据实际容量设置
void W25Q128_Erase_Sector(u32 Dst_Addr)   
{  
	//监视falsh擦除情况,测试用   
 	printf("fe:%x\r\n",Dst_Addr);	  
 	Dst_Addr*=4096;
    W25QXX_Write_Enable();                  //SET WEL 	 
    W25QXX_Wait_Busy();                     //当设备正在执行时,状态寄存器为1;当为0时为空闲状态
  	W25QXX_CS=0;                            //使能器件   
    SPI1_ReadWriteByte(W25X_SectorErase);   //发送扇区擦除指令 
    SPI1_ReadWriteByte((u8)((Dst_Addr)>>16));  //发送24bit地址    
    SPI1_ReadWriteByte((u8)((Dst_Addr)>>8));   
    SPI1_ReadWriteByte((u8)Dst_Addr);  
	W25QXX_CS=1;                            //取消片选     	      
    W25QXX_Wait_Busy();   				   //等待擦除完成
}  

写数据                                                                                                                               

设计思路

        先获得首地址(WriteAddr)所在的扇区,并计算在扇区内的偏移,然后判断要写入的数据长度是否超过本扇区所剩下的长度,如果不超过,再先看看是 否要擦除;

如果不要,则直接写入数据即可,如果要则读出整个扇区,在偏移处开始写入指定长度的数据,然后擦除这个扇区,再一次性写入。

当所需要写入的数据长度超过一个扇区的长度的时候,我们先按照前面的步骤把扇区剩余部分写完,再在新扇区内执行同样的操作,如此循环,直到写入结束。 W25Q128_BUFFER 的全局变量,用于擦除时缓存扇区内的数据。 

//写SPI FLASH  
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)						
//NumByteToWrite:要写入的字节数(最大65535)   
u8 W25Q128_BUFFER[4096];
void W25Q128_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
	u32 secpos;
	u16 secoff;
	u16 secremain;
	u16 i;
    u8* W25Q128_BUF;	
	W25Q128_BUF=W25Q128_BUFFER;
	secpos = WriteAddr/4096;// /4096明确哪个扇区
	secoff = WriteAddr%4096;//在扇区内的偏移
	secremain = 4096-secoff;//扇区剩余空间大小
	if(NumByteToWrite<secremain)
		secremain=NumByteToWrite;
	while(1)
	{
		W25Q128_Read(W25Q128_BUF,secpos*4096,4096);//读取整个扇区内容
		for(i=0;i<secremain;i++)//校验数据
			if(W25Q128_BUF[secoff+i]!=0XFF)//是否需要擦除
				break;
			
		if(i<secremain)//需要擦除
		{
			W25Q128_Erase_Sector(secpos);
			for(i=0;i<secremain;i++)
				W25Q128_BUF[i+secoff]=pBuffer[i];
			
			W25Q128_Write_NoCheck(W25Q128_BUF,secpos*4096,4096);//写入整个扇区			
		}
		else
			W25Q128_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.
		if(NumByteToWrite == secremain)
			break;	//写入结束
		else{
			secpos++;//扇区地址增1
			secoff=0;//偏移地址为0
			
			pBuffer+=secremain;//指针偏移
			WriteAddr+=secremain;//写指针偏移
			NumByteToWrite-=secremain;	//字节数递减
			if(NumByteToWrite>4096)
				secremain=4096;//下一个扇区还是写不完
			else
				secremain = NumByteToWrite;//写一个扇区可以写完
		}
	}
}

读数据                                                                                                                               

//读取 SPI FLASH
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大 65535)
void W25Q128_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{
	u16 i;
	W25Q128_CS=0;
	SPI1_ReadWriteByte(W25Q128_ReadData);//发送读取命令
	SPI1_ReadWriteByte((u8)(ReadAddr)>>16);//发送24bit地址
	SPI1_ReadWriteByte((u8)(ReadAddr)>>8);
	SPI1_ReadWriteByte((u8)ReadAddr);
	
	for(i=0;i<NumByteToRead;i++)
		pBuffer[i]=SPI1_ReadWriteByte(0XFF);//循环读数
	W25Q128_CS=1;
}

测试读取芯片ID                                                                                                                

//在main实现
while(1)
{		
    //读取芯片ID
    usID=W25Q128_ReadID();	//读取FLASH ID.
	printf("usID:%x\r\n",usID);
	delay_ms(500);
}

测试W25Q128读写数据                                                                                                   

//要写入到W25Q128的字符串数组
const u8 TEXT_Buffer[]={"Explorer STM32F4 SPI TEST"};
#define SIZE sizeof(TEXT_Buffer)	 
while(1)
{
    W25Q128_Write((u8*)TEXT_Buffer,0x70,SIZE);
	W25Q128_Read(TempBuffer,0x70,SIZE);
	printf("TEXT_BUFF =%s\r\n",TEXT_Buffer);
	printf("TempBuffer =%s\r\n\n",TempBuffer);
	delay_ms(2000);
}

  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 中景园电子是一家专业生产OLED显示屏的公司,他们的0.96寸OLED显示屏适用于STM32 ZET系列开发板,并且可以通过SPI接口连接。下面我将简要介绍如何在STM32开发板上使用该OLED显示屏。 首先,我们需要在STM32开发板上连接OLED显示屏。可以将OLED的VCC引脚连接到STM32的3.3V电源引脚,GND引脚连接到GND,SDA引脚连接到STM32SPI MOSI引脚,SCL引脚连接到SPI SCK引脚,RES引脚连接到任意GPIO引脚,DC引脚连接到SPI CS引脚。另外,为了保证数据传输的稳定性,可以在VCC和GND之间加入一个电容。 接下来,在STM32的代码中引入OLED的库函数,并初始化SPI接口。可以使用STM32提供的标准库函数来实现SPI的初始化,包括初始化SPI的时钟源、引脚、速率等参数。然后,利用库函数对OLED进行初始化,设置其工作模式、亮度、对比度等参数。 在完成初始化之后,我们可以通过库函数来控制OLED显示屏的操作。例如,通过设置点亮的像素来显示图形和字符,可以在屏幕的指定位置绘制线条、矩形和圆等图形,也可以显示文字,甚至可以进行动画的显示。 通过以上步骤,我们可以在STM32开发板上通过SPI接口使用中景园电子的0.96寸OLED显示屏。可以根据实际需求编写相应的代码,实现各种丰富的显示效果。希望以上的回答对您有所帮助。 ### 回答2: 中景园电子0.96oled显示屏是一款高清显示屏,采用了0.96英寸的OLED屏幕,具有显示效果优良、显示画面清晰等特点。该显示屏可以通过SPI总线与STM32ZET系列微控制器进行通信。 在使用中景园电子0.96oled显示屏时,我们可以使用STM32ZET系列微控制器提供的SPI外设来与显示屏进行通信。SPI是一种同步串行通信协议,通过传输时钟、数据、以及控制信号来实现数据的传输。我们可以通过配置SPI外设的寄存器来实现与显示屏的通信。 在使用SPI来控制中景园电子0.96oled显示屏时,我们需要先配置SPI的时钟分频因子、数据传输位序、数据传输模式等参数。然后,可以通过向SPI发送特定命令来控制显示屏的初始化、设置显示位置、写入数据等操作。 例如,可以使用SPI向显示屏发送指令0xAE来关闭显示,使用指令0xAF来开启显示。可以使用指令0x20和0x21来设置显示位置等。 此外,为了简化编程的过程,我们可以参考STM32ZET系列微控制器的SPI例程进行开发。例程中会提供一些用于配置SPI的代码和一些操作显示屏的示例代码。我们可以根据自己的需求进行修改和扩展,以实现更丰富的显示功能。 综上所述,中景园电子0.96oled显示屏与STM32ZET系列微控制器的SPI外设结合使用,可以实现高清、清晰的显示效果。通过合理配置SPI参数和使用相关指令,我们可以实现对显示屏的初始化、控制和数据操作等功能。通过参考SPI例程,我们可以快速上手并进行开发。 ### 回答3: 中景园电子的0.96 OLED显示屏与STM32ZET系列芯片的SPI接口可以通过以下例程进行操作。 首先,需要在STM32ZET系列芯片的开发环境中导入相关的库文件。然后,在主函数中进行初始化设置,包括引脚设置、SPI初始化和OLED显示屏的初始化等。 接下来,在主循环中,可以通过SPI接口与0.96 OLED显示屏进行通信。在传输数据之前,先向OLED发送指令以选择相应的显示模式或设置参数。然后,根据需要调用相应的函数发送数据到OLED显示屏上。例如,可以使用spi_transfer函数将需要显示的数据传输到OLED显示屏的缓冲区中。最后,调用更新显示函数,将缓冲区中的数据显示在屏幕上。 需要注意的是,SPI传输是通过片选信号进行控制的。通过拉低片选信号(选通OLED显示屏),可以与OLED进行通信;反之,拉高片选信号(非选通OLED显示屏),可以与其他SPI设备进行通信。 此外,还可以根据需要添加其他功能,如图形绘制、文字显示等。 总结来说,中景园电子的0.96 OLED显示屏与STM32ZET系列芯片的SPI接口可以通过适当的库文件和例程进行初始化和通信设置,让开发者能够实现自定义的显示内容和功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值