OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 173|回复: 0

GD32H737通过ospi方式与w25n01通信,读写flash数据有问题

[复制链接]

2

主题

7

帖子

0

精华

初级会员

Rank: 2

积分
73
金钱
73
注册时间
2017-10-25
在线时间
12 小时
发表于 2025-2-27 17:10:51 | 显示全部楼层 |阅读模式
GD32H737通过ospi方式与w25n01通信,读写flash过程中发现写入7块0页0列一串数,读3块0页/15块0页...的数据均为写入3块0页0列的数(3块0页/15块0页之前读出来的数据肯定不是目前数据),现在可以确定flash读取函数中数据位置肯定不对,但不清楚写入的时候是否也是将数据写在3块0页,而非7块0页。

ospi方式连接w25n01如图



```
  /* 配置使用的OSPIM引脚
           OSPIM_P0_SCK(PB2)
           OSPIM_P0_CSN(PC11)
           OSPIM_P0_IO0(PD11)
           OSPIM_P0_IO1(PD12)
           OSPIM_P0_IO2(PB13)
           OSPIM_P0_IO3(PD13) */
```

初始化函数如下
```
void ospi_flash_init()
{
    /* reset the OSPI and OSPIM peripheral */
    ospi_deinit(OSPI0);                                /* 复位OSPI */
    ospim_deinit();                                    /* 复位OSPIM */
    /* enable OSPIM and GPIO clock */
    rcu_periph_clock_enable(RCU_OSPI0);                /* 使能OSPIM时钟 */  
    rcu_periph_clock_enable(OSPIM_PX_SCK_GPIO_CLK);    /* 使能OSPIM_PX_SCK IO口时钟 */
    rcu_periph_clock_enable(OSPIM_PX_CSN_GPIO_CLK);    /* 使能OSPIM_PX_CSN IO口时钟 */
    rcu_periph_clock_enable(OSPIM_PX_IO0_GPIO_CLK);    /* 使能OSPIM_PX_IO0 IO口时钟 */
    rcu_periph_clock_enable(OSPIM_PX_IO1_GPIO_CLK);    /* 使能OSPIM_PX_IO1 IO口时钟 */
    rcu_periph_clock_enable(OSPIM_PX_IO2_GPIO_CLK);    /* 使能OSPIM_PX_IO2 IO口时钟 */
    rcu_periph_clock_enable(OSPIM_PX_IO3_GPIO_CLK);    /* 使能OSPIM_PX_IO3 IO口时钟 */

    /* 配置OSPIM引脚的复用功能 */
    gpio_af_set(OSPIM_PX_SCK_GPIO_PORT, OSPIM_PX_SCK_GPIO_AF, OSPIM_PX_SCK_GPIO_PIN);
    gpio_af_set(OSPIM_PX_CSN_GPIO_PORT, OSPIM_PX_CSN_GPIO_AF, OSPIM_PX_CSN_GPIO_PIN);  
    gpio_af_set(OSPIM_PX_IO0_GPIO_PORT, OSPIM_PX_IO0_GPIO_AF, OSPIM_PX_IO0_GPIO_PIN);
    gpio_af_set(OSPIM_PX_IO1_GPIO_PORT, OSPIM_PX_IO1_GPIO_AF, OSPIM_PX_IO1_GPIO_PIN);
    gpio_af_set(OSPIM_PX_IO2_GPIO_PORT, OSPIM_PX_IO2_GPIO_AF, OSPIM_PX_IO2_GPIO_PIN);
    gpio_af_set(OSPIM_PX_IO3_GPIO_PORT, OSPIM_PX_IO3_GPIO_AF, OSPIM_PX_IO3_GPIO_PIN);

    /* OSPIM_PX_SCK引脚模式设置 */
    gpio_mode_set(OSPIM_PX_SCK_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, OSPIM_PX_SCK_GPIO_PIN);
    gpio_output_options_set(OSPIM_PX_SCK_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, OSPIM_PX_SCK_GPIO_PIN);

    /* OSPIM_PX_CSN引脚模式设置 */
    gpio_mode_set(OSPIM_PX_CSN_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, OSPIM_PX_CSN_GPIO_PIN);
    gpio_output_options_set(OSPIM_PX_CSN_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, OSPIM_PX_CSN_GPIO_PIN);

    /* OSPIM_PX_IO0引脚模式设置 */
    gpio_mode_set(OSPIM_PX_IO0_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, OSPIM_PX_IO0_GPIO_PIN);
    gpio_output_options_set(OSPIM_PX_IO0_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, OSPIM_PX_IO0_GPIO_PIN);

    /* OSPIM_PX_IO1引脚模式设置 */
    gpio_mode_set(OSPIM_PX_IO1_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, OSPIM_PX_IO1_GPIO_PIN);
    gpio_output_options_set(OSPIM_PX_IO1_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, OSPIM_PX_IO1_GPIO_PIN);

    /* OSPIM_PX_IO2引脚模式设置 */
    gpio_mode_set(OSPIM_PX_IO2_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, OSPIM_PX_IO2_GPIO_PIN);
    gpio_output_options_set(OSPIM_PX_IO2_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, OSPIM_PX_IO2_GPIO_PIN);

    /* OSPIM_PX_IO3引脚模式设置 */
    gpio_mode_set(OSPIM_PX_IO3_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, OSPIM_PX_IO3_GPIO_PIN);
    gpio_output_options_set(OSPIM_PX_IO3_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, OSPIM_PX_IO3_GPIO_PIN);

    ospim_port_sck_config(OSPIM_PORT0, OSPIM_PORT_SCK_ENABLE);    /* 使能OSPIM端口0的SCK */
    ospim_port_csn_config(OSPIM_PORT0, OSPIM_PORT_CSN_ENABLE);    /* 使能OSPIM端口0的CSN */
    ospim_port_io3_0_config(OSPIM_PORT0, OSPIM_IO_LOW_ENABLE);    /* 使能OSPIM端口0的IO[3-0] */

    rcu_periph_clock_enable(RCU_OSPI0);                                        /* 使能OSPI0时钟 */
                ospim_port_sck_source_select(OSPIM_PORT0, OSPIM_SCK_SOURCE_OSPI0_SCK);     /* 选择OSPIM端口0的SCK源为OSPI0_SCK */
                ospim_port_csn_source_select(OSPIM_PORT0, OSPIM_CSN_SOURCE_OSPI0_CSN);     /* 选择OSPIM端口0的CSN源为OSPI0_CSN */
                ospim_port_io3_0_source_select(OSPIM_PORT0, OSPIM_SRCPLIO_OSPI0_IO_LOW);   /* 选择OSPIM端口0的IO[3-0]源为OSPI0_IO[3-0] */

    ospi_struct_init(&QSPIHandle);                                 /* 初始化OSPI结构体参数为默认值 */

    QSPIHandle.prescaler = (4 - 1);                              /* 从内核时钟分频产生OSPI时钟的分频因子 */
                                                                   /* OSPI时钟来自AHB3时钟,设置使用4分频, 则OSPI时钟为300M / 4 = 75Mhz      
                                                                    */
    QSPIHandle.sample_shift = OSPI_SAMPLE_SHIFTING_HALF_CYCLE;   /* 采样移位二分之一个周期 */
    QSPIHandle.fifo_threshold = OSPI_CS_HIGH_TIME_4_CYCLE;           /* FIFO中的阈值为4个字节 */
    QSPIHandle.device_size = OSPI_MESZ_128_MBS;                  /* 外部存储器大小为128MB(W25N01GVZEIG) */
    QSPIHandle.wrap_size = OSPI_DIRECT;                          /* 外部存储器设备不支持回卷读取 */
    QSPIHandle.cs_hightime = OSPI_CS_HIGH_TIME_4_CYCLE;          /* 片选高电平时间为4个周期(4*1000/75=53ns),即手册里面的tSHSL参数 */
    QSPIHandle.memory_type = OSPI_MICRON_MODE;                   /* 外部设备类型为Micron模式 */
    QSPIHandle.delay_hold_cycle = OSPI_DELAY_HOLD_QUARTER_CYCLE;          /* 不延迟保持 */

    ospi_init(OSPI0,&QSPIHandle);                                /* 初始化OSPI参数 */

    ospi_enable(OSPI0);                                          /* 使能OSPI */     

               
                ospi_flash_Write_StaRegister(0x1f,0xA0,0x00);
                ospi_flash_Write_StaRegister(0x1f,0xB0,0x18);
}
void ospi_flash_Write_StaRegister(u8 tmpCMD,u8 tmpAddr,u8 tmpSta)//写状态寄存器
{
                ospi_regular_cmd_struct cmd_struct = {0};
               
          ospi_flash_write_enbale();
                       
                cmd_struct.ins_mode = OSPI_INSTRUCTION_1_LINE;
                cmd_struct.dummy_cycles = OSPI_DUMYC_CYCLES_0;
                cmd_struct.instruction = tmpCMD;           // 写寄存器指令
                cmd_struct.ins_size = OSPI_INSTRUCTION_8_BITS;
               
                cmd_struct.addr_mode = OSPI_ADDRESS_1_LINE;
                cmd_struct.address = tmpAddr;              // 写入地址
                cmd_struct.addr_dtr_mode = OSPI_ADDRDTR_MODE_DISABLE; // 仅支持GD25LX512ME。
                cmd_struct.addr_size = OSPI_ADDRESS_8_BITS;           // 地址的位数

                cmd_struct.data_mode = OSPI_DATA_1_LINE;
                cmd_struct.operation_type = OSPI_OPTYPE_COMMON_CFG;   // 选择常规操作
                cmd_struct.nbdata = 1;
                cmd_struct.data_dtr_mode = OSPI_DADTR_MODE_DISABLE;   //仅支持GD25LX512ME。
               
                cmd_struct.alter_bytes_mode = OSPI_ALTERNATE_BYTES_NONE;
                 
                /* send the command */
                ospi_command_config(OSPI0, &QSPIHandle, &cmd_struct);       
               
                ospi_transmit(OSPI0,&tmpSta);
}
```
flash的写使能函数如下
```
void ospi_flash_write_enbale()
{
    ospi_autopolling_struct autopl_cfg_struct = {0};
    ospi_regular_cmd_struct cmd_struct = {0};

    /* initialize write enable command */
    cmd_struct.ins_mode = OSPI_INSTRUCTION_1_LINE;
               
    cmd_struct.operation_type = OSPI_OPTYPE_COMMON_CFG;
    cmd_struct.instruction = 0x06;
    cmd_struct.ins_size = OSPI_INSTRUCTION_8_BITS;
    cmd_struct.addr_mode = OSPI_ADDRESS_NONE;
    cmd_struct.addr_size = OSPI_ADDRESS_24_BITS;
    cmd_struct.addr_dtr_mode = OSPI_ADDRDTR_MODE_DISABLE;
    cmd_struct.alter_bytes_mode = OSPI_ALTERNATE_BYTES_NONE;
    cmd_struct.alter_bytes_size = OSPI_ALTERNATE_BYTES_24_BITS;
    cmd_struct.alter_bytes_dtr_mode = OSPI_ABDTR_MODE_DISABLE;
    cmd_struct.data_mode = OSPI_DATA_NONE;
    cmd_struct.data_dtr_mode = OSPI_DADTR_MODE_DISABLE;
    cmd_struct.dummy_cycles = OSPI_DUMYC_CYCLES_0;
    cmd_struct.nbdata = 0;

    /* send the command */
    ospi_command_config(OSPI0, &QSPIHandle, &cmd_struct);

    /* configure automatic polling mode to wait for write enabling */
    cmd_struct.data_mode = OSPI_DATA_1_LINE;
    cmd_struct.dummy_cycles = OSPI_DUMYC_CYCLES_0;
                               
    cmd_struct.instruction = W25_REG_STATUS;
    cmd_struct.addr_dtr_mode = OSPI_ADDRDTR_MODE_DISABLE;
    cmd_struct.nbdata = 1;

    /* send the command */
    ospi_command_config(OSPI0, &QSPIHandle, &cmd_struct);

    autopl_cfg_struct.match = 2U;
    autopl_cfg_struct.mask  = 2U;
    autopl_cfg_struct.match_mode = OSPI_MATCH_MODE_AND;
    autopl_cfg_struct.interval = 0x10; //注意该参数
    autopl_cfg_struct.automatic_stop = OSPI_AUTOMATIC_STOP_MATCH;
    ospi_autopolling_mode(OSPI0, &QSPIHandle, &autopl_cfg_struct);
}
```
块擦除

