-
Notifications
You must be signed in to change notification settings - Fork 0
AXI QSPI寄存器调试w25q64
minichao9901 edited this page May 6, 2024
·
6 revisions
https://blog.csdn.net/qq_38695100/article/details/135832883 https://blog.csdn.net/weixin_38384271/article/details/134265938
- z7020开发板
- v2扩展版(at24c02+w25q64)
- 例程power_test_spi_flash_i2c_eeprom.rar
#include "ACZ702_Lib/COMMON.h"
//xsct% mrd 0x4100001C 1
//4100001C: 00000000
//
//xsct% mrd 0x41000020 1
//41000020: 00000400
//
//xsct% mrd 0x41000028 1
//41000028: 0000202B
//
//xsct% mrd 0x41000040 1
//Memory read error at 0x41000040. Blocked address 0x41000040. Cannot read write-only register
//xsct% mrd 0x41000060 1
//41000060: 00000106
//
//xsct% mrd 0x41000064 1
//41000064: 00000025
//
//xsct% mrd 0x41000068 1
//Memory read error at 0x41000068. Blocked address 0x41000068. Cannot read write-only register
//xsct% mrd 0x4100006C 1
//Memory read error at 0x4100006C. Memory read aborted. External abort
//xsct% mrd 0x41000070 1
//41000070: 00000001
//
//xsct% mrd 0x41000074 1
//41000074: 00000000
#define PS_KEY 15
#define PS_LED 11
int main_ps_io(void)
{
uint8_t State; //存放按键(MIO47)的电平状态,0为低电平,1为高电平
PS_GPIO_Init(); //初始化PS端MIO和EMIO
PS_GPIO_SetMode(PS_LED, OUTPUT, 0);
//设置PL_LED(EMIO0)为输出并且初始为低电平
PS_GPIO_SetMode(PS_KEY, INPUT, 0); //设置PS_KEY(MIO47)方向为输入
// while(1)
// {
// State = PS_GPIO_GetPort(PS_KEY); //读取PS_KEY的电平值并存储到State变量里
// PS_GPIO_SetPort(PS_LED,!State); //将State变量的值取非赋予PS_LED来输出
// }
// return 0;
}
typedef struct{
u8 txbuf[256];
u8 rxbuf[256];
u8 length;
}t_buf;
t_buf WriteBuffer[]={
//s1: read id
{{0x90},{0x00},6},
{{0x9f},{0x00},8},
{{0x4b},{0x00},13},
//s2: sector erase
{{0x06},{0x00},1},
{{0x20,0x00,0x00,0x10},{0x00},4},
{{0xff},{0x00},0}, //length=0 => delay_ms
//s3: page program
{{0x06},{0x00},1},
{{0x02,0x00,0x00,0x10,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88},{0x00},250},
{{0xff},{0x00},0}, //length=0 => delay_ms
//s4: spi/dspi/qspi read
{{0x0b,0x00,0x00,0x10},{0x00},250},
{{0x3b,0x00,0x00,0x10},{0x00},250},
{{0x6b,0x00,0x00,0x10},{0x00},250},
// //s5: sector erase
// {{0x06},{0x00},1},
// {{0x20,0x00,0x00,0x10},{0x00},4},
// {{0xff},{0x00},0}, //length=0 => delay_ms
//
// //s6: qspi program
// {{0x06},{0x00},1},
// {{0x32,0x00,0x00,0x10,0xaa,0xbb,0xcc,0xdd,0xee,0xff,0x11,0x22},{0x00},12},
// {{0xff},{0x00},0}, //length=0 => delay_ms
//
// //s7: qspi read
// {{0x6b,0x00,0x00,0x10},{0x00},14},
// {{0xbb,0x00,0x00,0x10},{0x00},14},
// {{0xeb,0x00,0x00,0x10},{0x00},14},
//s8: enter qpi mode (QE=1), 貌似不支持
// {{0x38},{0x00},1}, //not support
// {{0x01,0x00,0x02},{0x00},3},
// {{0xA3,0x00,0x00,0x00},{0x00},4},
//
// //s9: sector erase
// {{0x06},{0x00},1},
// {{0x20,0x00,0x00,0x10},{0x00},4},
// {{0xff},{0x00},0}, //length=0 => delay_ms
//
// //s10: qpi read
// {{0x0b,0x00,0x00,0x10},{0x00},14},
//
// //s11: qpi program
// {{0x06},{0x00},1},
// {{0x02,0x00,0x00,0x10,0x88,0x77,0x66,0x55,0x44,0x33,0x22,0x11},{0x00},12},
// {{0xff},{0x00},0}, //length=0 => delay_ms
//
// //s12: qpi read
// {{0xbb,0x00,0x00,0x10},{0x00},14},
// {{0xeb,0x00,0x00,0x10},{0x00},14},
};
void print_buffer(u8 *pdata, int length)
{
for(int i=0; i<length; i++){
xil_printf("pdata[%d]=%x\r\n", i, pdata[i]);
}
xil_printf("********************\r\n");
}
void test_flash()
{
for(int i=0; i<sizeof(WriteBuffer)/sizeof(*WriteBuffer); i++){
if(WriteBuffer[i].length==0){
usleep(WriteBuffer[i].txbuf[0]*1000);
continue;
}
AXI_SPI_Transfer(&AXI_SPI0, 0, WriteBuffer[i].rxbuf, WriteBuffer[i].txbuf, WriteBuffer[i].length);
xil_printf("Exec CMD=%x\r\n\r\n", WriteBuffer[i].txbuf[0]);
print_buffer(WriteBuffer[i].rxbuf, WriteBuffer[i].length);
}
}
u8 txBuffer[4096];
u8 rxBuffer[4096];
void pulling_sr()
{
u32 ticks=0;
while(ticks++<10){
txBuffer[0]=0x05;
AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 2);
u8 sr0=rxBuffer[1];
usleep(1);
txBuffer[0]=0x35;
AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 2);
u8 sr1=rxBuffer[1];
usleep(1);
txBuffer[0]=0x15;
AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 2);
u8 sr2=rxBuffer[1];
usleep(1);
//u32 sr=(sr2<<15)+(sr1<<8)+sr0;
xil_printf("%x,%x,%x\r\n",sr0,sr1,sr2);
}
}
void test_flash2()
{
// //s1:QE=1
// txBuffer[0]=0x06;
// AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 1);
//
// txBuffer[0]=0x31;
// txBuffer[1]=1<<1;
// AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 1);
//s2: sector erase
txBuffer[0]=0x06;
AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 1);
txBuffer[0]=0x20;
txBuffer[1]=0x00;
txBuffer[2]=0x00;
txBuffer[3]=0x10;
AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 4);
//pulling_sr();
usleep(100*1000);
//s3: page program
txBuffer[0]=0x06;
AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 1);
txBuffer[0]=0x02;
txBuffer[1]=0x00;
txBuffer[2]=0x00;
txBuffer[3]=0x10;
for(int i=4; i<4096; i++){
txBuffer[i]=i;
}
AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 4096);
//pulling_sr();
usleep(100*1000);
//s4: spi/dspi/qspi read
txBuffer[0]=0x0b;
txBuffer[1]=0x00;
txBuffer[2]=0x00;
txBuffer[3]=0x10;
AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 4096);
usleep(100*1000);
for(int i=0; i<32; i++) xil_printf("%d\r\n", rxBuffer[i]);
xil_printf("*******************\r\n");
txBuffer[0]=0x3b;
txBuffer[1]=0x00;
txBuffer[2]=0x00;
txBuffer[3]=0x10;
AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 4096);
usleep(100*1000);
for(int i=0; i<32; i++) xil_printf("%d\r\n", rxBuffer[i]);
xil_printf("*******************\r\n");
txBuffer[0]=0x6b;
txBuffer[1]=0x00;
txBuffer[2]=0x00;
txBuffer[3]=0x10;
AXI_SPI_Transfer(&AXI_SPI0, 0, rxBuffer, txBuffer, 4096);
usleep(100*1000);
for(int i=0; i<32; i++) xil_printf("%d\r\n", rxBuffer[i]);
xil_printf("*******************\r\n");
}
#define QSPI_BASE 0x41000000
void config_spi()
{
Xil_Out32(QSPI_BASE+0x40, 0xa); //soft_rst
Xil_Out32(QSPI_BASE+0x28, 0x3fff); //global_ie
Xil_Out32(QSPI_BASE+0x1c, 0x80000000); //ie
}
void polling_tx_fifo_is_empty()
{
u32 status=0;
do{
status=Xil_In32(QSPI_BASE+0x64);
}
while(!(status&0b100));
}
void start_spi()
{
Xil_Out32(QSPI_BASE+0x70, 0x00); //选择0通道cs
Xil_Out32(QSPI_BASE+0x60, 0x86); //使能master,开始发数据
polling_tx_fifo_is_empty();
Xil_Out32(QSPI_BASE+0x70, 0x01); //选择0通道cs的cs拉高
Xil_Out32(QSPI_BASE+0x60, 0x186); //禁用master
}
void read_data(u32 num)
{
for(int i=0; i<num; i++){
u8 v=Xil_In32(QSPI_BASE+0x6c);
printf("%x\r\n",v);
}
}
void read_cmd(u8 cmd, u32 num)
{
config_spi();
Xil_Out32(QSPI_BASE+0x60, 0x1e6); //复位tx,rx fifo
Xil_Out32(QSPI_BASE+0x60, 0x186); //释放tx,rx fifo
Xil_Out32(QSPI_BASE+0x68, cmd);
for(int i=0; i<num; i++){
Xil_Out32(QSPI_BASE+0x68, 0x00);
}
start_spi();
read_data(num);
}
void read_id()
{
read_cmd(0x9f,8);
}
void read_status()
{
read_cmd(0x05,2);
read_cmd(0x35,2); //QE bit is default=1
//read_cmd(0x15,2); //cmd15 do not support
}
void write_data(u8 cmd, u8 *pdata, u8 length)
{
Xil_Out32(QSPI_BASE+0x60, 0x1e6);
Xil_Out32(QSPI_BASE+0x60, 0x186);
Xil_Out32(QSPI_BASE+0x68, cmd);
for(int i=0; i<length; i++){
Xil_Out32(QSPI_BASE+0x68, *pdata++);
}
start_spi();
}
void polling_qspi_busy()
{
u8 status;
do{
config_spi();
Xil_Out32(QSPI_BASE+0x60, 0x1e6);
Xil_Out32(QSPI_BASE+0x60, 0x186);
Xil_Out32(QSPI_BASE+0x68, 0x05);
Xil_Out32(QSPI_BASE+0x68, 0x00);
start_spi();
Xil_In32(QSPI_BASE+0x6c); //first data drop
status=Xil_In32(QSPI_BASE+0x6c);
}
while(status&0b001);
}
void wren()
{
write_data(0x06, NULL, 0);
}
void se(u8 sector_num)
{
wren();
u8 tbuffer[3]={sector_num, 0x00, 0x00};
write_data(0x20, &tbuffer[0],3);
}
void be()
{
wren();
write_data(0xc7, NULL,0);
}
void pp(u8 cmd, u32 address, u8 *pdata, u8 length)
{
wren();
Xil_Out32(QSPI_BASE+0x68, cmd);
Xil_Out32(QSPI_BASE+0x68, (address>>16)&0xff);
Xil_Out32(QSPI_BASE+0x68, (address>>8)&0xff);
Xil_Out32(QSPI_BASE+0x68, address&0xff);
for(int i=0; i<length; i++){
Xil_Out32(QSPI_BASE+0x68, *pdata++);
}
start_spi();
}
void read(u8 cmd, u32 address, u8 *pdata, u8 length)
{
Xil_Out32(QSPI_BASE+0x68, cmd);
Xil_Out32(QSPI_BASE+0x68, (address>>16)&0xff);
Xil_Out32(QSPI_BASE+0x68, (address>>8)&0xff);
Xil_Out32(QSPI_BASE+0x68, address&0xff);
for(int i=0; i<length; i++){
Xil_Out32(QSPI_BASE+0x68, 0x00);
}
start_spi();
read_data(length);
}
void pp_try_multi(u8 cmd, u32 address, u8 *pdata, u8 length)
{
/* 这个程序表明,AXI QUAD SPI这个IP的CS是完全由软件管理的,可以非常灵活的拉高或者拉低
* 这样可以实现,一个很长的Packet。这个是在API函数中没有实现的功能。必须用寄存器手搓。
*/
wren();
Xil_Out32(QSPI_BASE+0x68, cmd);
Xil_Out32(QSPI_BASE+0x68, (address>>16)&0xff);
Xil_Out32(QSPI_BASE+0x68, (address>>8)&0xff);
Xil_Out32(QSPI_BASE+0x68, address&0xff);
u8 *pdata_tmp=pdata;
for(int i=0; i<length; i++){
Xil_Out32(QSPI_BASE+0x68, *pdata_tmp++);
}
Xil_Out32(QSPI_BASE+0x70, 0x00); //选择0通道cs
Xil_Out32(QSPI_BASE+0x60, 0x86); //使能master,开始发数据
polling_tx_fifo_is_empty();
// Xil_Out32(QSPI_BASE+0x70, 0x01); //选择0通道cs的cs拉高
Xil_Out32(QSPI_BASE+0x60, 0x186); //禁用master
pdata_tmp=pdata;
for(int i=0; i<length; i++){
Xil_Out32(QSPI_BASE+0x68, *pdata_tmp++);
}
// Xil_Out32(QSPI_BASE+0x70, 0x00); //选择0通道cs
Xil_Out32(QSPI_BASE+0x60, 0x86); //使能master,开始发数据
polling_tx_fifo_is_empty();
// Xil_Out32(QSPI_BASE+0x70, 0x01); //选择0通道cs的cs拉高
Xil_Out32(QSPI_BASE+0x60, 0x186); //禁用master
pdata_tmp=pdata;
for(int i=0; i<length; i++){
Xil_Out32(QSPI_BASE+0x68, *pdata_tmp++);
}
// Xil_Out32(QSPI_BASE+0x70, 0x00); //选择0通道cs
Xil_Out32(QSPI_BASE+0x60, 0x86); //使能master,开始发数据
polling_tx_fifo_is_empty();
// Xil_Out32(QSPI_BASE+0x70, 0x01); //选择0通道cs的cs拉高
Xil_Out32(QSPI_BASE+0x60, 0x186); //禁用master
}
int main(void)
{
//初始化通用中断控制器
ScuGic_Init();
main_ps_io();
//初始化AXI_SPI0,设为主机模式
AXI_SPI_Init(&AXI_SPI0, XPAR_SPI_0_DEVICE_ID, XSP_MASTER_OPTION);
//初始化私有定时器中断,定时间隔100ms
ScuTimer_Int_Init(100000);
// for(int i=0; i<sizeof(WriteBuffer)/sizeof(*WriteBuffer); i++){
// if(WriteBuffer[i].length==0){
// usleep(WriteBuffer[i].txbuf[0]*1000);
// continue;
// }
//
// AXI_SPI_Transfer(&AXI_SPI0, 0, WriteBuffer[i].rxbuf, WriteBuffer[i].txbuf, WriteBuffer[i].length);
// xil_printf("Exec CMD=%x\r\n\r\n", WriteBuffer[i].txbuf[0]);
// print_buffer(WriteBuffer[i].rxbuf, WriteBuffer[i].length);
// }
while(1) {
//刷新间隔由私有定时器中断控制,中断服务函数在ISR.c中
// if(Refresh_Flag) {
// Refresh_Flag = 0;
//
// //xil_printf("timer run!\r\n");
// }
if(PS_GPIO_GetPort(PS_KEY)==0){
usleep(100*1000);
if(PS_GPIO_GetPort(PS_KEY)==0){
//test_flash2();
read_id(8); //ok
read_status(); //ok
se(11); //ok
//read_status();
polling_qspi_busy(); //ok,33ms
//usleep(200*1000);
u8 tbuffer[128];
for(int i=0; i<128; i++){
tbuffer[i]=i;
}
pp(0x32,0x0b0000,&tbuffer[0],128);
polling_qspi_busy(); //ok,172us
//read_status();
//usleep(100*1000);
read(0x03,0x0b0000, NULL, 128); //ok,1wire
read(0x0b,0x0b0000, NULL, 128); //ok,1wire
read(0x3b,0x0b0000, NULL, 128); //ok,2wire
read(0x6b,0x0b0000, NULL, 128); //half-ok, 4wire
read(0xeb,0x0b0000, NULL, 128); //half-ok, 4wire
pp_try_multi(0x32,0x0b0000,&tbuffer[0],128); //多个burst,也是可以的
polling_qspi_busy();
}
}
}
return 0;
}
- 1线,4线pp都ok
- 1线,2线read ok, 4线read只有前16个bytes ok,后面的bytes错误
- AXI Quad SPI对windond系列的读status command只支持0x05, 0x35。如果输入0x15指令就死机。
- polling_status_reg成功
- polling_fifo_empty成功
- AXI Quad SPI用寄存器手搓,效果很好。不过要注意,寄存器配置的顺序非常重要。错了就不对。 正确的顺序是:软件复位->打开中断使能(全局+局部)->复位tx,rx fifo->释放tx,rx fifo->往txfifo写数据(写N个)-> 配置通道0的cs=0 -> 使能master发送数据 -> polling_tx_fifo_empty-> 配置通道0的cs=1 -> 除能master发送数据
- pp_try_multi表示AXI QUAD SPI这个IP的CS是完全由软件管理的,可以非常灵活的拉高或者拉低。这样可以实现,一个很长的Packet。这个是在API函数中没有实现的功能。必须用寄存器手搓。
pp_try_multi(0x32,0x0b0000,&tbuffer[0],128)的执行过程,是ok的。中间的间隔是polling_fifo_empty()函数的执行过程。
- 当使用v3扩展板上的w25q64后,4线的读都是正常的(3b/6b/eb都可以),这说明是芯片的问题。