完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

第33章       STM32H7的定时器应用之TIM1-TIM17的中断实现

本章教程为大家讲解定时器应用之TIM1 – TIM17所有定时器的周期性中断实现。实际项目中用到的地方较多,特别是周期性的事件查询。

33.1 初学者重要提示

33.2 定时器中断的驱动设计

33.3 定时器板级支持包(bsp_tim_pwm.c)

33.4 定时器驱动移植和使用

33.5 实验例程设计框架

33.6 实验例程说明(MDK)

33.7 实验例程说明(IAR)

33.8 总结

33.1 初学者重要提示

  1. 学习本章节前,务必优先学习第32章,HAL库的几个常用API均作了讲解和举例。
  2. STM32H7支持TIM1-TIM8,TIM12-TIM17共14个定时器,而中间的TIM9,TIM10,TIM11是不存在的,这点要注意。
  3. STM32H7的进出中断的速度能跑到12.5MHz,所有程序在TCM和Flash运行没差别,详情可看本章2.3小节。
  4. 实际应用中,中断入口函数名称不要写错,有些中断的入口函数名称比较特殊,详情可看本章的2.2小节。

33.2 定时器中断的驱动设计

定时器中断的实现相对比较简单,仅需一个函数即可实现TIM1-TIM17定时器的中断更新配置。

33.2.1 定时器中断初始化

实现代码如下:

