完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
最近在调试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_v5ARMFlash目录下,打开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("rnboot:%s,%srn", __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_Mmaprn"); #ifdef APPLICATION_ADDRESS_SRAM12 memcpy(appXIPBuff, (u8*)(EXT_FLASH_ADDRESS), 0x40000); // 256K #endif myprintf("goto SYSTEMrn"); GoToUserSystem(); while (1) { Delayus(1000*1000); LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_3); myprintf("bootrn"); } } void W25QXX_Init(void) { u16 k = 0; QSPI_Init(); //初始化QSPI W25QXX_TYPE=W25QXX_ReadID(); //读取FLASH ID. //必须1 Line printf("ID:%Xrn",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:%drn",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博客,加上时间紧迫,匆匆而过~ |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1617 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1543 浏览 1 评论
977 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
683 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1595 浏览 2 评论
1863浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
644浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
515浏览 3评论
531浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
504浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 04:44 , Processed in 0.733961 second(s), Total 76, Slave 59 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号