你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32F7-discovery QSPI接口使用心得  

[复制链接]
feiting94 发布时间:2015-9-8 22:09
STM32电机培训online,大佬带你玩电机

FMC和QSPI引脚冲突的解决

分享一个 QSPI N25Q256A的读写程序,支持QUAD, 4字节模式


1.QuadSPI接口的特点。与普通的SPI Flash接口相比,quadSPI可以接四位数据线,传输速率大大提高
STM32F7的quad-spi接口有三种模式
(1)indirect mode(间接模式):所有操作都是用的QUADSPI寄存器,通常在对FLASH寄存器配置时用这种模式
(2)status polling mode(状态轮询模式):外部Flash的状态寄存器查询使用的是这种模式,如果开启中断,可以产生中断信号
(3)memory-mapped mode(内存映射模式):外部flash映射到MCU的地址空间,可以视为内部闪存,读写数据用的这种模式

STM32F7的quad-spi接口主要特点:
(1)三种工作模式
(2)Dual-Flash模式,可以同时接两片Flash,共用CLK和CS片选线。这样可以最多同时传输8位数据(4+4)
(3)支持SDR和DDR
(4)间接模式的DMA通道
(5)内嵌接收和发送FIFO
(6)支持FIFO threshold, timeout, operation complete, access error四种中断

Quad-spi完整的命令格式由5部分组成,分别是Instruction,Address,Alternate-bytes,dummy-cycles和Data这5个阶段,时序图如图所示
3.png
总结一下特点:
(1)每个阶段都可以选择是 1bit(SO/SI线 single SPI mode),2bit(IO0/IO1线 dual SPI mode),和4bit(IO0/IO1/IO2/IO3线 quad SPI mode)传输,
(2)写数据时,dummy cycle可以为0;读数据时,为了保证足够的转换时间,因为之前是写数据,现在要变成读,至少要1个dummy cycle
(3)这5个阶段都不是必须的,可以没有
(4)indirect mode模式,数据读取时通过QUADSPI_DR寄存器;memory-mapped mode模式,数据直接返回和输出通过AHB总线或者DMA
(5)SDR和DDR模式的区别:两者的instruction阶段都是CLK信号的下降沿数据传输;在DDR模式中,Address,Alternate-bytes和Data这3个阶段都是上升沿和下降沿都有数据传输
(6)F7有32-byte FIFO,可以设置threshold(阈值),接收数据数目超过该值时,FTF(FIFO threshold flag)=1



3.STM32F7-Discovery的quad-spi flash使用的是micron公司的N25Q128A系列,有128Mbit容量,后面附上数据手册,原理图如图所示

1.png
这里主要说明quad-spi flash代码流程分析
论坛可以下载STM32Cube_FW_F7_V1.1.0压缩包,我也是从里面的例程中学习的。
选择STM32Cube_FW_F7_V1.1.0/project/STM32746G-Discovery/example/QSPI/QSPI_ExecuteInPlace例程
(1)Flash配置寄存器初始化
  1.   /* Initialize QuadSPI ------------------------------------------------------ */
  2.   QSPIHandle.Instance = QUADSPI;         
  3.   HAL_QSPI_DeInit(&QSPIHandle);
  4.         
  5.   /* ClockPrescaler set to 2, so QSPI clock = 216MHz / (2+1) = 72MHz */
  6.   QSPIHandle.Init.ClockPrescaler     = 2;                                                                // <span style="background-color: rgb(255, 255, 255);">查阅手册可知,最大频率108MHz,这里为什么不用1呢??</span>
  7.   QSPIHandle.Init.FifoThreshold      = 4;                                                                 // FIFO的阈值为4bytes,
  8.   QSPIHandle.Init.SampleShifting     = QSPI_SAMPLE_SHIFTING_HALFCYCLE;
  9.   QSPIHandle.Init.FlashSize          = POSITION_VAL(0x1000000) - 1;                         //0x1000000=16MB,
  10.   QSPIHandle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_2_CYCLE;        //nCS stay high for at least 2 clock cycles between commands
  11.   QSPIHandle.Init.ClockMode          = QSPI_CLOCK_MODE_0;                                    //Clk stays low while nCS is released
  12.   QSPIHandle.Init.FlashID            = QSPI_FLASH_ID_1;                                               //选择第1片flash
  13.   QSPIHandle.Init.DualFlash          = QSPI_DUALFLASH_DISABLE;
  14.   
  15.   if (HAL_QSPI_Init(&QSPIHandle) != HAL_OK)
  16.   {
  17.     Error_Handler();
  18.   }
