实战!基于MM32实现NORFLASH运行应用程序的过程解析

嵌入式ARM 2022-03-28 12:00


在项目开发之初,我们会根据设计需求来选择符合要求的芯片。其中,RAM决定了程序运行时的内存大小,ROM决定了应用程序的存储空间大小。但对于应用功能特别大的项目,MCU内部的FLASH(ROM)存储空间就无法满足要求了。这个时候就需要外扩程序存储空间,而NOR FLASH就是其中一种解决方案。


文结合涉及到的技术点一一做了详细介绍和说明,包含如下四大内容:


1. KEIL下载程序配置及说明
2. MM32实现跳转到NOR FLASH运行程序
3. 基于MM32的NOR FLASH下载算法实现
4. MM32实现NOR FLASH应用程序编程,下载程序并运行



一、KEIL下载程序配置及说明


通过对项目工程的配置,在KEIL在编译完工程后,会生成相应的HEX或BIN格式的程序烧录文件,点击Download下载按键后,会将烧录文件烧录到芯片中。


点击Option for Target魔术棒按键,在弹出的Option for Target窗口中的Output选项卡中设置生成程序烧录文件名,并勾选Create HEX file选项,如下图所示:



如果需要同时再生成BIN格式的程序烧录文件,我们可以在User选项卡中进行如下图的配置:



接着,在Debug选项卡中选择相应的调试下载工具。



在Utilities选项卡中配置下载设置(使用与Debug相同的工具进行下载程序),如下图所示:



另外Utilities选项卡中,点击Settings按键,在弹出的窗口中Flash Download选项卡中进行如下配置:



下载功能设置:包括擦出选择、编程、校验和下载完成后复位芯片并运行应用程序。


编程(下载)算法选择:一般在创建工程选择相应的芯片后,这边的下载算法就默认了,如果没有我们可以点击ADD按键添加相对应的下载算法;下载算法列举了描述、设备存储空间大小、设备类型以及地址区间范围。


编程(下载)算法在RAM中的位置:这个一般是默认的,START表示芯片RAM的起始地址,这个是由芯片决定的;SIZE表示最大支持的编程(下载)算法的程序空间大小,这个可以修改,但不得小于下载算法程序大小。


待这些都配置完成后,我们编译工程代码,无错误警告后,点击Download下载按键,即可下载应用程序到芯片中。


那么,在点击了Download按键后,那KEIL是如何将烧录程序下载到芯片中去的呢?


简单来说,就是KEIL软件根据配置将编程(下载)算法加载到芯片指定的RAM空间去(这个空间就是上述的以START作为起始地址,SIZE大小的RAM空间),并运行下载算法,对下载文件进行解析,将需要写入的数据通过下载算法写到指定的存储地址上,完成上述编程的过程。


二、MM32实现跳转到NOR FLASH运行程序


MM32F3270系列MCU支持存储控制器FSMC功能,可配置的静态存储器包括SRAM、NOR FLASH;另外还支持8080\6800接口,可以应用到LCD显示上。


NOR FLASH之所以可以运行程序,主要是因为其内部地址/数据线是分开的,支持字节访问,符合CPU指令译码执行的要求(NOR FLASH上储存了指令代码,MCU给NOR FLASH一个地址,NOR FLASH就向MCU返回相应地址上的数据,让MCU执行,中间不需要额外的处理操作)。


那通过上述的描述,使用MM32实现跳转到NOR FLASH运行程序,我们就只需要在基于MM32内部FLASH运行的基础上实现NOR FLASH与MCU之间的FSMC初始化配置,以及程序跳转这两个操作步骤,具体参考代码如下所示:


FSMC初始化配置:


/*******************************************************************************
* [url=home.php?mod=space&uid=288409]@file[/url] NOR.c
* [url=home.php?mod=space&uid=187600]@author[/url] King
* [url=home.php?mod=space&uid=895143]@version[/url] V1.00
* [url=home.php?mod=space&uid=212281]@date[/url] 25-Jan-2021
* [url=home.php?mod=space&uid=247401]@brief[/url] ......
*******************************************************************************/




/* Define to prevent recursive inclusion -------------------------------------*/
#define __NOR_C__




/* Includes ------------------------------------------------------------------*/
#include "NOR.h"




/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/




/* Exported variables --------------------------------------------------------*/
/* Exported function prototypes ----------------------------------------------*/




