完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
4个回答
|
|
注:博客所涉及的关于 stm32 的代码,均在仓库【stm32f013_study】下,包括底层驱动和应用测试代码。
本文设计的文件包含: (1)hardware_spi.c:硬件 SPI 驱动实现 (2)drvsfspi.c:软件模拟 SPI 实现代码 (3)drvexflash.c:SPI FLASH 操作部分代码 (4)hal_spi.c:SPI 软件、硬件方式封装统一接口 (5)头文件: hardware_spi.h :硬件 SPI 相关 drvsfspi.h :软件模拟 SPI 相关 drvexflash.hSPI FLASH 相关 hal_spi.h:软件、硬件 SPI 接口封装 1. 硬件连接 W25Q64 将 8M 的容量分为 128 个块(Block),每个块大小为 64K 字节,每个块又分为 16个扇区(Sector),每个扇区 4K 个字节。 W25Q64 的**最少擦除单位为一个扇区,也就是每次必须擦除 4K 个字节。**操作需要给 W25Q64 开辟一个至少 4K 的缓存区,对 SRAM 要求比较高,要求芯片必须有 4K 以上 SRAM 才能很好的操作。 W25Q64 的擦写周期多达 10W 次,具有 20 年的数据保存期限,支持电压为 2.7~3.6V,W25Q64 支持标准的 SPI,还支持双输出/四输出的 SPI,最大 SPI 时钟可以到 80Mhz(双输出时相当于 160Mhz,四输出时相当于 320M)。 1.1 硬件连接 与 STM32 的引脚连接如下:这里是使用SPI1配置。 STM32引脚 对应SPI功能 PA2 片选CS PA5 时钟SCK PA6 MISO PA7 MOSI STM32 的 SPI 功能很强大, SPI 时钟最多可以到 18Mhz,支持 DMA,可以配置为 SPI 协议或者 I2S 协议(仅大容量型号支持)。 1.2 SPI 通讯的通讯时序 SPI 协议定义了通讯的起始和停止信号、数据有效性、时钟同步等环节。 我们以读取 FLASH 的状态寄存器的时序图分析一下,时序图也是书写软件模拟时序的依据。 如上图,我们知道书写 FLASH (来自华邦 W25X 手册)支持的是模式 0 (CPOL = 0 && CPHA == 0) 和 模式 3(CPOL = 1 && CPHA == 1) CS、SCK、MOSI 信号都由主机控制产生,而 MISO 的信号由从机产生,主机通过该信号线读取从机的数据。MOSI 与 MISO 的信号只在 CS 为低电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据。 1.2.1. 通讯的起始和停止信号 在上图,CS 信号线由高变低,为 SPI 通讯的起始信号。CS 是每个从机各自独占的信号线,当从机在自己的 CS 线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。当 CS 信号由低变高,为 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。 1.2.2. 数据有效性 SPI 使用 MOSI 及 MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。 MOSI 及 MISO 数据线在 SCK 的每个时钟周期传输一位数据。数据传输时,MSB 先行或 LSB 先行并没有作硬性规定,但要保证两个 SPI 通讯设备之间使用同样的协定,一般都会采用图中的 MSB 先行模式。 观察上图,可知模式 0 和 3 都是在上升沿读取数据。 示例: FLASH 读取 JEDEC_ID (0x9F),SPI 模式 0,,频率 f = 1MHz。 读取 JEDEC_ID 时,FLASH 回复的一个字节:0xC8。 1.2.3 STM32 SPI外设 STM32 的 SPI 外设可用作通讯的主机及从机,支持最高的 SCK 时钟频率为 f pclk / 2 (STM32F103 型号的芯片默认 f pclk1 为 72MHz,f pclk2 为 36MHz),完全支持 SPI 协议的 4 种模式,数据帧长度可设置为 8 位或 16 位,可设置数据 MSB 先行或 LSB 先行。它还支持双线全双工、双线单向以及单线模式。 SPI架构: 通讯引脚 : SPI 的所有硬件架构都从上图中左 MOSI、MISO、SCK及 NSS 线展开的。 STM32 芯片有多个 SPI 外设,它们的 SPI 通讯信号引出到不同的 GPIO 引脚上,使用时必须配置到这些指定的引脚。 2. 软件配置 这里使用 STM32 的 SPI1 的主模式,SPI 相关的库函数和定义分布在文件 stm32f10x_spi.c 以及头文件 stm32f10x_spi.h 中。 2.1 配置相关引脚的复用功能 第一步就要使能 SPI1 的时钟, SPI1 的时钟通过 APB2ENR 的第 12 位来设置。其次要设置 SPI1 的相关引脚为复用输出,这样才会连接到 SPI1 上否则这些 IO 口还是默认的状态,也就是标准输入输出口。这里我们使用的是 PA5、 PA6、 PA7 这 3 个(SCK、 MISO、 MOSI、CS 使用软件管理方式),所以设置这三个为复用 IO。 宏定义: #define SPIM1_GPIO_PORT GPIOA #define SPIM1_CLK_IO (GPIO_Pin_5) #define SPIM1_MISO_IO (GPIO_Pin_6) #define SPIM1_MOSI_IO (GPIO_Pin_7) #define FLASH_CS_IO (GPIO_Pin_2) #define FLASH_CS_0() (GPIO_ResetBits(SPIM1_GPIO_PORT, FLASH_CS_IO)) #define FLASH_CS_1() (GPIO_SetBits(SPIM1_GPIO_PORT, FLASH_CS_IO)) #define RCC_PCLK_SPIM1_GPIO RCC_APB2Periph_GPIOA #define RCC_PCLK_SPIM1_HD RCC_APB2Periph_SPI1 IO 配置: //-------------------------------------------------------------------------------------------------------- // 函 数 名: spi_gpio_init // 功能说明: SPI 硬件IO初始化 // 形 参: spi_chl:SPIM 通道 // 返 回 值: 无 // 日 期: 2020-03-12 // 备 注:采用 Unix like 方式 // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void spi_gpio_init(uint8_t spi_chl) { GPIO_InitTypeDef gpio_config_init; if (spi_chl == 1) { RCC_APB2PeriphClockCmd(RCC_PCLK_SPIM1_GPIO, ENABLE); //开启SPIM1 GPIO时钟、 // gpio_config_init.GPIO_Pin = SPIM1_CLK_IO | SPIM1_MISO_IO | SPIM1_MOSI_IO; //SPIM1_CLK_IO IO初始化 gpio_config_init.GPIO_Pin = SPIM1_CLK_IO | SPIM1_MOSI_IO; gpio_config_init.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 gpio_config_init.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(SPIM1_GPIO_PORT, &gpio_config_init); gpio_config_init.GPIO_Pin = SPIM1_MISO_IO; //SPIM1_MISO_IO IO初始化 gpio_config_init.GPIO_Mode = GPIO_Mode_IN_FLOATING; //MISO浮空输入 gpio_config_init.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(SPIM1_GPIO_PORT, &gpio_config_init); GPIO_SetBits(SPIM1_GPIO_PORT, SPIM1_CLK_IO | SPIM1_MISO_IO | SPIM1_MOSI_IO); //IO初始状态都设置为高电平 } } 2.2 初始化 SPI1,设置 SPI1 工作模式 接下来初始化 SPI1,设置 SPI1 为主机模式,设置数据格式为 8 位,然设置 SCK 时钟极性及采样方式。并设置 SPI1 的时钟频率(最大 18Mhz),以及数据的格式(MSB 在前还是 LSB 在前)。这在库函数中是通过 SPI_Init 函数来实现。 函数原型: void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct); 1 第一个参数是 SPI 标号,第二个参数结构体类型 SPI_InitTypeDef 为相关属性设置。 SPI_InitTypeDef 的定义如下: typedef struct { uint16_t SPI_Direction; uint16_t SPI_Mode; uint16_t SPI_DataSize; uint16_t SPI_CPOL; uint16_t SPI_CPHA; uint16_t SPI_NSS; uint16_t SPI_BaudRatePrescaler; uint16_t SPI_FirstBit; uint16_t SPI_CRCPolynomial; }SPI_InitTypeDef; 参数 解释 SPI_Direction 设置 SPI 的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式 SPI_Mode 设置 SPI 的主从模式,主机模式 (SPI_Mode_Master),从机模式 (PI_Mode_Slave)。 SPI_DataSiz 数据为 8 位还是 16 位帧格式选择项。SPI_DataSize_8b(8 位),SPI_DataSize_16b (16位) SPI_CPOL 设置时钟极性 SPI_CPHA 设置时钟相位,也就是选择在串行同步时钟的第几个跳变沿(上升或下降)数据被采样,可以为第一个或者第二个条边沿采集 SPI_NSS 设置 NSS 信号由硬件(NSS 管脚)还是软件控制 SPI_BaudRatePrescaler 设置 SPI 波特率预分频值也就是决定 SPI 的时钟的参数 ,从不分频道 256 分频 8 个可选值 ,选择 256 分频值SPI_BaudRatePrescaler_256, 传输速度为 36M/256=140.625KHz。 SPI_FirstBit 设置数据传输顺序是 MSB 位在前还是 LSB 位在前。SPI_FirstBit_MSB (高位在前) SPI_CRCPolynomial 设置 CRC 校验多项式,提高通信可靠性,大于 1 即可 初始化的范例格式为: //-------------------------------------------------------------------------------------------------------- // 函 数 名: spi_master_init // 功能说明: SPI 硬件配置参数初始化 // 形 参: spi_chl:SPIM 通道 // 返 回 值: 无 // 日 期: 2020-03-12 // 备 注:采用 Unix like 方式 // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void spi_master_init(uint8_t spi_chl) { SPI_InitTypeDef spi_config_init; #if 1 if(spi_chl == 1) { spi_flash_gpio_init(); //spi flash cs 初始化 // sd_gpio_init(); //spi sd cs 初始化 // nrf24l01_gpio_init();//spi nrf24l01 cs 初始化 spi_gpio_init(1); //spi gpio 初始化 RCC_APB2PeriphClockCmd(RCC_PCLK_SPIM1_HD, ENABLE); //SPI1时钟使能 spi_config_init.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工 spi_config_init.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI spi_config_init.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构 spi_config_init.SPI_CPOL = SPI_CPOL_Low; //选择了串行时钟的稳态:空闲时钟低 spi_config_init.SPI_CPHA = SPI_CPHA_1Edge; //数据捕获(采样)于第1个时钟沿 spi_config_init.SPI_NSS = SPI_NSS_Soft;//SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制 spi_config_init.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定义波特率预分频的值:波特率预分频值为256 spi_config_init.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始 spi_config_init.SPI_CRCPolynomial = 7; //CRC值计算的多项式 SPI_Init(SPI1, &spi_config_init); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器 SPI_Cmd(SPI1, ENABLE); //使能SPI外设 // spi_master_send_recv_byte(1, 0xFF); //启动传输 } #endif } 2.3 SPI 传输数据 通信接口需要有发送数据和接受数据的函数,固件库提供的发送数据函数原型为: void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data); 1 往 SPIx 数据寄存器写入数据 Data,从而实现发送。 固件库提供的接受数据函数原型为: uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ; 1 这从 SPIx 数据寄存器读出接收到的数据。 收发单个字节数据: //-------------------------------------------------------------------------------------------------------- // 函 数 名: spi_master_send_recv_byte // 功能说明: SPI 收发数据 // 形 参: spi_chl:SPIM 通道 // send_byte:发送的数据 // 返 回 值: 无 // 日 期: 2020-03-14 // 备 注:采用 Unix like 方式 // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- uint8_t spi_master_send_recv_byte(uint8_t spi_chl, uint8_t spi_byte) { uint8_t time = 0; if (spi_chl == 1) { while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位 { time++; if(time>200) { return false; } } SPI_I2S_SendData(SPI1, spi_byte); //通过外设SPIx发送一个数据 time = 0; while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)//检查指定的SPI标志位设置与否:接受缓存非空标志位 { time++; if(time>200) { return false; } } return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据 } else { return false; } } 收发多个字节数据: //-------------------------------------------------------------------------------------------------------- // 函 数 名: spi_master_send_some_bytes // 功能说明: SPI 发送多个字节数据 // 形 参: spi_chl:SPIM 通道 // pbdata:发送的数据首地址 // send_length:发送数据长度 // 返 回 值: 无 // 日 期: 2020-03-12 // 备 注:采用 Unix like 方式 // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void spi_master_send_some_bytes(uint8_t spi_chl, uint8_t *pbdata, uint16_t send_length) { uint16_t i = 0; for (i = 0; i < send_length; i++) { spi_master_send_recv_byte(spi_chl, pbdata); } // while (send_length--) // { // spi_master_send_byte(spi_chl, *pbdata++); // } } //-------------------------------------------------------------------------------------------------------- // 函 数 名: spi_master_recv_some_bytes // 功能说明: SPI 接收多个字节数据 // 形 参: spi_chl:SPIM 通道 // pbdata:接收的数据首地址 // send_length:接收数据长度 // 返 回 值: 无 // 日 期: 2020-03-12 // 备 注:采用 Unix like 方式 // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void spi_master_recv_some_bytes(uint8_t spi_chl, uint8_t *pbdata, uint16_t recv_length) { uint8_t *temp_data = pbdata; while (recv_length--) { *temp_data++ = spi_master_send_recv_byte(spi_chl, 0xFF); //发送 0xff 为从设备提供时钟 } } |
|
|
|
2.4 查看 SPI 传输状态
在 SPI 传输过程中,要判断数据是否传输完成,发送区是否为空等等状态, 通过函数 SPI_I2S_GetFlagStatus 实现的,判断发送是否完成的方法是: SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE); 1 3. SPI FLASH 操作 3.1 宏定义部分 #define FLASH_WRITE_ENABLE_CMD 0x06 #define FLASH_WRITE_DISABLE_CMD 0x04 #define FLASH_READ_SR_CMD 0x05 #define FLASH_WRITE_SR_CMD 0x01 #define FLASH_READ_DATA 0x03 #define FLASH_FASTREAD_DATA 0x0b #define FLASH_WRITE_PAGE 0x02 #define FLASH_ERASE_PAGE 0x81 #define FLASH_ERASE_SECTOR 0x20 #define FLASH_ERASE_BLOCK 0xd8 #define FLASH_ERASE_CHIP 0xc7 #define FLASH_POWER_DOWN 0xb9 #define FLASH_RELEASE_POWER_DOWN 0xab #define FLASH_READ_DEVICE_ID 0x90 #define FLASH_READ_JEDEC_ID 0x9f #define FLASH_SIZE (1*1024*1024) // 1M字节 #define PAGE_SIZE 8192 // 256 bytes #define SECTOR_SIZE 512 // 4-Kbyte #define BLOCK_SIZE 32 // 64-Kbyte #define PAGE_LEN 255 //一页256字节 3.2 中间层函数封装 注明: 此部分函数的封装是为了统一硬件 SPI 和软件模拟 SPI 接口。 //-------------------------------------------------------------------------------------------------------- // 函 数 名: hal_spi_send_bytes // 功能说明: SPI 发送数据,包含软件和硬件通信方式 // 形 参: mode:通信方式选择(0:软件SPI;1:硬件SPI) // pbdata:发送数据的首地址 // send_length:发送数据长度 // 返 回 值: 执行状态(true or false) // 日 期: 2020-03-12 // 备 注: 中间层封装底层接口 // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- uint8_t hal_spi_send_bytes(uint8_t mode, uint8_t *pbdata, uint16_t send_length) { if (mode == 0) { for (uint16_t i = 0; i < send_length; i++) { Spi_WriteByte(pbdata); } return true; } else if (mode == 1) { spi_master_send_some_bytes(1, pbdata, send_length); // for (uint16_t i = 0; i < send_length; i++) // { // spi_master_send_recv_byte(1, pbdata); // } return true; } else { return false; } } //-------------------------------------------------------------------------------------------------------- // 函 数 名: hal_spi_recv_bytes // 功能说明: SPI 接收数据,包含软件和硬件通信方式 // 形 参: mode:通信方式选择(0:软件SPI;1:硬件SPI) // pbdata:发送数据的首地址 // send_length:发送数据长度 // 返 回 值: 执行状态(true or false) // 日 期: 2020-03-12 // 备 注: 中间层封装底层接口 // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- uint8_t hal_spi_recv_bytes(uint8_t mode, uint8_t *pbdata, uint16_t recv_length) { if (mode == 0) { for (uint16_t i = 0; i < recv_length; i++) { *pbdata++ = Spi_ReadByte(); //软件模拟SPI } return true; } else if (mode == 1) { spi_master_recv_some_bytes(1, pbdata, recv_length); //硬件SPI // for (uint16_t i = 0; i < recv_length; i++) // { // *pbdata++ = spi_master_send_recv_byte(1, 0xFF); // } return true; } else { return false; } } 关于软件模拟 SPI 部分代码,参看:软件模拟SPI代码 。此处不再贴出。 3.3 FLASH 部分 __align(4) uint8_t g_DataTmpBuffer[0x1000] = {0}; #define SectorBuf g_DataTmpBuffer //-------------------------------------------------------------------------------------------------------- // 函 数 名: Flash_WriteEnable // 功能说明: 写使能,置位 WEL 位 WEL 位(WEL-->1) // 形 参: 无 // 返 回 值: 无 // 日 期: 2020-03-07 // 备 注: // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void Flash_WriteEnable(void) { uint8_t command = FLASH_WRITE_ENABLE_CMD; FLASH_CS_LOW; hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);//开启写使能 FLASH_CS_HIGH; } //-------------------------------------------------------------------------------------------------------- // 函 数 名: Flash_WriteDisable // 功能说明: 写失能,复位 WEL 位(WEL-->0) // 形 参: 无 // 返 回 值: 无 // 日 期: 2020-03-07 // 备 注: // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void Flash_WriteDisable(void) { uint8_t command = FLASH_WRITE_DISABLE_CMD; FLASH_CS_LOW; hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); // Spi_WriteByte(FLASH_WRITE_DISABLE_CMD); //开启写失能 04h FLASH_CS_HIGH; } //-------------------------------------------------------------------------------------------------------- // 函 数 名: Flash_WriteSR // 功能说明: 读状态寄存器 // 形 参: 无 // 返 回 值: 无 // 日 期: 2020-03-07 // 备 注: 多用于检查 BUSY 位 // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- uint8_t Flash_ReadSR(void) { uint8_t ucTmpVal = 0; uint8_t command = FLASH_READ_SR_CMD; FLASH_CS_LOW; hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); //05h hal_spi_recv_bytes(SPI_COMM_MODE, &ucTmpVal, 1); // ucTmpVal = Spi_ReadByte(); FLASH_CS_HIGH; return ucTmpVal; } //-------------------------------------------------------------------------------------------------------- // 函 数 名: Flash_WriteSR // 功能说明: 写状态寄存器 // 形 参: _ucByte:写入状态寄存器的数值 // 返 回 值: no // 日 期: 2020-03-07 // 备 注: // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void Flash_WriteSR(uint8_t _ucByte) { uint8_t command = FLASH_WRITE_SR_CMD; Flash_WriteEnable(); Flash_WaitNobusy(); FLASH_CS_LOW; hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); //01h hal_spi_send_bytes(SPI_COMM_MODE, &_ucByte, 1); //写入一个字节 FLASH_CS_HIGH; } //-------------------------------------------------------------------------------------------------------- // 函 数 名: Flash_WaitNobusy // 功能说明: 检查 FLASH BUSY 位状态 // 形 参: no // 返 回 值: no // 日 期: 2020-03-07 // 备 注: 调用Flash_ReadSR(),判断状态寄存器的R0位,执行结束操作清零 // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void Flash_WaitNobusy(void) { //FLASH_READ_SR_CMD 指令的发送,有的FLASH仅需发送一次,FLASH自动回复,有的FLASH无法自动回复,需要循环一直发送等待 while(((Flash_ReadSR()) & 0x01)==0x01); //等待BUSY位清空 } //-------------------------------------------------------------------------------------------------------- // 函 数 名: Flash_FastReadByte // 功能说明: flash 都数据(快速读取:Fast read operate at the highest poossible frequency) // 形 参: ucpBuffer:数据存储区首地址 // _ulReadAddr: 要读出Flash的首地址 // _usNByte: 要读出的字节数(最大65535B) // 返 回 值: no // 日 期: 2020-03-07 // 备 注: 从_ulReadAddr地址,连续读出_usNByte长度的字节 // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void Flash_ReadSomeBytes(uint8_t *ucpBuffer, uint32_t _ulReadAddr, uint16_t _usNByte) { uint8_t command = FLASH_READ_DATA; uint8_t temp_buff[3] = {0}; temp_buff[0] = (uint8_t)(_ulReadAddr >> 16); temp_buff[1] = (uint8_t)(_ulReadAddr >> 8); temp_buff[2] = (uint8_t)(_ulReadAddr >> 0); FLASH_CS_LOW; hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1); hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1); hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1); hal_spi_recv_bytes(SPI_COMM_MODE, ucpBuffer, _usNByte); // Spi_WriteByte(FLASH_READ_DATA); //连续读取数据 03h // Spi_WriteByte((uint8_t)(_ulReadAddr>>16)); //写入24位地址 // Spi_WriteByte((uint8_t)(_ulReadAddr>>8)); // Spi_WriteByte((uint8_t)(_ulReadAddr>>0)); // while(_usNByte--) // { // *ucpBuffer = Spi_ReadByte(); // ucpBuffer++; // } FLASH_CS_HIGH; } //-------------------------------------------------------------------------------------------------------- // 函 数 名: Flash_FastReadByte // 功能说明: flash 都数据(快速读取:Fast read operate at the highest poossible frequency) // 形 参: ucpBuffer:数据存储区首地址 // _ulReadAddr: 要读出Flash的首地址 // _usNByte: 要读出的字节数(最大65535B) // 返 回 值: no // 日 期: 2020-03-07 // 备 注: 从_ulReadAddr地址,连续读出_usNByte长度的字节 // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void Flash_FastReadByte(uint8_t *ucpBuffer, uint32_t _ulReadAddr, uint16_t _usNByte) { uint8_t command = FLASH_FASTREAD_DATA; uint8_t temp_buff[3] = {0}; temp_buff[0] = (uint8_t)(_ulReadAddr >> 16); temp_buff[1] = (uint8_t)(_ulReadAddr >> 8); temp_buff[2] = (uint8_t)(_ulReadAddr >> 0); FLASH_CS_LOW; hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1); hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1); hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1); hal_spi_recv_bytes(SPI_COMM_MODE, ucpBuffer, _usNByte); // Spi_WriteByte(FLASH_FASTREAD_DATA);//快速读取数据 0bh // Spi_WriteByte((uint8_t)(_ulReadAddr>>16));//写入24位地址 // Spi_WriteByte((uint8_t)(_ulReadAddr>>8)); // Spi_WriteByte((uint8_t)(_ulReadAddr>>0)); // Spi_WriteByte(0xFF);//等待8个时钟(dummy byte) // while(_usNByte--) // { // *ucpBuffer = Spi_ReadByte(); // ucpBuffer++; // } FLASH_CS_HIGH; } |
|
|
|
//--------------------------------------------------------------------------------------------------------
// 函 数 名: Flash_WritePage // 功能说明: flash 写数据(按页写入,一页256字节,写入之前FLASH地址上必须为0xFF) // 形 参: ucpBuffer:数据存储区首地址 // _ulWriteAddr: 要读写入Flash的首地址 // _usNByte: 要写入的字节数(最大65535B = 64K 块) // 返 回 值: no // 日 期: 2020-03-07 // 备 注: _ulWriteAddr,连续写入_usNByte长度的字节 // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void Flash_WritePage(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte) { uint8_t command = FLASH_WRITE_PAGE; uint8_t temp_buff[3] = {0}; temp_buff[0] = (uint8_t)(_ulWriteAddr >> 16); temp_buff[1] = (uint8_t)(_ulWriteAddr >> 8); temp_buff[2] = (uint8_t)(_ulWriteAddr >> 0); Flash_WriteEnable(); //写使能 Flash_WaitNobusy(); //等待写入结束 FLASH_CS_LOW; hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1); hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1); hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1); hal_spi_send_bytes(SPI_COMM_MODE, ucpBuffer, _usNByte); // Spi_WriteByte(FLASH_WRITE_PAGE); //02h // Spi_WriteByte((uint8_t)(_ulWriteAddr>>16)); //写入24位地址 // Spi_WriteByte((uint8_t)(_ulWriteAddr>>8)); // Spi_WriteByte((uint8_t)(_ulWriteAddr>>0)); // while(_usNByte--) // { // Spi_WriteByte(*ucpBuffer); //SPI 写入单个字节 // ucpBuffer++; // } FLASH_CS_HIGH; Flash_WaitNobusy(); //等待写入结束 } //-------------------------------------------------------------------------------------------------------- // 函 数 名: Flash_WriteNoCheck // 功能说明: flash 写数据(不带擦除,写入之前必须确保写入部分FLASH的数据全为0xFf,否则写入失败) // 形 参: ucpBuffer:数据存储区首地址 // _ulWriteAddr: 要读写入Flash的首地址 // _usNByte: 要写入的字节数(最大65535B = 64K 块) // 返 回 值: no // 日 期: 2020-03-07 // 备 注: _ulWriteAddr,连续写入_usNByte长度的字节,程序带FLASH数据检查写入 // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void Flash_WriteNoCheck(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte) { uint16_t PageByte = 256 - _ulWriteAddr % 256;//单页剩余可写字节数 if(_usNByte <= PageByte) //不大于256字节 { PageByte = _usNByte; } while(1) { Flash_WritePage(ucpBuffer, _ulWriteAddr, PageByte); if(_usNByte == PageByte) //写入结束 break; else { ucpBuffer += PageByte; //下一页写入的数据 _ulWriteAddr += PageByte; //下一页写入的地址 _usNByte -= PageByte; //待写入的字节数递减 if(_usNByte > 256) { PageByte = 256; } else { PageByte = _usNByte; } } } } //-------------------------------------------------------------------------------------------------------- // 函 数 名: Flash_WriteSomeBytes // 功能说明: flash 写数据 // 形 参: ucpBuffer:数据存储区首地址 // _ulWriteAddr: 要读写入Flash的首地址 // _usNByte: 要写入的字节数(最大65535B = 64K 块) // 返 回 值: no // 日 期: 2020-03-07 // 备 注: _ulWriteAddr,连续写入_usNByte长度的字节,程序带FLASH数据检查写入 // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void Flash_WriteSomeBytes(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte) { uint32_t ulSecPos = 0; //得到扇区位置 uint16_t usSecOff = 0; //扇区偏移 uint16_t usSecRemain = 0; //剩余扇区 uint32_t i = 0; ulSecPos = _ulWriteAddr / 4096;//地址所在扇区(0--511) usSecOff = _ulWriteAddr % 4096;//扇区内地址偏移 usSecRemain = 4096 - usSecOff;//扇区除去偏移,还剩多少字节 if(_usNByte <= usSecRemain) //写入数据大小 < 剩余扇区空间大小 { usSecRemain = _usNByte; } while(1) { Flash_ReadSomeBytes(SectorBuf, ulSecPos*4096, 4096);//读出整个扇区的内容 for (i = 0; i < usSecRemain; i++) //校验数据 { if (SectorBuf[usSecOff + i] != 0xFF)//储存数据不为0xFF,需要擦除 break; } if(i < usSecRemain) //需要擦除 { Flash_EraseSector(ulSecPos); //擦除这个扇区 for(i = 0; i < usSecRemain; i++) //保存写入的数据 { SectorBuf[usSecOff + i] = ucpBuffer; } Flash_WriteNoCheck(SectorBuf, ulSecPos*4096, 4096); //写入整个扇区(扇区=老数据+新写入数据) } else { Flash_WriteNoCheck(ucpBuffer, _ulWriteAddr, usSecRemain);//不需要擦除,直接写入扇区 } if(_usNByte == usSecRemain) //写入结束 { Flash_WriteDisable(); break; } else { ulSecPos++; //扇区地址增加1 usSecOff = 0; //扇区偏移归零 ucpBuffer += usSecRemain; //指针偏移 _ulWriteAddr += usSecRemain; //写地址偏移 _usNByte -= usSecRemain; //待写入的字节递减 if(_usNByte > 4096) { usSecRemain = 4096; //待写入一扇区(4096字节大小) } else { usSecRemain = _usNByte; //待写入少于一扇区的数据 } } } } //-------------------------------------------------------------------------------------------------------- // 函 数 名: Flash_ErasePage // 功能说明: flash erase page // 形 参: no // 返 回 值: no // 日 期: 2020-03-07 // 备 注: 有的 FLASH 支持 // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void Flash_ErasePage(uint32_t _ulPageAddr) { _ulPageAddr *= 256; Flash_WriteEnable(); Flash_WaitNobusy(); FLASH_CS_LOW; Spi_WriteByte(FLASH_ERASE_PAGE); //页擦除指令 Spi_WriteByte((uint8_t)(_ulPageAddr>>16)); //写入24位地址 Spi_WriteByte((uint8_t)(_ulPageAddr>>8)); Spi_WriteByte((uint8_t)(_ulPageAddr>>0)); FLASH_CS_HIGH; Flash_WaitNobusy(); //等待写入结束 } //-------------------------------------------------------------------------------------------------------- // 函 数 名: Flash_EraseSector // 功能说明: flash erase sector // 形 参: no // 返 回 值: no // 日 期: 2020-03-07 // 备 注: 1扇区 = 4K Bytes // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void Flash_EraseSector(uint32_t _ulSectorAddr) { uint8_t command = FLASH_ERASE_SECTOR; uint8_t temp_buff[3] = {0}; temp_buff[0] = (uint8_t)(_ulSectorAddr >> 16); temp_buff[1] = (uint8_t)(_ulSectorAddr >> 8); temp_buff[2] = (uint8_t)(_ulSectorAddr >> 0); _ulSectorAddr *= 4096; //1个扇区 4 KBytes Flash_WriteEnable(); Flash_WaitNobusy(); FLASH_CS_LOW; hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1); hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1); hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1); // Spi_WriteByte(FLASH_ERASE_SECTOR); //20h // Spi_WriteByte((uint8_t)(_ulSectorAddr>>16)); //写入24位地址 // Spi_WriteByte((uint8_t)(_ulSectorAddr>>8)); // Spi_WriteByte((uint8_t)(_ulSectorAddr)); FLASH_CS_HIGH; Flash_WaitNobusy(); //等待写入结束 } //-------------------------------------------------------------------------------------------------------- // 函 数 名: Flash_EraseBlock // 功能说明: flash erase block // 形 参: no // 返 回 值: no // 日 期: 2020-03-07 // 备 注: 1块 = 64K Bytes // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void Flash_EraseBlock(uint32_t _ulBlockAddr) { uint8_t command = FLASH_ERASE_BLOCK; _ulBlockAddr *= 65536; //块地址,一块64K Flash_WriteEnable(); Flash_WaitNobusy(); FLASH_CS_LOW; hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); hal_spi_send_bytes(SPI_COMM_MODE, (uint8_t *)(_ulBlockAddr>>16), 1); hal_spi_send_bytes(SPI_COMM_MODE, (uint8_t *)(_ulBlockAddr>>8), 1); hal_spi_send_bytes(SPI_COMM_MODE, (uint8_t *)(_ulBlockAddr>>0), 1); // Spi_WriteByte(FLASH_ERASE_BLOCK); //d8h // Spi_WriteByte((uint8_t)(_ulBlockAddr>>16)); //写入24位地址 // Spi_WriteByte((uint8_t)(_ulBlockAddr>>8)); // Spi_WriteByte((uint8_t)(_ulBlockAddr)); FLASH_CS_HIGH; Flash_WaitNobusy(); //等待写入结束 } //-------------------------------------------------------------------------------------------------------- // 函 数 名: Flash_EraseChip // 功能说明: flash erase chip , it makes flash recovery FF // 形 参: no // 返 回 值: no // 日 期: 2020-03-07 // 备 注: 软件模拟SPI // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void Flash_EraseChip(void) { uint8_t command = FLASH_ERASE_CHIP; Flash_WriteEnable(); //flash芯片写使能 Flash_WaitNobusy(); //等待写操作完成 FLASH_CS_LOW; hal_spi_recv_bytes(SPI_COMM_MODE, &command, 1); // Spi_WriteByte(FLASH_ERASE_CHIP); //c7h FLASH_CS_HIGH; Flash_WaitNobusy(); //等待写入结束 } |
|
|
|
//--------------------------------------------------------------------------------------------------------
// 函 数 名: Flash_PowerDown // 功能说明: flash into power down mode // 形 参: no // 返 回 值: no // 日 期: 2020-03-07 // 备 注: 软件模拟SPI // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void Flash_PowerDown(void) { uint8_t command = FLASH_POWER_DOWN; FLASH_CS_LOW; hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); // Spi_WriteByte(FLASH_POWER_DOWN); //b9h FLASH_CS_HIGH; Sys_delay_us(3); // cs go high , need to delay 3us } //-------------------------------------------------------------------------------------------------------- // 函 数 名: Flash_WakeUp // 功能说明: wake up flash from power down mode or hign performance mode // 形 参: no // 返 回 值: no // 日 期: 2020-03-07 // 备 注: 软件模拟SPI // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- void Flash_WakeUp(void) { uint8_t command = FLASH_RELEASE_POWER_DOWN; FLASH_CS_LOW; hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); // Spi_WriteByte(FLASH_RELEASE_POWER_DOWN);//abh FLASH_CS_HIGH; Sys_delay_us(3); //CS go high , need delay 3us } //-------------------------------------------------------------------------------------------------------- // 函 数 名: Flash_ReadDeviceID // 功能说明: 读取FLASH ID(manufacturer ID-1Byte + Device ID-2Byte:type+density) // 形 参: 无 // 返 回 值: ulJedId:FLASH ID 3字节 // 日 期: 2020-03-06 // 备 注: 软件模拟SPI // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- uint16_t Flash_ReadDeviceID(void) { uint8_t command = FLASH_READ_DEVICE_ID; uint16_t usFlashId = 0; uint8_t temp_buff[3] = {0}; FLASH_CS_LOW; hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); //90h hal_spi_send_bytes(SPI_COMM_MODE, temp_buff, 3); //写入24位地址;假地址 hal_spi_recv_bytes(SPI_COMM_MODE, temp_buff, 2); // Spi_WriteByte(FLASH_READ_DEVICE_ID); //90h // Spi_WriteByte(0x00);//写入24位地址;假地址 // Spi_WriteByte(0x00); // Spi_WriteByte(0x00); //如果0x01,先输出 Device ID // usFlashId |= Spi_ReadByte()<<8; // usFlashId |= Spi_ReadByte(); FLASH_CS_HIGH; usFlashId = (uint16_t)(temp_buff[0] << 8) | (temp_buff[1] << 0); return usFlashId; } //-------------------------------------------------------------------------------------------------------- // 函 数 名: Flash_ReadJEDECID // 功能说明: 读取FLASH ID(manufacturer ID-1Byte + Device ID-2Byte:type+density) // 形 参: 无 // 返 回 值: ulJedId:FLASH ID 3字节 // 日 期: 2020-03-06 // 备 注: 软件模拟SPI // 作 者: by 霁风AI //-------------------------------------------------------------------------------------------------------- uint32_t Flash_ReadJEDECID(void) { uint8_t command = FLASH_READ_JEDEC_ID; uint32_t flash_jed_id = 0; uint8_t recv_buff[3] = {0}; FLASH_CS_LOW; hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); //9fh hal_spi_recv_bytes(SPI_COMM_MODE, recv_buff, 3); FLASH_CS_HIGH; flash_jed_id = (recv_buff[0] << 16) | (recv_buff[1] << 8) | (recv_buff[2] << 0); return flash_jed_id; } |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1771 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1619 浏览 1 评论
1070 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
724 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1673 浏览 2 评论
1936浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
729浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
569浏览 3评论
594浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
552浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-22 21:25 , Processed in 1.019152 second(s), Total 85, Slave 68 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号