【技术分享】Apollo3 SDK平台添加支持复旦微FM25Q128 SPI Flash的方法

上海润欣科技股份有限公司创研社
本文详细介绍了,如何在apollo3 sdk v2.0平台上增加对复旦微fm25q128 spi flash支持的方法。本文将结合软件和硬件,阐述如何通过apollo3片上串行外设mspi接口以quad spi模式访问spi flash的底层驱动代码的实现步骤。
 1、  什么是mspi
mspi,即multi-bit spi的缩写,是apollo3新引入的串行外设,可以支持单线spi,dual spi,quad spi,以及八线octal spi(可以是单个octal设备,也可以是两个quad pair组成的octal 设备)等模式,最高速率24mhz,可以外接串行存储设备,如psram,spi nor flash等,支持xip片上直接运行代码。mpsi支持全部的4种spi cpol/cpha模式,即模式0-3。支持dma。支持command queue。2个片选,以quad-spi为例,mspi最多支持两路quad-spi,这两路共用一个mspi clk。两路qspi分时操作。mspi具体的管脚复用图如下图1,
图1 mspi管脚复用图
2、  mspi连接spi flash,quad spi四线模式,如图2
图2 mspi采用quad spi接口连接外部spi flash器件原理图 
3、根据以上图2所示硬件原理图,我们需要对sdk中的bsp库进行相应的修改,将mspi使用到的相关管脚一一进行分配和初始化,如这里用到的:
gp1    --- ce0 (片选)
gp26   --- dq1(数据io1, spi do)
gp4    --- dq2 (数据io2,spi wp#)
gp22   --- dq0 (数据io0,spi di)
gp24   --- clk (时钟信号)
gp23   --- dq3 (数据io3,spi hold#)
打开sdk v2.0目录下的工程libam_bsp,找到文件am_bsp_pins.h,按照上面分配的管脚修改定义:
再在同一工程里面找到am_bsp_pins.c文件,逐一修改以上管脚对应的属性结构体,如下:
如果不需要修改其他外设接口的管脚定义,保存文件后退出编辑模式,sdk v2.0下的示例代码默认包含bsp和hal库文件进行编译,所以,这里需要重新将libam_bsp工程进行重新编译,假设我们使用keil mdk编译器,那么生成的库文件为libam_bsp.lib,可以直接包含到其他的keil mdk示例工程下进行使用。iar生成的库文件为libam_bsp.a。
4、了解复旦微 spi flash器件fm25q128的基本特性
fm25q128是复旦微电子生产的串行spi接口nor flash存储器,支持单线spi, 双线dual spi和四线quad spi(qpi)模式,我们采用的是quad spi,即器件的qpi模式(4-4-4)。
fm25q128的容量为128mbit(16mb),单个sector扇区大小为4kb,支持32kb或者64kb大小的block块,可编程页大小为256字节。sector扇区,block块大小和数量的宏定义详见如下代码:
我们采用的芯片封装和引脚定义如下图3所示;
图3 fm25q125芯片引脚定义图 
请特别注意管脚wp#和hold#/reset#(dq3)的描述
出厂时,qe位为0,芯片处于单线spi模式,硬件上,芯片的pin3为wp#信号,pin7为hold#/reset#信号,继续了解wp#和hold#硬件关键的特点,
spi模式下,如果wp#管脚为低,则禁止改写status状态寄存器的值;当hold#为低时,即使芯片cs片选脚被激活,芯片仍将进入暂停模式,spi总线无法正常访问spi flash,包括读取status状态寄存器的值,芯片相当于进入了复位状态。因为芯片出厂默认模式是单线spi模式,因此,我们需要先通过apollo3的mspi接口的单线spi模式(1-1-1)来初始化器件,让器件进入qpi模式(4-4-4)后,然后再使用quad spi进行访问spi flash,以实现利用最高的效率访问spi flash外设。spi的不同接口类型在传输指令码、地址码、数据码时所使用的传输通道数量不同,如下图4所示:
图 4 spi不同接口类型的区别
fm25q128上电初始化流程如下图所示,要使能芯片的qpi模式,需要使能状态寄存器status register-2中的bit1位qe,然后使用0x38命令使能进入qpi模式。退出qpi模式可以发送0xff命令或者发送软件复位序列0x66 + 0x99,如下图5所示。
图5 fm25q125上电初始化流程
如上所述,初次上电后,flash芯片处于单线spi模式,wp#和hold#管脚可能影响状态寄存器的写入操作,实际调试底层驱动代码的过程中,笔者也发现了这个问题,由于hold#没有外接上拉电阻,在spi模式下flash芯片的hold#脚电平不稳,导致读写状态寄存器时而正常,时而失败,开发板正常,客户的板子失败等现象,调试了很长时间才发现问题根源在于此。我们可以将apollo3芯片的mspi接口配置成spi模式,并将连接到fm25q128的dq2(wp#)和dq3(hold#/reset#)的管脚gp4和gp23配置成gpio输出,使能内部上拉电阻,并且同时输出高电平,让wp#和hold#管脚处于非激活状态。
我们先通过阅读datasheet来了解一下apollo3的gpio口结构,以及内部上拉电阻的配置。
apollo3的所有的gpio都可以通过软件来配置(padnpull)使能内部上拉电阻(除了gpio20为下拉),上拉电阻默认状态是禁用的。另外,包含有i2c功能的gpio的内部上拉电阻是可以选择四种不同阻值的。我们用到的gp4和gp23不包含i2c功能,因此不能选择上拉电阻的阻值。经过与ambiqmicro原厂工程师沟通,了解到内部通用上拉电阻的阻值大约为63kω,这个内部的弱上拉足可以保证在spi模式初始化spi flash时,wp#和hold#/reset#管脚稳定为高电平状态。参考代码如下:
#define am_bsp_gpio_gp23   23
#define am_bsp_gpio_gp4    4
const am_hal_gpio_pincfg_t g_am_bsp_gpio_gp23_hold =
{
    .ufuncsel            = am_hal_pin_23_gpio,
    .edrivestrength      = am_hal_gpio_pin_drivestrength_8ma,
    .epullup             = am_hal_gpio_pin_pullup_weak,
.egpoutcfg           = am_hal_gpio_pin_outcfg_pushpull,
.egpinput            = am_hal_gpio_pin_input_none
};
const am_hal_gpio_pincfg_t g_am_bsp_gpio_gp4_wp =
{
    .ufuncsel            = am_hal_pin_4_gpio,
    .edrivestrength      = am_hal_gpio_pin_drivestrength_8ma,
    .epullup             = am_hal_gpio_pin_pullup_weak,
.egpoutcfg           = am_hal_gpio_pin_outcfg_pushpull,
.egpinput            = am_hal_gpio_pin_input_none
};
static void configgp23asgpiooutputforpullhighholdpin (void)
{
        //
        // configure the pin as a push-pull gpio output.
        //
        am_hal_gpio_pinconfig(am_bsp_gpio_gp23, g_am_bsp_gpio_gp23_hold); 
        //
        // disable the output driver, and set the output value to the high level
        // state.  note that for apollo3 gpios in push-pull mode, the output
        // enable, normally a tri-state control, instead functions as an enable
        // for fast gpio. its state does not matter on previous chips, so for
        // normal gpio usage on apollo3, it must be disabled.
        //
        am_hal_gpio_state_write(am_bsp_gpio_gp23, am_hal_gpio_output_tristate_disable);
        am_hal_gpio_state_write(am_bsp_gpio_gp23, am_hal_gpio_output_set);   // pull high
}
static void configgp4asgpiooutputforpullhighwppin (void)
{
        //
        // configure the pin as a push-pull gpio output.
        //
        am_hal_gpio_pinconfig(am_bsp_gpio_gp4, g_am_bsp_gpio_gp4_wp);          //
        // disable the output driver, and set the output value to the high level
        // state.  note that for apollo3 gpios in push-pull mode, the output
        // enable, normally a tri-state control, instead functions as an enable
        // for fast gpio. its state does not matter on previous chips, so for
        // normal gpio usage on apollo3, it must be disabled.
        //
        am_hal_gpio_state_write(am_bsp_gpio_gp4, am_hal_gpio_output_tristate_disable);
        am_hal_gpio_state_write(am_bsp_gpio_gp4, am_hal_gpio_output_set);   // pull high
}
代码详见sdk v2.0目录下的示例工程mspi_quad_example,文件am_devices_mspi_flash.c。
5、  在sdk v2.0示例代码mspi_quad_example的基础上添加fm25q128的支持
am_devices_mspi_flash.h文件中增加器件型号宏定义:#define fudan_fm25q128
以及器件支持的相关操作命令,id码以及容量宏定义
#if defined (fudan_fm25q128)
//*******************************************************************
//
// device specific identification.
//
//*******************************************************************
#define am_devices_mspi_flash_id                        0x001840a1
#define am_devices_mspi_flash_id_mask                   0x00ffffff
//*******************************************************************
//
// device specific definitions for flash commands
//
//*******************************************************************
#define am_devices_mspi_flash_read_status_register1     0x05    // rdsr1, read status_register 1
#define am_devices_mspi_flash_read_status_register2     0x35    // rdsr2, read status_register 2
#define am_devices_mspi_flash_read_status_register3     0x15    // rdsr3, read status_register 3
#define am_devices_mspi_flash_write_status_register1    0x01    // wrsr1, write status_register 1
#define am_devices_mspi_flash_write_status_register2    0x31    // wrsr2, write status_register 2
#define am_devices_mspi_flash_write_status_register3    0x11    // wrsr3, write status_register 3
#define am_devices_mspi_flash_enable_qpi_mode           0x38    // eqio, enable qpi
#define am_devices_mspi_flash_disable_qpi_mode          0xff    // disable qpi
#define am_devices_mspi_flash_enable_reset              0x66    // enable reset
#define am_devices_mspi_flash_reset                     0x99    // reset
#define am_devices_mspi_flash_write_enable_volatile     0x50    // write enable for volatile status register
//*******************************************************************
//
// device specific definitions for the configuration register(s)
//
//*******************************************************************
#define am_devices_mspi_clear_status_register1          (0x00)   // the value to clear the status register-1
#define am_devices_mspi_enable_quad_status_register2    (0xf2)   // lc = 11b, dummy cycles = 4 for quad enable, 0 for spi mdode
#define am_devices_mspi_clear_status_register3          (0xfc)   // to clear the status register-3, the reserved bits will be bypassed
//*******************************************************************
//
// device specific definitions for the flash size information
//
//*******************************************************************
#define am_devices_mspi_flash_page_size                 0x100    // 256 bytes, minimum program unit
#define am_devices_mspi_flash_sector_size               0x1000   // 4k bytes
#define am_devices_mspi_flash_block_size                0x10000  // 64k bytes.
#define am_devices_mspi_flash_max_sectors               4096     // sectors within 3-byte address range. 4096 * 4kb = 16mb
#define am_devices_mspi_flash_max_blocks                256      // 256 * 64kb = 16mb
#endif
上电后初始化先清除sr1和sr3中的相应的状态位和错误标志位。
说明:状态寄存器sr2-3中的有些位标注为r,其为reserved bit,即保留位,不能被软件改写,建议采用先读出sr2-3的值,再使用与或操作置位或者清除可以改写的位,同时保留那些原有标注r的位的值。
am_devices_mspi_flash.c文件中增加对fm25q128支持的初始化代码:
spi接口初始化宏定义:
// configure the mspi for serial operation during initialization
am_hal_mspi_dev_config_t  serialce0mspiconfig =    // mspi serial ce0(spi mode)
{
    .espimode             = am_hal_mspi_spi_mode_0,
    .eclockfreq           = am_hal_mspi_clk_3mhz,   // lower speed rate for more stable initialization operation
#if defined(micron_n25q256a)
  .ui8turnaround        = 3,
#elif defined (cypress_s25fs064s)
  .ui8turnaround        = 3,
#elif defined (macronix_mx25u12835f)
  .ui8turnaround        = 8,
#elif defined (fudan_fm25q128)
  .ui8turnaround        = 8,  // the dummy clocks is 8 under spi mode by default. will update automatically
#endif
    .eaddrcfg             = am_hal_mspi_addr_3_byte,
    .einstrcfg            = am_hal_mspi_instr_1_byte,
    .edeviceconfig        = am_hal_mspi_flash_serial_ce0,
    .bseparateio          = true,
    .bsendinstr           = true,
    .bsendaddr            = true,
    .bturnaround          = true,
    .ui8readinstr         = am_devices_mspi_flash_fast_read,
    .ui8writeinstr        = am_devices_mspi_flash_page_program,
    .ui32tcbsize          = 0,
    .ptcb                 = null,
    .scramblingstartaddr  = 0,
    .scramblingendaddr    = 0,
};
上电后,选择将mspi配置为ce0,单线spi模式,各参数说明如下:
am_hal_mspi_spi_mode_0:  配置为spi mode
am_hal_mspi_clk_3mhz: 配置较低的spi速率提高稳定性和兼容性
.ui8turnaround = 8:芯片出厂lc默认值为00,对应的spi模式下dummy cycles为8
am_hal_mspi_addr_3_byte: 24位即3字节地址码
am_hal_mspi_instr_1_byte: 1字节指令码
am_hal_mspi_flash_serial_ce0:  单线spi模式,片选通道ce0
fm25q128初始化代码如下:
//*******************************************************************
//
// fudan fm25q128 support
// added by roger
//
//*******************************************************************
#if defined (fudan_fm25q128)
//
// device specific initialization function.
//
uint32_t am_device_init_flash(am_hal_mspi_dev_config_t *psmspisettings)
{
    uint32_t      ui32status = am_hal_status_success;
// workaround for the hold# pin bug
    // when read the spi flash device at the first time powered-on, the device will present as spi mode
    // if the wp# or hold# pin is tied directly to a low level (such as, connect to the ground) during standard spi
    // or dual spi operation, the qe bit should never be set to a 1.
    // pull the wp# and hold# pin of spi flash as a high level to prevent hold state
    configgp23asgpiooutputforpullhighholdpin();     // added by roger
    configgp4asgpiooutputforpullhighwppin();        // added by roger
#if 1
    //
    // reset the fudan fm25q128. using the existing api
    //
    if (am_hal_status_success != am_devices_mspi_flash_reset())
    {
        return am_devices_mspi_flash_status_error;
    }
// software reset code
    #else 
    ui32status = am_device_command_write(am_devices_mspi_flash_enable_reset, false, 0, g_piobuffer, 0);  // enable reset
    if (am_hal_status_success != ui32status)
    {
        return am_devices_mspi_flash_status_error;
    }
ui32status = am_device_command_write(am_devices_mspi_flash_reset, false, 0, g_piobuffer, 0);  // reset
    if (am_hal_status_success != ui32status)
    {
        return am_devices_mspi_flash_status_error;
    }
    #endif
am_util_delay_us(1000);  // for stable concern, delay 1000us after software reset
g_ui32sr1 = 0;
    g_ui32sr2 = 0;
    g_ui32sr3 = 0;
am_util_stdio_printf(dump out the status register 1 - 3 before configurating:\n\r);
ui32status = am_device_command_read(am_devices_mspi_flash_read_status_register1, false, 0, (uint32_t *)&g_ui32sr1, 1);
    am_util_stdio_printf(status register 1 is 0x%02x\n, (uint8_t)g_ui32sr1);
ui32status = am_device_command_read(am_devices_mspi_flash_read_status_register2, false, 0, (uint32_t *)&g_ui32sr2, 1);
    am_util_stdio_printf(status register 2 is 0x%02x\n, (uint8_t)g_ui32sr2);
ui32status = am_device_command_read(am_devices_mspi_flash_read_status_register3, false, 0, (uint32_t *)&g_ui32sr3, 1);
    am_util_stdio_printf(status register 3 is 0x%02x\n, (uint8_t)g_ui32sr3);
//
    // enable writing to the status/configuration register.
    //
    ui32status = am_device_command_write(am_devices_mspi_flash_write_enable, false, 0, g_piobuffer, 0);  // non-volatile
    //ui32status = am_device_command_write(am_devices_mspi_flash_write_enable_volatile, false, 0, g_piobuffer, 0);  // volatile
    if (am_hal_status_success != ui32status)
    {
        return am_devices_mspi_flash_status_error;
    }
//
    // configure the fudan fm25q128 status register-1.
    //
    g_piobuffer[0] = am_devices_mspi_clear_status_register1 ;  // write status register + data (0x00) --> clear
    ui32status = am_device_command_write(am_devices_mspi_flash_write_status_register1, false, 0, g_piobuffer, 1);  // sr1
    if (am_hal_status_success != ui32status)
    {
        return am_devices_mspi_flash_status_error;
    }
//
    // enable writing to the status/configuration register.
    //
    //ui32status = am_device_command_write(am_devices_mspi_flash_write_enable, false, 0, g_piobuffer, 0);  // non-volatile
    ui32status = am_device_command_write(am_devices_mspi_flash_write_enable_volatile, false, 0, g_piobuffer, 0); // volatile
    if (am_hal_status_success != ui32status)
    {
        return am_devices_mspi_flash_status_error;
    }
//
    // configure the fudan fm25q128 status register-3.
    //
    g_piobuffer[0] = g_ui32sr3 & ~am_devices_mspi_clear_status_register3;  
    ui32status = am_device_command_write(am_devices_mspi_flash_write_status_register3, false, 0, g_piobuffer, 1);
    if (am_hal_status_success != ui32status)
    {
        return am_devices_mspi_flash_status_error;
    }
// spi mode does not need to config the qe bit, move the qe setting to the quad spi initial code
    #if 0 
    //
    // enable writing to the status/configuration register.
    //
    ui32status = am_device_command_write(am_devices_mspi_flash_write_enable, false, 0, g_piobuffer, 0);  // non-volatile
    //ui32status = am_device_command_write(am_devices_mspi_flash_write_enable_volatile, false, 0, g_piobuffer, 0);  // volatile
    if (am_hal_status_success != ui32status)
    {
        return am_devices_mspi_flash_status_error;
    }
//
    // configure the fudan fm25q128 status register-2.
    //
    g_piobuffer[0] = (uint8_t)g_ui32sr2 | am_devices_mspi_enable_quad_status_register2;    // setting lc and enable qe(quad enable)
    ui32status = am_device_command_write(am_devices_mspi_flash_write_status_register2, false, 0, g_piobuffer, 1);
    if (am_hal_status_success != ui32status)
    {
        return am_devices_mspi_flash_status_error;
    }
    #endif
am_util_stdio_printf(dump out the status register 1 & 3 after having been overwritten:\n\r);
    g_ui32sr1 = 0;
    g_ui32sr2 = 0;
    g_ui32sr3 = 0;
ui32status = am_device_command_read(am_devices_mspi_flash_read_status_register1, false, 0, (uint32_t *)&g_ui32sr1, 1);
    am_util_stdio_printf(status register 1 is 0x%02x\n, (uint8_t)g_ui32sr1);
ui32status = am_device_command_read(am_devices_mspi_flash_read_status_register2, false, 0, (uint32_t *)&g_ui32sr2, 1);
    am_util_stdio_printf(status register 2 is 0x%02x\n, (uint8_t)g_ui32sr2);
ui32status = am_device_command_read(am_devices_mspi_flash_read_status_register3, false, 0, (uint32_t *)&g_ui32sr3, 1);
    am_util_stdio_printf(status register 3 is 0x%02x\n, (uint8_t)g_ui32sr3);
switch (psmspisettings->edeviceconfig)
    {
        case am_hal_mspi_flash_serial_ce0:
        case am_hal_mspi_flash_serial_ce1:
            // nothing to do.  device defaults to spi mode for initializing spi flash device. disable qpi mode
            ui32status = am_device_command_write(am_devices_mspi_flash_disable_qpi_mode, false, 0, g_piobuffer, 0); // disable qpi, return to spi
            if (am_hal_status_success != ui32status)
            {
                return am_devices_mspi_flash_status_error;
            }
            break;
        case am_hal_mspi_flash_dual_ce0:
        case am_hal_mspi_flash_dual_ce1:
            // device does not support dual mode.
            return am_devices_mspi_flash_status_error;
            //break;
        case am_hal_mspi_flash_quad_ce0:
        case am_hal_mspi_flash_quad_ce1:
        case am_hal_mspi_flash_quadpaired:
        case am_hal_mspi_flash_quadpaired_serial:
am_util_stdio_printf(check the qe bit in status register 2.\n);
            // check the qe bit in sr2
            if ( ((uint8_t)g_ui32sr2 & (1    {
        case am_hal_mspi_flash_serial_ce0:
        case am_hal_mspi_flash_serial_ce1:
            // nothing to do.  device defaults to spi mode.
            break;
        case am_hal_mspi_flash_dual_ce0:
        case am_hal_mspi_flash_dual_ce1:
            // device does not support dual mode.
            return am_devices_mspi_flash_status_error;
            //break;
        case am_hal_mspi_flash_quad_ce0:
        case am_hal_mspi_flash_quad_ce1:
        case am_hal_mspi_flash_quadpaired:
        case am_hal_mspi_flash_quadpaired_serial:
            ui32status = am_device_command_write(am_devices_mspi_flash_disable_qpi_mode, false, 0, g_piobuffer, 0);
            if (am_hal_status_success != ui32status)
            {
                return am_devices_mspi_flash_status_error;
            }
            break;
        case am_hal_mspi_flash_octal_ce0:
        case am_hal_mspi_flash_octal_ce1:
            return am_devices_mspi_flash_status_error;
            //break;
    }
return am_devices_mspi_flash_status_success;
}
#endif
uint32_t
am_devices_mspi_flash_init(am_hal_mspi_dev_config_t *psmspisettings, void **phandle)
{
    uint32_t      ui32status;
//
    // enable fault detection.
    //
#if am_apollo3_mcuctrl
    am_hal_mcuctrl_control(am_hal_mcuctrl_control_fault_capture_enable, 0);
#else
    am_hal_mcuctrl_fault_capture_enable();
#endif
//
    // configure the mspi for serial or quad-paired serial operation during initialization.
    //
    switch (psmspisettings->edeviceconfig)
    {
        case am_hal_mspi_flash_serial_ce0:           // select ce0 spi mode
        case am_hal_mspi_flash_dual_ce0:
        case am_hal_mspi_flash_quad_ce0:             // select ce0 quad mode
        case am_hal_mspi_flash_octal_ce0:
            g_psmspisettings = serialce0mspiconfig;  // configure the mspi for serial operation during initialization
if (am_hal_status_success != am_hal_mspi_initialize(am_devices_mspi_flash_mspi_instance, &g_pmspihandle))
            {
                am_util_stdio_printf(error - failed to initialize mspi.\n);
                return am_devices_mspi_flash_status_error;
            }
if (am_hal_status_success != am_hal_mspi_power_control(g_pmspihandle, am_hal_sysctrl_wake, false))
            {
                am_util_stdio_printf(error - failed to power on mspi.\n);
                return am_devices_mspi_flash_status_error;
            }
if (am_hal_status_success != am_hal_mspi_device_configure(g_pmspihandle, &serialce0mspiconfig))
            {
                am_util_stdio_printf(error - failed to configure mspi.\n);
                return am_devices_mspi_flash_status_error;
            }
            if (am_hal_status_success != am_hal_mspi_enable(g_pmspihandle))
            {
                am_util_stdio_printf(error - failed to enable mspi.\n);
                return am_devices_mspi_flash_status_error;
            }
            am_bsp_mspi_pins_enable(serialce0mspiconfig.edeviceconfig);  // set up the mspi pins for ce0
            break;
        case am_hal_mspi_flash_serial_ce1:
        case am_hal_mspi_flash_dual_ce1:
        case am_hal_mspi_flash_quad_ce1:
        case am_hal_mspi_flash_octal_ce1:
            g_psmspisettings = serialce1mspiconfig;
            if (am_hal_status_success != am_hal_mspi_initialize(am_devices_mspi_flash_mspi_instance, &g_pmspihandle))
            {
                am_util_stdio_printf(error - failed to initialize mspi.\n);
                return am_devices_mspi_flash_status_error;
            }
if (am_hal_status_success != am_hal_mspi_power_control(g_pmspihandle, am_hal_sysctrl_wake, false))
            {
                am_util_stdio_printf(error - failed to power on mspi.\n);
                return am_devices_mspi_flash_status_error;
            }
if (am_hal_status_success != am_hal_mspi_device_configure(g_pmspihandle, &serialce1mspiconfig))
            {
                am_util_stdio_printf(error - failed to configure mspi.\n);
                return am_devices_mspi_flash_status_error;
            }
            if (am_hal_status_success != am_hal_mspi_enable(g_pmspihandle))
            {
                am_util_stdio_printf(error - failed to enable mspi.\n);
                return am_devices_mspi_flash_status_error;
            }
            am_bsp_mspi_pins_enable(serialce1mspiconfig.edeviceconfig);  // set up the mspi pins for ce1
            break;
        case am_hal_mspi_flash_quadpaired:
            g_psmspisettings = quadpairedserialmspiconfig;
            if (am_hal_status_success != am_hal_mspi_initialize(am_devices_mspi_flash_mspi_instance, &g_pmspihandle))
            {
                am_util_stdio_printf(error - failed to initialize mspi.\n);
                return am_devices_mspi_flash_status_error;
            }
if (am_hal_status_success != am_hal_mspi_power_control(g_pmspihandle, am_hal_sysctrl_wake, false))
            {
                am_util_stdio_printf(error - failed to power on mspi.\n);
                return am_devices_mspi_flash_status_error;
            }
if (am_hal_status_success != am_hal_mspi_device_configure(g_pmspihandle, &quadpairedserialmspiconfig))
            {
                am_util_stdio_printf(error - failed to configure mspi.\n);
                return am_devices_mspi_flash_status_error;
            }
            if (am_hal_status_success != am_hal_mspi_enable(g_pmspihandle))
            {
                am_util_stdio_printf(error - failed to enable mspi.\n);
                return am_devices_mspi_flash_status_error;
            }
            am_bsp_mspi_pins_enable(quadpairedserialmspiconfig.edeviceconfig);  // set up the mspi pins
            break;
        case am_hal_mspi_flash_quadpaired_serial:
            return am_devices_mspi_flash_status_error;
            //break;
    }
if (am_hal_status_success != am_devices_mspi_flash_reset())  // software reset the external flash device
    {
        return am_devices_mspi_flash_status_error;
    }
// added by roger
    am_util_delay_us(1000);  // for stable concern, delay 1000us after software reset
//
    // device specific mspi flash initialization.
    // added by roger
    //
    ui32status = am_device_init_flash(psmspisettings);
    if (am_hal_status_success != ui32status)
    {
        am_util_stdio_printf(error - failed to initial device specific mspi flash.\n);
        return am_devices_mspi_flash_status_error;
    }
//
    // initialize the mspi settings for the spi flash.
    //
    g_psmspisettings = *psmspisettings;
// disable mspi before re-configuring it
    ui32status = am_hal_mspi_disable(g_pmspihandle);
    if (am_hal_status_success != ui32status)
    {
        return am_devices_mspi_flash_status_error;
    }
// added by roger
    #if defined (fudan_fm25q128)
    am_util_stdio_printf(status register 2 is 0x%02x.\n,(uint8_t)g_ui32sr2);
if ((g_psmspisettings.edeviceconfig == am_hal_mspi_flash_quad_ce0) || (g_psmspisettings.edeviceconfig == am_hal_mspi_flash_quad_ce1))
    {
        am_util_stdio_printf(mspi operation with quad spi mode.\n);
// added by roger, for updating the dummy clocks according to the lc setting under qpi mode
        switch (((uint8_t)g_ui32sr2 & 0xc0) >> 6)
        {
            case 0:                                   // lc = 00
                g_psmspisettings.ui8turnaround = 6;
                break;
            case 1:                                   // lc = 01
                g_psmspisettings.ui8turnaround = 8;
                break;
            case 2:                                   // lc = 10        
                g_psmspisettings.ui8turnaround = 10;
                break;
            case 3:                                   // lc = 11
                g_psmspisettings.ui8turnaround = 4;
                break; 
            default:
                g_psmspisettings.ui8turnaround = 6;
                break;
        }
    }
if ((g_psmspisettings.edeviceconfig == am_hal_mspi_flash_serial_ce0) || (g_psmspisettings.edeviceconfig == am_hal_mspi_flash_serial_ce1))
    {
        am_util_stdio_printf(mspi operation with spi mode.\n);
        // added by roger, for updating the dummy clocks according to the lc setting under spi mode
        switch (((uint8_t)g_ui32sr2 & 0xc0) >>6)
        {
            case 0:                                   // lc = 00
            case 1:                                   // lc = 01
            case 2:                                   // lc = 10        
                g_psmspisettings.ui8turnaround = 8;
                break;
            case 3:                                   // lc = 11
                g_psmspisettings.ui8turnaround = 0;
                break;
            default:
                g_psmspisettings.ui8turnaround = 8;
                break;             
        }
    }
am_util_stdio_printf(g_psmspisettings.ui8turnaround = 0x%02x.\n, g_psmspisettings.ui8turnaround);
    #endif
//
    // re-configure the mspi for the requested operation mode.
    //
    //ui32status = am_hal_mspi_device_configure(g_pmspihandle, psmspisettings);
    ui32status = am_hal_mspi_device_configure(g_pmspihandle, &g_psmspisettings);  // modified by roger
    if (am_hal_status_success != ui32status)
    {
        return am_devices_mspi_flash_status_error;
    }
    // re-enable mspi
    ui32status = am_hal_mspi_enable(g_pmspihandle);
    if (am_hal_status_success != ui32status)
    {
        return am_devices_mspi_flash_status_error;
    }
//
    // configure the mspi pins.
    //
    am_bsp_mspi_pins_enable(g_psmspisettings.edeviceconfig);  // set up the mspi pins
//
    // enable mspi interrupts.
    //
#if mspi_use_cq
ui32status = am_hal_mspi_interrupt_clear(g_pmspihandle, am_hal_mspi_int_cqupd | am_hal_mspi_int_err );
    if (am_hal_status_success != ui32status)
    {
        return am_devices_mspi_flash_status_error;
    }
ui32status = am_hal_mspi_interrupt_enable(g_pmspihandle, am_hal_mspi_int_cqupd | am_hal_mspi_int_err );
    if (am_hal_status_success != ui32status)
    {
        return am_devices_mspi_flash_status_error;
    }
#else
    ui32status = am_hal_mspi_interrupt_clear(g_pmspihandle, am_hal_mspi_int_err | am_hal_mspi_int_dmacmp | am_hal_mspi_int_cmdcmp );
    if (am_hal_status_success != ui32status)
    {
        return am_devices_mspi_flash_status_error;
    }
ui32status = am_hal_mspi_interrupt_enable(g_pmspihandle, am_hal_mspi_int_err | am_hal_mspi_int_dmacmp | am_hal_mspi_int_cmdcmp );
    if (am_hal_status_success != ui32status)
    {
        return am_devices_mspi_flash_status_error;
    }
#endif
#if am_cmsis_regs
    nvic_enableirq(mspi_irqn);
#else // am_cmsis_regs
    am_hal_interrupt_enable(am_hal_interrupt_mspi);
#endif // am_cmsis_regs
am_hal_interrupt_master_enable();
//
    // return the handle.
    //
    *phandle = g_pmspihandle;
//
    // return the status.
    //
    return am_devices_mspi_flash_status_success;
}
至此,fm25q128的底层驱动代码与sdk中的mspi外设api已经对接完成,我们可以操作以下的api对flash进行擦除扇区,擦除块,擦除全片,读,写等测试。
//spi flash初始化
uint32_t
am_devices_mspi_flash_init(am_hal_mspi_dev_config_t *psmspisettings, void **phandle);
// 获取spi flash的器件id码
uint32_t
am_devices_mspi_flash_id(void);
// spi flash扇区擦除。注意:实际上是调用的block块擦除命令0xd8,擦除单位为64kb
uint32_t
am_devices_mspi_flash_sector_erase(uint32_t ui32sectoraddress);
// spi flash全片擦除
uint32_t am_devices_mspi_flash_mass_erase(void);
// 读取spi flash内容
uint32_t
am_devices_mspi_flash_read(uint8_t *pui8rxbuffer,
                           uint32_t ui32readaddress,
                           uint32_t ui32numbytes,
                           bool bwaitforcompletion);
// spi flash写入内容
uint32_t
am_devices_mspi_flash_write(uint8_t *pui8txbuffer,
                            uint32_t ui32writeaddress,
                            uint32_t ui32numbytes);
spi flash的相关api操作demo详见mspi_quad_example工程下的mspi_quad_example.c文件。注意:开启宏定义#define test_quad_spi,即为测试quad spi模式读写spi flash;如果注释掉该宏,则为单线spi模式读写spi flash。spi flash操作成功后,通过j-link的swo输出log信息显示操作结果,截图如下:
后记:
整理一下调试过程中遇到的一些坑:
1、sdk v2.0与v1.2.12启动代码startup_keil.s作了改动,直接将sdk v1.2.12中调试好的驱动代码放到v2.0中进行编译,导致无法进入mspi中断服务程序,读取flash内容时卡死,原因是sdk v2.0中把mspi中断服务函数名称修改了:void am_mspi0_isr(void)
2、新的片子fm25q128在初始化过程中无法正常读取和修改状态寄存器,这是因为芯片默认qe=0,芯片处于单线spi模式,pin3为wp#脚,pin7位hold#/reset#脚,由于pin7电压不稳定,或者处于低电平状态,导致芯片进入reset状态,mspi无法正确访问flash状态寄存器,导致读写失败!初始化为spi后,将mcu连接到spi flash的pin3和pin7的io设置为gpio输出并使能内部上拉,输出高电平,初始化spi flash操作过程中,拉高wp#和hold#/reset#管脚,让flash芯片处于非写保护和reset/hold状态。代码片段示例如下:

Facebook将推出一款可以用人脑控制电脑的手环
智原科技针对网络摄影机和网络电视推出SoC设计平台
美法院裁决暂缓实施将TikTok下架
正运动部件库导入步骤 正运动部件库数据查看指南
诺基亚X5评测 一款性价比较高的手机
【技术分享】Apollo3 SDK平台添加支持复旦微FM25Q128 SPI Flash的方法
共达地自研AutoML,实现AI算法自动化生产
语音通信,汉云通信自动通知,多维度精准预估到达率/接收率
英威腾光伏亮相英国Solar & Storage Live 2023
运营商重兵布局智慧家庭行业,探索智慧家庭发展模式
现代汽车发动机电控技术
DapuStor与星辰天合分布式存储产品完成兼容性互认证
USB设备枚举过程
电子芯闻早报:希捷再裁员6500人 骁龙821首发竟是它!
大数据与病毒的殊死较量
深圳三元锂电池定制多少钱?
国内车联网应用市场如何 实现真正的自动驾驶应该具备什么
第88届中国电子展:智能制造与工业4.0推动电子产业提升
使用手机控制所有智能家居的生活你期待吗?
回顾2018机器人TOP10及其技术