/*******************************************************************************
* @brief
* @param
* @retval
* [url=home.php?mod=space&uid=93590]@Attention[/url]
*******************************************************************************/
void NOR_InitGPIO(void)
{
GPIO_InitTypeDef GPIO_InitStructure;


RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD | RCC_AHBPeriph_GPIOE |
RCC_AHBPeriph_GPIOF | RCC_AHBPeriph_GPIOG , ENABLE);


GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_12); /* FSMC_D0 */
GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_12); /* FSMC_D1 */
GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_12); /* FSMC_D2 */
GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_12); /* FSMC_D3 */
GPIO_PinAFConfig(GPIOE, GPIO_PinSource7, GPIO_AF_12); /* FSMC_D4 */
GPIO_PinAFConfig(GPIOE, GPIO_PinSource8, GPIO_AF_12); /* FSMC_D5 */
GPIO_PinAFConfig(GPIOE, GPIO_PinSource9, GPIO_AF_12); /* FSMC_D6 */
GPIO_PinAFConfig(GPIOE, GPIO_PinSource10, GPIO_AF_12); /* FSMC_D7 */
GPIO_PinAFConfig(GPIOE, GPIO_PinSource11, GPIO_AF_12); /* FSMC_D8 */
GPIO_PinAFConfig(GPIOE, GPIO_PinSource12, GPIO_AF_12); /* FSMC_D9 */
GPIO_PinAFConfig(GPIOE, GPIO_PinSource13, GPIO_AF_12); /* FSMC_D10 */
GPIO_PinAFConfig(GPIOE, GPIO_PinSource14, GPIO_AF_12); /* FSMC_D11 */
GPIO_PinAFConfig(GPIOE, GPIO_PinSource15, GPIO_AF_12); /* FSMC_D12 */
GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_12); /* FSMC_D13 */
GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_12); /* FSMC_D14 */
GPIO_PinAFConfig(GPIOD, GPIO_PinSource10, GPIO_AF_12); /* FSMC_D15 */


GPIO_PinAFConfig(GPIOF, GPIO_PinSource0, GPIO_AF_12); /* FSMC_A0 */
GPIO_PinAFConfig(GPIOF, GPIO_PinSource1, GPIO_AF_12); /* FSMC_A1 */
GPIO_PinAFConfig(GPIOF, GPIO_PinSource2, GPIO_AF_12); /* FSMC_A2 */
GPIO_PinAFConfig(GPIOF, GPIO_PinSource3, GPIO_AF_12); /* FSMC_A3 */
GPIO_PinAFConfig(GPIOF, GPIO_PinSource4, GPIO_AF_12); /* FSMC_A4 */
GPIO_PinAFConfig(GPIOF, GPIO_PinSource5, GPIO_AF_12); /* FSMC_A5 */


GPIO_PinAFConfig(GPIOF, GPIO_PinSource12, GPIO_AF_12); /* FSMC_A6 */
GPIO_PinAFConfig(GPIOF, GPIO_PinSource13, GPIO_AF_12); /* FSMC_A7 */
GPIO_PinAFConfig(GPIOF, GPIO_PinSource14, GPIO_AF_12); /* FSMC_A8 */
GPIO_PinAFConfig(GPIOF, GPIO_PinSource15, GPIO_AF_12); /* FSMC_A9 */


GPIO_PinAFConfig(GPIOG, GPIO_PinSource0, GPIO_AF_12); /* FSMC_A10 */
GPIO_PinAFConfig(GPIOG, GPIO_PinSource1, GPIO_AF_12); /* FSMC_A11 */
GPIO_PinAFConfig(GPIOG, GPIO_PinSource2, GPIO_AF_12); /* FSMC_A12 */
GPIO_PinAFConfig(GPIOG, GPIO_PinSource3, GPIO_AF_12); /* FSMC_A13 */
GPIO_PinAFConfig(GPIOG, GPIO_PinSource4, GPIO_AF_12); /* FSMC_A14 */
GPIO_PinAFConfig(GPIOG, GPIO_PinSource5, GPIO_AF_12); /* FSMC_A15 */


GPIO_PinAFConfig(GPIOD, GPIO_PinSource11, GPIO_AF_12); /* FSMC_A16 */
GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_12); /* FSMC_A17 */
GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_12); /* FSMC_A18 */


GPIO_PinAFConfig(GPIOE, GPIO_PinSource3, GPIO_AF_12); /* FSMC_A19 */
GPIO_PinAFConfig(GPIOE, GPIO_PinSource4, GPIO_AF_12); /* FSMC_A20 */
GPIO_PinAFConfig(GPIOE, GPIO_PinSource5, GPIO_AF_12); /* FSMC_A21 */
GPIO_PinAFConfig(GPIOE, GPIO_PinSource6, GPIO_AF_12); /* FSMC_A22 */


