电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
【联盛德W806-KIT开发板试用体验】w25q32读写
分 享
扫描二维码分享
【联盛德W806-KIT开发板试用体验】w25q32读写
w806
联盛德
w806-kit
Sixer
关注
发布时间: 2021-11-30
丨
阅读: 1333
### 【联盛德W806-KIT开发板试用体验】w25q32读写 ### 1. 开发环境搭建 程序开发平台:CDK 程序下载软件:Upgrade_Tools_V1.4.8.exe 驱动:ch340 usb转串口驱动 相关开发平台搭建及安装,可矣查看论坛其他文章。 ### 2. 实验目的 像w25q32第一个扇区写入 0~4095,然后读出打印。 ### 3. 硬件平台 联盛德W806-KIT 如图,飞线是为了实现串口一件下载,不用手动复位了。 ![undefined](https://cf01.ickimg.com/bbsimages/202111/32ae7d1de8f0a17c44ee98ea54ba5fce.png "undefined") w25q32模块 ![undefined](https://cf01.ickimg.com/bbsimages/202111/1ff0f20cd3ba5d440ad87d49bfa38860.png "undefined") 查看板子原理图,led对应引脚情况如下: VCC --> 3.3v GND --> GND CS --> PB4 CLK --> PB2 DI --> PB5 DO --> PB3 ![undefined](https://cf01.ickimg.com/bbsimages/202111/f137de640de2d89c6dc506eb61421e26.png "undefined") ### 4. 软件编写 本实验在官方sdk基础上添加 spi.c,spi.h,w25qxx.c,w25qxx.h 四个文件 spi.c 实现芯片外设 spi 的初始化 ```c //spi.c #include "spi.h" SPI_HandleTypeDef hspi; void spi_init(void) { hspi.Instance = SPI; hspi.Init.Mode = SPI_MODE_MASTER; hspi.Init.CLKPolarity = SPI_POLARITY_LOW; hspi.Init.CLKPhase = SPI_PHASE_1EDGE; hspi.Init.NSS = SPI_NSS_SOFT; hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_20; hspi.Init.FirstByte = SPI_LITTLEENDIAN; if (HAL_SPI_Init(&hspi) != HAL_OK) { while(1); } } void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) { __HAL_RCC_SPI_CLK_ENABLE(); __HAL_AFIO_REMAP_SPI_CS(GPIOB, GPIO_PIN_4); __HAL_AFIO_REMAP_SPI_CLK(GPIOB, GPIO_PIN_2); __HAL_AFIO_REMAP_SPI_MISO(GPIOB, GPIO_PIN_3); __HAL_AFIO_REMAP_SPI_MOSI(GPIOB, GPIO_PIN_5); } void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi) { __HAL_RCC_SPI_CLK_DISABLE(); HAL_GPIO_DeInit(GPIOB, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5); } uint8_t spi2_read_write_byte(uint8_t txdata) { uint8_t rxdata; HAL_SPI_TransmitReceive(&hspi, &txdata, &rxdata, 1, 1000); return rxdata; } ``` spi.h 包含函数声明等 ```c //spi.h #ifndef _SPI_H #define _SPI_H #include "stdint.h" #include "wm_hal.h" extern SPI_HandleTypeDef hspi; void spi_init(void); uint8_t spi2_read_write_byte(uint8_t txdata); #endif ``` w25qxx.c 实现 flash读写 ```c //w25qxx.c #include "w25qxx.h" #include "wm_hal.h" #include "spi.h" #include "stdio.h" //方便移植,实现如下几个函数 #define w25qxx_cs_high() __HAL_SPI_SET_CS_HIGH(&hspi); #define w25qxx_cs_low() __HAL_SPI_SET_CS_LOW(&hspi); #define w25qxx_r_w_byte(n) spi2_read_write_byte(n) #define w25qxx_spi_init() spi_init() #define w25qxx_delay_us(n) HAL_Delay(n) w25qxx_device_t w25q32_dev = { .init = w25qxx_init, .wr = w25qxx_write, .rd = w25qxx_read, .type = 0x00, }; void w25qxx_init(void) { w25qxx_spi_init(); //初始化SPI w25q32_dev.type = w25qxx_readid(); //读取FLASH ID. } //读取SPI_FLASH的状态寄存器 //BIT7 6 5 4 3 2 1 0 //SPR RV TB BP2 BP1 BP0 WEL BUSY //SPR:默认0,状态寄存器保护位,配合WP使用 //TB,BP2,BP1,BP0:FLASH区域写保护设置 //WEL:写使能锁定 //BUSY:忙标记位(1,忙;0,空闲) //默认:0x00 uint8_t w25qxx_readsr(void) { uint8_t byte=0; w25qxx_cs_low(); //使能器件 w25qxx_r_w_byte(W25X_ReadStatusReg); //发送读取状态寄存器命令 byte=w25qxx_r_w_byte(0xff); //读取一个字节 w25qxx_cs_high(); //取消片选 return byte; } //写SPI_FLASH状态寄存器 //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!! void w25qxx_write_sr(uint8_t sr) { w25qxx_cs_low(); //使能器件 w25qxx_r_w_byte(W25X_WriteStatusReg); //发送写取状态寄存器命令 w25qxx_r_w_byte(sr); //写入一个字节 w25qxx_cs_high(); //取消片选 } //SPI_FLASH写使能 //将WEL置位 void w25qxx_write_enable(void) { w25qxx_cs_low(); //使能器件 w25qxx_r_w_byte(W25X_WriteEnable); //发送写使能 w25qxx_cs_high(); //取消片选 } //SPI_FLASH写禁止 //将WEL清零 void w25qxx_write_disable(void) { w25qxx_cs_low(); //使能器件 w25qxx_r_w_byte(W25X_WriteDisable); //发送写禁止指令 w25qxx_cs_high(); //取消片选 } //读取芯片ID W25X16的ID:0XEF15 uint16_t w25qxx_readid(void) { uint16_t temp = 0; w25qxx_cs_low(); w25qxx_r_w_byte(0x90); //发送读取ID命令 w25qxx_r_w_byte(0x00); w25qxx_r_w_byte(0x00); w25qxx_r_w_byte(0x00); temp|=w25qxx_r_w_byte(0xFF)<<8; temp|=w25qxx_r_w_byte(0xFF); w25qxx_cs_high(); return temp; } //读取SPI FLASH //在指定地址开始读取指定长度的数据 //pbuffer:数据存储区 //read_addr:开始读取的地址(24bit) //num_byte_to_read:要读取的字节数(最大65535) void w25qxx_read(uint8_t* pbuffer, uint32_t read_addr, uint16_t num_byte_to_read) { uint16_t i; w25qxx_cs_low(); w25qxx_r_w_byte(W25X_ReadData); //发送读取命令 w25qxx_r_w_byte((uint8_t)((read_addr)>>16)); //发送24bit地址 w25qxx_r_w_byte((uint8_t)((read_addr)>>8)); w25qxx_r_w_byte((uint8_t)read_addr); for(i = 0; i < num_byte_to_read; i++) { pbuffer[i] = w25qxx_r_w_byte(0XFF); //循环读数 } w25qxx_cs_high(); } //SPI在一页(0~65535)内写入少于256个字节的数据 //在指定地址开始写入最大256字节的数据 //pbuffer:数据存储区 //write_addr:开始写入的地址(24bit) //num_byte_to_write:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!! void w25qxx_write_page(uint8_t* pbuffer, uint32_t write_addr, uint16_t num_byte_to_write) { uint16_t i; w25qxx_write_enable(); //SET WEL w25qxx_cs_low(); w25qxx_r_w_byte(W25X_PageProgram); //发送写页命令 w25qxx_r_w_byte((uint8_t)((write_addr) >> 16)); //发送24bit地址 w25qxx_r_w_byte((uint8_t)((write_addr) >> 8)); w25qxx_r_w_byte((uint8_t)write_addr); for(i = 0; i < num_byte_to_write; i++) w25qxx_r_w_byte(pbuffer[i]); //循环写数 w25qxx_cs_high(); w25qxx_wait_busy(); //等待写入结束 } //无检验写SPI FLASH //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败! //具有自动换页功能 //在指定地址开始写入指定长度的数据,但是要确保地址不越界! //pbuffer:数据存储区 //write_addr:开始写入的地址(24bit) //num_byte_to_write:要写入的字节数(最大65535) //CHECK OK void w25qxx_write_nocheck(uint8_t* pbuffer, uint32_t write_addr, uint16_t num_byte_to_write) { uint16_t pageremain; pageremain = 256 - write_addr % 256; //单页剩余的字节数 if(num_byte_to_write <= pageremain) pageremain = num_byte_to_write; //不大于256个字节 while(1) { w25qxx_write_page(pbuffer, write_addr, pageremain); if(num_byte_to_write == pageremain) //写入结束了 break; else //num_byte_to_write>pageremain { pbuffer += pageremain; write_addr += pageremain; num_byte_to_write -= pageremain; //减去已经写入了的字节数 if(num_byte_to_write > 256) pageremain = 256; //一次可以写入256个字节 else pageremain = num_byte_to_write; //不够256个字节了 } }; } //写SPI FLASH //在指定地址开始写入指定长度的数据 //该函数带擦除操作! //pbuffer:数据存储区 //write_addr:开始写入的地址(24bit) //num_byte_to_write:要写入的字节数(最大65535) uint8_t W25QXX_BUFFER[4096]; void w25qxx_write(uint8_t* pbuffer, uint32_t write_addr, uint16_t num_byte_to_write) { uint32_t secpos; uint16_t secoff; uint16_t secremain; uint16_t i; secpos = write_addr / 4096; //扇区地址 0~511 for w25x16 secoff = write_addr % 4096; //在扇区内的偏移 secremain = 4096 - secoff; //扇区剩余空间大小 if(num_byte_to_write <= secremain) secremain = num_byte_to_write; //不大于4096个字节 while(1) { w25qxx_read(W25QXX_BUFFER, secpos * 4096, 4096);//读出整个扇区的内容 for(i = 0; i < secremain; i++) //校验数据 { if(W25QXX_BUFFER[secoff+i] != 0XFF) break; //需要擦除 } if(i < secremain) //需要擦除 { w25qxx_erase_sector(secpos); //擦除这个扇区 for(i = 0; i < secremain; i++) //复制 { W25QXX_BUFFER[i + secoff] = pbuffer[i]; } w25qxx_write_nocheck(W25QXX_BUFFER, secpos * 4096, 4096);//写入整个扇区 }else w25qxx_write_nocheck(pbuffer, write_addr, secremain); //写已经擦除了的,直接写入扇区剩余区间. if(num_byte_to_write == secremain) break; //写入结束了 else //写入未结束 { secpos++; //扇区地址增1 secoff = 0; //偏移位置为0 pbuffer += secremain; //指针偏移 write_addr += secremain; //写地址偏移 num_byte_to_write -= secremain; //字节数递减 if(num_byte_to_write > 4096) secremain = 4096; //下一个扇区还是写不完 else secremain = num_byte_to_write; //下一个扇区可以写完了 } }; } //擦除整个芯片 //整片擦除时间: //W25X16:25s //W25X32:40s //W25X64:40s //等待时间超长... void W25QXX_Erase_Chip(void) { w25qxx_write_enable(); //SET WEL w25qxx_wait_busy(); w25qxx_cs_low(); //使能器件 w25qxx_r_w_byte(W25X_ChipErase); //发送片擦除命令 w25qxx_cs_high(); //取消片选 w25qxx_wait_busy(); //等待芯片擦除结束 } //擦除一个扇区 //dst_addr:扇区地址 0~511 for w25x16 //擦除一个山区的最少时间:150ms void w25qxx_erase_sector(uint32_t dst_addr) { dst_addr *= 4096; w25qxx_write_enable(); //SET WEL w25qxx_wait_busy(); w25qxx_cs_low(); //使能器件 w25qxx_r_w_byte(W25X_SectorErase); //发送扇区擦除指令 w25qxx_r_w_byte((uint8_t)((dst_addr) >> 16)); //发送24bit地址 w25qxx_r_w_byte((uint8_t)((dst_addr) >> 8)); w25qxx_r_w_byte((uint8_t)dst_addr); w25qxx_cs_high(); //取消片选 w25qxx_wait_busy(); //等待擦除完成 } //等待空闲 void w25qxx_wait_busy(void) { while((w25qxx_readsr() & 0x01) == 0x01); // 等待BUSY位清空 } //进入掉电模式 void W25QXX_PowerDown(void) { w25qxx_cs_low(); //使能器件 w25qxx_r_w_byte(W25X_PowerDown); //发送掉电命令 w25qxx_cs_high(); //取消片选 w25qxx_delay_us(3); //等待TPD } //唤醒 void W25QXX_WAKEUP(void) { w25qxx_cs_low(); //使能器件 w25qxx_r_w_byte(W25X_ReleasePowerDown); //send W25X_PowerDown command 0xAB w25qxx_cs_high(); //取消片选 w25qxx_delay_us(3); //等待TRES1 } ``` w25qxx.h ```c #ifndef _W25QXX_H #define _W25QXX_H #include "stdint.h" //W25X系列/Q系列芯片列表 #define W25Q80 0XEF13 #define W25Q16 0XEF14 #define W25Q32 0XEF15 #define W25Q64 0XEF16 #define W25Q128 0XEF17 //W25X16读写 //#define FLASH_ID 0XEF14 //指令表 #define W25X_WriteEnable 0x06 #define W25X_WriteDisable 0x04 #define W25X_ReadStatusReg 0x05 #define W25X_WriteStatusReg 0x01 #define W25X_ReadData 0x03 #define W25X_FastReadData 0x0B #define W25X_FastReadDual 0x3B #define W25X_PageProgram 0x02 #define W25X_BlockErase 0xD8 #define W25X_SectorErase 0x20 #define W25X_ChipErase 0xC7 #define W25X_PowerDown 0xB9 #define W25X_ReleasePowerDown 0xAB #define W25X_DeviceID 0xAB #define W25X_ManufactDeviceID 0x90 #define W25X_JedecDeviceID 0x9F typedef struct w25qxx_device_s{ void (*init) (void); void (*wr) (uint8_t* pbuffer, uint32_t read_addr, uint16_t num_byte_to_read); void (*rd) (uint8_t* pbuffer, uint32_t write_addr, uint16_t num_byte_to_write); uint16_t type; }w25qxx_device_t; extern w25qxx_device_t w25q32_dev; void w25qxx_init(void); uint16_t w25qxx_readid(void); uint8_t w25qxx_readsr(void); //读取状态寄存器 void w25qxx_write_sr(uint8_t sr); //写状态寄存器 void w25qxx_write_enable(void); //写使能 void w25qxx_write_disable(void); //写保护 void w25qxx_read(uint8_t* pbuffer, uint32_t read_addr, uint16_t num_byte_to_read); //读取flash void w25qxx_write(uint8_t* pbuffer, uint32_t write_addr, uint16_t num_byte_to_write); //写入flash void w25qxx_erase_chip(void); //整片擦除 void w25qxx_erase_sector(uint32_t dst_addr); //扇区擦除 void w25qxx_wait_busy(void); //等待空闲 void w25qxx_powerdown(void); //进入掉电模式 void w25qxx_wakeup(void); //唤醒 #endif ``` 修改main.c ```c #include
#include "wm_hal.h" #include "led.h" //#include "csi_config.h" #include "w25qxx.h" void Error_Handler(void); void led_init(void); uint16_t rd_date[2048] = {0}; uint16_t wr_date[2048] = {0}; int main(void) { uint16_t i = 0; SystemClock_Config(CPU_CLK_160M); printf("enter main\r\n"); printf("hello,world\r\n"); led_init(); w25q32_dev.init(); if(w25q32_dev.type != W25Q32) { printf("w25q32 flash init error!\r\n"); } else { printf("w25q32 flash init OK!\r\n"); } printf("write flash data(0~4095): \r\n"); for(i = 0; i < 2048; i++) { wr_date[i] = i; } w25q32_dev.wr((uint8_t *)wr_date, 0, 4096); w25q32_dev.rd((uint8_t *)rd_date, 0, 4096); printf("read flash data: \r\n"); for(i = 0; i < 2048; i++) { printf("%d, ", rd_date[i]); } printf("\r\n"); while (1) { printf("."); HAL_Delay(500); } } void Error_Handler(void) { while (1) { } } void assert_failed(uint8_t *file, uint32_t line) { printf("Wrong parameters value: file %s on line %d\r\n", file, line); } ``` ### 5. 程序编译下载 右击工程 -> 选择build 工程编译输出如下,没有错误或警告,表示编译成功。
打开 Upgrade_Tools_V1.4.8.exe,开始下载程序 ![](https://cf01.ickimg.com/bbsimages/202111/88b5fc9835d66ef4d096a453d537babd.png) ### 6. 实验现象 数据写入及读出成功! ![undefined](https://cf01.ickimg.com/bbsimages/202111/6e2ea6791d51342a1b20f099013876bb.png "undefined") ### 7. 总结 这里将spi相关的代码与w25q32的代码仅从分层,方便了w25q32移植到其他平台。 测试了下 spi 在其他速度下的读写,均正常。
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
Sixer
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交