```
void ospi_flash_block_erase(uint32_t addr)
{
    ospi_regular_cmd_struct cmd_struct = {0};
               
          ospi_flash_write_enbale();
                       
                cmd_struct.ins_mode = OSPI_INSTRUCTION_1_LINE;
                cmd_struct.dummy_cycles = OSPI_DUMYC_CYCLES_8;
                cmd_struct.instruction = 0xd8;         // 块擦除指令
                cmd_struct.ins_size = OSPI_INSTRUCTION_8_BITS;
               
                cmd_struct.addr_mode = OSPI_ADDRESS_1_LINE;
                cmd_struct.address = addr;                            // 写入擦除地址
                cmd_struct.addr_dtr_mode = OSPI_ADDRDTR_MODE_DISABLE; // 仅支持GD25LX512ME。
                cmd_struct.addr_size = OSPI_ADDRESS_16_BITS;          // 地址的位数

                cmd_struct.data_mode = OSPI_DATA_NONE;
                cmd_struct.operation_type = OSPI_OPTYPE_COMMON_CFG;   // 选择常规操作
                cmd_struct.data_dtr_mode = OSPI_DADTR_MODE_DISABLE;   // 仅支持GD25LX512ME。
               
                cmd_struct.alter_bytes_mode = OSPI_ALTERNATE_BYTES_NONE;
                 
                /* send the command */
                ospi_command_config(OSPI0, &QSPIHandle, &cmd_struct);       
}
```
读页和缓存代码
```
void ospi_ReadPage(uint32_t _PageAddr)
{
                ospi_regular_cmd_struct cmd_struct = {0};
                                       
                cmd_struct.ins_mode = OSPI_INSTRUCTION_1_LINE;
                cmd_struct.dummy_cycles = OSPI_DUMYC_CYCLES_8;
                cmd_struct.instruction = 0x13;
                cmd_struct.ins_size = OSPI_INSTRUCTION_8_BITS;
               
                cmd_struct.addr_mode = OSPI_ADDRESS_1_LINE;
                cmd_struct.address = _PageAddr;                       // 页读取地址
                cmd_struct.addr_dtr_mode = OSPI_ADDRDTR_MODE_DISABLE; //仅支持GD25LX512ME。
                cmd_struct.addr_size = OSPI_ADDRESS_16_BITS;          // 地址的位数

                cmd_struct.data_mode = OSPI_DATA_NONE;
                cmd_struct.operation_type = OSPI_OPTYPE_COMMON_CFG;   // 选择常规操作
                cmd_struct.data_dtr_mode = OSPI_DADTR_MODE_DISABLE;   // 仅支持GD25LX512ME。
               
                cmd_struct.alter_bytes_mode = OSPI_ALTERNATE_BYTES_NONE;
                 
                /* send the command */
                ospi_command_config(OSPI0, &QSPIHandle, &cmd_struct);       
}

void ospi_ReadBuffer(uint8_t * _pBuf, uint32_t ReadColumAddr, uint32_t Size)
{
                ospi_regular_cmd_struct cmd_struct = {0};
                                               
                cmd_struct.ins_mode = OSPI_INSTRUCTION_1_LINE;
                cmd_struct.dummy_cycles = OSPI_DUMYC_CYCLES_4;
                cmd_struct.instruction = 0xeb;   
                cmd_struct.ins_size = OSPI_INSTRUCTION_8_BITS;
               
                cmd_struct.addr_mode = OSPI_ADDRESS_4_LINES;
                cmd_struct.address = ReadColumAddr;                   // 读取缓存区地址
                cmd_struct.addr_dtr_mode = OSPI_ADDRDTR_MODE_DISABLE; //仅支持GD25LX512ME。
                cmd_struct.addr_size = OSPI_ADDRESS_16_BITS;          // 地址的位数

                cmd_struct.data_mode = OSPI_DATA_4_LINES;             // 4线数据读取方式
                cmd_struct.operation_type = OSPI_OPTYPE_COMMON_CFG;   // 选择常规操作
                cmd_struct.nbdata = Size;
                cmd_struct.data_dtr_mode = OSPI_DADTR_MODE_DISABLE;   // 仅支持GD25LX512ME。
               
                cmd_struct.alter_bytes_mode = OSPI_ALTERNATE_BYTES_NONE;
                 
                /* send the command */
                ospi_command_config(OSPI0, &QSPIHandle, &cmd_struct);       
                               
                ospi_receive(OSPI0,_pBuf);       
```
写缓存和写入flash的代码如下

