历史上的今天

今天是:2024年08月26日(星期一)

正在发生

2019年08月26日 | 【STM32H7教程】第28章 STM32H7时间关键代码在ITCM执行的方法

发布者:自在自由 来源: eefocus关键字:STM32H7  时间关键代码  ITCM 手机看文章 扫描二维码
随时随地手机看文章

28.1 初学者重要提示


学习本章节前,务必优先学习第25章,了解TCM,SRAM等五块内存区的基础知识,比较重要。


TCM : Tightly-Coupled Memory 紧密耦合内存 。ITCM用于指令,DTCM用于数据,特点是跟内核速度一样(400MHz),而片上RAM的速度基本都达不到这个速度(200MHz)。很多时候我们希望将需要实时性的程序和变量分别放在ITCM和DTCM里面执行,本章就是解决这个问题。


实现方法比较简单,基于MDK的Option选项设置下即可,无需操作分散加载。使用分散加载的好处是灵活,在设置复杂工程的内存映射方面比较方便。


实现这个功能的关键是要把所有程序都下载到Flash,系统上电后让MDK中的库函数去将所需的程序加载到RAM里面,用户不要自己去加载,太麻烦。如果用户自己去加载就得搞个bootloader加载应用程序到ITCM。这里所说的库函数是MDK里面的__main封装起来了。


28.2 简单实现方法

28.2.1 第1步,设置DTCM


设置DTCM空间,前0x400大小的空间用于中断向量表,所以这里从0x20000400开始,用于各种变量需求:

28.2.2 第2步,添加ITCM

ITCM的首地址是0x0000 0000,大小64KB:

28.2.3 第3步,选择在ITCM执行的代码

右击MDK分组,选择使用ITCM,这里设置了APP分组、BSP分组和SEGGER/HardFault分组。


以APP分组为例,设置方法如下:


 

BSP分组和SEGGER/HardFault分组也设置完毕后,可以看到小雪花标识


而进入main函数之前的所有代码,含main函数所在的文件main.c切不要设置,这个之前的代码我们都需要在flash里面执行。这些代码仅执行一次以后不会执行,所以不用管他们,之后的所有代码都可以放在ITCM里面。


28.2.4 第4步,复制中断向量表到DTCM

前面三步设置完毕后,将中断向量表从flash中复制到DTCM,主要存储的DTCM地址要0x200对齐。


/*

*********************************************************************************************************

* 函 数 名: main

* 功能说明: 标准c程序入口。

* 形    参: 无

* 返 回 值: 无

*********************************************************************************************************

*/

int main(void)

{

uint32_t *SouceAddr = (uint32_t *)FLASH_BANK1_BASE;

uint32_t *DestAddr =  (uint32_t *)D1_DTCMRAM_BASE;

 

memcpy(DestAddr, SouceAddr, 0x400);

/* 设置中断向量表到ITCM里面 */

SCB->VTOR = D1_DTCMRAM_BASE;

MainRAM();

}

至此就设置完毕了,另外注意以下两点:


  不限制设置分组,单独设置一个C文件也是可以的。

  如果大家将HAL_Driver分组也放在了ITCM里面,会有如下警告,这个不用管,是删除了冗余函数。

28.3 实验例程说明(MDK)

配套例子:


V7-007_时间关键代码在ITCM执行的超简单方法


实验目的:


学习时间关键代码在ITCM执行的超简单方法,同时中断向量表和变量放DTCM。

实验内容:


系统上电后驱动了1个软件定时器,每100ms翻转一次LED2。

启动1个TIM6周期性中断,频率10KHz,在中断服务程序里面翻转FMC扩展引脚20和23。

实验操作:


K1按键按下,开启TIM6的周期性中断。

K2按键按下,关闭TIM6的周期性中断。

上电后串口打印的信息:


波特率 115200,数据位 8,奇偶校验位无,停止位 1


程序设计:


  系统栈大小分配:


  RAM空间用的DTCM:


  硬件外设初始化


硬件外设的初始化是在 bsp.c 文件实现:


/*

*********************************************************************************************************

* 函 数 名: bsp_Init

* 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次

* 形    参:无

* 返 回 值: 无

*********************************************************************************************************

*/

void bsp_Init(void)

{

    /* 配置MPU */

MPU_Config();

/* 使能L1 Cache */

CPU_CACHE_Enable();

 

/* 

       STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:

   - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。

   - 设置NVIV优先级分组为4。

*/

HAL_Init();

 

/* 

       配置系统时钟到400MHz

       - 切换使用HSE。

       - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。

    */

SystemClock_Config();

 

/* 

   Event Recorder:

   - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。

   - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章

*/

#if Enable_EventRecorder == 1  

/* 初始化EventRecorder并开启 */

EventRecorderInitialize(EventRecordAll, 1U);

EventRecorderStart();

#endif

bsp_InitKey();    /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */

bsp_InitTimer();  /* 初始化滴答定时器 */

bsp_InitUart(); /* 初始化串口 */

bsp_InitExtIO(); /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */

bsp_InitLed();    /* 初始化LED */

}

  MPU配置和Cache配置:


