STM32通用定时器,端口复用和重映射

STM32定时器是一种内置在STM32微控制器中的硬件模块,用于测量和控制时间。它具有高精度、可配置性和灵活性的特点,能够支持多种不同的工作模式和应用场景。以下是对STM32定时器的详细讲解:

一、定时器的基本构成

STM32定时器主要由以下几个部分构成:

  • 预分频器(Prescaler):用于对定时器的时钟频率进行分频,以降低计数器的计数速度,从而实现更长时间的定时。
  • 计数器(Counter):对预分频后的时钟信号进行计数,当计数值达到预设的自动重装载值(ARR)时,产生溢出事件或中断。
  • 自动重装载寄存器(Auto-reload Register, ARR):用于存储计数器的重装载值,当计数器溢出时,自动将重装载寄存器的值加载到计数器中,以实现周期性定时。

二、定时器的分类

STM32定时器根据其功能和特点可以分为以下几类:

  • 基本定时器(Basic Timers):功能较为简单,通常用于基本的计时和触发任务,如定时、触发输出直接驱动DAC等。
  • 通用定时器(General-Purpose Timers):提供了更多的功能和配置选项,包括PWM生成、脉冲计数、输入捕获、输出比较等功能。适用于需要更复杂的定时功能的应用,如PWM控制、频率测量、脉冲计数等。

特点:

  • 高级定时器(Advanced-Control Timers):除了通用定时器的功能外,还支持更高级的特性,如相位锁定回路(PLL)、编码器接口、三角波生成等。适用于需要更高级、复杂计时功能的应用,如音频处理、电机控制、编码器接口等。

三、定时器的工作原理

STM32定时器的工作原理基于计数器的计数功能。当定时器的时钟源开始工作时,计数器开始按照预设的计数方向和速率进行计数。当计数值达到自动重装载寄存器的值时,产生溢出事件或中断,同时计数器可以选择性地重新从0开始计数,以实现周期性定时。

四、定时器的配置要点

在配置STM32定时器时,需要注意以下几个要点:

  • 时钟源选择:定时器可以选择内部时钟或外部时钟作为时钟源。内部时钟通常来源于系统时钟(如APB1或APB2总线时钟),而外部时钟可以来自定时器引脚或其他外设的输出。
  • 计数模式选择:定时器支持向上计数、向下计数和中心对齐计数等多种计数模式。不同的计数模式适用于不同的应用场景。

  • 预分频值和重装载值设置:通过设置预分频值和重装载值,可以控制定时器的定时周期和精度。预分频值用于对时钟频率进行分频,而重装载值则决定了计数器的计数上限。
  • 中断和DMA配置:定时器可以配置为在溢出时产生中断或DMA请求,以便在定时结束后执行相应的处理函数或数据传输操作。

五、应用实例

通过中断控制LED灯闪烁

需要掌握的:

TIM_TimeBaseInitTypeDef

这个结构体用于配置定时器的基本时间基准参数,包括预分频器、计数器模式、自动重装载值、时钟分频和重复计数器(仅适用于TIM1和TIM8)。

  • TIM_Prescaler:预分频器值,用于将定时器时钟源进行分频,以减慢计数器的计数速度。
  • TIM_CounterMode:计数器模式,控制计数器的计数方向(向上、向下或中心对齐模式)。
  • TIM_Period:自动重装载值,当计数器达到此值时,将触发更新事件,并将此值重新加载到计数器中,实现周期性计数。
  • TIM_ClockDivision:时钟分频,用于控制定时器内部时钟的进一步分频,影响定时器的某些功能(如死区时间、更新事件等)的分辨率。
  • TIM_RepetitionCounter:重复计数器值,用于在更新事件时重复计数,常用于PWM模式中以控制PWM周期的数量。此参数仅对TIM1和TIM8有效。

NVIC_InitTypeDef

结构体是用于STM32微控制器中,特别是在配置嵌套向量中断控制器(NVIC)时使用的。这个结构体包含了配置一个中断通道所需的所有参数。下面是对每个成员的详细解释:

  1. uint8_t NVIC_IRQChannel;
    • 这个成员指定了要启用或禁用的中断通道。它的值应该来自于IRQn_Type枚举类型,这个枚举类型在STM32的库文件(如stm32f10x.h对于STM32F1系列)中定义,列出了所有可用的中断请求(IRQ)通道。
  2. uint8_t NVIC_IRQChannelPreemptionPriority;
    • 这个成员指定了NVIC_IRQChannel指定的中断通道的抢占优先级。STM32的NVIC支持基于优先级的嵌套中断,抢占优先级决定了中断是否能够打断另一个正在执行的中断。这个值的范围是从0到15,数值越小,优先级越高。
  3. uint8_t NVIC_IRQChannelSubPriority;
    • 这个成员指定了NVIC_IRQChannel指定的中断通道的子优先级。在抢占优先级相同的情况下,子优先级用于决定中断的执行顺序。这个值的范围也是从0到15,数值越小,优先级越高。
  4. FunctionalState NVIC_IRQChannelCmd;
    • 这个成员指定了NVIC_IRQChannel指定的中断通道是启用还是禁用。FunctionalState是一个枚举类型,通常有两个值:ENABLEDISABLE。这个成员用于控制中断的使能状态。

