SPI通信协议

一、SPI通信

1、SPI(Serial Peripheral Interface)是由Motorola公司开发的一种通用数据总线

2、四根通信线SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select)

3、同步,全双工

4、支持总线挂载多设备(一主多从

5、硬件电路

(1)所有SPI设备的SCK、MOSI、MISO分别连在一起

(2)主机另外引出多条SS控制线,分别接到各从机的SS引脚

(3)输出引脚配置为推挽输出输入引脚配置为浮空或上拉输入

6、移位示意图

7、SPI时序基本单元

(1)起始条件:SS从高电平切换到低电平

(2)终止条件:SS从低电平切换到高电平

(3)交换一个字节(模式0

        CPOL=0空闲状态时,SCK为低电平

        CPHA=0SCK第一个边沿移入数据,第二个边沿移出数据

        CPOL表示时钟极性

        CPHA表示时钟相位,决定是第一个时钟采样移入还是第二个时钟采样移入

(4)交换一个字节(模式1

        CPOL=0空闲状态时,SCK为低电平

        CPHA=1SCK第一个边沿移出数据,第二个边沿移入数据

(5)交换一个字节(模式2

        CPOL=1空闲状态时,SCK为高电平

        CPHA=0SCK第一个边沿移入数据,第二个边沿移出数据

(6)交换一个字节(模式3

        CPOL=1空闲状态时,SCK为高电平

        CPHA=1SCK第一个边沿移出数据,第二个边沿移入数据

8、SPI时序

(1)发送指令

        向SS指定的设备,发送指令0x06

(2)指定地址写

        向SS指定的设备,发送写指令0x02),      随后在指定地址(Address[23:0])下,写入指定数据(Data)

 

(3)指定地址读

        向SS指定的设备,发送读指令0x03),      随后在指定地址(Address[23:0])下,读取从机数据(Data)

二、W25Q64简介

1、W25Q64简介

(1)W25Qxx系列是一种低成本小型化使用简单非易失性存储器,常应用于数据存储字库存储固件程序存储等场景

(2)存储介质:Nor Flash(闪存)

(3)时钟频率80MHz / 160MHz (Dual SPI) / 320MHz (Quad SPI)

(4)存储容量(24位地址):     

                W25Q40:      4Mbit / 512KByte     

                W25Q80:      8Mbit / 1MByte     

                W25Q16:      16Mbit / 2MByte     

                W25Q32:      32Mbit / 4MByte     

                W25Q64:      64Mbit / 8MByte     

                W25Q128:  128Mbit / 16MByte     

                W25Q256:  256Mbit / 32MByte

2、硬件电路

3、W25Q64框图

4、Flash操作注意事项

(1)写入操作时:

        a.写入操作前,必须先进行写使能

        b.每个数据位只能由1改写为0,不能由0改写为1

        c.写入数据前必须先擦除擦除后,所有数据位变为1

        d.擦除必须按最小擦除单元进行(最小的擦除单元是一个扇区)

        e.连续写入多字节时,最多写入一页的数据超过页尾位置的数据会回到页首覆盖写入

        f.写入操作结束后,芯片进入忙状态不响应新的读写操作

(2)读取操作时:

        a.直接调用读取时序无需使能无需额外操作没有页的限制,读取操作结束后不会进入忙状态,但不能在忙状态时读取

三、软件SPI读写W25Q64

1、按照以下接线方式连接,并将STLINK插到电脑上

2、多层模块架构

(1)最底层:SPI层

        MySPI.c

#include "stm32f10x.h"                  // Device header

/*
	丛机选择
*/
void MySPI_W_SS(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}

/*
	时钟线
*/
void MySPI_W_SCK(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA,GPIO_Pin_5,(BitAction)BitValue);
}

/*
	主机输出丛机输入
*/
void MySPI_W_MOSI(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA,GPIO_Pin_7,(BitAction)BitValue);
}

/*
	主机输入丛机输出
*/
uint8_t MySPI_R_MISO(void)
{
	GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6);
}
	
