STM32H750_QSPI_W25QXX_XIP

**

STM32H750_QSPI_W25QXX_XIP_仿真

**

	最近在调试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博客,加上时间紧迫,匆匆而过~
	****************************************
STM32H750单片机开发板基础代码60例实验例程软件工程源码合集: 实验0 新建工程实验 实验1 跑马灯实验 实验10 电容触摸按键实验 实验11 OLED实验 实验12 内存保护(MPU)实验 实验13 TFTLCD(MCU屏)实验 实验14 SDRAM实验 实验15 LTDC LCD(RGB屏)实验 实验16 USMART调试实验 实验17 RTC实验 实验18 硬件随机数实验 实验19 待机唤醒实验 实验2 按键输入实验 实验20 ADC实验 实验21 内部温度传感器实验 实验22 DAC实验 实验23 PWM DAC实验 实验24 DMA实验 实验25 IIC实验 实验26 IO扩展实验 实验27 光环境传感器实验 实验28 SPI实验 实验29 QSPI实验 实验3 串口通信实验 实验30 485实验 实验31 FDCAN实验 实验32 触摸屏实验 实验33 红外遥控器实验 实验34 DS18B20数字温度传感器实验 实验35 DHT11数字温湿度传感器实验 实验36 ICM20608六轴传感器实验 实验37 无线通信实验 实验38 FLASH模拟EEPROM实验 实验39 摄像头实验 实验4 外部中断实验 实验40 内存管理实验 实验41 SD卡实验 实验42 NAND FLASH实验 实验43 FATFS实验 实验44 汉字显示实验 实验45 图片显示实验 实验46 硬件JPEG解码实验 实验47 照相机实验 实验48 音乐播放器实验 实验49 录音机实验 实验5 独立看门狗实验 实验50 SPDIF(光纤音频)实验 实验51 视频播放器实验 实验52 FPU测试(Julia分形)实验 实验53 DSP测试实验 实验54 手写识别实验 实验55 T9拼音输入法实验 实验56 串口IAP实验 实验57 USB读卡器(Slave)实验 实验58 USB声卡(Slave)实验 实验59 USB虚拟串口(Slave)实验 实验6 窗口看门狗实验 实验60 USB U盘(Host)实验 实验61 USB鼠标键盘实验(Host) 实验62 网络通信实验 实验63 UCOSII实验1-任务调度 实验64 UCOSII实验2-信号量和邮箱 实验65 UCOSII实验3-消息队列、信号量集和软件定时器 实验65 综合例程 实验7 定时器中断实验 实验8 PWM输出实验 实验9 输入捕获实验
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值