复制代码
(2)使能写操作
  1.   /* Enable write operations ------------------------------------------ */
  2.   sCommand.InstructionMode   = QSPI_INSTRUCTION_1_LINE;            
  3.   sCommand.Instruction       = WRITE_ENABLE_CMD;
  4.   sCommand.AddressMode       = QSPI_ADDRESS_NONE;
  5.   sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
  6.   sCommand.DataMode          = QSPI_DATA_NONE;
  7.   sCommand.DummyCycles       = 0;
  8.   sCommand.DdrMode           = QSPI_DDR_MODE_DISABLE;
  9.   sCommand.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
  10.   sCommand.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;

  11.   if (HAL_QSPI_Command(&QSPIHandle, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  12.   {
  13.     Error_Handler();
  14.   }
  15.   
  16.   /* Configure automatic polling mode to wait for write enabling ---- */
  17.   sConfig.Match           = 0x02;
  18.   sConfig.Mask            = 0x02;
  19.   sConfig.MatchMode       = QSPI_MATCH_MODE_AND;
  20.   sConfig.StatusBytesSize = 1;
  21.   sConfig.Interval        = 0x10;
  22.   sConfig.AutomaticStop   = QSPI_AUTOMATIC_STOP_ENABLE;

  23.   sCommand.Instruction    = READ_STATUS_REG_CMD;
  24.   sCommand.DataMode       = QSPI_DATA_1_LINE;

  25.   if (HAL_QSPI_AutoPolling(&QSPIHandle, &sCommand, &sConfig, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  26.   {
  27.     Error_Handler();
  28.   }
复制代码
首先使用indrect mode,使用1bit instruction传输,命令是 WRITE_ENABLE_CMD,无其他4个阶段
源码中定义   #define WRITE_ENABLE_CMD                     0x06;
这里要查阅芯片手册
5.png
确实是一致的。
接着使用automatic polling mode,命令是READ_STATUS_REG_CMD,读status register
automatic polling mode有如下功能,它将查询的得到的值和设置的Match值比较,只比较Mask中bit=1的位,设置可以为AND或者OR模式,
查阅手册:

6.png
确实是bit1,即mask=0x02,当该位为1时,说明写使能,故match=0x02;
(3)擦除flash的第一个sector
  1.         /* Erasing Sequence -------------------------------------------------- */
  2.         sCommand.Instruction = SECTOR_ERASE_CMD;   //块擦除,64KB one sector
  3.         sCommand.AddressMode = QSPI_ADDRESS_1_LINE;
  4.         sCommand.Address     = qspi_addr;                          //qspi_addr=0
  5.         sCommand.DataMode    = QSPI_DATA_NONE;
  6.         sCommand.DummyCycles = 0;

  7.         if (HAL_QSPI_Command_IT(&QSPIHandle, &sCommand) != HAL_OK)
  8.         {
  9.           Error_Handler();
  10.         }
复制代码
(4)等待擦除完毕,使用automatic polling mode查询,这里还是读status register,但读的是bit0位,而且预期值bit0=0;查阅手册
7.png
  1.   /* Configure automatic polling mode to wait for memory ready ------ */  
  2.   sCommand.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
  3.   sCommand.Instruction       = READ_STATUS_REG_CMD;
  4.   sCommand.AddressMode       = QSPI_ADDRESS_NONE;
  5.   sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
  6.   sCommand.DataMode          = QSPI_DATA_1_LINE;
  7.   sCommand.DummyCycles       = 0;
  8.   sCommand.DdrMode           = QSPI_DDR_MODE_DISABLE;
  9.   sCommand.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
  10.   sCommand.SIOOMode         = QSPI_SIOO_INST_EVERY_CMD;

  11.         //bit0:write in progress 0:ready 1:busy
  12.   sConfig.Match           = 0x00;
  13.   sConfig.Mask            = 0x01;
  14.   sConfig.MatchMode       = QSPI_MATCH_MODE_AND;
  15.   sConfig.StatusBytesSize = 1;
  16.   sConfig.Interval        = 0x10;
  17.   sConfig.AutomaticStop   = QSPI_AUTOMATIC_STOP_ENABLE;

  18.   if (HAL_QSPI_AutoPolling_IT(&QSPIHandle, &sCommand, &sConfig) != HAL_OK)
  19.   {
  20.     Error_Handler();
  21.   }
复制代码
(5)写使能并且写数据
  1. /* Enable write operations ----------------------------------------- */
  2.           QSPI_WriteEnable(&QSPIHandle);

  3.                                         //QUAD INPUT FAST PROGRAM Data In:DQ[3:0];Address In:DQ0
  4.           /* Writing Sequence ------------------------------------------------ */
  5.           sCommand.Instruction = QUAD_IN_FAST_PROG_CMD;
  6.           sCommand.AddressMode = QSPI_ADDRESS_1_LINE;
  7.           sCommand.Address     = qspi_addr;                                              //qspi_addr=0
  8.           sCommand.DataMode    = QSPI_DATA_4_LINES;
  9.           sCommand.NbData      = size;                                                         //size = QSPI_PAGE_SIZE = 256

  10.           if (HAL_QSPI_Command(&QSPIHandle, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  11.           {
  12.             Error_Handler();
  13.           }

  14.           if (HAL_QSPI_Transmit_DMA(&QSPIHandle, flash_addr) != HAL_OK)         //flash_addr = (uint8_t *)(&Load$QSPI$Base);
  15.           {
  16.             Error_Handler();
  17.           }
复制代码
看到这里就不得不提一下scatter file文件
8.png
打开这个文件,STM32746G-DISCOVERY.sct
  1. ; *************************************************************
  2. ; *** Scatter-Loading Description File generated by uVision ***
  3. ; *************************************************************

  4. LR_IROM1 0x08000000 0x00100000  {    ; load region size_region
  5.   ER_IROM1 0x08000000 0x00100000  {  ; load address = execution address
  6.    *.o (RESET, +First)
  7.    *(InRoot$Sections)
  8.    .ANY (+RO)
  9.   }
  10.   RW_IRAM1 0x20000000 0x00050000  {  ; RW data
  11.    .ANY (+RW +ZI)
  12.   }
  13.   QSPI 0x90000000 0x00100000  {
  14.    *.o (.qspi)
  15.   }
  16. }
复制代码
可以看到和一般的scatter file(分散加载描述文件)相比,多了一个QSPI执行域,
学过的都知道,这里表示链接时,将所有目标文件的.qspi段放在QSPI执行域,
再看一下F7的内存映射
9.png
在0x8000 0000到0x9FFF FFFF有一个Quad SPI,这就是memory-mapped mode的由来。
因此上面flash_addr = (uint8_t *)(&Load$$QSPI$$Base) = 0x9000 0000; DMA传输就是将 0x9000 0000处开始的size = 256bytes的数据传递到flash,模式是indrect read mode
(6)判断传输是否完成,判断的方法同步骤4
(7)重复5、6步直到传输完max_size = (uint32_t)(&Load$$QSPI$$Length)=  0x00100000
(8)设置dummy-cycle的值,配置为memory-mapped mode,命令是 QUAD_OUT_FAST_READ_CMD=0x6B,就是说映射完后,相对于内部flash
  1. /* Configure Volatile Configuration register (with new dummy cycles) */
  2.             QSPI_DummyCyclesCfg(&QSPIHandle);

  3.             /* Reading Sequence ------------------------------------------------ */
  4.             sCommand.Instruction = QUAD_OUT_FAST_READ_CMD;
  5.             sCommand.DummyCycles = DUMMY_CLOCK_CYCLES_READ_QUAD;

  6.             sMemMappedCfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE;

  7.             if (HAL_QSPI_MemoryMapped(&QSPIHandle, &sCommand, &sMemMappedCfg) != HAL_OK)
  8.             {
  9.               Error_Handler();
  10.             }
复制代码
(9)执行.qspi段的代码
  1. /* Execute the code from QSPI memory ------------------------------- */
  2.           GpioToggle();
复制代码
注意
  1. #if defined(__CC_ARM)
  2. #pragma arm section code = ".qspi"
  3. #pragma no_inline
  4. static void GpioToggle(void)
  5. #elif defined(__ICCARM__)
  6. static void GpioToggle(void) @ ".qspi"
  7. #elif defined(__GNUC__)
  8. static void __attribute__((section(".qspi"), noinline)) GpioToggle(void)
  9. #endif

  10. {
  11.   BSP_LED_Toggle(LED1);
  12.   /* Insert delay 200 ms */
  13.   HAL_Delay(200);
  14. }
复制代码

我的问题:
学完之后,我一直对一个问题感到困惑,再用st-link烧写程序时,这个.qspi段是不是放在内部flash中,只有在执行时,MCU才会到0x9000 0000处执行,这时MCU读flash,但是对我们来说是屏蔽的,我们可以直接当内部flash使用。如果前面没有执行DMA将0x9000 0000处数据传递到flash,后面是不是就不对了。这个时候为什么0x9000 0000处会有数据呢,实际上这里并没有flash啊,希望有人能帮忙解答一下
收藏 8 评论21 发布时间:2015-9-8 22:09

举报

21个回答
feiting94 回答时间:2015-9-9 12:00:44
netlhx 发表于 2015-9-9 09:14
这个位置就是映射到4G空间的FLASH的地址

请问一下,这个位置实际上并没有flash,那程序在执行步骤5,6,7,将这个位置的内容通过DMA写入到实际的flash上时,MCU是怎么怎么知道要传输数据的内容,毕竟映射的地址那里并没有flash。还有程序烧写进去时 .qspi段是放在哪里呢
党国特派员 回答时间:2015-9-9 09:35:02
学习了。。。 blank.png blank1.png blank2.png blank3.png blank4.png
feiting94 回答时间:2016-3-13 09:28:04
狂奔的小冉 发表于 2016-3-11 17:46
我想问一下,WRIRE ENABLE这些参数的说明是那份文档,我最近做F446,但是user manual没有Instruction的具体 ...

n25q_128mb_3v_65nm.pdf (1.11 MB, 下载次数: 133)
moyanming2013 回答时间:2015-9-8 23:30:21
先学习下。。。
你好我好大家好! 回答时间:2015-9-8 23:43:28
学习下                     
netlhx 回答时间:2015-9-9 09:14:44
这个位置就是映射到4G空间的FLASH的地址
yanhaijian 回答时间:2015-9-9 12:37:42
研究的比较细。
埃斯提爱慕 回答时间:2015-9-10 23:23:52
提示: 作者被禁止或删除 内容自动屏蔽
anny 回答时间:2015-9-17 15:03:36

支持楼主的原创分享
yanhaijian 回答时间:2015-9-17 15:21:05
学习了,顶顶。
狂奔的小冉 回答时间:2016-3-11 17:45:52
我想问一下,WRIRE ENABLE这些参数的说明是那份文档,我最近做F446,但是user manual没有Instruction的具体说明。谢谢
狂奔的小冉 回答时间:2016-3-11 17:46:10
我想问一下,WRIRE ENABLE这些参数的说明是那份文档,我最近做F446,但是user manual没有Instruction的具体说明。谢谢
狂奔的小冉 回答时间:2016-3-13 10:01:09

原来是在EEPROM的 datasheet里。。。谢谢了!
绝影 回答时间:2016-8-26 13:37:49
今天也在研究Qspi 在keil添加FLASH的界面可以添加外部的Qspi存储器,应该可以将程序下载进去
11223.png
12下一页
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版