.    /*
2. ******************************************************************************************************
3. * 函 数 名: bsp_RCC_TIM_Enable
4. * 功能说明: 使能TIM RCC 时钟
5. * 形 参: 无
6. * 返 回 值: 无
7. ******************************************************************************************************
8. */
. void bsp_RCC_TIM_Enable(TIM_TypeDef* TIMx)
. {
. if (TIMx == TIM1) __HAL_RCC_TIM1_CLK_ENABLE();
. else if (TIMx == TIM2) __HAL_RCC_TIM2_CLK_ENABLE();
. else if (TIMx == TIM3) __HAL_RCC_TIM3_CLK_ENABLE();
. else if (TIMx == TIM4) __HAL_RCC_TIM4_CLK_ENABLE();
. else if (TIMx == TIM5) __HAL_RCC_TIM5_CLK_ENABLE();
. else if (TIMx == TIM6) __HAL_RCC_TIM6_CLK_ENABLE();
. else if (TIMx == TIM7) __HAL_RCC_TIM7_CLK_ENABLE();
. else if (TIMx == TIM8) __HAL_RCC_TIM8_CLK_ENABLE();
. // else if (TIMx == TIM9) __HAL_RCC_TIM9_CLK_ENABLE();
. // else if (TIMx == TIM10) __HAL_RCC_TIM10_CLK_ENABLE();
. // else if (TIMx == TIM11) __HAL_RCC_TIM11_CLK_ENABLE();
. else if (TIMx == TIM12) __HAL_RCC_TIM12_CLK_ENABLE();
. else if (TIMx == TIM13) __HAL_RCC_TIM13_CLK_ENABLE();
. else if (TIMx == TIM14) __HAL_RCC_TIM14_CLK_ENABLE();
. else if (TIMx == TIM15) __HAL_RCC_TIM15_CLK_ENABLE();
. else if (TIMx == TIM16) __HAL_RCC_TIM16_CLK_ENABLE();
. else if (TIMx == TIM17) __HAL_RCC_TIM17_CLK_ENABLE();
. else
. {
. Error_Handler(__FILE__, __LINE__);
. }
. }
.
. /*
35. ******************************************************************************************************
36. * 函 数 名: bsp_SetTIMforInt
37. * 功能说明: 配置TIM和NVIC,用于简单的定时中断,开启定时中断。另外注意中断服务程序需要由用户应
38. * 用程序实现。
39. * 形 参: TIMx : 定时器
40. * _ulFreq : 定时频率 (Hz)。 0 表示关闭。
41. * _PreemptionPriority : 抢占优先级
42. * _SubPriority : 子优先级
43. * 返 回 值: 无
44. ******************************************************************************************************
45. */
. void bsp_SetTIMforInt(TIM_TypeDef* TIMx, uint32_t _ulFreq, uint8_t _PreemptionPriority,
. uint8_t _SubPriority)
. {
. TIM_HandleTypeDef TimHandle = {};
. uint16_t usPeriod;
. uint16_t usPrescaler;
. uint32_t uiTIMxCLK;
.
. /* 使能TIM时钟 */
. bsp_RCC_TIM_Enable(TIMx);
.
. /*-----------------------------------------------------------------------
58. bsp.c 文件中 void SystemClock_Config(void) 函数对时钟的配置如下:
59.
60. System Clock source = PLL (HSE)
61. SYSCLK(Hz) = 400000000 (CPU Clock)
62. HCLK(Hz) = 200000000 (AXI and AHBs Clock)
63. AHB Prescaler = 2
64. D1 APB3 Prescaler = 2 (APB3 Clock 100MHz)
65. D2 APB1 Prescaler = 2 (APB1 Clock 100MHz)
66. D2 APB2 Prescaler = 2 (APB2 Clock 100MHz)
67. D3 APB4 Prescaler = 2 (APB4 Clock 100MHz)
68.
69. 因为APB1 prescaler != 1, 所以 APB1上的TIMxCLK = APB1 x 2 = 200MHz;
70. 因为APB2 prescaler != 1, 所以 APB2上的TIMxCLK = APB2 x 2 = 200MHz;
71. APB4上面的TIMxCLK没有分频,所以就是100MHz;
72.
73. APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14,LPTIM1
74. APB2 定时器有 TIM1, TIM8 , TIM15, TIM16,TIM17
75.
76. APB4 定时器有 LPTIM2,LPTIM3,LPTIM4,LPTIM5
77. ----------------------------------------------------------------------- */
. if ((TIMx == TIM1) || (TIMx == TIM8) || (TIMx == TIM15) || (TIMx == TIM16) || (TIMx == TIM17))
. {
. /* APB2 定时器时钟 = 200M */
. uiTIMxCLK = SystemCoreClock / ;
. }
. else
. {
. /* APB1 定时器 = 200M */
. uiTIMxCLK = SystemCoreClock / ;
. }
.
. if (_ulFreq < )
. {
. usPrescaler = - ; /* 分频比 = 10000 */
. usPeriod = (uiTIMxCLK / ) / _ulFreq - ; /* 自动重装的值 */
. }
. else if (_ulFreq < )
. {
. usPrescaler = - ; /* 分频比 = 100 */
. usPeriod = (uiTIMxCLK / ) / _ulFreq - ; /* 自动重装的值 */
. }
. else /* 大于4K的频率,无需分频 */
. {
. usPrescaler = ; /* 分频比 = 1 */
. usPeriod = uiTIMxCLK / _ulFreq - ; /* 自动重装的值 */
. }
.
. /*
106. 定时器中断更新周期 = TIMxCLK / usPrescaler + 1)/usPeriod + 1)
107. */
. TimHandle.Instance = TIMx;
. TimHandle.Init.Prescaler = usPrescaler;
. TimHandle.Init.Period = usPeriod;
. TimHandle.Init.ClockDivision = ;
. TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
. TimHandle.Init.RepetitionCounter = ;
. TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
. if (HAL_TIM_Base_Init(&TimHandle) != HAL_OK)
. {
. Error_Handler(__FILE__, __LINE__);
. }
.
. /* 使能定时器中断 */
. __HAL_TIM_ENABLE_IT(&TimHandle, TIM_IT_UPDATE);
.
.
. /* 配置TIM定时更新中断 (Update) */
. {
. uint8_t irq = ; /* 中断号, 定义在 stm32h7xx.h */
.
. if (TIMx == TIM1) irq = TIM1_UP_IRQn;
. else if (TIMx == TIM2) irq = TIM2_IRQn;
. else if (TIMx == TIM3) irq = TIM3_IRQn;
. else if (TIMx == TIM4) irq = TIM4_IRQn;
. else if (TIMx == TIM5) irq = TIM5_IRQn;
. else if (TIMx == TIM6) irq = TIM6_DAC_IRQn;
. else if (TIMx == TIM7) irq = TIM7_IRQn;
. else if (TIMx == TIM8) irq = TIM8_UP_TIM13_IRQn;
. else if (TIMx == TIM12) irq = TIM8_BRK_TIM12_IRQn;
. else if (TIMx == TIM13) irq = TIM8_UP_TIM13_IRQn;
. else if (TIMx == TIM14) irq = TIM8_TRG_COM_TIM14_IRQn;
. else if (TIMx == TIM15) irq = TIM15_IRQn;
. else if (TIMx == TIM16) irq = TIM16_IRQn;
. else if (TIMx == TIM16) irq = TIM17_IRQn;
. else
. {
. Error_Handler(__FILE__, __LINE__);
. }
. HAL_NVIC_SetPriority((IRQn_Type)irq, _PreemptionPriority, _SubPriority);
. HAL_NVIC_EnableIRQ((IRQn_Type)irq);
. }
.
. HAL_TIM_Base_Start(&TimHandle);
. }

