STM32F10x定时器中断

文章详细介绍了STM32F10x系列微控制器中定时器的中断配置,包括TIM_TimeBaseInit(),TIM_ITConfig()和TIM_Cmd()函数的使用,以及如何通过定时器控制LED灯的翻转。还涉及到了与NVIC中断管理的相关内容。
摘要由CSDN通过智能技术生成

前言

stm32f10x的定时器功能十分丰富,光是寄存器就已经超过15个了,应用十分灵活,本文仅仅讨论定时器中断这个简单的功能,暂不讨论定时器的PWM、捕获等功能。

所涉及寄存器:

  • 控制寄存器 1(TIMx_CR1)
  • DMA/中断使能寄存器(TIMx_DIER)
  • 自动重装载寄存器(TIMx_ARR)
  • 预分频寄存器(TIMx_PSC)
  • 重复计数寄存器(TIMx_RCR)
  • 事件产生寄存器(TIMx_EGR)

所涉及函数:

  • TIM_TimeBaseInit()
  • TIM_ITConfig()
  • TIM_Cmd();

当然,要想在stm32里使用中断不可避免的要和NVIC打交道,不过它不是本章的重点。

TIM_TimeBaseInit()函数

其源码如下:

void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
{
  uint16_t tmpcr1 = 0;

  /* Check the parameters */
  assert_param(IS_TIM_ALL_PERIPH(TIMx)); 
  assert_param(IS_TIM_COUNTER_MODE(TIM_TimeBaseInitStruct->TIM_CounterMode));
  assert_param(IS_TIM_CKD_DIV(TIM_TimeBaseInitStruct->TIM_ClockDivision));

  tmpcr1 = TIMx->CR1;  

  if((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM2) || (TIMx == TIM3)||
     (TIMx == TIM4) || (TIMx == TIM5)) 
  {
    /* Select the Counter Mode */
    tmpcr1 &= (uint16_t)(~((uint16_t)(TIM_CR1_DIR | TIM_CR1_CMS)));
    tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode;
  }
 
  if((TIMx != TIM6) && (TIMx != TIM7))
  {
    /* Set the clock division */
    tmpcr1 &= (uint16_t)(~((uint16_t)TIM_CR1_CKD));
    tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_ClockDivision;
  }

  TIMx->CR1 = tmpcr1;

  /* Set the Autoreload value */
  TIMx->ARR = TIM_TimeBaseInitStruct->TIM_Period ;
 
  /* Set the Prescaler value */
  TIMx->PSC = TIM_TimeBaseInitStruct->TIM_Prescaler;
    
  if ((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM15)|| (TIMx == TIM16) || (TIMx == TIM17))  
  {
    /* Set the Repetition Counter value */
    TIMx->RCR = TIM_TimeBaseInitStruct->TIM_RepetitionCounter;
  }

  /* Generate an update event to reload the Prescaler and the Repetition counter
     values immediately */
  TIMx->EGR = TIM_PSCReloadMode_Immediate;           
}

这段代码干的事情主要是:

  1. 获取当前定时器控制寄存器的状态字
tmpcr1 = TIMx->CR1;
  1. 清空该定时器控制寄存器的位4,即DIR位位5和位6,即CMS位,再根据所选用的计数模式重新配置这3个位
tmpcr1 &= (uint16_t)(~((uint16_t)(TIM_CR1_DIR | TIM_CR1_CMS)));
tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode;

这里需要配合相关宏定义和寄存器说明表来理解:

#define  TIM_CR1_DIR                         ((uint16_t)0x0010)            /*!< Direction */
#define  TIM_CR1_CMS                         ((uint16_t)0x0060)            /*!< CMS[1:0] bits (Center-aligned mode selection) */