GPIO_PinAFConfig(GPIOD, GPIO_PinSource4, GPIO_AF_12); /* FSMC_NOE */
GPIO_PinAFConfig(GPIOD, GPIO_PinSource5, GPIO_AF_12); /* FSMC_NWE */
GPIO_PinAFConfig(GPIOD, GPIO_PinSource6, GPIO_AF_12); /* FSMC_NWAIT*/
GPIO_PinAFConfig(GPIOG, GPIO_PinSource9, GPIO_AF_12); /* FSMC_NE2 */


/* D00 - D01 */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOD, &GPIO_InitStructure);


/* D02 - D03 */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOD, &GPIO_InitStructure);


/* D04 - D12 */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 |
GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 |
GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOE, &GPIO_InitStructure);


/* D13 - D15 */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOD, &GPIO_InitStructure);


/* A00 - A05 */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 |
GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOF, &GPIO_InitStructure);


/* A06 - A09 */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 |
GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOF, &GPIO_InitStructure);


/* A10 - A15 */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 |
GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOG, &GPIO_InitStructure);


/* A16 - A18 */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOD, &GPIO_InitStructure);


/* A19 - A22 */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 |
GPIO_Pin_5 | GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOE, &GPIO_InitStructure);


/* NOE(PD4) NWE(PD5) configuration */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOD, &GPIO_InitStructure);


/* NWAIT(PD6) configuration */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOD, &GPIO_InitStructure);


/* NE2(PG9) configuration */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOG, &GPIO_InitStructure);
}




/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void NOR_InitFSMC(void)
{
FSMC_InitTypeDef FSMC_InitStructure;
FSMC_NORSRAM_Bank_InitTypeDef FSMC_BankInitStructure;


RCC_AHB3PeriphClockCmd(RCC_AHB3ENR_FSMC, ENABLE);


FSMC_NORSRAM_BankStructInit(&FSMC_BankInitStructure);
FSMC_BankInitStructure.FSMC_SMReadPipe = 0;
FSMC_BankInitStructure.FSMC_ReadyMode = 0;
FSMC_BankInitStructure.FSMC_WritePeriod = 5; /* W:WE Pulse Width : [0, 63] */
FSMC_BankInitStructure.FSMC_WriteHoldTime = 3; /* W:Address/Data Hold Time : [0, 3] */
FSMC_BankInitStructure.FSMC_AddrSetTime = 3; /* W:Address Setup Time : [0, 3] */
FSMC_BankInitStructure.FSMC_ReadPeriod = 8; /* R:Read Cycle Time : [0, 63] */
FSMC_BankInitStructure.FSMC_DataWidth = FSMC_DataWidth_16bits;
FSMC_NORSRAM_Bank_Init(&FSMC_BankInitStructure, FSMC_NORSRAM_BANK1);


FSMC_NORSRAMStructInit(&FSMC_InitStructure);
FSMC_InitStructure.FSMC_Mode = FSMC_Mode_NorFlash;
FSMC_InitStructure.FSMC_TimingRegSelect = FSMC_TimingRegSelect_1;
FSMC_InitStructure.FSMC_MemSize = FSMC_MemSize_64MB;
FSMC_InitStructure.FSMC_MemType = FSMC_MemType_FLASH;
FSMC_InitStructure.FSMC_AddrDataMode = FSMC_AddrDataDeMUX;
FSMC_NORSRAMInit(&FSMC_InitStructure);
}




/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void NOR_Init(void)
{
NOR_InitGPIO();


NOR_InitFSMC();
}




/******************* (C) COPYRIGHT 2022 *************************END OF FILE***/



程序跳转代码:


/*******************************************************************************
* @file main.c
* @author King
* @version V1.00
* @date 25-Jan-2021
* @brief ......
*******************************************************************************/




/* Define to prevent recursive inclusion -------------------------------------*/
#define __MAIN_C__




/* Includes ------------------------------------------------------------------*/
#include "main.h"




/* Private typedef -----------------------------------------------------------*/
typedef void (*pFunction)(void);




/* Private define ------------------------------------------------------------*/
#define ApplicationAddress ((uint32_t)0x64000000)




/* Private macro -------------------------------------------------------------*/




/* Private variables ---------------------------------------------------------*/
uint32_t JumpAddress = 0;
pFunction JumpToApplication;




