2021网站建设前景怎么样,下列不能反应企业网站建立网络,网站背景视频,做网站上传信息软件本文使用PS-SPI实现Flash读写#xff0c;PS-SPI的基础资料参考Xilinx UG1085的文档说明#xff0c;其基础使用方法是#xff0c;配置SPI模式#xff0c;控制TXFIFO/RXFIFO#xff0c;ZYNQ的IP自动完成发送TXFIFO数据#xff0c;接收数据到RXFIFO#xff0c;FIFO深度为12… 本文使用PS-SPI实现Flash读写PS-SPI的基础资料参考Xilinx UG1085的文档说明其基础使用方法是配置SPI模式控制TXFIFO/RXFIFOZYNQ的IP自动完成发送TXFIFO数据接收数据到RXFIFOFIFO深度为128Byte。本文介绍了使用PS-SPI的Flash开发。 软硬件介绍
硬件平台:Xilinx ZYNQFlash芯片:华邦W25Q80软件平台:Vitis Standalone 芯片信息/配置:
容量:8MbitSPI时钟:25MHZIO电平:3.3VSPI FIFO深度:128ByteSPI 标准模式 方案: 在ZYNQ平台上使用PS的SPI进行读写Flash芯片约束EMIO芯片管脚在Vitis上读写SPI总线。 测试项目:
擦除、读、写功能芯片容量擦除、读、写速度 硬件设计
使能PS端的SPI(SPI0)模块FIFO位宽8Bit约束CS/DI/DO/CLK管脚生成XSA提供给软件 软件设计
使用PS SPI功能读写寄存器封装读ID、写使能、读取状态、擦除、读、写接口C语言。 调试和测试流程
读取芯片ID验证SPI通路验证全片擦除、页写入、读功能页写入、读功能, 验证数据读写正确性容量测试测试读写时间 调试手段
发送数据写PS-SPI对应的写缓存地址写入数据到“写FIFO缓冲区”等待发送完成读取数据读PS-SPI对应的读缓存地址读取“读FIFO缓冲区”数据等待读取完成信号分析测试过程中使用逻辑分析仪抓取CS/DI/DO/CLK信号。
#define SPIPS_RECV_BYTE(BaseAddress) \Xil_In8((BaseAddress) XSPIPS_RXD_OFFSET)#define SPIPS_SEND_BYTE(BaseAddress, Data) \Xil_Out8((BaseAddress) XSPIPS_TXD_OFFSET, (Data))void spi_read(int byte_count)
{int count;u32 status_reg;status_reg XSpiPs_ReadReg(g_spi0_handle.Config.BaseAddress,XSPIPS_SR_OFFSET);/** Polling the Rx Buffer for Data*/do{status_reg XSpiPs_ReadReg(g_spi0_handle.Config.BaseAddress,XSPIPS_SR_OFFSET);}while(!(status_reg XSPIPS_IXR_RXNEMPTY_MASK));/** Reading the Rx Buffer*/for(count 0; count byte_count; count){g_read_buffer[count] SPIPS_RECV_BYTE(g_spi0_handle.Config.BaseAddress);}}void spi_write(u8 *send_buffer, int byte_count)
{u32 status_reg;int trans_count 0;status_reg XSpiPs_ReadReg(g_spi0_handle.Config.BaseAddress,XSPIPS_SR_OFFSET);while ((byte_count 0) (trans_count XSPIPS_FIFO_DEPTH)) {SPIPS_SEND_BYTE(g_spi0_handle.Config.BaseAddress,*send_buffer);send_buffer;trans_count;byte_count--;}/** Wait for the transfer to finish by polling Tx fifo status.*/do {status_reg XSpiPs_ReadReg(g_spi0_handle.Config.BaseAddress,XSPIPS_SR_OFFSET);} while ((status_reg XSPIPS_IXR_TXOW_MASK) 0);}
代码SPI读写接口 图逻辑分析仪 1.读取芯片ID验证SPI通路
写入唤醒寄存器0xAB,后面再发送3个字节数据0共发送4个字节;再发送一个字节为了提供时钟读取FIFO数据5字节。
char release_powerdown_and_read_id()
{memset(g_write_buffer, 0x00, sizeof(g_write_buffer));g_write_buffer[0] 0xAB;//CS 1set_csn();usleep(10);//CS 0set_cs0();spi_write(g_write_buffer,5);set_csn();spi_read(5);return g_read_buffer[4];
}
代码读ID
注意的是SPI只要有时钟“读FIFO缓冲区”就会写入数据MISO。主机发送5个字节接着读取5个字节丢弃前4个数据第5个就是读到的ID。 0xFF 0xFF 0xFF 0xFF 0x13 小结读到的ID是0x13和datasheet一致。 2.验证全片擦除
执行操作前先配置写使能能为1读取状态寄存器全片擦除,写入擦除全片寄存器0xC7等待BUSY信号为0时结束,判断信号间隔为1ms(参考官方驱动) 注意的是写完后写时能标记为会置0
int wait_busy(int max_count)
{int r,busy,busy_cnt 0x00;r 0;do {busy is_busy();if(busy 1)usleep(1000);busy_cnt 1;if (max_count 0){if (busy 0){r 0;break;}if (busy_cnt max_count){r -1;break;}}} while(busy 1);return r;
}void erase_entire()
{set_write_enable();g_write_buffer[0] 0xC7;set_csn();usleep(10);set_cs0();spi_write(g_write_buffer,1);set_csn();spi_read(1);wait_busy(1000);return ;
}
代码擦除全片 ---------------------------------------- ...erase entire chip... erase entire consume time:831 ms ---------------------------------------- 小结全片擦除用了0.8S,比手册提供的2S典型值小。 3.页写入、读功能, 验证数据读写正确性
页读取 写入读页数据寄存器0x03后面跟一个24位地址按照手册要求先发送高位地址即依次发送addr[23:16]addr[15:8]addr[7:0]。主机还要继续写入提供时钟写入一个页的数据(0)。读取“读FIFO缓冲区”数据读取一个页的数据量得到读取内容。
void page_read(int address, unsigned char * recv, int size)
{int i;set_csn();usleep(10);set_cs0();g_write_buffer[0] 0x03;g_write_buffer[1] address 16;g_write_buffer[2] address 8;g_write_buffer[3] address 0;spi_write(g_write_buffer,4);spi_read(4);g_write_buffer[0] 0;memset(g_write_buffer, 0x00, sizeof(g_write_buffer));if (size 128){spi_write(g_write_buffer,128);spi_read(128);memcpy(recv, g_read_buffer, 128);spi_write(g_write_buffer 128,size - 128);spi_read(size - 128);memcpy(recv 128, g_read_buffer, size - 128);}else{spi_write(g_write_buffer, size);spi_read(size);memcpy(recv, g_read_buffer, size);}set_csn();return;
}
代码页读取 页写入 写入写页数据寄存器0x02后面跟一个24位地址按照手册要求先发送高位地址即依次发送addr[23:16]addr[15:8]addr[7:0]。主机继续写入一个页的数据,页数据Pattern是一个递增数据。
//Pattern
u8 data[] {0x00,0x01,0x02....0x0FF};
读取“读FIFO缓冲区”数据排空无用“读缓冲数据”。
void page_write(int address, unsigned char * data, int size)
{//int i;set_write_enable();set_csn();usleep(10);set_cs0();g_write_buffer[0] 0x02;g_write_buffer[1] address 16;g_write_buffer[2] address 8;g_write_buffer[3] address 0;spi_write(g_write_buffer,4);spi_read(4);if (size 128){spi_write(data,128);spi_read(128);spi_write(data 128,size - 128);spi_read(size - 128);}else{spi_write(data,size);spi_read(size);}wait_busy(1000);set_csn();return;
}代码页写入 验证
执行“擦除”操作执行“页写入”操作执行“页读取”操作打印读取数据比较写入数据和读取数据内容使用memcmp进行比较。
void page_rw_test()
{char send_data[PAGE_SIZE] {...};//自行填充char recv_data[PAGE_SIZE];erase_entire();page_write(0x00, send_data, PAGE_SIZE);page_read(0x00, recv_data, PAGE_SIZE);r memcmp(send_data, recv_data, PAGE_SIZE);return r;
}
代码页读写验证
小结:读写功能正常数据比较一致。 4.容量测试
方法:每4个字节当作一个单元每个单元数据递增1。写入再读出作比较。 芯片共有16x16x16个4K个页往4K个页写入递增数据(0,262143)。打印部分页内容对比所有写入数据和读取数据使用memcmp进行比较。
表地址和容量地址
byte_address01234567........8388604838860583886068388607vol_address01........262143data01........262143 小结:打印内容符合递增预期数据比较一致。 5.测试读写时间
容量测试加入时间打印分别记录擦除时间、写入时间和读取时间。
时间计数方式采用读取CPU计数器计数转换成时间。
write_data/read_data是page_write/page_read的封装可以写入/读取任意数据。
int entire_volume_test(const int value_start, int step)
{int blkn, r, value, i,last_value;int escape;XTime start,end;blkn BLOCK_NUMBER;printf([ entire volume test ]\r\n);printf([information]:\r\n);printf(----------------------------------------\r\n);printf( Capicity( Bit ):%d\r\n, blkn * BLOCK_SIZE * 8);printf( Capicity(1Byte):%d\r\n, blkn * BLOCK_SIZE);printf( Capicity(4Byte):%d [*]\r\n, blkn * BLOCK_SIZE/4);printf( SPI CLK :%d MHZ\r\n, 25);printf(----------------------------------------\r\n);printf([test parttern] value start:%d,step:%d\n, value_start, step);printf([test parttern] value range:(%d , %d)\n, value_start, step * (blkn * BLOCK_SIZE/4 - 1));printf(----------------------------------------\r\n);printf(...erase entire chip...\r\n);start get_sys_count();erase_entire();end get_sys_count();escape get_useconds(start, end);printf(erase entire consume time:%02d ms \r\n, escape/1000);usleep(200000);last_value step * (blkn * BLOCK_SIZE/4 - 1);printf( fill data\r\n);value value_start;for (i 0; i blkn * BLOCK_SIZE/4; i){g_data[i * 4 3 ] (value 24) 0xFF;g_data[i * 4 2 ] (value 16) 0xFF;g_data[i * 4 1 ] (value 8) 0xFF;g_data[i * 4 0 ] (value 0) 0xFF;value step;}printf( write data sequence\r\n);start get_sys_count();write_data(0, g_data, blkn * BLOCK_SIZE);end get_sys_count();escape get_useconds(start, end);printf(write data consume time:%02d ms \r\n, escape/1000);printf( reading.....\r\n);start get_sys_count();read_data (0, g_recv_buffer,blkn * BLOCK_SIZE);end get_sys_count();escape get_useconds(start, end);printf(read consume time:%02d ms \r\n, escape/1000);printf( dump last 2 page \r\n);printf(value will range:(%08d , %08d)\r\n, 1 last_value - 2 * PAGE_SIZE/ 4, last_value - 1 * PAGE_SIZE/ 4);dec_print(g_recv_buffer (blkn * BLOCK_SIZE - 2 * PAGE_SIZE) , PAGE_SIZE/4);printf(value will range:(%08d , %08d)\r\n, 1 last_value - 1 * PAGE_SIZE/ 4, last_value - 0 * PAGE_SIZE/ 4);dec_print(g_recv_buffer (blkn * BLOCK_SIZE - 1 * PAGE_SIZE) , PAGE_SIZE/4);printf(compare write data and read data values, compare size:%d Bytes\n, blkn * BLOCK_SIZE);printf(----------------------------------------\r\n);if (memcmp(g_data, g_recv_buffer, blkn * BLOCK_SIZE) 0){printf( [*] pass volume test !!!\r\n);printf(----------------------------------------\r\n);return 0;}printf([*] !!fail volume test !!!\r\n);printf(----------------------------------------\r\n);return -1;
}
代码容量测试 write page data consume time:1346 us read page data consume time:275 us ...erase entire chip... erase entire consume time:831 ms fill data write data sequence write data consume time:5509 ms reading..... read consume time:1127 ms ---------------------------------------- [*] pass volume test !!! ---------------------------------------- 记录测试结果到下表
测试项目测试值(ms) 参考值[典型值最大值](ms)页写入时间1.34[0.8, 3]页读取时间0.28/全片擦除时间831[2000, 6000]全片写入时间5509[3276,24576]全片读取时间1127/ 表测试结果
总结:擦除速度比datasheet参考值快其他均正常。 其他相关 Flash读写特性 Flash的特性是写数据只能将1写为00不能写为1。擦除数据是将所有数据都写为1。因此如果想在已经数据的flash上写入新的数据则必须先擦除。
Flash相关知识学习记录以W25Q128为例 芯片地址相关 以WQ25Q80为例一个地址24位由块地址、扇地址、页地址、页内偏移组成。
#define ADDRESS(block, sector, page, offset) ((block) 16 | (sector) 12 | (page) 8 | (offset))
代码使用C语言表示芯片地址
地址项块地址扇区地址页地址页内偏移地址大小(bit)4(冗余)4448
表WQ25Q80地址
比如一个地址0x04E3AA表示块地址0x04扇区地址0xE页地址0x03,页内偏移0xAA。 关于CS使用 使用芯片时候需要把CS引脚拉低在命令写完成后需要把CS引脚拉高。手册里都会有The instruction is completed by driving /CS high的说明这也成为Flash芯片操作的通用操作。 关于PS-SPI软件配置 可以配置CS控制模式、时钟频率时钟频率通过SPI主频分频得到分频系数可配置。
int spi_init() {unsigned int config_value;int status;char spi_dev_id SPI_DEVICE_ID;XSpiPs_Config *spi_config;/** Initialize the SPI device.*/spi_config XSpiPs_LookupConfig(spi_dev_id);if (NULL spi_config) {return XST_FAILURE;}status XSpiPs_CfgInitialize(g_spi_handle, spi_config, spi_config-BaseAddress);if (status ! XST_SUCCESS) {return XST_FAILURE;}/** Perform a self-test to check hardware build.*/status XSpiPs_SelfTest(g_spi_handle);if (status ! XST_SUCCESS) {return XST_FAILURE;}XSpiPs_ResetHw(spi_config-BaseAddress);printf(%s self test succ\r\n, __func__);status XSpiPs_SetOptions(g_spi_handle, XSPIPS_MASTER_OPTION | XSPIPS_FORCE_SSELECT_OPTION);//status XSpiPs_SetOptions(g_spi_handle, XSPIPS_MASTER_OPTION);if (status ! XST_SUCCESS) {printf(%s XSpiPs_SetOptions fail\n, __func__);return XST_FAILURE;}/** PS SPI CLK DOMAIN 200MHZ* */status XSpiPs_SetClkPrescaler(g_spi_handle, XSPIPS_CLK_PRESCALE_8);if (status ! XST_SUCCESS) {printf(%s XSpiPs_SetClkPrescaler fail\n, __func__);return XST_FAILURE;}XSpiPs_Enable(g_spi_handle);printf(spi %d config finish\r\n, spi_dev_id);//config_value XSpiPs_ReadReg(g_spi_handle.Config.BaseAddress,XSPIPS_CR_OFFSET);//printf(config_value :0x%08X\n, config_value);return XST_SUCCESS;}