/*
	初始化SPI
*/
void MySPI_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;//输出
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//上拉输入
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;//MISO:输入
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	MySPI_W_SS(1);//丛机先不选
	MySPI_W_SCK(0);//模式0
}

/*
	起始条件
*/
void MySPI_Start(void)
{
	MySPI_W_SS(0);
}

/*
	终止条件
*/
void MySPI_Stop(void)
{
	MySPI_W_SS(1);
}

/*
	交换一个字节(模式0)掩码
*/
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	uint8_t i,ByteReceive=0x00;
	
	for(i=0;i<8;i++)
	{
		MySPI_W_MOSI(ByteSend & (0x80>>i));//使用掩码依次提取数据每一位
		MySPI_W_SCK(1);
		if(MySPI_R_MISO() == 1){ByteReceive |= (0x80>>i);}
		MySPI_W_SCK(0);
	}
	
	return ByteReceive;
}

///*
//	交换一个字节(模式0)移位
//*/
//uint8_t MySPI_SwapByte(uint8_t ByteSend)
//{
//	uint8_t i;
//	
//	for(i=0;i<8;i++)
//	{
//		MySPI_W_MOSI(ByteSend & 0x80);//用移位数据本身来进行操作,效率高,但是ByteSend在移位的过程中改变了
//		ByteSend <<= 1;
//		MySPI_W_SCK(1);
//		if(MySPI_R_MISO() == 1){ByteSend |= 0x01;}
//		MySPI_W_SCK(0);
//	}
//	
//	return ByteSend;
//}

         MySPI.h

#ifndef __MYSPI_H
#define __MYSPI_H

void MySPI_Init(void);
void MySPI_Start(void);
void MySPI_Stop(void);
uint8_t MySPI_SwapByte(uint8_t ByteSend);


#endif

(2)协议层之上:W25Q64的驱动层

        W25Q64.c

#include "stm32f10x.h"                  // Device header
#include "MySPI.h"
#include "W25Q64_Ins.h"

/*
	初始化W25Q64
*/
void W25Q64_Init(void)
{
	MySPI_Init();
}

/*
	获取ID号
*/
void W25Q64_ReadID(uint8_t *MID,uint16_t *DID)//厂商ID、设备ID
{
	MySPI_Start();
	MySPI_SwapByte(W25Q64_JEDEC_ID);
	*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);//厂商ID
	*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);//设备ID高八位
	*DID <<= 8;
	*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);//设备ID低八位,用移位的方法得到16位设备ID
	MySPI_Stop();
}
	
/*
	写使能
*/
void W25Q64_WriteEnable(void)
{
	MySPI_Start();
	MySPI_SwapByte(W25Q64_WRITE_ENABLE);
	MySPI_Stop();
}

/*
	读状态寄存器忙等待
*/
void W25Q64_WaitBusy(void)
{
	uint32_t Timeout;
	MySPI_Start();
	MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);
	Timeout =100000;
	while((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)	//busy为1,进入循环,直到busy为0跳出
	{
		Timeout--;
		if(Timeout ==0)
		{
			break;
		}
	}
	MySPI_Stop();
}

/*
	页编程
*/
void W25Q64_PageProgram(uint32_t Address,uint8_t *DataArray,uint16_t Count)
{
	uint16_t i;
	
	W25Q64_WriteEnable();//写入操作前,必须先进行写使能
	
	MySPI_Start();
	MySPI_SwapByte(W25Q64_PAGE_PROGRAM);
	MySPI_SwapByte(Address >> 16);//高八位
	MySPI_SwapByte(Address >> 8);//中八位,高中16位,只能存8位,所以舍弃高八位
	MySPI_SwapByte(Address);//低八位,高中低24位,只能存8位,所以舍弃高中16位
	for(i=0;i<Count;i++)
	{
		MySPI_SwapByte(DataArray[i]);//发送写入数据
	}
	MySPI_Stop();
	
	W25Q64_WaitBusy();//写入操作结束后,芯片进入忙状态,必须进入忙等待
}
	
/*
	擦除扇区
*/
void W25Q64_SectorErase(uint32_t Address)
{
	W25Q64_WriteEnable();//写入操作前,必须先进行写使能
	
	MySPI_Start();
	MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);
	MySPI_SwapByte(Address >> 16);//高八位
	MySPI_SwapByte(Address >> 8);//中八位,高中16位,只能存8位,所以舍弃高八位
	MySPI_SwapByte(Address);//低八位,高中低24位,只能存8位,所以舍弃高中16位
	MySPI_Stop();
	
	W25Q64_WaitBusy();//写入操作结束后,芯片进入忙状态,必须进入忙等待
}

/*
	读取数据
*/
void W25Q64_ReadData(uint32_t Address,uint8_t *DataArray,uint32_t Count)	
{
	uint32_t i;
	MySPI_Start();
	MySPI_SwapByte(W25Q64_READ_DATA);
	MySPI_SwapByte(Address >> 16);//高八位
	MySPI_SwapByte(Address >> 8);//中八位,高中16位,只能存8位,所以舍弃高八位
	MySPI_SwapByte(Address);//低八位,高中低24位,只能存8位,所以舍弃高中16位
	for(i=0;i<Count;i++)
	{
		DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);
	}
	MySPI_Stop();
}	

        W25Q64.h

#ifndef __W25Q64_H
#define __W25Q64_H

void W25Q64_Init(void);
void W25Q64_ReadID(uint8_t *MID,uint16_t *DID);
void W25Q64_PageProgram(uint32_t Address,uint8_t *DataArray,uint16_t Count);
void W25Q64_SectorErase(uint32_t Address);
void W25Q64_ReadData(uint32_t Address,uint8_t *DataArray,uint32_t Count);

#endif

(3)应用层:主函数

        mian.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "W25Q64.h"

uint8_t MID;
uint16_t DID;

uint8_t ArrayWrite[] = {0x01,0x02,0x03,0x04};
uint8_t ArrayRead[4];

int main(void)
{
	OLED_Init();
	W25Q64_Init();
	
	OLED_ShowString(1,1,"MID:   DID:");
	OLED_ShowString(2,1,"W:");
	OLED_ShowString(3,1,"R:");
	
	W25Q64_ReadID(&MID,&DID);
	OLED_ShowHexNum(1,5,MID,2);
	OLED_ShowHexNum(1,12,DID,4);
	
	W25Q64_SectorErase(0X000000);//擦除扇区 掉电不丢失
	W25Q64_PageProgram(0X000000,ArrayWrite,4);//页编辑
	
	W25Q64_ReadData(0X000000,ArrayRead,4);//写数据
	
	OLED_ShowHexNum(2,3,ArrayWrite[0],2);
	OLED_ShowHexNum(2,6,ArrayWrite[1],2);
	OLED_ShowHexNum(2,9,ArrayWrite[2],2);
	OLED_ShowHexNum(2,12,ArrayWrite[3],2);
	
	OLED_ShowHexNum(3,3,ArrayRead[0],2);
	OLED_ShowHexNum(3,6,ArrayRead[1],2);
	OLED_ShowHexNum(3,9,ArrayRead[2],2);
	OLED_ShowHexNum(3,12,ArrayRead[3],2);
	
	while(1)
	{
		
	}
}

3、实现效果

(1)掉电不丢失:注释掉擦除和写入的代码,断电后重启,读取的数据不变

(2)Flash擦除之后变为FF:注释掉写入的代码,只擦除不写入

(3)Flash只能1写0,不能0写1:

        a.先写入AA、BB、CC、DD

b.注释擦除,写入55、66、77、88,尝试不擦除直接写入

(4)写入数据不能跨页:

        修改页编辑的地址为0x0000FF,一页的最后一个地址,从最后一个地址开始写,验证是否可以跨页到下一页的0x000100

        修改读取数据地址为0x0000FF,也从最后一个开始读

再将读取数据地址修改为0x000000

四、SPI外设简介

1、SPI外设简介

(1)STM32内部集成了硬件SPI收发电路,可以由硬件自动执行时钟生成、数据收发等功能,减轻CPU的负担

(2)可配置8位/16位数据帧、高位先行/低位先行

(3)时钟频率 fPCLK / (2, 4, 8, 16, 32, 64, 128, 256)

(4)支持多主机模型主或从操作

(5)可精简为半双工/单工通信

(6)支持DMA

(7)兼容I2S协议(数字音频信号传输的专用协议)

(8)STM32F103C8T6 硬件SPI资源:SPI1(挂载在APB2,PCLK是72M)、SPI2(挂载在APB1,PCLK是36M)

2、SPI框图

3、SPI基本结构

4、主模式全双工连续传输

5、非连续传输

6、软件/硬件波形对比

五、硬件SPI读写W25Q64

1、SPI引脚

2、修改底层的MySPI.c文件

把初始化和时序的执行步骤,由软件实现改成硬件实现

(1)SPI库函数的功能

(2)MySPI.c

#include "stm32f10x.h"                  // Device header

/*
	丛机选择(软件模拟)
*/
void MySPI_W_SS(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}
	
/*
	初始化SPI(硬件)
*/
void MySPI_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;				//初始化GPIO
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//通用推挽输出
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4;//SS软件控制的输出信号
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5 | GPIO_Pin_7;//硬件外设控制的输出信号
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//上拉输入
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;//硬件外设的输入信号
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	SPI_InitTypeDef SPI_InitStructure;					//初始化SPI外设
	SPI_InitStructure.SPI_Mode=SPI_Mode_Master;//主模式
	SPI_InitStructure.SPI_Direction=SPI_Direction_2Lines_FullDuplex;//双线全双工
	SPI_InitStructure.SPI_DataSize=SPI_DataSize_8b;//配置8位数据帧
	SPI_InitStructure.SPI_FirstBit=SPI_FirstBit_MSB;//高位先行
	SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_128;//波特率预分频器
	SPI_InitStructure.SPI_CPOL=SPI_CPOL_Low;//模式0
	SPI_InitStructure.SPI_CPHA=SPI_CPHA_1Edge;//第一个边沿开始采样,SCK第一个边沿移入数据,CPHA=0
	SPI_InitStructure.SPI_NSS=SPI_NSS_Soft;//软件NSS
	SPI_InitStructure.SPI_CRCPolynomial=7;
	SPI_Init(SPI1,&SPI_InitStructure);
	
	SPI_Cmd(SPI1,ENABLE);								//使能
	
	MySPI_W_SS(1);//丛机先不选
}

/*
	起始条件(软件模拟)
*/
void MySPI_Start(void)
{
	MySPI_W_SS(0);
}

/*
	终止条件(软件模拟)
*/
void MySPI_Stop(void)
{
	MySPI_W_SS(1);
}

/*
	交换一个字节(非连续传输)
*/
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) != SET);//等待TXE为1
	SPI_I2S_SendData(SPI1,ByteSend);//写发送数据至TDR,一旦写入数据时序就会自动生成
	while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE) != SET);//等待RXNE为1,表示收到一个字节,同时表示发送时序产生完成
	return SPI_I2S_ReceiveData(SPI1);//读取RDR接受的数据,就是置换接收的一个字节
}