数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区。


/*

*********************************************************************************************************

* 函 数 名: MPU_Config

* 功能说明: 配置MPU

* 形    参: 无

* 返 回 值: 无

*********************************************************************************************************

*/

static void MPU_Config( void )

{

MPU_Region_InitTypeDef MPU_InitStruct;

 

/* 禁止 MPU */

HAL_MPU_Disable();

 

/* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */

MPU_InitStruct.Enable           = MPU_REGION_ENABLE;

MPU_InitStruct.BaseAddress      = 0x24000000;

MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;

MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;

MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;

MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;

MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;

MPU_InitStruct.Number           = MPU_REGION_NUMBER0;

MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;

MPU_InitStruct.SubRegionDisable = 0x00;

MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

 

HAL_MPU_ConfigRegion(&MPU_InitStruct);

/* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */

MPU_InitStruct.Enable           = MPU_REGION_ENABLE;

MPU_InitStruct.BaseAddress      = 0x60000000;

MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;

MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;

MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;

MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;

MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;

MPU_InitStruct.Number           = MPU_REGION_NUMBER1;

MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;

MPU_InitStruct.SubRegionDisable = 0x00;

MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

HAL_MPU_ConfigRegion(&MPU_InitStruct);

 

/*使能 MPU */

HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);

}

 

/*

*********************************************************************************************************

* 函 数 名: CPU_CACHE_Enable

* 功能说明: 使能L1 Cache

* 形    参: 无

* 返 回 值: 无

*********************************************************************************************************

*/

static void CPU_CACHE_Enable(void)

{

/* 使能 I-Cache */

SCB_EnableICache();

 

/* 使能 D-Cache */

SCB_EnableDCache();

}

  主功能:


主程序实现如下操作:


  系统上电后驱动了1个软件定时器,每100ms翻转一次LED2。

  启动1个TIM6周期性中断,频率10KHz,在中断服务程序里面翻转FMC扩展引脚20和23。

  K1按键按下,开启TIM6的周期性中断。

  K2按键按下,关闭TIM6的周期性中断。

/*

*********************************************************************************************************

* 函 数 名: main

* 功能说明: 标准c程序入口。

* 形    参: 无

* 返 回 值: 无

*********************************************************************************************************

*/

int main(void)

{

uint32_t *SouceAddr = (uint32_t *)FLASH_BANK1_BASE;

uint32_t *DestAddr =  (uint32_t *)D1_DTCMRAM_BASE;

 

memcpy(DestAddr, SouceAddr, 0x400);

/* 设置中断向量表到ITCM里面 */

SCB->VTOR = D1_DTCMRAM_BASE;

MainRAM();

}

 

/*

*********************************************************************************************************

* 函 数 名: MainRAM

* 功能说明: c程序入口

* 形    参: 无

* 返 回 值: 错误代码(无需处理)

*********************************************************************************************************

*/

int MainRAM(void)

{

uint8_t ucKeyCode; /* 按键代码 */

    

 

bsp_Init(); /* 硬件初始化 */

PrintfLogo(); /* 打印例程名称和版本等信息 */

PrintfHelp(); /* 打印操作提示 */

 

bsp_StartAutoTimer(0, 100); /* 启动1个100ms的自动重装的定时器 */

bsp_SetTIMforInt(TIM6, 10000, 2, 0); /* 设置为10KHz频率定时器中断*/

/* 进入主程序循环体 */

while (1)

{

bsp_Idle(); /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */

 

/* 判断定时器超时时间 */

if (bsp_CheckTimer(0))

{

/* 每隔100ms 进来一次 */  

bsp_LedToggle(2);

}

 

/* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */

ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */

if (ucKeyCode != KEY_NONE)

{

switch (ucKeyCode)

{

case KEY_DOWN_K1: /* K1键按下,开启TIM6的周期性中断*/

TIM6->DIER |= TIM_IT_UPDATE;

break;

 

case KEY_DOWN_K2: /* K2键按下,关闭TIM6的周期性中断*/

TIM6->DIER &= ~TIM_IT_UPDATE;

break;

 

default:

/* 其它的键值不处理 */

break;

}

}

}

}

 

/*

*********************************************************************************************************

* 函 数 名: TIM6_DAC_IRQHandler

* 功能说明: TIM6定时中断服务程序

* 返 回 值: 无

*********************************************************************************************************

*/

void TIM6_DAC_IRQHandler(void)

{

if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)

{

/* 清除更新标志 */

TIM6->SR = ~ TIM_FLAG_UPDATE;

/* 翻转FMC扩展引脚20和23脚 */

HC574_TogglePin(GPIO_PIN_23);

HC574_TogglePin(GPIO_PIN_20);

}

}

