小白跟做江科大32单片机之定时器

发布于:2024-06-16 ⋅ 阅读:(20) ⋅ 点赞:(0)

原理部分

1.

计数器每遇到一个上升沿就会计数值+1,。

72MHZ=72000000

72000000/65536/65536=0.01676380634307861328125=59.652323555555554  (s) 

2.

3.

计数时钟每来一个上升沿,计数值+1,自动运行。如果计数值与存储在自动重装载寄存器中的值相等,计数器清0,产生更新中断和更新事件,然后更新到NVIC,向上的那个表示中断,向下的表示更新事件。

这个主模式触发DAC的功能,就是直接通过更新事件直接映射到别的设备。

4.通用定时器

滤波信号之后会给触发控制器或者TRGI

外部时钟模式1输入:ETR,其他定时器,CH1引脚,CH1边沿引脚,CH2

5.主要看这个

6.

这个主要是由预分配缓冲器使得本次计数周期结束时,改变分频值不会受影响

7.RCC时钟树左边是系统时钟,右边是时钟频率

时钟树左边

时钟树右边

时钟树右边部分电路

这就是我们在程序中写RCC APB2/1Periphclockgmd作用的地方,打开时钟,就是在这个位置写1/让左边的时钟能够通过与门输出给外设


代码编写

实验一

1.根据江科大老师给的电路图进行连接

2.创建新项目

3.将显示屏相关代码复制到新项目中

3.思路

①写一个TIMER_init函数,其中包括

开启RCC时钟,

内部时钟模式,

配置时基单元

定时器中断控制

NVIC

配置运行控制

②在main函数中写一个定时器中断函数代码

4.编写TIMER.c代码

#include "stm32f10x.h"                  // Device header

void TIMER_Init()
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);    //开启GPIOB的时钟,通用定时器
    
    /*配置时钟源*/
    TIM_InternalClockConfig(TIM2);        //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
    /*时基单元初始化*/
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;                //定义结构体变量
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;        //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;    //计数器模式,选择向上计数
    TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;                //计数周期,即ARR的值
    TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;                //预分频器,即PSC的值
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);                //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元    
    
    /*中断输出配置*/
    TIM_ClearFlag(TIM2, TIM_FLAG_Update);                        //清除定时器更新标志位
                                                                //TIM_TimeBaseInit函数末尾,手动产生了更新事件
                                                                //若不清除此标志位,则开启中断后,会立刻进入一次中断
                                                                //如果不介意此问题,则不清除此标志位也可
    
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);                    //开启TIM2的更新中断
    
    /*NVIC中断分组*/
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                //配置NVIC为分组2
                                                                //即抢占优先级范围:0~3,响应优先级范围:0~3
                                                                //此分组配置在整个工程中仅需调用一次
                                                                //若有多个中断,可以把此代码放在main函数内,while循环之前
                                                                //若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
    
    /*NVIC配置*/
    NVIC_InitTypeDef NVIC_InitStructure;                        //定义结构体变量
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;                //选择配置NVIC的TIM2线
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                //指定NVIC线路使能
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;    //指定NVIC线路的抢占优先级为2
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;            //指定NVIC线路的响应优先级为1
    NVIC_Init(&NVIC_InitStructure);                                //将结构体变量交给NVIC_Init,配置NVIC外设
    
    /*TIM使能*/
    TIM_Cmd(TIM2, ENABLE);            //使能TIM2,定时器开始运行
}

    
 

5.编写TIMER.h代码

#ifndef _TIMER__H
#define _TIMER__H

void TIMER_Init(void);


#endif

6.实验结果

实验二

1.接线

2.修改TIMER.c代码

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:定时中断初始化
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数配置为外部时钟,定时器相当于计数器
  */
void Timer_Init(void)
{
    /*开启时钟*/
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);            //开启TIM2的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);            //开启GPIOA的时钟
    
    /*GPIO初始化*/
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);                        //将PA0引脚初始化为上拉输入
    
    /*外部时钟配置*/
    TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);
                                                                //选择外部时钟模式2,时钟从TIM_ETR引脚输入
                                                                //注意TIM2的ETR引脚固定为PA0,无法随意更改
                                                                //最后一个滤波器参数加到最大0x0F,可滤除时钟信号抖动
    
    /*时基单元初始化*/
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;                //定义结构体变量
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;        //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;    //计数器模式,选择向上计数
    TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;                    //计数周期,即ARR的值
    TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;                //预分频器,即PSC的值
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);                //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元    
    
    /*中断输出配置*/
    TIM_ClearFlag(TIM2, TIM_FLAG_Update);                        //清除定时器更新标志位
                                                                //TIM_TimeBaseInit函数末尾,手动产生了更新事件
                                                                //若不清除此标志位,则开启中断后,会立刻进入一次中断
                                                                //如果不介意此问题,则不清除此标志位也可
                                                                
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);                    //开启TIM2的更新中断
    
    /*NVIC中断分组*/
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                //配置NVIC为分组2
                                                                //即抢占优先级范围:0~3,响应优先级范围:0~3
                                                                //此分组配置在整个工程中仅需调用一次
                                                                //若有多个中断,可以把此代码放在main函数内,while循环之前
                                                                //若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
    
    /*NVIC配置*/
    NVIC_InitTypeDef NVIC_InitStructure;                        //定义结构体变量
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;                //选择配置NVIC的TIM2线
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                //指定NVIC线路使能
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;    //指定NVIC线路的抢占优先级为2
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;            //指定NVIC线路的响应优先级为1
    NVIC_Init(&NVIC_InitStructure);                                //将结构体变量交给NVIC_Init,配置NVIC外设
    
    /*TIM使能*/
    TIM_Cmd(TIM2, ENABLE);            //使能TIM2,定时器开始运行
}

/**
  * 函    数:返回定时器CNT的值
  * 参    数:无
  * 返 回 值:定时器CNT的值,范围:0~65535
  */
uint16_t Timer_GetCounter(void)
{
    return TIM_GetCounter(TIM2);    //返回定时器TIM2的CNT
}

3.修改TIMER.h代码

#ifndef __TIMER_H
#define __TIMER_H

void Timer_Init(void);
uint16_t Timer_GetCounter(void);

#endif
 

4.修改main.c代码

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"

uint16_t Num;            //定义在定时器中断里自增的变量

int main(void)
{
    /*模块初始化*/
    OLED_Init();        //OLED初始化
    Timer_Init();        //定时中断初始化
    
    /*显示静态字符串*/
    OLED_ShowString(1, 1, "Num:");            //1行1列显示字符串Num:
    OLED_ShowString(2, 1, "CNT:");            //2行1列显示字符串CNT:
    
    while (1)
    {
        OLED_ShowNum(1, 5, Num, 5);            //不断刷新显示Num变量
        OLED_ShowNum(2, 5, Timer_GetCounter(), 5);        //不断刷新显示CNT的值
    }
}

/**
  * 函    数:TIM2中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */
void TIM2_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)        //判断是否是TIM2的更新事件触发的中断
    {
        Num ++;                                                //Num变量自增,用于测试定时中断
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);            //清除TIM2更新事件的中断标志位
                                                            //中断标志位必须清除
                                                            //否则中断将连续不断地触发,导致主程序卡死
    }
}
5.实验结果

定时器外部时钟试验结果