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


    原理部分

    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.实验结果

    定时器外部时钟试验结果

  • 相关阅读:
    PHP中的魔术方法并给出一些例子
    2024初三集训模拟测试1
    使用TensorFlow构建,绘制和解释人工神经网络
    当JAVA注解、AOP、SpEL相遇,更多可能变为了现实
    Java函数式接口
    selnium操作输入框无法输入内容
    LeetCode热题100——贪心算法
    Java String.indexOf()方法具有什么功能呢?
    Linux中的DNS服务搭建与管理
    【Leecode】代码随想录栈和队列篇day12(应用)
  • 原文地址:https://blog.csdn.net/weixin_58051657/article/details/139646449