程序中的注释已经比较详细,这里把几个关键的地方再阐释下:

  • 第9- 32行,函数bsp_RCC_TIM_Enable用于获取要使能的定时器时钟。
  • 第49行,HAL库的定时器句柄变量要初始化为0,这个问题在教程上一章的4.1小节有专门说明。
  • 第78 – 103行,计算出要配置的分频和周期。这里要注意一点,因为除了TIM2和TIM5,其它定时器都是16位的,相关寄存器大部分也都是16位的,配置的时候不可以超出0 -65535。这里分频变量usPrescaler和周期变量usPeriod统一按照16位计算,所以有了这几行代码做频率区分,防止超出范围。
  • 第108 – 118行,通过函数HAL_TIM_Base_Init初始化定时器的基本功能。
  • 第125 – 148行,配置定时器中断的优先级,并使能中断。

33.2.2 定时器中断服务程序

定时器初始化完毕了,定时器中断服务程序不要忘了写。比如使用定时器6的中断。

/*
*********************************************************************************************************
* 函 数 名: TIM6_DAC_IRQHandler
* 功能说明: TIM6定时中断服务程序
* 返 回 值: 无
*********************************************************************************************************
*/
void TIM6_DAC_IRQHandler(void)
{
if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)
{
/* 清除标志 */
TIM6->SR = ~ TIM_FLAG_UPDATE;
/* 添加用户程序 */
}
}

使用定时器中断不要把中断入口函数的名字写错了,比如这个定时器6,很容易错搞成TIM6__IRQHandler。

TIM1 – TIM17中断入口名如下(在startup_stm32h743xx.s文件里面有弱定义):

TIM1_BRK_IRQHandler
TIM1_UP_IRQHandler
TIM1_TRG_COM_IRQHandler
TIM1_CC_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM4_IRQHandler
TIM5_IRQHandler
TIM6_DAC_IRQHandler
TIM7_IRQHandler
TIM8_BRK_TIM12_IRQHandler /* 注意这里是TIM8 BRK和TIM12公用 */
TIM8_UP_TIM13_IRQHandler /* 注意这里是TIM8 UP和TIM13公用 */
TIM8_TRG_COM_TIM14_IRQHandler /* 注意这里是TIM8 TRG COM和TIM14公用 */
TIM8_CC_IRQHandler
TIM15_IRQHandler
TIM16_IRQHandler
TIM17_IRQHandler

33.2.3 定时器中断的最高频率

有时候我们希望定时器的中断服务程序执行频率越快越好,这样可以方便的在中断里面执行一些特定功能,比如控制引脚输出指定个数的IO脉冲,使用定时器中断就可以方便的实现。

测试时开启MDK的最高等级优化和时间优化。

测试下面情况下,性能没差别:

  • 程序在Flash运行,变量在DTCM,开启Cache。
  • 程序和变量都在DTCM运行。

中断部分的测试程序:

/*
*********************************************************************************************************
* 函 数 名: TIM6_DAC_IRQHandler
* 功能说明: TIM6定时中断服务程序
* 返 回 值: 无
*********************************************************************************************************
*/
void TIM6_DAC_IRQHandler(void)
{
TIM6->SR = ~TIM_FLAG_UPDATE;
//GPIOB->ODR ^= GPIO_PIN_1; /* 使用通用GPIO */
HC574_TogglePin(GPIO_PIN_23); /* 使用的FMC扩展IO */
}

测试结果是STM32H7的进出中断的速度能跑到12.5MHz,所有程序在TCM和Flash运行没差别。

IO翻转10MHz,方波频率5MHz:

IO翻转12.5MHz,方波频率6.25MHz:

12.5Hz是最高频率,实际应用中别跑这么高,因为这个频率下,程序基本一直在执行中断服务程序。实际应用做个1MHz及其以下还是没问题的。

33.3 定时器板级支持包(bsp_tim_pwm.c)