主函数:
 

/**  
 * @brief TIM2定时器更新中断服务例程  
 *   
 * 当TIM2定时器发生更新事件(即计数器从最大值回绕到0时)时,此函数会被自动调用。  
 * 它利用定时器的更新中断来切换连接到GPIOA端口的第1个引脚(GPIO_Pin_1)的电平状态。  
 * 这可以用于生成简单的方波信号或进行周期性的任务处理。  
 */  
void TIM2_IRQHandler(void)  
{  
    static uint16_t temp; // 定义一个静态变量temp,用于在中断之间保持状态  
  
    // 检查是否发生了TIM2的更新中断  
    if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)  
    {  
        // 如果确实发生了更新中断  
  
        // 使用temp变量来切换状态,每次中断时temp自增  
        // 如果temp是奇数,则执行一个分支;如果是偶数,则执行另一个分支  
        if(temp++ % 2 == 1)  
        {  
            // 如果temp是奇数,将GPIOA的第1个引脚置为低电平  
            GPIO_ResetBits(GPIOA, GPIO_Pin_1);  
              
        }  
        else  
        {  
            // 如果temp是偶数,将GPIOA的第1个引脚置为高电平  
            GPIO_SetBits(GPIOA, GPIO_Pin_1);  
        }  
  
        // 清除TIM2的更新中断标志,以便允许下一个中断被识别  
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);  
    }  
  
    // 注意:在大多数情况下,不需要在这里添加else分支,  
    // 因为如果TIM_GetITStatus返回RESET,则没有必要执行任何操作。  
    // 但是,如果你希望在未发生更新中断时执行某些操作,可以在这里添加代码。  
}

定时器函数:

/**  
 * @brief 配置TIM2定时器  
 *   
 * 这个函数负责初始化STM32的TIM2定时器,包括时钟源、预分频器、自动重装载值、计数器模式等,  
 * 并启用定时器的更新中断,以及配置NVIC以允许定时器中断。  
 */  
void tim_config(void)  
{  
    TIM_TimeBaseInitTypeDef timinitstructer; // 定义一个定时器基本初始化结构体变量  
    NVIC_InitTypeDef nvicinitstruct;         // 定义一个NVIC初始化结构体变量  
  
    // 使能TIM2定时器的时钟  
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);  
  
    // 配置NVIC的优先级分组为NVIC_PriorityGroup_1  
    // 这会影响所有中断的优先级设置方式  
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);  
  
    // 配置TIM2定时器的基本参数  
    timinitstructer.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频因子设置为不分频  
    timinitstructer.TIM_CounterMode = TIM_CounterMode_Up; // 计数器模式设置为向上计数  
    timinitstructer.TIM_Period = 10000 - 1; // 自动重装载寄存器周期值,这里设置为10000(实际计数到9999)  
    timinitstructer.TIM_Prescaler = 7200 - 1; // 预分频器值,这里设置为7200(实际分频7199)  
    // 注意:TIM_RepetitionCounter 字段在某些定时器中用于高级控制模式,如PWM重复计数,这里不适用  
    TIM_TimeBaseInit(TIM2, &timinitstructer); // 使用上述配置初始化TIM2定时器  
  
    // 启用TIM2定时器的更新中断  
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);  
  
    // 启用TIM2定时器  
    TIM_Cmd(TIM2, ENABLE);  
  
    // 配置NVIC以允许TIM2中断  
    nvicinitstruct.NVIC_IRQChannel = TIM2_IRQn; // 指定中断通道为TIM2中断  
    nvicinitstruct.NVIC_IRQChannelCmd = ENABLE; // 使能该中断通道  
    nvicinitstruct.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级设置为1  
    nvicinitstruct.NVIC_IRQChannelSubPriority = 1; // 子优先级设置为1(在NVIC_PriorityGroup_1中,子优先级可能不被使用)  
    NVIC_Init(&nvicinitstruct); // 使用上述配置初始化NVIC  
}

理解为什么这段代码能够让定时器“看起来”一秒一秒地执行,需要关注几个关键点:定时器的预分频器(Prescaler)、自动重装载值(Period)、以及STM32的时钟系统。

