一、W25Q128烧录算法的制作
1、前言
最近做项目用到STM32H743这款芯片,其内部FLash有2M,但是项目中用到touchgfx,如果资源放到内部Flash中会造成两个问题,一是图片过多会导致内部Flash不够用,二是每次修改一下程序都要下载整个Flash,下载时间过长。所以想到用外部QSPI接口的Flash专门存放图片等资源文件。
2、创建下载算法工程
a、工程搭建
如果你电脑已经安装Keil,那么可以在以下目录找到官方的工程模板:{Keil安装录}\ARM\PACK\ARM\CMSIS\5.3.0\Device\_Template_Flash
将这些全部复制到新文件夹,再将相关的芯片驱动库复制进去,之后打开工程如果打开之后提示:
需要将工程模板的文件选择、然后右键进属性选项,把只读选项去掉,再重新打开即可。
b、文件添加
需要将qspi和w25qxx的驱动文件添加进来。
之后点编译如果遇到以下问题:
\xxx.axf: Error: L6265E: Non-PI Section xxxxxxxxx.o(.data) cannot be assigned to PI Exec region PrgData.
暂时不用管
b、算法实现
先根据片外FLASH芯片调整参数,在FlashDev.c文件里头的结构体:
具体解释如下:
struct FlashDevice const FlashDevice = {
FLASH_DRV_VERS, // 驱动版本 不需要修改!
"XP_H743_W25Q128", // 下载算法名称(Keil里显示)
EXTSPI, // 设备类型:片外FLASH
0x90000000, // 片外FLASH地址
0x1000000, // 片外FLASH大小(32MB)
4096, // 页大小(一次写入的数据大小,这边W25Q32页大小是256,为了写入快点这边设置1024)
0, // 保留位,必须是0
0xFF, // 擦除后数据值
1000, // 页写入超时时间(1S)
3000, // 扇区擦除超时时间(6S)
0x001000, 0x000000, // 扇区大小:4KB 地址:0x000000
SECTOR_END
};
之后实现FlashPrg.c里的相关函数:
/* -----------------------------------------------------------------------------
* Copyright (c) 2016 ARM Ltd.
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software. Permission is granted to anyone to use this
* software for any purpose, including commercial applications, and to alter
* it and redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
*
* $Date: 10. October 2018
* $Revision: V1.0.0
*
* Project: Flash Device Description for
* STM32H743 W25Q256 SPIFI Flash
* --------------------------------------------------------------------------- */
#include "..\FlashOS.H" // FlashOS Structures
#include "sys.h"
#include "W25QXX.h"
#include "qspi.h"
#define PAGE_SIZE 4096
/*
* Initialize Flash Programming Functions
* Parameter: adr: Device Base Address
* clk: Clock Frequency (Hz)
* fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
* Return Value: 0 - OK, 1 - Failed
*/
uint8_t aux_buf[PAGE_SIZE];
uint32_t base_adr;
//这个函数在keil下载后,在擦除、编程都会重新调用一次此函数,而且在调用此函数之前keil会复位单片机,相当于单片机跑起来的初始化功能要在擦除、编程时都要初始化一遍
int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {
Stm32_Clock_Init(160,5,2,4); //设置时钟,400Mhz
W25QXX_Init(); //初始化NORFLASH
base_adr = adr;
return (0);
}
/*
* De-Initialize Flash Programming Functions
* Parameter: fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
* Return Value: 0 - OK, 1 - Failed
*/
int UnInit (unsigned long fnc) {
return (0);
}
/*
* Erase complete Flash Memory
* Return Value: 0 - OK, 1 - Failed
*/
int EraseChip (void) {
W25QXX_Erase_Chip();
return (0); /* Finished without Errors */
}
/*
* Erase Sector in Flash Memory
* Parameter: adr: Sector Address
* Return Value: 0 - OK, 1 - Failed
*/
int EraseSector (unsigned long adr) {
W25QXX_Erase_Sector(adr-base_adr);
return (0); /* Finished without Errors */
}
/*
* Blank Check Checks if Memory is Blank
* Parameter: adr: Block Start Address
* sz: Block Size (in bytes)
* pat: Block Pattern
* Return Value: 0 - OK, 1 - Failed
*/
int BlankCheck (unsigned long adr, unsigned long sz, unsigned char pat) {
return (1); /* Always Force Erase */
}
/*
* Program Page in Flash Memory
* Parameter: adr: Page Start Address
* sz: Page Size
* buf: Page Data
* Return Value: 0 - OK, 1 - Failed
*/
int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {
W25QXX_Write_NoCheck(buf,adr-base_adr,sz);
return (0); /* Finished without Errors */
}
/*
* Verify Flash Contents
* Parameter: adr: Start Address
* sz: Size (in bytes)
* buf: Data
* Return Value: (adr+sz) - OK, Failed Address
*/
/*
Verify function is obsolete because all other function leave
the SPIFI in memory mode so a memory compare could be used.
*/
unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf) {
int i;
W25QXX_Read(aux_buf, adr-base_adr, sz);
for (i = 0; i< sz; i++)
{
if (aux_buf[i] != buf[i])
return (adr+i); // Verification Failed (return address)
}
return (adr+sz); // Done successfully
}
d、工程设置
输出名称设置
设置算法与地址无关,因为下载算法是加载到ram中运行的
汇编设置如上
链接器设置
在Target.lin
文件中填入如下内容:
; Linker Control File (scatter-loading)
;
PRG 0 PI ; Programming Functions
{
PrgCode +0 ; Code
{
* (+RO)
}
PrgData +0 ; Data
{
* (+RW,+ZI)
}
}
DSCR +0 ; Device Description
{
DevDscr +0
{
FlashDev.o
}
}
格式如下:
cmd.exe /C copy "!L" "..\@L.FLM"
编译生成烧录文件,后缀格式为FLM。
3、下载算法的使用
将生成的XXXX.FLM文件复制到keil下载算法目录:
路径:{Keil安装目录}\ARM\Flash
之后就可以在配置界面看到了
二、内存映射代码编写
void QSPI_Enable_Memmapmode(void)
{
unsigned int tempreg=0;
volatile unsigned int *data_reg=&QUADSPI->DR;
GPIO_InitTypeDef qspi_gpio;
RCC->AHB4ENR|=1<<1; //使能PORTB时钟
RCC->AHB4ENR|=1<<5; //使能PORTF时钟
RCC->AHB3ENR|=1<<14; //QSPI时钟使能
qspi_gpio.Pin=GPIO_PIN_6; //PB6 AF10
qspi_gpio.Mode=GPIO_MODE_AF_PP;
qspi_gpio.Speed=GPIO_SPEED_FREQ_VERY_HIGH;
qspi_gpio.Pull=GPIO_NOPULL;
qspi_gpio.Alternate=GPIO_AF10_QUADSPI;
HAL_GPIO_Init(GPIOB,&qspi_gpio);
qspi_gpio.Pin=GPIO_PIN_2; //PB2 AF9
qspi_gpio.Alternate=GPIO_AF9_QUADSPI;
HAL_GPIO_Init(GPIOB,&qspi_gpio);
qspi_gpio.Pin=GPIO_PIN_6|GPIO_PIN_7; //PF6,7 AF9
qspi_gpio.Alternate=GPIO_AF9_QUADSPI;
HAL_GPIO_Init(GPIOF,&qspi_gpio);
qspi_gpio.Pin=GPIO_PIN_8|GPIO_PIN_9; //PF8,9 AF10
qspi_gpio.Alternate=GPIO_AF10_QUADSPI;
HAL_GPIO_Init(GPIOF,&qspi_gpio);
//QSPI设置,参考QSPI实验的QSPI_Init函数
RCC->AHB3RSTR|=1<<14; //复位QSPI
RCC->AHB3RSTR&=~(1<<14); //停止复位QSPI
while(QUADSPI->SR&(1<<5)); //等待BUSY位清零
QUADSPI->CR=0X01000310; //设置CR寄存器,这些值怎么来的,请参考QSPI实验/看H750参考手册寄存器描述分析
QUADSPI->DCR=0X00160401; //设置DCR寄存器
QUADSPI->CR|=1<<0; //使能QSPI
//注意:QSPI QE位的使能,在QSPI烧写算法里面,就已经设置了
//所以,这里可以不用设置QE位,否则需要加入对QE位置1的代码
//不过,代码必须通过仿真器下载,直接烧录到外部QSPI FLASH,是不可用的
//如果想直接烧录到外部QSPI FLASH也可以用,则需要在这里添加QE位置1的代码
//W25QXX进入QPI模式(0X38指令)
while(QUADSPI->SR&(1<<5)); //等待BUSY位清零
QUADSPI->CCR=0X00000138; //发送0X38指令,W25QXX进入QPI模式
while((QUADSPI->SR&(1<<1))==0); //等待指令发送完成
QUADSPI->FCR|=1<<1; //清除发送完成标志位
//W25QXX写使能(0X06指令)
while(QUADSPI->SR&(1<<5)); //等待BUSY位清零
QUADSPI->CCR=0X00000106; //发送0X06指令,W25QXX写使能
while((QUADSPI->SR&(1<<1))==0); //等待指令发送完成
QUADSPI->FCR|=1<<1; //清除发送完成标志位
//W25QXX设置QPI相关读参数(0XC0)
while(QUADSPI->SR&(1<<5)); //等待BUSY位清零
QUADSPI->CCR=0X030003C0; //发送0XC0指令,W25QXX读参数设置
QUADSPI->DLR=0;
while((QUADSPI->SR&(1<<2))==0); //等待FTF
*(unsigned char *)data_reg=3<<4; //设置P4&P5=11,8个dummy clocks,104M
QUADSPI->CR|=1<<2; //终止传输
while((QUADSPI->SR&(1<<1))==0); //等待数据发送完成
QUADSPI->FCR|=1<<1; //清除发送完成标志位
while(QUADSPI->SR&(1<<5)); //等待BUSY位清零
//MemroyMap 模式设置
while(QUADSPI->SR&(1<<5)); //等待BUSY位清零
QUADSPI->ABR=0; //交替字节设置为0,实际上就是W25Q 0XEB指令的,M0~M7=0
tempreg=0XEB; //INSTRUCTION[7:0]=0XEB,发送0XEB指令(Fast Read QUAD I/O)
tempreg|=3<<8; //IMODE[1:0]=3,四线传输指令
tempreg|=3<<10; //ADDRESS[1:0]=3,四线传输地址
tempreg|=2<<12; //ADSIZE[1:0]=2,24位地址长度
tempreg|=3<<14; //ABMODE[1:0]=3,四线传输交替字节
tempreg|=0<<16; //ABSIZE[1:0]=0,8位交替字节(M0~M7)
tempreg|=6<<18; //DCYC[4:0]=6,6个dummy周期
tempreg|=3<<24; //DMODE[1:0]=3,四线传输数据
tempreg|=3<<26; //FMODE[1:0]=3,内存映射模式
QUADSPI->CCR=tempreg; //设置CCR寄存器
//设置QSPI FLASH空间的MPU保护
SCB->SHCSR&=~(1<<16); //禁止MemManage
MPU->CTRL&=~(1<<0); //禁止MPU
MPU->RNR=0; //设置保护区域编号为0(1~7可以给其他内存用)
MPU->RBAR=0X90000000; //基地址为0X9000 000,即QSPI的起始地址
MPU->RASR=0X0303002D; //设置相关保护参数(禁止共用,允许cache,允许缓冲),详见MPU实验的解析
MPU->CTRL=(1<<2)|(1<<0); //使能PRIVDEFENA,使能MPU
SCB->SHCSR|=1<<16; //使能MemManage
}
三、分散加载文件编写
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08000000 0x00180000 { ; load region size_region
ER_IROM1 0x08000000 0x00180000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
* (Veneer$$Code)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x24000000 0x00080000 { ; RW data
.ANY (+RW +ZI)
}
}
LR_QSPI 0X90000000 0X1000000 {
ER_QSPI 0X90000000 0X1000000 {
*.o (ExtFlashSection)
}
}
0X90000000 就是外部FLASH加载地址
四、源码分享
点击我下载