赞
踩
**
**
最近在调试STM32H750片子,担心片内flash不够用,在QSPI bank2外挂了 W25Q40CL做XIP,当然也可以copy到片上ram运行。总结途中遇到些问题,避免新手少走弯路。
本例程基于原子哥的“STM32H750_W25Q256”例程修改而来,首先在这里非常感谢原子哥的分享!
这里主要实现的工作有:
1)新建appsystem工程,运行地址在0x90000000
2)通过keil把appsystem在线烧录进W25Q40CL,需要制作烧录算法工具
3)新建bootloader工程,并用bootloader启动appsystem,在片外W25Q40CL运行(XIP,不可仿真)
4)用bootLoader从片外W25Q40CL复制appsystem到片内ram,并启动在ram运行(可仿真)
其中第四点作为补充。
**
(一、)新建appsystem工程(appsystem)
**
这就比较简单,直接上图:
**
(二、)制作烧录算法工具(Keil_Flash_W25Q40)
**
本人一向比较懒,喜欢拿来主义,之前没使用过类似flash,首先想到是在www上寻找相关例程,下载回来简单修改端口等参数即可使用,所以一搜索即找到了原子哥的例程,心情如雨后阳光明媚又灿烂!开始依葫芦画瓢:
1)修改端口CS/CLK/IO0~IO3
2)修改为bank2
3)修改QSPI时钟
4)修改存储容量大小
5)修改时钟空闲时候极性(可选)
//初始化QSPI接口 //返回值:0,成功; // 1,失败; u8 QSPI_Init(void) { #if 1 // 这里用的是QSPI bank2 // CS: PC11 // CLK: PB2 // IO0: PE7 // IO1: PE8 // IO2: PE9 // IO3: PE10 u32 tempreg=0; RCC->AHB4ENR|=1<<0; //使能GPIOA时钟 RCC->AHB4ENR|=1<<1; //使能GPIOB时钟 RCC->AHB4ENR|=1<<2; //使能GPIOC时钟 RCC->AHB4ENR|=1<<3; //使能GPIOD时钟 RCC->AHB4ENR|=1<<4; //使能GPIOE时钟 RCC->AHB3ENR|=1<<14; //QSPI时钟使能 // led PA3 GPIO_Set(GPIOA,1<<3,GPIO_MODE_OUT,GPIO_OTYPE_PP,GPIO_SPEED_HIGH,GPIO_PUPD_PU); GPIO_Pin_Set(GPIOA, 1 << 3, 1); // RS485_TX PC7 GPIO_Set(GPIOC,1<<7,GPIO_MODE_OUT,GPIO_OTYPE_PP,GPIO_SPEED_HIGH,GPIO_PUPD_PU); GPIO_Pin_Set(GPIOC, 1 << 7, 1); GPIO_Pin_Set(GPIOC, 1 << 7, 1); // BANK2 //PB2 QUADSPI1_CLK GPIO_Set(GPIOB,1<<2,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_HIGH,GPIO_PUPD_PU); //PC11 QUADSPI1_BK2_NCS GPIO_Set(GPIOC,1<<11,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_HIGH,GPIO_PUPD_PU); // 这里一定要PP, OD即使上拉驱动能力不够 //PE7, PE8, PE9, PE10 QUADSPI1_BK2_IO0, IO1, IO2, IO3 GPIO_Set(GPIOE,0x0F << 7,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_HIGH,GPIO_PUPD_NONE); // Table 8. Port A alternate functions GPIO_AF_Set(GPIOB,2,9); //PB2,AF9 GPIO_AF_Set(GPIOC,11,9); //PC11,AF9 GPIO_AF_Set(GPIOE,7,10); //PE7,AF10 GPIO_AF_Set(GPIOE,8,10); //PE8,AF10 GPIO_AF_Set(GPIOE,9,10); //PE9,AF10 GPIO_AF_Set(GPIOE,10,10); //PE10,AF10 RCC->AHB3RSTR|=1<<14; //复位QSPI RCC->AHB3RSTR&=~(1<<14); //停止复位QSPI if(QSPI_Wait_Flag(1<<5,0,0XFFFF)==0)//等待BUSY空闲 { //QSPI时钟默认来自rcc_hclk3(由RCC_D1CCIPR的QSPISEL[1:0]选择) tempreg=(3-1)<<24; //设置QSPI时钟为AHB时钟的1/2,即200M/2=100Mhz,10ns // 240/3:80MHz tempreg|=(4-1)<<8; //设置FIFO阈值为4个字节(最大为31,表示32个字节) tempreg|=1<<7; // 0:选择FLASH1, 1:选择FLASH2 tempreg|=0<<6; //禁止双闪存模式 tempreg|=1<<4; //采样移位半个周期(DDR模式下,必须设置为0) QUADSPI->CR=tempreg; //设置CR寄存器 tempreg=(19-1)<<16; //设置FLASH大小为2^25=32MB tempreg|=(5-1)<<8; //片选高电平时间为5个时钟(10*5=50ns),即手册里面的tSHSL参数 tempreg|=1<<0; //Mode3,空闲时CLK为高电平 QUADSPI->DCR=tempreg; //设置DCR寄存器 QUADSPI->CR|=1<<0; //使能QSPI }else return 1; return 0; #else u32 tempreg=0; RCC->AHB4ENR|=1<<1; //使能PORTB时钟 RCC->AHB4ENR|=1<<5; //使能PORTF时钟 RCC->AHB3ENR|=1<<14; //QSPI时钟使能 GPIO_Set(GPIOB,1<<2,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_HIGH,GPIO_PUPD_PU); //PB2复用功能输出 GPIO_Set(GPIOB,1<<6,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_HIGH,GPIO_PUPD_PU); //PB6复用功能输出 GPIO_Set(GPIOF,0XF<<6,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_HIGH,GPIO_PUPD_PU); //PF6~9复用功能输出 GPIO_AF_Set(GPIOB,2,9); //PB2,AF9 GPIO_AF_Set(GPIOB,6,10); //PB6,AF10 GPIO_AF_Set(GPIOF,6,9); //PF6,AF9 GPIO_AF_Set(GPIOF,7,9); //PF7,AF9 GPIO_AF_Set(GPIOF,8,10); //PF8,AF10 GPIO_AF_Set(GPIOF,9,10); //PF9,AF10 RCC->AHB3RSTR|=1<<14; //复位QSPI RCC->AHB3RSTR&=~(1<<14); //停止复位QSPI if(QSPI_Wait_Flag(1<<5,0,0XFFFF)==0)//等待BUSY空闲 { //QSPI时钟默认来自rcc_hclk3(由RCC_D1CCIPR的QSPISEL[1:0]选择) tempreg=(2-1)<<24; //设置QSPI时钟为AHB时钟的1/2,即200M/2=100Mhz,10ns tempreg|=(4-1)<<8; //设置FIFO阈值为4个字节(最大为31,表示32个字节) tempreg|=1<<7; // 0:选择FLASH1, 1:选择FLASH2 tempreg|=0<<6; //禁止双闪存模式 tempreg|=1<<4; //采样移位半个周期(DDR模式下,必须设置为0) QUADSPI->CR=tempreg; //设置CR寄存器 tempreg=(25-1)<<16; //设置FLASH大小为2^25=32MB tempreg|=(5-1)<<8; //片选高电平时间为5个时钟(10*5=50ns),即手册里面的tSHSL参数 tempreg|=1<<0; //Mode3,空闲时CLK为高电平 QUADSPI->DCR=tempreg; //设置DCR寄存器 QUADSPI->CR|=1<<0; //使能QSPI }else return 1; return 0; #endif }
针对硬件修改好端口,打开宏W25Q40CL并编译得出文件 Keil_Flash_W25Q40/STM32H750_W25Q40.FLM,满怀希望的把文件STM32H750_W25Q40.FLM复制到C:\Keil_v5\ARM\Flash\目录下,打开appsystem工程选择下载选项:
把原来默认STM32H750xx算法删掉,换成STM32H750_W25Q40
忐忑的点击download开始下载appsystem到W25Q40CL!成败在此一举! 事实证明,侥幸的心很难找到灵魂的归宿!
下载失败了!
首先,寻找硬件问题:
1)是不是端口配置错误?电源是否正常?(再次确认无误)
2)是不是端口物理连接不通?(配置成IO口输出脉冲,用示波器测量,全部畅通无阻)
3)是否驱动能力不足必须外加上拉?(上电默认是SPI模式,WP和HOLD应该需要外上拉稳定些,为了确保全部外加4.7K上拉)
再次尝试,依然Download failed!!! 此时测量:
CS:有循环的拉低片选
CLK:有对应CS片选的时钟信号80MHz正确
IO0:有对应CS高低跳变(且认为是可用信号)
IO1:有对应CS高低跳变(且认为是可用信号)
IO2:一直保持高电平(且认为是不可用信号)对应pin WP,怀疑没配置为QSPI
IO3:一直保持高电平(且认为是不可用信号)对应pin HOLD,怀疑没配置为QSPI
接着,进而看W25QXX初始化工作:发现读取flash id一直不成功!以后所有操作都不成功,导致一直在检测忙状态。
其中,为了正确读取ID,折腾了不少功夫。原子哥例程是W25Q256(32MB),我用的是W25Q40CL(512KB),我很愿意相信两者命令兼容的!我都怀疑是我的flash芯片挂掉了?为了心里那点侥幸,果断换一片flash,结果不用猜,依旧。
此时万般无奈,仅剩下没照对手册命令了(最烦人的,所以一直没有做),但是到了此步田地,除了一条条对照手册命令,别无他求!果然,一对照两者有些不兼容地方!
W25Q40CL操作总结大体如下:
1)没有复位命令
2)单线读取ID
3)只有两个状态寄存器,其写使能命令不同,且两个状态寄存器要一起写
4)并无特殊进入QSPI的命令,只需写状态寄存器的EQ位即刻进入QSPI
既然确定要对照命令表,那我们就要列出我们所用到的命令:
1)读取ID命令:0x90
2)读取状态寄存器命令:S1:0x05;S2:0x35
3)写入状态寄存器使能命令:0x50
4)写入状态寄存器命令:0x01
5)退出QSPI命令:0xFF (进入QSPI是写S2的EQ)
6)扇区擦除命令:0x20
7)整片擦除命令:0xC7
8)扇区写入命令:0x32 (FastReadQuad)
9)扇区读取命令:0x6B (PageProgramQuad)
STM32H750要配置的寄存器:
QUADSPI->CCR、QUADSPI->AR、QUADSPI->FCR
认真对照以上每一条命令的配置并用示波器测量输出信号,确保和手册相符,那恭喜你,工作已经完成一大半了!
提示下载成功,但是否真的把数据正确的写入了呢?虽然下载选项有勾选 Verify,但这里肯定还是要主动回读验证了(此处省略)。接着就要把appsystem运行起来。
**
(三、) bootloader引导运行appsystem(bootloader)
**
STM32H750不支持从外部flash启动,但可以在外部flash运行程序(XIP),所以必须要片内flash做个bootloader引导跳转到外部flash运行程序,相信同学们都很熟悉了。要注意的这里跳转到外部flash地址(0x90000000)之前先初始化QSPI,并设置为内存映射模式(可直接访问,硬件机制帮我们发送读取命令,此模式只读不可写)。上码:
#ifdef APPLICATION_ADDRESS_SRAM12 u8 appXIPBuff[0x40000] __attribute__((section(".ARM.__at_0x30000000"))); // 256K #endif void GoToUserSystem(void) { pFunction Jump_To_Application; u32 JumpAddress; _DI(); #ifdef APPLICATION_ADDRESS JumpAddress = *(volatile u32*)(EXT_FLASH_ADDRESS+4); Jump_To_Application = (pFunction) JumpAddress; __set_PSP(*(volatile u32*) EXT_FLASH_ADDRESS); __set_CONTROL(0); //设置使用主堆栈指针 //Initialize user application's Stack Pointer __set_MSP(*(volatile u32*) EXT_FLASH_ADDRESS); #elif APPLICATION_ADDRESS_SRAM12 JumpAddress = *(volatile u32*)(XIP_SRAM12_ADDRESS+4); Jump_To_Application = (pFunction) JumpAddress; __set_PSP(*(volatile u32*) XIP_SRAM12_ADDRESS); __set_CONTROL(0); //设置使用主堆栈指针 //Initialize user application's Stack Pointer __set_MSP(*(volatile u32*) XIP_SRAM12_ADDRESS); #else JumpAddress = *(volatile u32*)(UserProgramAddressEntry+4); Jump_To_Application = (pFunction) JumpAddress; __set_PSP(*(volatile u32*) UserProgramAddressEntry); __set_CONTROL(0); //设置使用主堆栈指针 //Initialize user application's Stack Pointer __set_MSP(*(volatile u32*) UserProgramAddressEntry); #endif __DSB(); __ISB(); Jump_To_Application(); } int main(void) { MPU_Config(); CPU_CACHE_Enable(); NVIC_SetPriorityGrouping(MY_NVIC_PRIORITYGROUP); SystemClock_Config(); LED_Init(); uart1_init(115200); myprintf("\r\nboot:%s,%s\r\n", __DATE__, __TIME__); W25QXX_Init(); // 4线传输数据; 24位地址; 4线传输地址; 1线传输指令; 4周期 QSPI_Config_Mmap(0xEB, 0x03, (3 << 6) | (2 << 4) | (3 << 2) | (1 << 0), 4); myprintf("QSPI_Config_Mmap\r\n"); #ifdef APPLICATION_ADDRESS_SRAM12 memcpy(appXIPBuff, (u8*)(EXT_FLASH_ADDRESS), 0x40000); // 256K #endif myprintf("goto SYSTEM\r\n"); GoToUserSystem(); while (1) { Delayus(1000*1000); LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_3); myprintf("boot\r\n"); } } void W25QXX_Init(void) { u16 k = 0; QSPI_Init(); //初始化QSPI W25QXX_TYPE=W25QXX_ReadID(); //读取FLASH ID. //必须1 Line printf("ID:%X\r\n",W25QXX_TYPE); if(W25QXX_TYPE == W25Q40) //SPI FLASH为W25Q40=0xEF12 { W25QXX_Qspi_Enable(); //使能QSPI模式 } } void QSPI_Config_Mmap(u8 cmd,u32 fmode,u8 mode,u8 dmcycle) { u32 tempreg=0; if(QSPI_Wait_Flag(1<<5,0,0XFFFF)==0) //等待BUSY空闲 { //printf("QSPI_Send_CMD:0x%X add:0x%X mode:0x%X cycle:%d\r\n",cmd, addr, mode, dmcycle); tempreg=0<<31; // 0:禁止DDR模式 tempreg|=0<<28; // 0:每次都发送指令 tempreg|=fmode<<26; // 0:间接写模式3:Memory-mapped mode tempreg|=((u32)mode>>6)<<24; //设置数据模式 tempreg|=(u32)dmcycle<<18; //设置空指令周期数 tempreg|=(u32)QSPI_ALTERNATE_BYTES_8_BITS<<16; tempreg|=(u32)QSPI_ALTERNATE_BYTES_4_LINE<<14; tempreg|=((u32)(mode>>4)&0X03)<<12; //设置地址长度 tempreg|=((u32)(mode>>2)&0X03)<<10; //设置地址模式 tempreg|=((u32)(mode>>0)&0X03)<<8; //设置指令模式 tempreg|=cmd; //设置指令 QUADSPI->CCR=tempreg; //设置CCR寄存器 } }
到这里,工程appsystem就可以把镜像烧录到W25Q40CL,工程bootloader把镜像烧录进片内128K flash,复位系统就能运行bootloader并跳转到片外flash地址0x90000000运行appsystem程序了,此时用示波器测量W25Q40CL的数据或者时钟,你会发现一直在读取。这个时候你会不会想,真TM累,cpu本来就已经够累的了,还要每次从外面觅食(读取外部falsh获取程序),主要是这通道太慢了,只有80MHz clock,就算W25Q40CL可以支持104MHz clock依然远远不够啊!对了!可以搬运到片内ram运行!
**
(四、)把片外程序搬运到片内ram运行
**
这样既可以把appsystem下载到W25Q40CL,又可以仿真appsystem!
1)准备SRAM1和SRAM2共256KB空间做代码运行区0x30000000~0x30040000
2)appsystem编译地址选择0x30000000,下载选项不变,依然选STM32H750_W25Q40,首地址0x90000000改为0x30000000
3)bootloader修改宏为APPLICATION_ADDRESS_SRAM12
片内RAM运行效果图:
仿真如图:
这几天调试,拿来主义确实方便,但也存在“懒人坑”,专坑像我这样的懒人,不过主要一步步往前推进,懒人坑也有懒人回填方法~
以上只是本人一些心得体会,难免错误,望各位大神指正,向大家学习!
稍后附上工程源码`
****************************************
这是首次编写CSDN博客,加上时间紧迫,匆匆而过~
****************************************
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。