#define TIM_CounterMode_Up                 ((uint16_t)0x0000)
#define TIM_CounterMode_Down               ((uint16_t)0x0010)
#define TIM_CounterMode_CenterAligned1     ((uint16_t)0x0020)
#define TIM_CounterMode_CenterAligned2     ((uint16_t)0x0040)
#define TIM_CounterMode_CenterAligned3     ((uint16_t)0x0060)
#define IS_TIM_COUNTER_MODE(MODE) (((MODE) == TIM_CounterMode_Up) ||  \
                                   ((MODE) == TIM_CounterMode_Down) || \
                                   ((MODE) == TIM_CounterMode_CenterAligned1) || \
                                   ((MODE) == TIM_CounterMode_CenterAligned2) || \
                                   ((MODE) == TIM_CounterMode_CenterAligned3))

在这里插入图片描述
在这里插入图片描述

tmpcr1 &= (uint16_t)(~((uint16_t)(TIM_CR1_DIR | TIM_CR1_CMS)));

这句代码:

(uint16_t)(TIM_CR1_DIR | TIM_CR1_CMS)) =  (uint16_t)(0x60|0x10) = 0000 0000 0111 0000(二进制)
然后取反即:
1111 1111 1000 1111
再执行
tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode;
就把此时的计数模式赋给了该寄存器
  1. 配置时钟分频系数
tmpcr1 &= (uint16_t)(~((uint16_t)TIM_CR1_CKD));
tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_ClockDivision;

这里的操作和上一个步骤同理。

  1. 然后把配置信息写入控制寄存器里
TIMx->CR1 = tmpcr1;
  1. 设置定时器的自动重装载值和分频系数
/* Set the Autoreload value */
TIMx->ARR = TIM_TimeBaseInitStruct->TIM_Period ;
/* Set the Prescaler value */
TIMx->PSC = TIM_TimeBaseInitStruct->TIM_Prescaler;
  1. 查看是否需要设置重复计数次数
/* Set the Repetition Counter value */
    TIMx->RCR = TIM_TimeBaseInitStruct->TIM_RepetitionCounter;
  1. 产生一个更新中断来重装载用户配置的重复计数信息和自动重装载值
/* Generate an update event to reload the Prescaler and the Repetition counter
     values immediately */
TIMx->EGR = TIM_PSCReloadMode_Immediate;  
如何计算每次中断的时间

首先算出来咱们每计数一次所需要的时间:
f 计数频率 f_{计数频率} f计数频率 = f 主频 f_{主频} f主频 / (分频系数+1)
比如,我的stm32f103主频72MHz,分频系数为719
那么 f 计数频率 f_{计数频率} f计数频率 = 72MHz / (71+1)= 0.1MHz = 100KHz
每次计数所需要的时间 t 0 t_0 t0 为:1/1MHz = 0.00001s

然后再看ARR的设定值,以999为例子,那么每次进入中断需要(999+1) t 0 t_0 t0的时间,也就是0.01s

于是有了一个公式:
t 中断 t_{中断} t中断 = (分频系数+1)×(ARR+1) / f 主频 f_{主频} f主频

TIM_ITConfig()TIM_Cmd()

这两个函数就很简单了

void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState)
{  
  /* Check the parameters */
  assert_param(IS_TIM_ALL_PERIPH(TIMx));
  assert_param(IS_TIM_IT(TIM_IT));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  
  if (NewState != DISABLE)
  {
    /* Enable the Interrupt sources */
    TIMx->DIER |= TIM_IT;
  }
  else
  {
    /* Disable the Interrupt sources */
    TIMx->DIER &= (uint16_t)~TIM_IT;
  }
}

TIM_ITConfig()这个函数操控中断使能寄存器来使定时器中断被开启

void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_TIM_ALL_PERIPH(TIMx));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  
  if (NewState != DISABLE)
  {
    /* Enable the TIM Counter */
    TIMx->CR1 |= TIM_CR1_CEN;
  }
  else
  {
    /* Disable the TIM Counter */
    TIMx->CR1 &= (uint16_t)(~((uint16_t)TIM_CR1_CEN));
  }
}

TIM_Cmd()这个函数操控控制寄存器 1(TIMx_CR1)来开启和关闭定时器。