/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/




/* Exported variables --------------------------------------------------------*/
/* Exported function prototypes ----------------------------------------------*/




/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
int main(void)
{
NOR_Init();


/* Jump to code loaded in NOR memory and execute it ***********************/
JumpAddress = *(volatile uint32_t *)(ApplicationAddress + 4);
JumpToApplication = (pFunction)JumpAddress;


/* Initialize user application's Stack Pointer */
__set_MSP(*(volatile uint32_t *)ApplicationAddress);
JumpToApplication();


while(1);
}




/******************* (C) COPYRIGHT 2022 *************************END OF FILE***/



KEIL工程的相关配置:




三、基于MM32的NOR FLASH下载算法实现


在能够成功跳转到NOR FLASH运行程序后,我们就需要将应用程序下载到NOR FLASH就可以了。


结合第一小结,一般MCU自带的下载算法只是将程序下载到自身内部的FLASH存储空间,如果需要将程序下载到外扩的NOR FLASH,我们可以使用烧录工具,将程序烧录进NOR FLASH。但在开发调试过程中,这显然不太方便,所以我们就需要自行编写下载算法,来实现MM32与NOR FLASH之间的程序编程。


应该怎么去编写这个下载算法呢?


首先,在KEIL的安装路径下:C:\Keil_v5\ARM\Flash有一个_Template的下载算法工程模板,我们将其直接拷贝一份,重命名为MM32F3270_NOR_FLM;打开KEIL工程,里面主要有两个文件FlashDev.c和FlashPrg.c


FlashDev.c主要是用来描述存储特性的,包括Device Name、Device Type、Device Start Address、Device Size in Bytes等等,这些特性的值,我们可以根据NOR FLASH的特性值来填写,编程超时时间和擦出超时时间可以尽量长一些,做些冗余,具体参考如下所示:



FlashPrg.c就是用来实现编程下载算法的接口函数,其中有些函数是必须实现的,有些函数则是可选择的,如下所示:



我们需要实现的就是通过对这些函数的补充实现,来达到NOR FLASH的读写操作,实现将程序下载到NOR FLASH。


在第一小节,我们知道下载算法是需要先加载到RAM中运行,再去进行编程操作的,对于这个加载到RAM空间的下载算法程序空间大小其实是有大小限制有,根据每个芯片的RAM大小而定,但最大也不能超过0x10000。


所以,我们在实现自己定义的下载算法的时候就尤其需要注意,尽量的不去使用库函数,以使用寄存器或者是直接操作芯片内部地址最佳,这样可以最大程序的节省程序空间。


不过,这也会使代码的阅读理解变得有些困难。因为下载算法运行的程序不能够实现的在线调试,所以对于NOR FLASH操作的这些函数功能,最好在其它工程上先进行验证,确认功能正常后,再移植到下载算法的工程当中来。


接下来,就是点击KEIL工程魔术棒按键对工程进行设置。在Target选项卡中选择芯片型号为MM32F3277G9P,在C/C++选项卡中根据需要进行宏定义(因为这个下载算法,支持多个NOR FLASH,所以我这边有相应的宏定义),在添加好NOR FLASH的相关函数,并补全接口函数后,进行编译。


此时工程目录中就会生成一个FLM文件(因为在User选项卡中有一个命令:cmd.exe /C copy "Objects\%L" ".\@L.FLM",它会将编译后的文件转换成FLM格式)





我们将这个生成的FLM下载算法文件拷贝到C:\Keil_v5\ARM\Flash目录下,在后面工程使用到的时候就可以添加进来了。


四、MM32实现NOR FLASH应用程序编程,下载程序并运行


对于运行在NOR FLASH中的MM32程序来说,程序编写并没有特殊的要求,唯一不同的就是对于KEIL工程的设置。例程中,我们只实现了LED灯的闪烁功能,功能比较简单。对于KEIL工程的设置,如下图所示:


将默认的程序空间和程序启动空间设置为NOR FLASH的起始地址作为开始:



根据DEBUG调试工具进行对应的选择:



下载工具与调试工具相同:



将编程(下载)算法选择为第三小节生成的FLM算法,在添加进来的时候,就会自动显示相应的描述信息:



这样配置完成后,编译后点击下载按键,程序就被下载到NOR FLASH外部存储空间了。因为勾选了RESET AND RUN,下载完成后会复位MCU,此时MCU FLASH中已经存在了跳转程序,这个时候就会跳转到NOR FLASH执行LED闪烁程序。此时LED灯若能够正常运行,就说明程序功能已经正常运行了。