定时器驱动文件bsp_tim_pwm.c主要实现了如下两个API供用户调用:

  • bsp_SetTIMOutPWM
  • bsp_SetTIMforInt

这两个函数都是TIM1-TIM17所有定时器都支持,函数bsp_SetTIMOutPWM用于PWM,下个章节为大家讲解,本小节主要把函数bsp_SetTIMforInt做个说明。

33.3.1 函数bsp_SetTIMforInt

函数原型:

void bsp_SetTIMforInt(TIM_TypeDef* TIMx, uint32_t _ulFreq, uint8_t _PreemptionPriority, uint8_t _SubPriority)

函数描述:

此函数主要用配置定时器周期性中断。

函数参数:

  • 第1个参数用于指定使用那个定时器,参数可以是TIM1 – TIM17所有定时器(不含TIM9,TIM10和TIM11,因为STM32H7不支持这三个定时器)。
  • 第2个参数是要实现的定时器中断频率,单位Hz,如果填0的话,表示关闭。
  • 第3个参数是定时器抢占式优先级,范围0 – 15。
  • 第4个参数是定时器子优先级,范围0 – 15。

注意事项:

  1. 定时器中断频率最好别超过10MHz,本章2.3小节有说明。
  2. 初始化后,别忘了写对应的中断服务程序。

使用举例:

比如使用定时器6设置为20Hz频率, 周期0.05秒定时中断:

bsp_SetTIMforInt(TIM6, 20, 2, 0);

33.4 定时器驱动移植和使用

定时器的移植比较简单:

  • 第1步:复制bsp_tim_pwm.c和bsp_tim_pwm.h到自己的工程目录,并添加到工程里面。
  • 第2步:这几个驱动文件主要用到HAL库的GPIO和TIM驱动文件,简单省事些可以添加所有HAL库.C源文件进来。
  • 第3步:应用方法看本章节配套例子即可。

33.5 实验例程设计框架

通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:

第1阶段,上电启动阶段:

  • 这部分在第14章进行了详细说明。

  第2阶段,进入main函数:

  • 第1步,硬件初始化,主要是MPU,Cache,HAL库,系统时钟,滴答定时器,LED和串口。
  • 第2步,按键应用程序设计部分。
  • 定时器中断服务程序里面实现翻转LED4和FMC扩展引脚23。

33.6 实验例程说明(MDK)

配套例子:

V7-018_定时器周期性中断(驱动支持TIM1-TIM17)

实验目的:

  1. 学习定时器周期性中断实现,支持TIM1-TIM17所有定时器。

实验内容:

  1. 系统上电后驱动了1个软件定时器,每100ms翻转一次LED2,同时启动1个TIM6周期性中断,每50ms执行一次,在中断服务程序里面翻转LED4和FMC扩展引脚23。
  2. STM32H7支持TIM1-TIM8,TIM12-TIM17共14个定时器,而中间的TIM9,TIM10,TIM11是不存在的。
  3. STM32H7的进出中断的速度能跑到12.5MHz,所有程序在TCM和Flash运行没差别,实际应用中最好别超过1MHz。
  4. 中断入口函数名称不要写错,有些中断的入口函数名称比较特殊,详情可看V7开发板用户手册。

实验操作:

  1. K1按键按下,开启TIM6的周期性中断。
  2. K2按键按下,关闭TIM6的周期性中断。

FMC扩展引脚23的位置:

上电后串口打印的信息:

波特率 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();
}

定时器中断服务程序:

定时器6的中断服务程序如下,主要实现了LED4翻转和FMC扩展引脚23的翻转:

/*
*********************************************************************************************************
* 函 数 名: TIM6_DAC_IRQHandler
* 功能说明: TIM6定时中断服务程序
* 返 回 值: 无
*********************************************************************************************************
*/
void TIM6_DAC_IRQHandler(void)
{
if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)
{
/* 清除更新标志 */
TIM6->SR = ~ TIM_FLAG_UPDATE; /* 翻转LED4和FMC扩展引脚23 */
bsp_LedToggle();
HC574_TogglePin(GPIO_PIN_23);
}
}

主功能:

主程序实现如下操作:

  • K1按键按下,开启TIM6的周期性中断。
  • K2按键按下,关闭TIM6的周期性中断。
