STM32--寄存器与标准库函数--基本定时器

目录

前言

基本定时器概念

定时时间

定时器时钟确定

倍频锁相环被正确配置为168MHz

定时器的库函数操作

代码

定时器的寄存器操作

代码

寄存器

后言


前言

使用平台:STM32F407ZET6

使用数据手册:STM32F407数据手册.pdf

使用参考手册:STM32F4xx参考手册(中文).pdf

使用cortex-M手册:Cortex M3与M4权威指南.pdf

定时器的作用一般是为了使用定时功能和中断功能(洗衣机、微波炉、电风扇、智能空调......),当然在STM32中也可以利用定时器产生周期性的脉冲信号来控制不同的外设(电机的转速、舵机的角度、灯光的亮度........),所以掌握STM32中的定时器对于项目开发是很有必要的。

定时器有多种,常用的外设定时器有基本定时器、通用定时器与高级定时器。 对于STM32F407微处理器而言,内部一共集成了14 个定时器,其中有2个基本定时器(TIM6和TIM7)、10个通用定时器(TIM2~TIM5、TIM9~TIM14)、2个高级定时器(TIM1和TIM8)。其中通用定时器TIM2和TIM5为32位定时器,其他为16位定时器,当然,定时器位数越大,定时时间越久。2的32次方--4 294 967 296,2的16次方65 536。

基本定时器概念

本文使用的是基本定时器,基本定时器具有16位 的自动重载计数器(TIM_Period)与16位 的预分频器(TIM_Prescaler)。并且自动重载计数器只能递增计数( TIM_CounterMode_Up**),发生更新事件(** TIM_IT_Update**)会生成中断/DMA请求(计数器上溢)。**

定时时间

定时时间需要三个参数决定,定时器的时钟频率、预分频器的值与自动重载计数器的值。

这里我提供一下我写的自动计算填的值代码(这个只匹配16位的定时器哦,32位的不一定适用哦):

.\main.exe 84 1 8400这样使用即可,第一个参数84是定时器的频率以MHZ为单位,第二个参数是需要定时的时间,这里以秒为单位,第三个参数可以选择填自己确定的预分配值或者自动重载值即可。这里可以得到匹配的自动重载值或者预分频值(在正式填的时候,不要忘记减1)

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**
 * @brief 获得分频系数或者计数器
 * 
 * @param argc 
 * @param argv 
 * @return int 
 */
int main(int argc, char const *argv[])
{
    if (argc != 4) // 需要两个参数:时间 和 预分频
        return -1;

    int clock = atoi(argv[1])*1000000;              // 84 MHz 定时器时钟
    float tim_sec = atof(argv[2]); // 期望的定时秒数
    int prescaler = atoi(argv[3]); // 预分频值

    // 计算 ARR(自动重装载值)
    int result = (int)(((float)tim_sec * clock) / ((float)prescaler));

    if (result > 0 && result < 65536)
        printf("%d\n", result);
    else
        printf("Out of range\n");

    return 0;
}

定时器时钟确定

我的板子芯片是STM32F407ZET6,最大支持的频率是168MHz,通过锁相环分频器(PLLCLOCK)可以将外部高速时钟(HSE)的8MHz转化为168MHz,这里我们通过时钟树来分析。

我们的定时器的频率不是单纯的像总线一样有84MHz或者42MHz限制,这里面的关键就是。

如果设置了板子的频率为168MHz,并且设置了非1的的分频系数,那么定时器的时钟频率是总线的频率的两倍

倍频锁相环被正确配置为168MHz

如果在标准库中设置倍频锁相环被正确配置为168MHz。我们其实只要修改两个地方即可,其一外部晶振的正确频率,其一PLL_M

对应代码处为:

我后面会写一篇文章专门介绍为什么这样就可以设置系统时钟为168MHz。可以期待一波。

1.system_stm32f4xx.c文件修改PLL_M值8MHz晶振即为8b

2.stm32f4xx.h文件修改HSE_VALUE,8MHZ晶振即为8000000

定时器的库函数操作

代码

本质使用了若干函数

cpp 复制代码
RCC_APBxPeriphClockCmd(RCC_APBxPeriph_TIMx, ENABLE)
TIM_TimeBaseInit(TIMx, &TIM_TimeBaseInitStruct)
NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
TIM_ITConfig(TIMx, TIM_IT_Update) 
TIM_Cmd(ENABLE)

这里我使用了基本定时器的TIM7,通过定时器的定时中断操作,把led翻转。

cpp 复制代码
void TimerBaseInit(void)
{
    // 开启定时器时钟
    /* 开启TIM6与TIM7基本定时器时钟 84MHz*/
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);

    // 为每个定时器赋值
    /* 基本定时器TIM7 800ms 84MHz*/
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInitStruct.TIM_Period = 42000 - 1;
    TIM_TimeBaseInitStruct.TIM_Prescaler = 1600 - 1;
    TIM_TimeBaseInit(TIM7, &TIM_TimeBaseInitStruct);

    /* TIM7 */
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = TIM7_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0xf;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_Init(&NVIC_InitStruct);

    TIM_ITConfig(TIM7, TIM_IT_Update, ENABLE);

    TIM_Cmd(TIM7, ENABLE);
}

定时中断函数:

cpp 复制代码
void TIM7_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM7, TIM_IT_Update))
    {
        TIM_ClearITPendingBit(TIM7, TIM_IT_Update);
        GPIO_ToggleBits(GPIOF, GPIO_Pin_10);
    }
}

定时器的寄存器操作

代码

寄存器初始化:

cpp 复制代码
void TimerBaseRegisterInit(void)
{
    //开启定时器时钟
    RCC->APB1ENR |= (0b11<<4);    //TIM6、TIM7
    RCC->APB2ENR |= (0b11<<16);    //TIM9、TIM10

    //配置定时器
    /* 配置定时器前先关闭定时器,即关闭计数器 */
    TIM6->CR1 &= ~(1 <<0 );
    /* 配置定时器参数 */
    TIM6->PSC = 10000-1;    //分频值 10000
    TIM6->ARR = 8400 - 1;   // 对应分频器10000
    TIM6->CNT = 0;          // 清0计数器,个人认为自动重装初值应该从0开始

    TIM6->CR1 |= (0b10000100);
    TIM6->DIER |= (0b1);

    //配置NVIC
    NVIC->ISER[1] = 1u << (TIM6_DAC_IRQn %32);

    //配置完成定时器,开启计数器
    TIM6->CR1 |= (1 << 0);
}

定时中断函数:

cpp 复制代码
void TIM7_IRQHandler(void)
{
    if (TIM7->SR == 1)
    {
        TIM7->SR = 0;
        GPIO_ToggleBits(GPIOF, GPIO_Pin_10);
    }
}

寄存器

这里提供基本定时器使用的寄存器:

启动定时器时钟寄存器:

控制寄存器CR1:

寄存器配置寄存器,搭配使用

中断使能寄存器

状态寄存器

配置NVIC使能中断寄存器

后言