28.4 总结

本章节就为大家交流这么多,对速度有要求的应用部分,建议使用ITCM和DTCM来达到最高性能。

关键字:STM32H7  时间关键代码  ITCM 引用地址:【STM32H7教程】第28章 STM32H7时间关键代码在ITCM执行的方法

上一篇:【STM32H7教程】第29章 STM32H7的USART串口基础知识和HAL库API
下一篇:【STM32H7教程】第27章 STM32H7的TCM,SRAM等动态内存分配实现

推荐阅读

GPIO八种工作模式四种输入: GPIO_Mode_IPU(上拉输入) GPIO_Mode_IPD(下拉输入) 原理: 经过上拉开关和下拉开关的连接,再经过触发器转化为0,1的数字信号,存储到数据寄存器中,然后我们就可以通过配置寄存器CRL,CRH控制这两个开关。 用法: 若GPIO引脚配置为上拉输入模式,在默认状态下(GPIO引脚无输入),取得的GPIO引脚数据为1,既高电平. 而下拉输入模式则是相...
在华为Mate X公布之初,这款机型是搭载麒麟980处理器的。而后来因为各种原因延迟了产品上市时间,不过最近在华为举办的活动中,有外媒发现Mate X已经悄然把处理器升级为麒麟990。针对外媒发现华为为Mate X升级处理器一事,华为高层也作出了确认的态度,并且还透露了这款机型会在短期内上市的信息。这位华为高层还表示,麒麟990采用了7nm工艺制程,在能...
前言在计算机科学中,内联函数(有时称作在线函数或编译时期展开函数)是一种编程语言结构,用来建议编译器对一些特殊函数进行内联扩展(有时称作在线扩展);也就是说建议编译器将指定的函数体插入并取代每一处调用该函数的地方(上下文),从而节省了每次调用函数带来的额外时间开支。但在选择使用内联函数时,必须在程序占用空间和程序执行效率之间进行...
8月25日,工信部发布“关于政协第十三届全国委员会第四次会议第1323号(工交邮电类193号)提案答复的函”。针对《关于加强我国十四五化工新材料产业高质量发展的提案》,该答复文件指出,化工新材料是高端制造业发展不可或缺的重要材料。我国高度重视化工新材料产业发展,围绕产业高质量发展需要,不断增强自主创新能力,提高化工新材料产业发展水平。以下...

史海拾趣

问答坊 | AI 解惑

片上系统(SOC)设计与EDA

摘要:利用EDA工具和硬件描述语言(HDL),根据产品的特定要求设计性能价格比高的片上系统,是目前国际上广泛使用的方法。与传统的设计方法不同,在设计开始阶段并不一定需要具体的单片微控制器(MCU)和开发系统(仿真器)以及带有外围电路的线路板 ...…

查看全部问答∨

调查:您所知道的国内的做汽车电子的公司?进来看看

北京恒润科技,做车身网络,AFS等(提供+dspace、CAN开发工具,mathworks公司国内的代理) 山东济南捷特,做ABS等产品 广东那边有不少汽车音响、仪表产品的供应商,但因为和真正意义上的汽车电子产品(动力,安全)还有些差别,没有去罗列。 ...…

查看全部问答∨

请教单片机控制多路开关HI508A的问题

HI508A应该很多人都用过,其控制逻辑如下: A2        A1        A0        EN        被选通道 0        0        0& ...…

查看全部问答∨

传感器的问题

替别人询问一个问题: 向你请教个问题,我想利用霍尔传感器结合单片机测旋转轮的速度,我只需要单片机在超出设定转速时输出一个电信号就行,我再利用这个信号做下一步的控制,能帮我给个方案吗?我只是爱好 本身不太懂,想看能不能实现,望百忙 ...…

查看全部问答∨

arm7 变量值在cpu做状态切换时 更改了 请教高手!!!原因是什么?

arm7  变量值在cpu做状态切换时  更改了  请教高手!!!原因是什么?…

查看全部问答∨

前辈们请给点指点!谢谢

我现在只是一个大一新生,对机器人特别感兴趣,但对这个领域了解甚少.想知道作为初学者应该从哪些基础方面着手.对了我学的专业是信息安全,也不知道有哪些专业课对此爱好有用. …

查看全部问答∨

arm Ulink调试出错

我用的是Keil uVision4版本,和ULINK2 编译正确后,调试时,弹出一个窗口:JTAG communication failure!点击确定后出现ERROR:Target DLL has been cancelled debug 和Utilities 哩也选了Ulink Cotex Debugger 选项 之前也调试过 可以成功下载 ...…

查看全部问答∨

2012黑龙江省赛区TI杯竞赛题

本帖最后由 paulhyde 于 2014-9-15 04:12 编辑 2012黑龙江省赛区TI杯竞赛题  …

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved