上海润欣科技股份有限公司创研社
本文详细介绍了,如何在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及其技术