/*
*********************************************************************************************************
* 函 数 名: main
* 功能说明: c程序入口
* 形 参: 无
* 返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{
uint8_t ucKeyCode; /* 按键代码 */ bsp_Init(); /* 硬件初始化 */ PrintfLogo(); /* 打印例程名称和版本等信息 */
PrintfHelp(); /* 打印操作提示 */ bsp_StartAutoTimer(, ); /* 启动1个100ms的自动重装的定时器 */ bsp_SetTIMforInt(TIM6, , , ); /* 设置为20Hz频率, 周期0.05秒定时中断*/ /* 进入主程序循环体 */
while ()
{
bsp_Idle(); /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */ /* 判断定时器超时时间 */
if (bsp_CheckTimer())
{
/* 每隔50ms 进来一次 */
bsp_LedToggle();
} /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
if (ucKeyCode != KEY_NONE)
{
switch (ucKeyCode)
{
case KEY_DOWN_K1: /* K1键按下 */
TIM6->DIER |= TIM_IT_UPDATE;
break; case KEY_DOWN_K2: /* K2键按下 */
TIM6->DIER &= ~TIM_IT_UPDATE;
break; default:
/* 其它的键值不处理 */
break;
}
}
}
}

33.7 实验例程说明(IAR)

配套例子:

V7-018_定时器周期性中断(驱动支持TIM1-TIM17)

实验目的:

  1. 学习定时器周期性中断实现,支持TIM1-TIM17所有定时器。

实验内容:

  1. 系统上电后驱动了1个软件定时器,每100ms翻转一次LED2,同时启动1个TIM6周期性中断,每50ms执行一次,在中断服务程序里面翻转LED4和FMC扩展引脚23。
  2. STM32H7支持TIM1-TIM8,TIM12-TIM17共14个定时器,而中间的TIM9,TIM10,TIM11是不存在的。
  3. STM32H7的进出中断的速度能跑到12.5MHz,所有程序在TCM和Flash运行没差别,实际应用中最好别超过1MHz。
  4. 中断入口函数名称不要写错,有些中断的入口函数名称比较特殊,详情可看V7开发板用户手册。

实验操作:

  1. K1按键按下,开启TIM6的周期性中断。
  2. K2按键按下,关闭TIM6的周期性中断。

FMC扩展引脚23的位置:

上电后串口打印的信息:

波特率 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();
}

定时器中断服务程序:

定时器6的中断服务程序如下,主要实现了LED4翻转和FMC扩展引脚23的翻转:

/*
*********************************************************************************************************
* 函 数 名: TIM6_DAC_IRQHandler
* 功能说明: TIM6定时中断服务程序
* 返 回 值: 无
*********************************************************************************************************
*/
void TIM6_DAC_IRQHandler(void)
{
if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)
{
/* 清除更新标志 */
TIM6->SR = ~ TIM_FLAG_UPDATE; /* 翻转LED4和FMC扩展引脚23 */
bsp_LedToggle();
HC574_TogglePin(GPIO_PIN_23);
}
}

主功能:

主程序实现如下操作:

  • K1按键按下,开启TIM6的周期性中断。
  • K2按键按下,关闭TIM6的周期性中断。
/*
*********************************************************************************************************
* 函 数 名: main
* 功能说明: c程序入口
* 形 参: 无
* 返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{
uint8_t ucKeyCode; /* 按键代码 */ bsp_Init(); /* 硬件初始化 */ PrintfLogo(); /* 打印例程名称和版本等信息 */
PrintfHelp(); /* 打印操作提示 */ bsp_StartAutoTimer(, ); /* 启动1个100ms的自动重装的定时器 */ bsp_SetTIMforInt(TIM6, , , ); /* 设置为20Hz频率, 周期0.05秒定时中断*/ /* 进入主程序循环体 */
while ()
{
bsp_Idle(); /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */ /* 判断定时器超时时间 */
if (bsp_CheckTimer())
{
/* 每隔50ms 进来一次 */
bsp_LedToggle();
} /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
if (ucKeyCode != KEY_NONE)
{
switch (ucKeyCode)
{
case KEY_DOWN_K1: /* K1键按下 */
TIM6->DIER |= TIM_IT_UPDATE;
break; case KEY_DOWN_K2: /* K2键按下 */
TIM6->DIER &= ~TIM_IT_UPDATE;
break; default:
/* 其它的键值不处理 */
break;
}
}
}
}

33.8 总结

本章节就为大家讲解这么多,相对比较容易掌握,望初学者熟练运用。