3、实现效果

        只需要修改底层的MySPI.c文件,其他代码和软件实现一样,最终实现的效果也和软件一样,不再重复展示

  • 29
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: ILI9488是一款广泛使用的TFT液晶屏驱动芯片,其SPI通信协议如下: 1. 在SPI通信开始前,先拉低CS (Chip Select)管脚,然后拉低RS (Register Select)管脚,表示接下来要写入命令。 2. 向ILI9488写入命令时,先发送一个字节的命令代码,然后再发送该命令需要的参数(如果有的话)。 3. 写入命令后,可以拉高RS管脚,表示接下来要写入数据。 4. 向ILI9488写入数据时,直接发送数据即可。 5. SPI通信结束后,拉高CS管脚,表示本次通信结束。 具体的命令代码和参数可以参考ILI9488的数据手册,例如: - 0x36:设置扫描方向 - 0x3A:设置像素格式 - 0x2A:设置列地址 - 0x2B:设置行地址 - 0x2C:写入像素数据 在具体实现时,需要根据ILI9488的数据手册来编写SPI通信的代码。 ### 回答2: ILI9488是一种常用于液晶显示屏控制器的芯片,它使用SPI(串行外设接口)进行通信。SPI是一种串行通信协议,它通过四个信号线进行数据传输,包括时钟线(SCLK),主设备输出数据线(MOSI),主设备输入数据线(MISO)和片选线(CS)。 在ILI9488的SPI通信协议中,通信通过以下步骤进行: 1. 在通信开始前,主设备通过拉低片选线来选择ILI9488芯片作为通信对象。 2. 主设备根据芯片规定的时钟频率和极性生成时钟信号,从而同步数据传输。 3. 主设备将需要传输的数据发送到MOSI线上,高位先传送。 4. 在传输数据的过程中,芯片会检测到时钟的上升或下降边沿,并在边沿处读取MOSI线上的数据。数据传输完毕后,芯片会将数据写入相应的寄存器或进行相关操作。 5. 芯片可通过MISO线向主设备发送数据。但对于ILI9488来说,这个功能多数情况下是不被使用的。 6. 传输完成后,主设备通过拉高片选线来结束通信。 ILI9488的SPI通信协议具有简单、可靠和较快的特点。通过SPI接口,可以实现对ILI9488的控制和显示操作。同时,SPI通信协议也可以提供对其他外设的控制和通信,为应用提供了较大的灵活性和可扩展性。值得注意的是,具体的通信细节如时钟频率、数据格式等需要根据ILI9488的数据手册来设置,以确保通信的正常进行。 ### 回答3: ILI9488是一种常见的液晶显示驱动IC,它采用SPI通信协议与主控芯片进行通信。SPI全称为串行外围接口协议,它是一种同步的、全双工的串行通信协议。 ILI9488的SPI通信协议在硬件层面上,需要使用四个引脚进行通信,分别为时钟线CLK、数据命令控制线D/C、数据输入线MOSI和数据输出线MISO。其中CLK用于传输时钟信号,D/C用于控制数据或命令的传输,MOSI用于主控芯片向ILI9488发送数据,MISO用于ILI9488向主控芯片发送数据。 ILI9488的SPI通信协议在数据传输上是基于字节的,通信的数据帧包含一个命令字节和若干数据字节。根据具体的功能需要,主控芯片通过D/C引脚发送命令字节或数据字节。命令字节用于控制ILI9488的各种操作,如初始化设置、显示模式选择、像素颜色设置等,而数据字节则用于传输实际显示的像素数据。 在通信过程中,主控芯片通过CLK引脚向ILI9488提供时钟信号,ILI9488根据时钟信号判断数据的有效性,并按照时钟信号的边沿读取或发送数据。通过MOSI和MISO引脚的数据交换,主控芯片可以向ILI9488发送命令字节或数据字节,并接收ILI9488的响应数据。 总之,ILI9488的SPI通信协议是通过CLK、D/C、MOSI和MISO四个引脚进行数据传输的,主控芯片通过时钟信号控制数据的读写,通过命令字节和数据字节实现与ILI9488的各种交互操作。这种协议结构简单,适用于许多SPI接口的设备,使得ILI9488与主控芯片之间的通信更加方便和高效。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值