另外,有些小伙伴可能会问,在Option for Target窗口Utilities选项卡中,有看到Init File会配置一个INI文件,我上述的工程配置中却没有,这是怎么回事呢?


这边的INI文件包含的是对MCU内部地址配置相应参数值的数据,是在下载程序前先对MCU进行设置的操作。一般看到的就是对MCU与NOR FLASH的FSMC相关寄存器进行配置,好让下载算法能够正常的去操作NOR FLASH。但我的配置截图中却没有,是因为在实现下载算法时的INIT函数,已经实现了对FSMC的初始化配置,所以在INIT FILE的位置就不需要再重复操作了。


五、后续


有了这个下载算法,我们可以将部分程序或数据指令到外部存储空间;如果当我们通过LCD显示图片数据时,图片数据是一个很大的数组,我们就可以将这些数据指定到NOR FLASH中,而应用程序还是在MCU内部的FLASH中运行,操作如下图所示:


芯片有两个ROM空间,但默认的和启动的为MCU内部FLASH:



下载算法有两个,这两个的地址是不一样的,一个是MCU内部FLASH,一个是外扩的NOR FLASH:



将程序指定到NOR FLASH的方法,右击.C文件,选择Options for File ”IMAGE1.c”...



在弹出的窗口中将CODE/CONST指令到ROM1,如下图所示:



然后在程序中再调用IMAGE1.c文件中的数据,这样在编译后下载程序的时候,会先下载程序到MCU的FLASH,再下载图片数据到MCU扩展的NOR FLASH。



本文中的硬件是基本神舟III开发板实现的,MCU使用的是灵动微电子的MM32F3277G9P,NOR FLASH使用的是M29W128FH。


至此分享就结束了,希望能对各位小伙伴有所帮助!另外,如果有需要查看原图和代码的小伙伴,请点击底部“阅读原文”进行下载。


END

作者:xld0932
来源:21ic论坛

版权归原作者所有,如有侵权,请联系删除。

推荐阅读
为什么俄罗斯不担心芯片禁运?
C语言中比goto还“霸道”的跳转方式
高手常用的3个开源库,让单片机开发事半功倍!