```
void ospi_WriteBuffer(uint8_t *_pBuf, uint32_t WriteColumAddr, uint16_t WriteSize)
{
                ospi_regular_cmd_struct cmd_struct = {0};
                               
                ospi_flash_write_enbale();
               
                delay_ms(2);
                       
                cmd_struct.ins_mode = OSPI_INSTRUCTION_1_LINE;
                cmd_struct.dummy_cycles = OSPI_DUMYC_CYCLES_0;
                cmd_struct.instruction = 0x34;   // 4线写入缓冲区指令  随机加载
                cmd_struct.ins_size = OSPI_INSTRUCTION_8_BITS;

                cmd_struct.addr_mode = OSPI_ADDRESS_1_LINE;            // 1线地址方式
                cmd_struct.address = WriteColumAddr;                   // 写入缓存区列地址

                cmd_struct.addr_dtr_mode = OSPI_ADDRDTR_MODE_DISABLE;  // 仅支持GD25LX512ME。
                cmd_struct.addr_size = OSPI_ADDRESS_16_BITS;           // 地址的位数

                cmd_struct.data_mode = OSPI_DATA_4_LINES;              // 4线数据方式
                cmd_struct.operation_type = OSPI_OPTYPE_COMMON_CFG;    // 选择常规操作
                cmd_struct.nbdata = WriteSize;
                cmd_struct.data_dtr_mode = OSPI_DADTR_MODE_DISABLE;    // 仅支持GD25LX512ME。

                cmd_struct.alter_bytes_mode = OSPI_ALTERNATE_BYTES_NONE;
                 
                /* send the command */
                ospi_command_config(OSPI0, &QSPIHandle, &cmd_struct);               
               
                ospi_transmit(OSPI0,_pBuf);                            // 将数据写入缓存区       
}
void ospi_Program(uint32_t WritePageAddr)
{
                ospi_regular_cmd_struct cmd_struct = {0};
               

                cmd_struct.ins_mode = OSPI_INSTRUCTION_1_LINE;
                cmd_struct.dummy_cycles = OSPI_DUMYC_CYCLES_8;
                cmd_struct.instruction = 0x10;              // 编程指令
                cmd_struct.ins_size = OSPI_INSTRUCTION_8_BITS;

                cmd_struct.addr_mode = OSPI_ADDRESS_1_LINE;
                cmd_struct.address = WritePageAddr;                    // 写入页地址
                cmd_struct.addr_dtr_mode = OSPI_ADDRDTR_MODE_DISABLE;  // 仅支持GD25LX512ME。
                cmd_struct.addr_size = OSPI_ADDRESS_16_BITS;           // 地址的位数

                cmd_struct.data_mode = OSPI_DATA_NONE;                 // 无数据
                cmd_struct.operation_type = OSPI_OPTYPE_COMMON_CFG;    // 选择常规操作

                cmd_struct.data_dtr_mode = OSPI_DADTR_MODE_DISABLE;    // 仅支持GD25LX512ME。

                cmd_struct.alter_bytes_mode = OSPI_ALTERNATE_BYTES_NONE;
                 
                /* send the command */
                ospi_command_config(OSPI0, &QSPIHandle, &cmd_struct);       
}

```
代码中写入flash数据的代码
```
//在7块0页的0列开始写入flashwrite_test【12】
                ospi_flash_block_erase(0x1c0); //7块0页
                delay_ms(20);

                 ospi_WriteBuffer(flashwrite_test, 0, 12);
                delay_ms(10);

                ospi_Program(0x1c0);
                delay_ms(5);


```
代码中读取flash中数据的代码
```
//flash_test存储3块0页0列的前12个数据
memset(flash_test,0,sizeof(flash_test));   
    ospi_ReadPage(0xc0);
        delay_ms(20);
    ospi_ReadBuffer(flash_test, 0, 12);
        delay_ms(20);

//flash_test1存储7块0页0列的前12个数据   
    memset(flash_test1,0,sizeof(flash_test1));
    ospi_ReadPage(0x1c0);
        delay_ms(20);
    ospi_ReadBuffer(flash_test1, 0, 12);
        delay_ms(20);

/flash_test2存储15块0页0列的前12个数据   
    memset(flash_test1,0,sizeof(flash_test2));
    ospi_ReadPage(0x1c0);
        delay_ms(20);
    ospi_ReadBuffer(flash_test2, 0, 12);
        delay_ms(20);
```
请有经验的人士指点,是哪个配置有问题?

微信截图_20250227164059.png
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-4-2 02:01

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表