首先,让我们回顾一下tim_config函数中的关键设置:

  1. 预分频器(Prescaler):这个值用于将定时器的时钟源(通常是系统时钟经过APB1预分频后的时钟)进一步分频。例如,如果系统时钟是72MHz,并且APB1预分频器设置为1(意味着APB1时钟与系统时钟相同),然后TIM2的预分频器设置为7199(因为你写的是7200-1),那么TIM2的计数器时钟将是72MHz / (7199 + 1) = 10kHz。

  2. 自动重装载值(Period):这个值定义了定时器计数器在达到哪个值时会触发更新事件(通常是回绕到0并生成更新中断),并重新开始计数。在你的代码中,这个值被设置为9999(因为你写的是10000-1)。因此,从0计数到9999需要的时间是10kHz的倒数乘以10000,即0.0001秒 * 10000 = 1秒。

现在,让我们看看TIM2_IRQHandler函数是如何工作的:

  • 当定时器计数器从9999回绕到0时,会生成一个更新中断。
  • TIM2_IRQHandler函数被调用,并检查是否确实发生了更新中断。
  • 如果是,它会切换一个GPIO引脚的状态(在这个例子中是GPIOA的第1个引脚),并清除中断标志以准备下一次中断。
  • 由于定时器的周期被设置为1秒(基于预分频器和自动重装载值的设置),因此这个GPIO引脚的状态会每秒切换一次,从而“看起来”像是定时器一秒一秒地执行。

然而,这里有一点需要注意:虽然定时器的周期是精确的1秒(基于其配置),但GPIO引脚状态的切换(即方波的生成)的精确性还受到中断响应时间和中断处理函数执行时间的影响。在大多数情况下,这些影响是可以忽略不计的,但在对时间要求非常严格的应用中,可能需要考虑这些因素。

另外,请注意,temp变量在这个特定的例子中实际上并没有直接用于控制定时器的周期或频率。它仅用于在每次中断时切换GPIO引脚的状态。如果你想要的是纯粹的定时器功能(即不需要GPIO切换),那么temp变量和相关的条件判断就不是必需的。不过,在你的例子中,它用于生成一个周期为1秒的方波信号,从而实现LED灯的闪烁。

端口复用

定义
STM32的每个端口引脚通常具有多种功能,例如GPIO(通用输入输出)、USART(通用同步异步收发传输器)、TIM(定时器)等。端口复用是指将一个引脚从默认的GPIO功能切换到其他功能,如USART的TX(发送)或RX(接收)等。

实现步骤

使能GPIO端口时钟和外设时钟
首先,需要使能包含USART1 TX和RX引脚的GPIO端口(如果是PA9和PA10,则是GPIOA)的时钟,以及USART1的时钟。

配置GPIO引脚
将GPIO引脚(PA9和PA10)配置为复用推挽输出(对于TX)和浮空输入(对于RX)。注意,在STM32的库中,复用模式通常是通过设置GPIO的模式为GPIO_Mode_AF_PP(复用推挽输出)或GPIO_Mode_IN_FLOATING(浮空输入,但RX通常不需要显式设置,因为它在USART初始化时自动配置)来实现的。然而,对于USART的RX,你通常会设置它为GPIO_Mode_AF_IPU(复用输入上拉),但这取决于你的具体需求和硬件配置。

初始化USART
最后,初始化USART1,设置波特率、数据位长度、停止位等参数。在这个过程中,库函数会自动将TX和RX引脚配置为它们的外设功能模式。

重映射

定义
重映射功能允许开发者将一个外设的引脚映射到其他引脚,以适应不同的硬件布局需求。这在进行硬件设计时特别有用,可以解决引脚冲突或优化引脚布局。

实现步骤

使能AFIO时钟(对于某些STM32系列,如STM32F1,需要使能AFIO时钟以访问重映射功能。但在更新的STM32系列中,这一步骤可能已被简化或取消):通过配置RCC(复位和时钟控制)模块,使能包含重映射寄存器的时钟。

配置重映射寄存器:使用STM32的库函数或直接操作重映射寄存器,将外设的引脚映射到新的位置。这通常涉及到设置特定的重映射位或选择重映射选项。

注意

  • 复用与重映射的区别:复用是针对单个引脚,改变其功能;而重映射是针对一组引脚,改变其物理位置。
  • 部分重映射与完全重映射:部分重映射只改变部分功能引脚的位置,而完全重映射则改变所有功能引脚的位置。
    • 在进行重映射时,需要注意不要将同一功能映射到多个引脚上,因为这会导致冲突。、

  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值