STM32F10x 学习笔记10(基本定时器)

大容量的STM32F101xx、STM32F103xx系列和互联型产品中包含了两个基本定时器,TIM6TIM7。这两个定时器功能非常简单,基本上和SysTick 定时器相当。

但是我在使用的时候确是费了一番周折才调试通过,所以还是应该将这两个定时器的使用方法好好的写一写。

基本定时器TIM6TIM7各包含一个16位自动装载计数器,由各自的可编程预分频器驱动。简单的说两个定时器是从0计数到N(由自动装载计数器来确定N的具体值),然后复位回重新计数。每次复位到的时候可以产生对应的中断信号,或者产生DMA 请求,还可以触发DAC 同步电路。不过这次笔记不讨论触发DAC 的问题,等写到DAC 的使用时在讲解如何用这两定时器驱动DAC

基本定时器TIM6TIM7挂载在APB1总线上,在使用之前需要先使能对应的时钟信号。对应的语句如下:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); 
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE); 

APB1 总线上的时钟信号在进入定时器之后第一件事情是被预分频器TIMx_PSC分频。程序中随时都可以更改TIMx_PSC的值,但是TIMx_PSC是有缓冲的,只有发生了更新事件时新的预分频值才会生效。

另外有一点必须要特别注意:如果我们想对 CK_PSC 时钟信号分频的话,TIMx_PSC 中应该写入 M - 1,而不是 M

对应的语句如下:

TIMx->PSC = M - 1;

如果我们希望立刻更新预分频器也是有办法的,那就是软件触发一次更新事件。TIM6TIM7有个事件产生寄存器(TIMx_EGR) 就是起这个作用的。TIMx_EGR 只有最低位有用,称之为 UG 位,向这一位写入 则产生更新事件,定时器的计数器被归零。

对应的语句如下:

TIMx->EGR = 0x0001;

自动重装载寄存器TIMx_ARR决定了CNT 计数器最高记到多少就会归零。自动重装载寄存器也是带缓存的。如果TIMx_CR1寄存器中的自动重装载预加载使能位(ARPE)0,则写入自动重装载寄存器的值立即起作用。如果ARPE = 1,则要等到发生了更新事件后才会起作用。举例来说,当前自动重装载寄存器的值为20000CNT 计数器的值为1000ARPE = 1,这时我们向自动重装载寄存器写个 5000CNT 计数器记到 5000 时并不会归零,它还要继续直到记到20000然后归零,在归零的同时产生计数器溢出事件,计数器溢出事件可以产生更新事件,之后自动重装载寄存器的值才真的变为5000了。

这里出现了两个事件:计数器溢出事件和更新事件。这两个实际是不同的。每次当计数器溢出时都会产生计数器溢出事件,但是不一定产生更新事件。

TIM6TIM7控制寄存器1(TIMx_CR1) 有一位UDIS:禁止更新 (Update disable)位。

如果这位被设置为1,则计数器溢出事件不会产生更新事件。不产生更新事件则预分频器的系数不能被更新,自动重装载寄存器的值也可能不能更新(ARPE = 1)。

只是产生更新事件是没有用的,我们最终需要的是产生中断或DMA请求。这里先说中断的产生。

TIM6TIM7 DMA/中断使能寄存器(TIMx_DIER) 有一位为UIE。只有这一位为1时才能产生更新中断。

对应的语句如下:

TIMx->DIER |= 0x0001;

TIM6TIM7状态寄存器(TIMx_SR) 中也只有一位是有用的,位于这个寄存器的最低位,称之为UIF位。这一位为标志着产生了更新中断。什么时候这一位才能为1呢?简单的说就是产生更新事件这一位会被置位为1,不过也有特殊情况,我们知道有两种情况会产生更新事件:计数器溢出事件和 UG=1。如果TIMx_CR1URS位被设为了1,则UG=1 产生的更新事件不会将UIF位置位1

一般来说我们都不希望UG=1时产生中断,所以多数时间我们会设置URS位为1

对应的语句如下:

TIMx->CR1 |= 0x0004;

下面是一个例子程序,使用TIM6,产生一个5Hz的定时中断。时钟频率为72MHz

void TIM6_Init(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); 
	TIM6->PSC = 7200 - 1; // 分频之后的时钟频率为10KHz
	TIM6->ARR = 2000 - 1; // 5Hz 定时频率
	TIM6->CR1 = 0x0004 + 0x0001; // URS = 1 CEN = 1
	TIM6->DIER = 0x0001; // 使能更新中断
}
void TIM6_IRQHandler(void)
{
	LED_Flash(GPIO_LED2);
	TIM6->SR=0x0000;//清标志位
}

还可以完全用STM32F10x Standard Peripherals Firmware Library 来写这个程序。下面用TIM7 实现类似的功能。

void TIM7_Init(void)
{	
	TIM_TimeBaseInitTypeDef  TIM7_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn; 
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; 
	NVIC_Init(&NVIC_InitStructure);
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);  //RCC_APB1Periph_TIM7

	TIM_TimeBaseStructInit(&TIM7_TimeBaseStructure);
	TIM7_TimeBaseStructure.TIM_Prescaler = 36000 - 1;  // 分频之后的时钟频率为2KHz
	TIM7_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM7_TimeBaseStructure.TIM_Period = 400 - 1;      // 5Hz 定时频率
	TIM7_TimeBaseStructure.TIM_ClockDivision = 0;
	TIM_TimeBaseInit(TIM7, &TIM7_TimeBaseStructure);
	
	TIM_UpdateRequestConfig(TIM7, TIM_UpdateSource_Regular);
	 
	TIM_Cmd(TIM7, ENABLE);
	TIM_ITConfig(TIM7, TIM_IT_Update, ENABLE);
}

void TIM7_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM7, TIM_IT_Update)== SET)
	{
		LED_Flash(GPIO_LED3);
		TIM_ClearITPendingBit(TIM7 , TIM_FLAG_Update);
	}
}

程序写完了,这里说几点注意事项。

(1)在对TIMx 进行设置之前一定要打开APB1总线上对应的时钟,否则所有的设置都不起作用。

(2)一定要设置NVIC,因为默认情况下这些外设的中断都是被禁用的。


  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,下面介绍 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 实现基本定时器实验的步骤就介绍完毕了。需要注意的是,如果你使用的是其他的定时器外设,具体的步骤和代码会有所不同。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值