感谢您的阅读

下附一个定时器翻转LED灯代码与视频:

void TIM2_Int_Init(u16 arr,u16 psc)
{
  	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	TIM_TimeBaseStructure.TIM_Period = arr; 
	TIM_TimeBaseStructure.TIM_Prescaler =psc; 
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); 
 
	TIM_ITConfig(TIM2,TIM_IT_Update,DISABLE);

	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; 
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure); 

	TIM_Cmd(TIM2, DISABLE);			 
}
u8 LED_state = LED_ON;
int main(void)
{
	LED_Init();
	LED_Set(LED_state);
	TIM2_Int_Init(719,999);// 720 * 1000 / 72 000 000 = 0.01 s
	TIM_ITConfig(TIM2, TIM_IT_Update,ENABLE);
  	TIM_Cmd(TIM2, ENABLE);
	while (1)
	{
	}
}
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
		count++;
			
		if(count>=50)
		{
			LED_state = 1 - LED_state;
			LED_Set(LED_state);
			count = 0;
		}
	}
}

定时器中断LED翻转

  • 25
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面介绍 STM32F10x 实现基本定时器实验的步骤。 ### 1. 硬件连接 将 STM32F10x 系列单片机的定时器外设与外部 LED 灯相连,例如将 PA0 引脚连接到 LED 正极,将 GND 引脚连接到 LED 负极。 ### 2. 创建工程 使用 Keil 或者其他开发工具创建一个 STM32F10x 工程,并选择对应的芯片型号。 ### 3. 配置时钟 在 `main()` 函数中调用 `SystemClock_Config()` 函数配置系统时钟。例如,如果你想将系统时钟配置为 72MHz,可以按照以下代码进行配置: ```c static void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } ``` ### 4. 初始化定时器 在 `main()` 函数中,首先需要初始化定时器外设,例如: ```c TIM_HandleTypeDef htim; void TIM_Config(void) { TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; htim.Instance = TIM1; htim.Init.Prescaler = 7199; // 定时器时钟预分频值 htim.Init.CounterMode = TIM_COUNTERMODE_UP; htim.Init.Period = 999; // 定时器周期为 1 秒 htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(&htim); sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; HAL_TIM_ConfigClockSource(&htim, &sClockSourceConfig); sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig); } ``` 在上面的代码中,我们使用了 TIM1 定时器,配置了时钟预分频值为 7199,定时器周期为 1 秒。 ### 5. 启动定时器 在 `main()` 函数中,调用 `TIM_Config()` 函数初始化定时器,并调用 `HAL_TIM_Base_Start_IT(&htim)` 函数启动定时器并开启定时器中断。 ```c int main(void) { HAL_Init(); SystemClock_Config(); TIM_Config(); // 配置定时器 HAL_TIM_Base_Start_IT(&htim); // 启动定时器并开启定时器中断 while (1) { // 循环执行其他代码 } } ``` ### 6. 中断处理函数 在 `stm32f10x_it.c` 文件中实现定时器中断处理函数,并在其中控制 LED 灯的亮灭。例如,以下代码实现了每隔 1 秒钟交替点亮和熄灭 LED 灯的功能: ```c #include "stm32f10x_it.h" void TIM1_UP_IRQHandler(void) { HAL_TIM_IRQHandler(&htim); // 调用 HAL 库中的定时器中断处理函数 } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM1) // 判断是哪个定时器中断 { static uint8_t state = 0; // LED 灯状态,0 表示熄灭,1 表示点亮 if (state) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // 熄灭 LED state = 0; } else { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); // 点亮 LED state = 1; } } } ``` 在上面的代码中,我们在 `HAL_TIM_PeriodElapsedCallback()` 函数中判断定时器中断是哪个定时器,并在其中交替点亮和熄灭 LED 灯。 至此,STM32F10x 实现基本定时器实验的步骤就介绍完毕了。需要注意的是,如果你使用的是其他的定时器外设,具体的步骤和代码会有所不同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值