【STM32H7教程】第33章 STM32H7的定时器应用之TIM1-TIM17的中断实现的更多相关文章

  1. 【STM32H7教程】第34章 STM32H7的定时器应用之TIM1-TIM17的PWM实现

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第34章       STM32H7的定时器应用之TIM1-T ...

  2. 【STM32H7教程】第22章 STM32H7的SysTick实现多组软件定时器

    完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980 第22章       STM32H7的SysTick实现 ...

  3. 【STM32H7教程】第32章 STM32H7的TIM定时器基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第32章       STM32H7的TIM定时器基础知识和H ...

  4. 【STM32H7教程】第60章 STM32H7的DAC应用之定时器触发实现DMA方式双通道波形

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第60章       STM32H7的DAC应用之定时器触发实 ...

  5. 【STM32H7教程】第30章 STM32H7的USART应用之八个串口FIFO实现

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第30章       STM32H7的USART应用之八个串口 ...

  6. 【STM32H7教程】第18章 STM32H7的GPIO应用之跑马灯

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第18章       STM32H7的GPIO应用之跑马灯 本 ...

  7. 【STM32H7教程】第54章 STM32H7的LTDC应用之LCD电阻触摸和电容触摸

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第54章       STM32H7的LTDC应用之LCD电阻 ...

  8. 【STM32H7教程】第12章 STM32H7的HAL库框架设计学习

    完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980 第12章       STM32H7的HAL库框架设计学 ...

  9. 【STM32H7教程】第8章 STM32H7的终极调试组件Event Recorder

    完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980 第8章   STM32H7的终极调试组件Event Re ...

随机推荐

  1. bs4-爬取小说

    bs4 bs4有两种运行方式一种是处理本地资源,一种是处理网络资源 本地 from bs4 import BeautifulSoup if __name__ == '__main__': fr = o ...

  2. 01-TensorFlow2.0基础

    01-TensorFlow基础 Tensorflow是什么 Google的开源软件库 采取数据流图,用于数值计算 支持多种平台 - GPU.CPU. 移动设备 最初用于深度学习,变得越来越通用 Ten ...

  3. #华为云·寻找黑马程序员#【代码重构之路】使用Pattern的正确姿势

    1.问题 在浏览项目时,发现一段使用正则表达式的代码 这段代码,在循环里执行了Pattern.matches()方法进行正则匹配判断. 查看matches方法的源码,可以看到 每调用一次matches ...

  4. 移动前端不得不了解的HTML5 head 头标签 —— link 标签

    目录 link 标签 rss订阅 不推荐的link标签 favicon 图标 link 标签 说到 link 标签,估计大家的第一反应和我一样,就是引入外部CSS样式文件的,不错,这是 link 标签 ...

  5. 用jquery实现楼层滚动对应导航高亮

    html 结构排版: // 定位到页面左侧或者右侧 <div class="nav">         <ul id="menu-list"& ...

  6. 爬虫学习(二)--爬取360应用市场app信息

    欢迎加入python学习交流群 667279387 爬虫学习 爬虫学习(一)-爬取电影天堂下载链接 爬虫学习(二)–爬取360应用市场app信息 代码环境:windows10, python 3.5 ...

  7. 使用 nginx 实现虚拟主机

    当多个系统需要部署的时候,有系统访问很小,为了节省成本,就需要将多个系统部署到同一台服务器上,怎么在同一台服务器上,完成不同系统的部署和访问,就需要使用虚拟主机实现. 使用端口实现虚拟主机 配置 ng ...

  8. css Backgroud-clip (文字颜色渐变)

    首先来瞄一下background-clip,这个属性是干嘛的? 顾名思义,背景裁剪...   按照我自己的理解就是背景的显示区域 此处粘上MDN的示例链接(嫌麻烦的,后面我也贴上截图)https:// ...

  9. nmap扫描进阶、msfconsole攻击入门(网安全实训第二天)

    本期内容:nmap扫描.msfconsole攻击入门 1. nmap扫描进阶 2.msfconsole攻击入门 1.nmap扫描进阶 (1)nmap命令 nmap --sP -iL abin.txt ...

  10. 通过Redis 实现分布式锁_利用Jedis 客户端

    前言 分布式锁一般有三种实现方式: 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁. 本篇博客将介绍第二种方式,基于Redis实现分布式锁. 虽然网上已经有各种介 ...