STM32—SPI通信(上)

一、SPI通信协议

SPI 为全双工

SPI共有四根通信线:SCK(串行时钟线)、MOSI(主机输出从机输入)、MISO(主机输入从机输出)、SS(从机选择)。

1.1、硬件电路

  1. 所有SPI设备的SCK、MOSI、MISO分别连在一起
  2. 主机另外引出多条SS控制线,分别接到各从机的SS引脚
  3. 输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入
  4. SPI通信线为单端信号,器电平高低都是相对GND的电压差,因此所有设备需共地。

1.2、SPI 数据传输流程

在时钟驱动(波特率发生器)下,SPI主机与从机的移位寄存器在每个上升沿时,发生一个数据位的数据交换,主机移位寄存器数据左移一位数据,置于MOSI,从机数据寄存器同样左移一个数据位,将一位数据置于MISO,如下图。最终两个寄存器完成数据交换。

1.3、SPI 基本时序单元

1.3.1、SPI起始条件&终止条件

起始条件:SS从高电平切换到低电平
终止条件:SS从低电平切换到高电平

1.3.2、交换一个字节

SPI_CR寄存器的CPOLCPHA位,能够组合成四种可能的时序关系。CPOL(时钟极性)位控制在没有数据传输时时钟的空闲状态电平,此位对主模式和从模式下的设备都有效。

  • 如果CPOL被清 '0',SCK引脚在空闲状态保持低电平;
  • 如果CPOL被置 '1',SCK引脚在空闲状态保持高电平
     
  • 如果 CPHA(时钟相位) 位被置  '1',SCK时钟的第二个边沿  (CPOL位为0时就是下降沿,CPOL位为'1'时就是上升沿)  进行数据位的采样,数据在第二个时钟边沿被锁存。
  • 如果CPHA位被清  '0’,SCK时钟的第一边沿  (CPOL位为0时就是上升沿,CPOL位为’1时就是下降沿)  进行数据位采样,数据在第一个时钟边沿被锁存
1.3.2.1、模式0

CPOL=0:空闲状态时,SCK为低电平
CPHA=0: SCK第一个边沿移入数据,第二个边沿移出数据

        CPOL=0,  SCK引脚在空闲状态保持低电平CPHA=0,需要在SCK第一个边沿采集数据,即读取数据,根据1.2、SPI数据传输流程,需要我们在采集数据之前先移出数据,因此在SS下降沿时,MOSIMISO将数据提前放置到线上,待SCK在第一个边沿到来时(上升沿),即可读取数据。

        由于SPI同时连接多个从机,为避免多从机同时输入,因此在SS未被选中状态时,将从机MISO引脚关断输出,即配置为高组态。因此上图中的MISO开始与结束时都用一个处于中间的先表示处于高组态。

1.3.2.2、模式1

CPOL=0:空闲状态时,SCK为低电平
CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据

 CPOL=0,  SCK引脚在空闲状态保持低电平CPHA=1,需要在SCK第二个边沿采集数据,那第一个边沿需要移出数据。

1.3.2.3、模式2

CPOL=1:空闲状态时,SCK为高电平
CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据

除 CPOL=1:空闲状态时,SCK为高电平外,其余与模式0无差。

1.3.2.4、模式3

CPOL=1:空闲状态时,SCK为高电平
CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据

1.3.3、指定地址写

SPI 对于字节流的传输采用的是 指令码+读写数据 的方式,即起始条件开始后,主机第一个交换发给从机的数据一般成为指令码, 在从机中有对应的指令集,从机会根据接受到的指令码完成相应的指令集内容。

向SS指定的设备,发送写指令(0x02),随后在指定地址(Address[23:0])下,写入指定数据(Data)。由于本例采用的W65Q24芯片有8M字节存储空间,需要用到24位地址。

其中,第一个发送的数据 0x20,为指令码,对应写指令(W65Q24芯片)。

1.3.4、指定地址读

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

二、W25Q64芯片介绍

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

2.1、硬件电路

HOLD:遇中断离开时,可保存当前时序。

2.2、W25Q64硬件结构介绍

2.2.1、Flash 存储区域划分

【1】蓝色框选部分为W25Q64全部存储空间,存储器以字节为单位,且每个字节有唯一一个地址k,首地址为000000h(h代表16进制),最后一个地址位7FFFFFh。

【2】在蓝色框选部分内,又以 64KB为一个基本单元,分为若干块(Block)。由于W25Q64存储单元为8M,因此,蓝色框选部分共分为 8*1024/64=128 块。下图为 块0(Block0)地址,红色部分为起始地址,蓝色为结束地址。

【3】在 块(Block) 中又分为 多个扇区(Sector)。在扇区中,以4KB为单位,将64KB的 块(Block)切分为16 个扇区(Sector)。下图为 扇区(Sector)地址,红色部分为起始地址,蓝色为结束地址。

【4】在写入数据是,还会有一个比 扇区(Sector)更小的划分 页(Page),页大小为 256个字节,每个4KB大小的扇区(Sector)可分为 4*1024/256=16 页。图中黄色框选部分为页起始地址,绿色为页终止地址,变化范围是 xx xx 00~xx xx FF。

2.2.2、W25Q64控制框图解析

  • 下图红色框选部分为 控制逻辑,管理整个芯片运作。蓝色部分为状态寄存器,用于检查芯片是否处于忙状态,是否写使能,是否写保护等;
  • 黄色框选部分为高压生成器,用于配合Flash 编程(Flash掉电不丢失,需要高压刺激。)
  • 绿色框选部分别为页地址锁存寄存器/计数器、字节锁存寄存器/计数器,用于指定地址。例如通过SPI 发送3个地址数据,高两位字节地址进入页地址锁存寄存器/计数器,低位字节地址进入字节锁存寄存器/计数器。
  • 页地址锁存寄存器/计数器会通过 写保护和行解码(红色圈起部分),选择要操作的页数。
  • 字节锁存寄存器/计数器会通过列解码和256字节页缓存,进行指定字节读写操作

2.3、Flash 操作事项

2.3.1、写操作

  • 写入操作前,必须先进行写使能(防止误操作)
  • 每个数据位只能由1改写为0,不能由0改写为1
  • 写入数据前必须先擦除,擦除后,所有数据位变为1
  • 擦除必须按最小擦除单元(4KB)进行
  • 连续写入多字节时,最多写入一页的数据(即256KB),超过页尾位置的数据,会回到页首覆盖写入。
  • 写入操作结束后,芯片进入忙状态,不响应新的读写操作

2.3.2、读取操作

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

2.4、指令集

页面程序为页编程,有256KB大小限制。

为使代码具有可读性,在代码中一般通过宏定义将指令集转换成可读性语言,如下列代码。

#ifndef __W25Q64_INS_H
#define __W25Q64_INS_H

#define W25Q64_WRITE_ENABLE							0x06
#define W25Q64_WRITE_DISABLE						0x04
#define W25Q64_READ_STATUS_REGISTER_1				0x05
#define W25Q64_READ_STATUS_REGISTER_2				0x35
#define W25Q64_WRITE_STATUS_REGISTER				0x01
#define W25Q64_PAGE_PROGRAM							0x02
#define W25Q64_QUAD_PAGE_PROGRAM					0x32
#define W25Q64_BLOCK_ERASE_64KB						0xD8
#define W25Q64_BLOCK_ERASE_32KB						0x52
#define W25Q64_SECTOR_ERASE_4KB						0x20
#define W25Q64_CHIP_ERASE							0xC7
#define W25Q64_ERASE_SUSPEND						0x75
#define W25Q64_ERASE_RESUME							0x7A
#define W25Q64_POWER_DOWN							0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE				0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET			0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID		0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID				0x90
#define W25Q64_READ_UNIQUE_ID						0x4B
#define W25Q64_JEDEC_ID								0x9F
#define W25Q64_READ_DATA							0x03
#define W25Q64_FAST_READ							0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT				0x3B
#define W25Q64_FAST_READ_DUAL_IO					0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT				0x6B
#define W25Q64_FAST_READ_QUAD_IO					0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO				0xE3

#define W25Q64_DUMMY_BYTE							0xFF  //无用数据

#endif

三、功能代码(SPI软件读写W25Q64)

引脚接线图

3.1、MySPI.c文件

该层位SPI的通信协议层,可以认为是底层代码,用于开启时钟以及定义SPI工作基本时序。

3.1.1、MySPI_Init

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;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
}

其中引脚 4 5 7为推挽输出,引脚6为上拉输入(1.1、硬件电路已说明)。

31.2、引脚操作

//操作ss
void MySPI_W_SS(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA ,GPIO_Pin_4,(BitAction)BitValue);
}
//操作SCLK
void MySPI_W_SCLK(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA ,GPIO_Pin_5,(BitAction)BitValue);
}
//操作MOSI
void MySPI_W_MOSI(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA ,GPIO_Pin_7,(BitAction)BitValue);
}
//操作MISO
uint8_t MySPI_R_MISO(void)
{
	return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6);
}

由于需要人工手动翻转SCLK 以及 MISO、MOSI等引脚,所以为了便捷,将引脚翻转等操作封装为函数方便调用与移植。

3.1.3、基本时序

void MySPI_Star(void)
{
	MySPI_W_SS(0);
	MySPI_W_SCLK(0);//使用模式0
}

void MySPI_Stop(void)
{
	MySPI_W_SS(1);
}

//模式0
uint8_t MySPI_WriteRead_0(uint8_t Bytesend)
{
	uint8_t i, ByteReceive = 0x00;
	for (i = 0; i < 8; i ++)
	{
		MySPI_W_MOSI(Bytesend & (0x80 >> i));
		MySPI_W_SCLK(1);
		if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}
		MySPI_W_SCLK(0);
	}
	return ByteReceive;
}

对于模式的选择,是在  Star 中添加MySPI_W_SCLK()函数,通过翻转引脚高低电平实现。

3.2、W25Q64.c文件

该层是一个通信层,是基于底层文件MySPI.c文件运行。

void W25Q64_Init(void)
{
	MySPI_Init();
}

//写使能
void W25Q64_WriteEnable(void)
{
	MySPI_Star();
	MySPI_WriteRead_0(W25Q64_READ_STATUS_REGISTER_1);
	MySPI_Stop();
}

//读取状态寄存器
void W25Q64_WaitBusy()
{
	uint32_t Timeout=100000;
	MySPI_Star();
	MySPI_WriteRead_0(W25Q64_READ_STATUS_REGISTER_1);
	Timeout--;
	while((MySPI_WriteRead_0(W25Q64_DUMMY_BYTE)&0x01)==1)
	{
		if(Timeout==0)
		{
			break;
		}
	}
	MySPI_Stop();
}

//页编程
void W25Q64_PageProgram(uint32_t Address,uint8_t *DataArray,uint16_t Count)
{
	W25Q64_WriteEnable();
	uint16_t i;
	MySPI_Star();
	MySPI_WriteRead_0(W25Q64_PAGE_PROGRAM);
	MySPI_WriteRead_0(Address>>16);
	MySPI_WriteRead_0(Address>>8);
	MySPI_WriteRead_0(Address);
	for(i=0;i<Count;i++)
	{
		MySPI_WriteRead_0(DataArray[i]);
	}
	MySPI_Stop();
	W25Q64_WaitBusy();
}

//扇区擦除
void W25Q64_SectorErase(uint32_t Address)
{
	W25Q64_WriteEnable();
	MySPI_Star();
	MySPI_WriteRead_0(W25Q64_SECTOR_ERASE_4KB	);
	MySPI_WriteRead_0(Address>>16);
	MySPI_WriteRead_0(Address>>8);
	MySPI_WriteRead_0(Address);
	MySPI_Stop();
	W25Q64_WaitBusy();
}

//读取数据
void W25Q64_ReadData(uint32_t Address,uint8_t *DataArray,uint32_t Count)
{
	uint32_t i;
	MySPI_Star();
	MySPI_WriteRead_0(W25Q64_READ_DATA);
	MySPI_WriteRead_0(Address>>16);
	MySPI_WriteRead_0(Address>>8);
	MySPI_WriteRead_0(Address);
	for(i=0;i<Count;i++)
	{
		DataArray[i]=MySPI_WriteRead_0(W25Q64_DUMMY_BYTE);
	}
	MySPI_Stop();
}

【1】代码中写使能是因为2.3、Flash操作中规定,写入操作前需要先写使能。因此有页编程、扇区擦除需要添加写使能代码 W25Q64_WriteEnable()。并且由于写入操作结束后,芯片进入忙状态,不响应新的读写操作,因此需要再写入操作后加入等待状态标志位,所以页编程以及、扇区写入后会添加 等待标志位代码 W25Q64_WaitBusy()。读取数据无需添加(也可添加),因为芯片无非是 读、写以及擦除操作,写与擦书等待说明芯片处于读的忙状态。

【2】读取状态寄存器,是为了判断芯片是否处于忙状态。添加Timeout 是为了防止程序在while中卡死,可以及时跳出。Timeout 数值不可过小,否则未等到芯片不忙状态就跳出。读取状态寄存器仅需要判断最后一位 BUSY 位,为1时 处于忙状态,0是为空闲状态。

【3】页编程:即为写入数据,对应1.3.3、指定地址写,页编程实现的是数据的发送,而 MySPI_WriteRead_0 是页编程发送的基础或者手段。也可以理解为  MySPI_WriteRead_0 仅发送一个字节,对于长数据的发送不便,需要调用过多 MySPI_WriteRead_0 函数,如若封装,使用更加方便。

注意,参数中由于W25Q64为24位地址,因此参数类型选择uint32_t,而参数3:coumt的类型为uint16_t,因为Flash为最大存储量256zi,若选择uint8_t,取值范围是0~255,不能取到256。
 

以上即为本节内容(已舍去main 现象内容。后续会适当补充)

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: STM32H743SPI是指ST公司推出的一款基于STM32H743微控制器的SPI总线。它集成了大量的外设和接口,包括USB、SDIO、Ethernet、CAN、I2C、SPI、USART等。STM32H743SPI的特点在于其强大的性能和多样化的功能,可以满足大多数工业和消费电子的需求。该芯片的处理器核心采用了Cortex-M7架构,主频高达400MHz,可以支持多达2MB的闪存和1MB的SRAM。而且,STM32H743SPI还具有双倍精度浮点运算单元(MPU),可以在高速和低功耗模式下工作,并支持多种操作系统。此外,它还具备数据加密、校验和认证、随机数生成、安全引脚等安全功能,能够保证系统的可靠性和安全性。总之,STM32H743SPI是一款非常出色的微控制器,可广泛应用于各种工业和消费电子的领域,具有极高的市场和使用价值。 ### 回答2: STM32H743SPISTM32微控制器系列中的一种型号,主要特点是具备高性能、低功耗和丰富的外设接口。它采用ARM Cortex-M7内核,主频可达400MHz,内置多达1MB的闪存和1MB的RAM。另外,它还拥有丰富的通信接口,包括SPI、I2C、CAN和串口等。其中SPI接口可用于连接SPI设备,具备高速度和可靠性,适用于大容量数据传输和高要求的应用场景。此外,STM32H743SPI还支持多种电源管理技术,包括动态电压调节和低功耗模式等,可以有效延长电池寿命和减少功耗。总之,STM32H743SPI是一款高性能、低功耗的微控制器,具备丰富的外设接口和电源管理技术,适用于各种要求较高的应用场景。 ### 回答3: STM32H743SPI是指STMicroelectronics公司生产的基于ARM Cortex-M7内核的高性能微控制器,它内部集成了16个独立SPI接口,可用于实现高速数据通讯,同时还支持全双工、半双工和单向模式的传输。STM32H743SPI内建有高速Flash内存和RAM存储器,可支持512KB到2MB 的Flash存储容量和128KB到1MB RAM 存储容量,满足多种应用场合的需求。 除了SPI接口,STM32H743SPI还支持多种接口,如I2C、UART和CAN总线等,能够满足各种外设与主控之间的通讯需求。此外,该芯片还拥有多个DMA控制器,可快速处理各种高速数据传输和处理业务,提升运行效率。 STM32H743SPI集成了许多先进的外设,例如16位ADC、DAC和多种计时器等,可用于实现多种功能,如音频处理和高速测量、控制等。该芯片还内建了各种保护机制,如电源管理、CRC校验、错误检测、加密和防抄袭等,可有效保障系统安全性和稳定性。 总之,STM32H743SPI内建丰富、强大的硬件资源,拥有高性能和可靠性,是一款广泛应用于机器人、工业自动化、医疗器械、家电和电动车等领域的优秀微控制器。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘洲青年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值