STM32驱动FLASH(W25Q64)

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 功能很强大, 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_spi1io 配置:
//--------------------------------------------------------------------------------------------------------// 函 数 名: 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);第一个参数是 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_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);往 spix 数据寄存器写入数据 data,从而实现发送。
固件库提供的接受数据函数原型为:
uint16_t spi_i2s_receivedata(spi_typedef* spix) ;这从 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[i]); }// 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);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[i]); } 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[i]);// } 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 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 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 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;}

英创信息技术ARM9工控主板的ETA754 PWM模块应用
四大无线技术 谁才是智能家居的中意对象
小米跟风涨价是否会“引火烧身”?大家怎么看?
魅族5S千元机也有快充了!或将引发手机行业的震动
连接器塑料发展的七大最新趋势简析
STM32驱动FLASH(W25Q64)
商汤科技副总裁解读安防+人工智能的发展现状
SK海力士发布全球首款321层NAND!
基于闪存的MC9S12NE64微控制器解决单芯片以太网连接问题
新品OLED电视BRAVIA A1系列横空出世!信兴电器独家尊享!
细谈智能穿戴的五大关键技术
蓝牙音箱出口到韩国做KC认证有哪些要求
你知道怎么开发一款虚拟币交易平台吗
北京市将进一步探索人工智能、云计算等智能科技领域的新发展
什么是数据通信的拥塞控制
shijidianliLLC半桥谐振开关电源原理介绍与逆变电路简介
瘦死的骆驼比马大,诺基亚6之后马上就发诺基亚8,价格3999元?
GTN可以勾勒出夸张的几何线条?
HFP01SC自校准热通量传感器的主要功能、特点及应用范围
电感啸叫的原因及解决方法剖析