→点关注,不迷路←
嵌入式ARM 关注这个时代最火的嵌入式ARM,你想知道的都在这里。
评论
  • 本文介绍编译Android13 ROOT权限固件的方法,触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。关闭selinux修改此文件("+"号为修改内容)device/rockchip/common/BoardConfig.mkBOARD_BOOT_HEADER_VERSION ?= 2BOARD_MKBOOTIMG_ARGS :=BOARD_PREBUILT_DTB
    Industio_触觉智能 2025-01-08 00:06 100浏览
  • 村田是目前全球量产硅电容的领先企业,其在2016年收购了法国IPDiA头部硅电容器公司,并于2023年6月宣布投资约100亿日元将硅电容产能提升两倍。以下内容主要来自村田官网信息整理,村田高密度硅电容器采用半导体MOS工艺开发,并使用3D结构来大幅增加电极表面,因此在给定的占位面积内增加了静电容量。村田的硅技术以嵌入非结晶基板的单片结构为基础(单层MIM和多层MIM—MIM是指金属 / 绝缘体/ 金属) 村田硅电容采用先进3D拓扑结构在100um内,使开发的有效静电容量面积相当于80个
    知白 2025-01-07 15:02 145浏览
  • 「他明明跟我同梯进来,为什么就是升得比我快?」许多人都有这样的疑问:明明就战绩也不比隔壁同事差,升迁之路却比别人苦。其实,之间的差异就在于「领导力」。並非必须当管理者才需要「领导力」,而是散发领导力特质的人,才更容易被晓明。许多领导力和特质,都可以通过努力和学习获得,因此就算不是天生的领导者,也能成为一个具备领导魅力的人,进而被老板看见,向你伸出升迁的橘子枝。领导力是什么?领导力是一种能力或特质,甚至可以说是一种「影响力」。好的领导者通常具备影响和鼓励他人的能力,并导引他们朝着共同的目标和愿景前
    优思学院 2025-01-08 14:54 82浏览
  • 故障现象一辆2017款东风风神AX7车,搭载DFMA14T发动机,累计行驶里程约为13.7万km。该车冷起动后怠速运转正常,热机后怠速运转不稳,组合仪表上的发动机转速表指针上下轻微抖动。 故障诊断 用故障检测仪检测,发动机控制单元中无故障代码存储;读取发动机数据流,发现进气歧管绝对压力波动明显,有时能达到69 kPa,明显偏高,推断可能的原因有:进气系统漏气;进气歧管绝对压力传感器信号失真;发动机机械故障。首先从节气门处打烟雾,没有发现进气管周围有漏气的地方;接着拔下进气管上的两个真空
    虹科Pico汽车示波器 2025-01-08 16:51 86浏览
  • By Toradex 秦海1). 简介嵌入式平台设备基于Yocto Linux 在开发后期量产前期,为了安全以及提高启动速度等考虑,希望将 ARM 处理器平台的 Debug Console 输出关闭,本文就基于 NXP i.MX8MP ARM 处理器平台来演示相关流程。 本文所示例的平台来自于 Toradex Verdin i.MX8MP 嵌入式平台。  2. 准备a). Verdin i.MX8MP ARM核心版配合Dahlia载板并
    hai.qin_651820742 2025-01-07 14:52 113浏览
  • 在智能家居领域中,Wi-Fi、蓝牙、Zigbee、Thread与Z-Wave等无线通信协议是构建短距物联局域网的关键手段,它们常在实际应用中交叉运用,以满足智能家居生态系统多样化的功能需求。然而,这些协议之间并未遵循统一的互通标准,缺乏直接的互操作性,在进行组网时需要引入额外的网关作为“翻译桥梁”,极大地增加了系统的复杂性。 同时,Apple HomeKit、SamSung SmartThings、Amazon Alexa、Google Home等主流智能家居平台为了提升市占率与消费者
    华普微HOPERF 2025-01-06 17:23 211浏览
  •  在全球能源结构加速向清洁、可再生方向转型的今天,风力发电作为一种绿色能源,已成为各国新能源发展的重要组成部分。然而,风力发电系统在复杂的环境中长时间运行,对系统的安全性、稳定性和抗干扰能力提出了极高要求。光耦(光电耦合器)作为一种电气隔离与信号传输器件,凭借其优秀的隔离保护性能和信号传输能力,已成为风力发电系统中不可或缺的关键组件。 风力发电系统对隔离与控制的需求风力发电系统中,包括发电机、变流器、变压器和控制系统等多个部分,通常工作在高压、大功率的环境中。光耦在这里扮演了
    晶台光耦 2025-01-08 16:03 75浏览
  • 大模型的赋能是指利用大型机器学习模型(如深度学习模型)来增强或改进各种应用和服务。这种技术在许多领域都显示出了巨大的潜力,包括但不限于以下几个方面: 1. 企业服务:大模型可以用于构建智能客服系统、知识库问答系统等,提升企业的服务质量和运营效率。 2. 教育服务:在教育领域,大模型被应用于个性化学习、智能辅导、作业批改等,帮助教师减轻工作负担,提高教学质量。 3. 工业智能化:大模型有助于解决工业领域的复杂性和不确定性问题,尽管在认知能力方面尚未完全具备专家级的复杂决策能力。 4. 消费
    丙丁先生 2025-01-07 09:25 122浏览
  • 根据环洋市场咨询(Global Info Research)项目团队最新调研,预计2030年全球无人机锂电池产值达到2457百万美元,2024-2030年期间年复合增长率CAGR为9.6%。 无人机锂电池是无人机动力系统中存储并释放能量的部分。无人机使用的动力电池,大多数是锂聚合物电池,相较其他电池,锂聚合物电池具有较高的能量密度,较长寿命,同时也具有良好的放电特性和安全性。 全球无人机锂电池核心厂商有宁德新能源科技、欣旺达、鹏辉能源、深圳格瑞普和EaglePicher等,前五大厂商占有全球
    GIRtina 2025-01-07 11:02 127浏览
  • 每日可见的315MHz和433MHz遥控模块,你能分清楚吗?众所周知,一套遥控设备主要由发射部分和接收部分组成,发射器可以将控制者的控制按键经过编码,调制到射频信号上面,然后经天线发射出无线信号。而接收器是将天线接收到的无线信号进行解码,从而得到与控制按键相对应的信号,然后再去控制相应的设备工作。当前,常见的遥控设备主要分为红外遥控与无线电遥控两大类,其主要区别为所采用的载波频率及其应用场景不一致。红外遥控设备所采用的射频信号频率一般为38kHz,通常应用在电视、投影仪等设备中;而无线电遥控设备
    华普微HOPERF 2025-01-06 15:29 172浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