STM32的定时器

定时器的介绍

介绍:STM32F103C8T6微控制器内部集成了多种类型的定时器,这些定时器在嵌入式系统中扮演着重要角色,用于计时、延时、事件触发以及PWM波形生成、脉冲捕获等应用。

*几种定时器(STM32F103系列):

7个定时器
高级定时器(1)通用定时器(3)看门口(2)滴答定时器(1)

TIM1:

  • 116位带死区控制和紧急刹车,用于电机 控制的PWM高级控制定时器。
  • 不仅具备基本的定时中断功能,还拥有内外时钟源选择、输入捕获、输出比较、编码器接口以及主从触发模式等多种功能。

TIM2TIM3TIM4

  • 316位定时器,每个定时器有多达4个用于输入捕获/输出比较/PWM或脉冲计数的通道和增量编码器输入。
  • 这些是通用定时器,同样具有定时功能,但在功能上与高级定时器有所区别。
  • 用定时器通常用于实现一些基本的定时任务,如LED闪烁、脉冲宽度测量等。
2 个看门狗定时器 ( 独立的和窗口型的 )
系统时间定时器: 24 位自减型计数器

定时器的工作原理

定时器的核心:计数器

        每个定时器都由一个16位计数器预分频器自动重装寄存器时基单元组成。预分频器可以对时钟进行分频,计数器则对预分频后的时钟进行计数。当计数器的值达到设定值时,会触发中断,从而执行相应的定时任务。

 注意事项:设置预分频器系数(PSC)和自动重装值(ARR)

  • 代码PSC写 0,实际:分频系数是1(不分频);PSC写1,分频系数是2(2分频);
  • 代码ARR写65536-1,实际是2^16 = 65536。

 三种定时器的介绍

类型编号总线功能
高级定时器(本章先不介绍)TIM1、TIM8APB2
拥有通用定时器全部功能,并额外具有重复计数器、死区生
成、互补输出、刹车输入(三项无刷电机的特供:foc)等功能;-------三种计数模式。
通用定时器TIM2、TIM3、TIM4、TIM5APB1
拥有基本定时器全部功能,并额外具有 内外时钟源选择 输入捕获、输出比较 PWM 或 脉冲计数的通道 编码器接口、主从触发模式等功能;------三种计数模式。
基本定时器(这个芯没有)TIM6、TIM7APB1
(时基)拥有定时中断、主模式触发 DAC(绕过CPU) 的功能。------只支持向上计数。
  •  定时器都是72MHz

定时器的框图

(参考手册中)

  •  基本定时器

  •  通用定时器

  • 简图:

  •  高级定时器:

  •  通用定时器时钟的来源

简图:

框图: 

 定时器的计数模式

计数模式计数器溢出值计数器重装值
向上计数CNT = ARRCNT = 0
向下计数CNT = 0CNT = ARR
中心对齐计数

CNT = ARR - 1

CNT = 1

CNT = ARR

CNT = 0

定时器溢出时间 

 说明:

Tout:定时器溢出时间;Ft:定时器的时钟源频率;

ARR自动重装载寄存器的值;PSC预分频器寄存器的值。

举例:要定时500ms,PSC、ARR、Ft的值是多少? 

答:PSC = 7199,ARR = 4999,Ft = 72M。

(当频率大时,记一个数的时间短,ARR的值会变大;当频率小时,记一个数的时间短,ARR的值小)。

定时器寄存器


时基单元寄存器

  • 预分频寄存器( TIMx_PSC)-16位

  • 自动重装载寄存器( TIMx_ARR)-16位

  • DMA/中断使能寄存器 (TIMx_DIER)

 第 0 位, 该位是更新中断允许位, 当定时器的更新中断,该位要设置为 1,来允许由于更新事件所产生的中断。 

  •  状态寄存器( TIMx_SR)

该寄存器用来标记当前与定时器相关的各种事件/中断是否发生。 


 输入捕获/输出比较寄存器

  • 捕获/比较寄存器( TIMx_CCR1~4)

        该寄存器用来存储捕获发生时, TIMx_CNT的值,我们从 TIMx_CCR1 就可以读出通道 1 捕获发生时刻的 TIMx_CNT 值,通过两次捕获(一次上升沿捕获,一次下降沿捕获)的差值,就可以计算出高电平脉冲的宽度。 

捕获/比较模式寄存器( TIMx_CCMRx)

  • TIMx_CCMR1(模式寄存器1):控制通道1和通道2

TIMx_CCMR1 是针对 2 个通道的配置,低八位[7:0]用于捕获 / 比较通道 1 的控制,而高八位[15:8]则用于捕获 / 比较通道 2 的控制。 

  •  TIMx_CCMR2(模式寄存器2):控制通道3和通道4

四个通道的模式寄存器一样,重点介绍 TIMx_CMMR1 的[7:0]位:

输入捕获

输出比较 

捕获/比较使能寄存器( TIMx_CCER)

  •  TIMx_CCER(捕获/比较使能寄存器)


 定时器中断实验配置步骤

定时器中断的函数 

中断服务公共函数 

更新中断的回调函数

其他中断回调函数

设置PSC值的函数: 

设置/读取 ARR值的函数 :

小实验:定时器中断点灯 

实验目的:使用定时器TIM2进行中断点灯,500msLED灯翻转一次。

硬件清单:上官二号、ST-Link。

  • timer.c文件代码
#include "timer.h"
#include "led.h"TIM_HandleTypeDef time_hander = {0};     //定义一个全局结构体变量,结构体成员附一个默认值0。//时基单元初始化函数和中断公共回调函数中使用。
//定时器的时基单元初始化函数
void timer_base_init(uint16_t psc ,uint16_t arr){time_hander.Instance = TIM2;time_hander.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;  //设置的是影子寄存器,是否自动重装载。time_hander.Init.CounterMode = TIM_COUNTERMODE_UP;time_hander.Init.Prescaler = psc;time_hander.Init.Period = arr;HAL_TIM_Base_Init(&time_hander);HAL_TIM_Base_Start_IT(&time_hander);  //打开时钟和使能中断}
//msp函数:初始化MCU相关的硬件,例如:GPIO,NVIC,CLOCK
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim){  //这个函数在HAL_TIM_Base_Init()中调用if(htim->Instance == TIM2){                      //先判断这个是不是TIM2定时器占用。__HAL_RCC_TIM2_CLK_ENABLE();HAL_NVIC_SetPriority(TIM2_IRQn,2,2);HAL_NVIC_EnableIRQ(TIM2_IRQn);} 
}
//中断服务函数
void TIM2_IRQHandler(void){//中断公共处理的函数HAL_TIM_IRQHandler(&time_hander);
}
//更新中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){  //据饼if(htim->Instance == TIM2){                     //判断这个回调函数是否被其他定时器占用。led1_toggle();} 
}
  • time.h文件代码:
#ifndef __TIMER_H__
#define __TIMER_H__#include "stm32f1xx.h"void timer_base_init(uint16_t psc ,uint16_t arr);#endif
  • main.c文件代码 
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "timer.h"int main(void)
{HAL_Init();                         /* 初始化HAL库 */stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */led_init();                         /* LED初始化 */timer_base_init(7200-1,5000-1);     //500ms产生一次中断while(1){    }
}

 输出比较

 简介

输出比较可以通过比较定时计数器的值 CNT 与设定的比较值 CCR,可以控制输出引脚的电平状态(置高或置低),从而实现生成一定频率和占空比 PWM 波形

简图: 

框图(参考手册) :

PWM模式: 

PWM介绍 

定义:PWM波形(Pulse Width Modulation,脉冲宽度调制波形)是一种占空比可变的脉冲波形。这种调制方式通过改变脉冲的宽度来控制电路中的信号强度和频率。具体来说,PWM波形中的高电平持续时间和低电平持续时间可以根据需要进行调整,从而实现对模拟信号电平的数字编码。

应用:PWM波形在各种领域都有广泛的应用,包括电源管理、电机控制、LED亮度调节等。此外,生成PWM波形的方法有多种,例如使用波形发生器、单片机或可编程逻辑器件等。

相关参数: 

频率:1/Ts(定时器计数溢出的时间);

占空比:Ton / Ts(高电平占整个周期的比例,单位%);

分辨率:占空比变化步距。例:占空比50%,51%;分辨率:1%。分辨率越低对硬件的要求越高。

PWM相关的函数

宏函数: 修改CRR寄存器的值

输出比较的引脚 

  •  TIM1_CHx(x:1、2、3、4)

  • TIM2_CHx(x:1、2、3、4) 

  • TIM3_CHx(x:1、2、3、4) 

  • TIM4_CHx(x:1、2、3、4) 

定时器输出PWM波配置步骤 

根据上面简图配置: 

小实验:呼吸灯实验

实验目的:使用 定时器4 通道3(看引脚定义表:PB8引脚)生成 PWM 波控制 LED1 ,实现呼吸灯效果。

  • 频率:2kHz。根据定时器溢出时间计算:PSC=71,ARR=499。

硬件清单:开发板、ST-Link。

  •  pwm.c文件代码
#include "pwm.h"TIM_HandleTypeDef pwm_handle = {0};
void pwm_init(uint16_t psc,uint16_t arr){pwm_handle.Instance = TIM4;pwm_handle.Init.CounterMode = TIM_COUNTERMODE_UP;pwm_handle.Init.Period = arr;pwm_handle.Init.Prescaler = psc;pwm_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;HAL_TIM_PWM_Init(&pwm_handle);//pwm模式和crr寄存器设置TIM_OC_InitTypeDef tim_oc_initstruct = {0};tim_oc_initstruct.OCMode = TIM_OCMODE_PWM1;          //pwm的模式tim_oc_initstruct.OCPolarity = TIM_OCPOLARITY_LOW;   //高电平有效还是低电平;led灯是低电平点亮,故低电平有效tim_oc_initstruct.Pulse = arr/2;                     //占空比CCR的值,这里随便填,后面crr修改函数HAL_TIM_PWM_ConfigChannel(&pwm_handle,&tim_oc_initstruct,TIM_CHANNEL_3);//使能输出,启动计时器HAL_TIM_PWM_Start(&pwm_handle,TIM_CHANNEL_3);
}
//初始化msp函数
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim){if(htim ->Instance == TIM4){                          //判断这个函数是否被定时器4占用__HAL_RCC_TIM4_CLK_ENABLE();                      //打开定时器4的时钟__HAL_RCC_GPIOB_CLK_ENABLE();                       //打开GPIO口的时钟GPIO_InitTypeDef gpio_initstruct;gpio_initstruct.Mode = GPIO_MODE_AF_PP;            //复用推挽输出,看GPIO口定义表gpio_initstruct.Pin = GPIO_PIN_8;gpio_initstruct.Pull = GPIO_PULLUP;gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOB,&gpio_initstruct);}
}
//修改crr的值的函数
void pwm_cmpare_set(uint16_t value){__HAL_TIM_SET_COMPARE(&pwm_handle,TIM_CHANNEL_3,value);
}
  • pwm.h文件代码
#ifndef __PWM_H__
#define __PWM_H__
#include "stm32f1xx.h"void pwm_init(uint16_t psc,uint16_t arr);
void pwm_cmpare_set(uint16_t value);#endif

main.c文件代码

#include "sys.h"
#include "led.h"
#include "delay.h"
#include "pwm.h"int main(void)
{HAL_Init();                         /* 初始化HAL库 */stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */led_init();                         /* LED初始化 */pwm_init(72-1,500-1);//在while函数中不停的修改CRR的值,来实现占空比不断改变while(1){ for(uint16_t i = 0;i<300;i++){pwm_cmpare_set(i);delay_ms(5);}for(uint16_t i = 0;i<300;i++){pwm_cmpare_set(300-i);delay_ms(5);}}
}

写这个代码遇到的问题:

  • 调用函数时要注意:DeInit函数和init函数。
  • 根据引脚定义表,输出比较口对应哪一个GPIO口。
  • 关于外设GPIO口的配置:(参考手册110)

 输入捕获

简介 

        输入捕获模式可以用来 测量脉冲宽度 或者 测量频率。STM32 的定时器,除了 TIM6 和 TIM7,其他定时器都有输入捕获功能。 STM32 的输入捕获,简单的说就是通过检测 TIMx_CHx 上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的通道的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。同时还可以配置捕获时是否触发中断/DMA 等。

框图:(参考手册253) 

输入捕获引脚

  • 和上面输出比较的引脚相同,参考上面输出比较引脚

输入捕获相关函数 

在输入捕获的时基单元里的定时器的使能更新中断的函数: 

捕获回调函数: 

读取输入捕获中CCR的值:

清除/设置 捕获的通道极性:

 关闭定时器:

设置计数器的值 :

获取计数器的值:

定时器输入捕获实验步骤配置 

 小实验:测量按键按下的时长(测量脉冲宽度)

实验目的:使用定时器 2 通道 2 (PA1)来捕获 按键 2 (PA1)按下时间,并通过串口打印

  • 计一个数的时间:1us,PSC=71ARR=65535
  • 下降沿捕获、输入通道 2 映射在 TI2 、不分频、不滤波。

硬件清单:开发板、ST-Link、USB转TTL

思路:

输入捕获回调函数中的流程图: 

拓展:串口打印功能

  • 利用串口调试助手:

  •  引脚接线:
USB转TTL开发板(型号不同,引脚不同)
TXDRX1(PA10)
RXDTX1(PA9)
GNDGND
  •  相关代码和配置:

注意事项:

  • 代码中的波特率要和串口助手中的波特率设置的相同,例如:9600 或 115200;
  • 上述代码中设置错了!!! 

实验1:捕获一次下降沿

  • ic.c代码文件
#include "ic.h"
#include "stdio.h"TIM_HandleTypeDef ic_handle = {0};
//初始化时基单元
void ic_init(uint16_t psc,uint16_t arr){ic_handle.Instance = TIM2;ic_handle.Init.Period = arr;ic_handle.Init.Prescaler = psc;ic_handle.Init.CounterMode = TIM_COUNTERMODE_UP;HAL_TIM_IC_Init(&ic_handle);//输入通道的配置TIM_IC_InitTypeDef ic_initstruct = {0};ic_initstruct.ICFilter = 0;                        //滤波ic_initstruct.ICPolarity = TIM_ICPOLARITY_FALLING;   //输入极性的判断ic_initstruct.ICPrescaler = TIM_ICPSC_DIV1;          //分频系数:这里不分频ic_initstruct.ICSelection = TIM_ICSELECTION_DIRECTTI;  //输入通道选择,还有TRC输入口HAL_TIM_IC_ConfigChannel(&ic_handle,&ic_initstruct,TIM_CHANNEL_2);//打开计数器,是能更新中断,输入捕获中断__HAL_TIM_ENABLE_IT(&ic_handle,TIM_IT_UPDATE);HAL_TIM_IC_Start_IT(&ic_handle,TIM_CHANNEL_2);
}//初始化msp函数
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim){if(htim->Instance == TIM2){//打开时钟__HAL_RCC_TIM2_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();//初始化GPIO口GPIO_InitTypeDef gpio_initstruct;gpio_initstruct.Mode = GPIO_MODE_INPUT;gpio_initstruct.Pin = GPIO_PIN_1;gpio_initstruct.Pull = GPIO_PULLUP;gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOA,&gpio_initstruct);//初始化NVICHAL_NVIC_SetPriority(TIM2_IRQn,2,2);HAL_NVIC_EnableIRQ(TIM2_IRQn);}
}//中断服务函数
void TIM2_IRQHandler(void){HAL_TIM_IRQHandler(&ic_handle);
}
//捕获中断的回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){ printf("下降沿触发\n");
}
  • ic.h文件代码
#ifndef __IC_H__
#define __IC_H__
#include "stm32f1xx.h"void ic_init(uint16_t psc,uint16_t arr);#endif
  • main.c文件代码
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "uart1.h"
#include "ic.h"int main(void)
{HAL_Init();                         /* 初始化HAL库 */stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */led_init();                         /* LED初始化 */uart1_init(115200);                  //初始化串口。设置波特率115200printf("hello,world!!!\n");          //打印到串口助手ic_init(72-1,65536-1);                //设置中断溢出时长while(1){ led1_on();led2_off();delay_ms(500);led1_off();led2_on();delay_ms(500);}
}

实验2:捕获一次完整的按键,并打印出按键按下的时长

  • ic.h文件代码

  • 根据上面流程图,写捕获中断的回调函数 
#include "ic.h"
#include "stdio.h"
#include "string.h"struct {uint8_t falling_flag;uint8_t success_flag;uint16_t timeout_cnt;
}capture_status = {0};uint16_t time_cnt = 0;TIM_HandleTypeDef ic_handle = {0};
//初始化时基单元
void ic_init(uint16_t psc,uint16_t arr){ic_handle.Instance = TIM2;ic_handle.Init.Period = arr;ic_handle.Init.Prescaler = psc;ic_handle.Init.CounterMode = TIM_COUNTERMODE_UP;HAL_TIM_IC_Init(&ic_handle);//输入通道的配置TIM_IC_InitTypeDef ic_initstruct = {0};ic_initstruct.ICFilter = 0;                        //滤波ic_initstruct.ICPolarity = TIM_ICPOLARITY_FALLING;   //输入极性的判断ic_initstruct.ICPrescaler = TIM_ICPSC_DIV1;          //分频系数:这里不分频ic_initstruct.ICSelection = TIM_ICSELECTION_DIRECTTI;  //输入通道选择,还有TRC输入口HAL_TIM_IC_ConfigChannel(&ic_handle,&ic_initstruct,TIM_CHANNEL_2);//打开计数器,是能更新中断,输入捕获中断__HAL_TIM_ENABLE_IT(&ic_handle,TIM_IT_UPDATE);   //使能更新中断HAL_TIM_IC_Start_IT(&ic_handle,TIM_CHANNEL_2);   //打开计时器,并使能捕获中断
}//初始化msp函数
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim){if(htim->Instance == TIM2){//打开时钟__HAL_RCC_TIM2_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();//初始化GPIO口GPIO_InitTypeDef gpio_initstruct;gpio_initstruct.Mode = GPIO_MODE_INPUT;gpio_initstruct.Pin = GPIO_PIN_1;gpio_initstruct.Pull = GPIO_PULLUP;gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOA,&gpio_initstruct);//初始化NVICHAL_NVIC_SetPriority(TIM2_IRQn,2,2);HAL_NVIC_EnableIRQ(TIM2_IRQn);}
}//中断服务函数
void TIM2_IRQHandler(void){HAL_TIM_IRQHandler(&ic_handle);
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){
//    printf("下降沿触发\n");if(capture_status.success_flag == 0){if(capture_status.falling_flag == 1){printf("上升沿捕获\n");capture_status.success_flag =1;time_cnt = HAL_TIM_ReadCapturedValue(&ic_handle,TIM_CHANNEL_2);//获取计数器的值TIM_RESET_CAPTUREPOLARITY(&ic_handle,TIM_CHANNEL_2);        //清除捕获通道TIM_SET_CAPTUREPOLARITY(&ic_handle,TIM_CHANNEL_2,TIM_ICPOLARITY_FALLING);  //设置为下降沿捕获}else{printf("下降沿捕获\n");capture_status.falling_flag = 1;capture_status.timeout_cnt = 0;__HAL_TIM_DISABLE(&ic_handle);__HAL_TIM_SetCounter(&ic_handle,0);                        //计数器的值清零TIM_RESET_CAPTUREPOLARITY(&ic_handle,TIM_CHANNEL_2);       //清除通道设置TIM_SET_CAPTUREPOLARITY(&ic_handle,TIM_CHANNEL_2,TIM_ICPOLARITY_RISING);  //设置为上升沿捕获__HAL_TIM_ENABLE(&ic_handle);}}
}
//定时器定时中断的回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){if(htim ->Instance == TIM2){//判断是否完成按键if(capture_status.falling_flag == 1)        //判断是否捕获到下降沿capture_status.timeout_cnt ++;   }
}//定义一个函数,打印按键按下的时间
void pressed_time_get(void){if(capture_status.success_flag == 1){printf("按下时间:%lf s \n",((double)capture_status.timeout_cnt*65536+time_cnt)/1000000);memset(&capture_status,0,sizeof(capture_status));}}
  •  ic.h文件代码
#ifndef __IC_H__
#define __IC_H__
#include "stm32f1xx.h"void ic_init(uint16_t psc,uint16_t arr);
void pressed_time_get(void);
#endif
  • main.c文件代码
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "uart1.h"
#include "ic.h"int main(void)
{HAL_Init();                         /* 初始化HAL库 */stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */led_init();                         /* LED初始化 */uart1_init(115200);printf("hello,world!!!\n");ic_init(72-1,65536-1);while(1){ pressed_time_get();}
}

要解决的问题:

  • 结构体中声明多个标志位。
  • 用到的函数。
  • 为什么要写到while函数中。 

 脉冲计数

简介

脉冲计数相关的函数

初始化函数 

打开捕获定时器 

定时器从模式的配置:通道、滤波、边沿捕获、映射 

 获取CNT的值

 脉冲计数实验步骤配置

 根据上面的框图:

小实验:脉冲计数实验 (测量频率)

实验目的:定时器 2 通道 2 输入(PA1)低电平脉冲(按下按键2 PA1)作为定时器 2 的时钟,并通过串口打印脉冲数。

  • PSC=1-1ARR=65536-1
  • 外部时钟模式1、触发选择:下降沿触发、不分频、不滤波

实验清单:开发板、ST-Link、USB转TTL

实验思路:

  1. 初始化时基单元;
  2. 配置输入通道,从模式:时钟模式1;
  3. msp函数初始化GPIO口、CLOCK、NVIC;
  4. 打开计数器;
  5. 获取计数器的值并进行打印。
  • counter.c文件代码
#include "counter.h"
#include "stdio.h"TIM_HandleTypeDef counter_handle = {0};void counter_init(uint16_t psc,uint16_t arr){//初始化时基单元counter_handle.Instance = TIM2;counter_handle.Init.Prescaler = psc;counter_handle.Init.Period = arr;counter_handle.Init.CounterMode = TIM_COUNTERMODE_UP;counter_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;HAL_TIM_IC_Init(&counter_handle);//配置输入通道(从模式)TIM_SlaveConfigTypeDef counter_initstruct;counter_initstruct.TriggerFilter = 0;                               //滤波器counter_initstruct.TriggerPolarity = TIM_TRIGGERPOLARITY_FALLING;   //极性的选择:上升沿或下降沿counter_initstruct.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;             //从模式的选择counter_initstruct.InputTrigger = TIM_TS_TI2FP2;                    //输入的通道。counter_initstruct.TriggerPrescaler =  TIM_TRIGGERPRESCALER_DIV1;   //分频:这里用不到HAL_TIM_SlaveConfigSynchro(&counter_handle,&counter_initstruct);//使能中断和打开时钟HAL_TIM_IC_Start(&counter_handle,TIM_CHANNEL_2);
}//msp配置mcu外设
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim){   //这是个公用的函数,用的时候要进行判断是否被占用if(htim->Instance == TIM2){//打开时钟__HAL_RCC_TIM2_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();//初始化GPIO口GPIO_InitTypeDef gpio_initstruct;gpio_initstruct.Pin = GPIO_PIN_1;gpio_initstruct.Mode = GPIO_MODE_INPUT;gpio_initstruct.Pull = GPIO_NOPULL;gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOA,&gpio_initstruct);}
}//获取计数器的值
uint16_t Ncounter_num = 0;
uint16_t Ocounter_num = 0;
void counter_get(void){Ncounter_num = __HAL_TIM_GetCounter(&counter_handle);
//避免在while循环中一直打印计数器的值,设置打印的条件:计数器的值是否发生变化。if(Ncounter_num != Ocounter_num){printf("计数器的值:%d\n",Ncounter_num);Ocounter_num = Ncounter_num;}
}
  • counter.h文件代码
#ifndef __COUNTER_H__
#define __COUNTER_H__#include "stm32f1xx.h"
void counter_init(uint16_t psc,uint16_t arr);
void counter_get(void);
#endif
  •  main.c文件代码
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "uart1.h"
#include "counter.h"int main(void)
{HAL_Init();                         /* 初始化HAL库 */stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */led_init();                         /* LED初始化 */uart1_init(115200);printf("hello,world!!!\n");counter_init(1-1,65536-1);while(1){ counter_get();}
}
  • 结果:

遇到的问题和注意事项:

  • main.c文件中的波特率要和串口助手的波特率相同。
  • 在counter.c文件中声明函数获取计数器的值时,要进行条件编译,不然会在主函数while循环中,一直打印。 (给打印函数一个条件)
  • 要使能输入和打开计数器。这个函数没有捕获中断。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/80256.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法中的数学:约数

1.求一个整数的所有约数 对于一个整数x&#xff0c;他的其中一个约数若为i&#xff0c;那么x/i也是x的一个约数。而其中一个约数的大小一定小于等于根号x&#xff08;完全平方数则两个约数都为根号x&#xff09;&#xff0c;所以我们只需要遍历到根号x&#xff0c;然后计算出另…

不同OS版本中的同一yum源yum list差异排查思路

问题描述&#xff1a; qemu-guest-agent二进制rpm包的yum仓库源和yum源仓库配置文件path_to_yum_conf&#xff0c; 通过yum list --available -c path_to_yum_conf 查询时&#xff0c;不同的OS版本出现了不同的结果 anolis-8无法识别 centos8可以识别 说明&#xff1a; 1 测试…

如何使用极狐GitLab 软件包仓库功能托管 helm chart?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 软件包库中的 Helm charts (BASIC ALL) WARNING:Helm chart 库正在开发中&#xff0c;由于功能有限&#xff0c;尚未准备好用…

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】3.1 数据质量评估指标(完整性/一致性/准确性)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 数据质量评估核心指标&#xff1a;完整性、一致性、准确性实战解析3.1 数据质量评估指标体系3.1.1 完整性&#xff1a;数据是否存在缺失1.1.1 核心定义与业务影响1.1.2 检测…

详解 FFMPEG 交叉编译 `FLAGS` 和 `INCLUDES` 的作用

FLAGS 和 INCLUDES这两行是 Android NDK 编译时的编译器选项&#xff0c;用于控制代码生成、优化、调试、安全性和头文件搜索路径。下面逐项详解&#xff1a; 1. FLAGS 详解&#xff08;编译器选项&#xff09; FLAGS 定义了传递给 C/C 编译器&#xff08;如 clang 或 gcc&…

【RK3588嵌入式图形编程】-Cairo-Cairo图形库支持后端

Cairo图形库支持后端 文章目录 Cairo图形库支持后端1、PNG图像后端2、PDF文件后端3、SVG文件后端4、GTK窗口支持Cairo库支持多种后端。在本文中,我们使用Cairo创建PNG图像、PDF文件、SVG文件,并在GTK窗口上绘制。 1、PNG图像后端 在第一个示例中,我们创建一个 PNG 图像。 …

【常用算法:排序篇】2.快速排序的算法精要

快速排序是算法领域的"九阳神功"&#xff0c;掌握其精髓能让你在算法修炼之路上突破瓶颈。 1. 快速排序的核心思想 快速排序&#xff08;Quicksort&#xff09;是一种基于分治思想的高效排序算法&#xff0c;核心步骤为&#xff1a; 选择基准值&#xff08;Pivot&…

在现代Web应用中集成 PDF.js (pdfjs-dist 5.2 ESM): 通过 jsdelivr 实现动态加载与批注功能的思考

PDF 文档在现代 Web 应用中越来越常见&#xff0c;无论是作为文档预览、报告展示还是在线编辑的载体。Mozilla 的 PDF.js 是一个功能强大的 JavaScript 库&#xff0c;它使得在浏览器端渲染和显示 PDF 文件成为可能&#xff0c;无需依赖原生插件。 本文将深入探讨如何在你的项…

基于FPGA控制ADC0832双通道采样+电压电流采样+LCD屏幕显示

基于FPGA控制ADC0832双通道采样电压电流采样LCD屏幕显示 前言一、芯片手册阅读1.SPI通信时序 二、仿真分析三、代码分析总结视频演示 前言 定制 要求使用ADC0832芯片进行ADC采样。其中电压采样以及电流采样是固定电路&#xff0c;是硬件设计&#xff0c;跟软件没没关系。本质上…

生产部署方案pm2配合python3脚本

前言 使用python3来处理redis 消息队列&#xff0c;记录下生产部署方案 「生产部署方案」&#xff1a; 多进程&#xff08;动态扩容&#xff09;无限自愈日志自动压缩系统级守护可多队列多worker 终极稳健版&#xff1a;PM2 Logrotate 自动扩容 守护链 适合&#xff1a…

Python全流程开发实战:基于IMAP协议安全下载个人Gmail邮箱内所有PDF附件

文章目录 一、需求分析与安全前置&#xff1a;为什么需要专用工具&#xff1f;1.1 痛点场景1.2 技术方案选择 二、准备工作&#xff1a;Gmail账号安全配置与环境搭建2.1 开启两步验证&#xff08;必做&#xff01;&#xff09;2.2 创建应用专用密码&#xff08;替代普通密码&am…

巧用python之--模仿PLC(PLC模拟器)

工作中用到了VM(VisionMaster4.3)有时候需要和PLC打交道,但是PLC毕竟是别人的,不方便修改别人的程序,这时候需要一个灵活的PLC模拟器是多么好呀! 先说背景: PLC型号 汇川Easy521: Modbus TCP 192.168.1.10:502 在汇川Easy521中Modbus保持寄存器D寄存器 ,在modbus协议中 0-4区…

docker构建镜像并上传dockerhub

docker构建镜像并上传dockerhub 前提条件&#xff1a;需要连接梯子 将梯子配置到虚拟机中&#xff08;确保主机能够连接 hub.docker.com&#xff09; 使用ipconfig 查询主机的 ip4地址虚拟机的连接模式改成桥接模式&#xff08;复制主机的地址网络&#xff09;将ip4配置到虚拟…

python实现的音乐播放器

python实现的音乐播放器 音乐播放器,原来写过一个简陋的例子,可见 https://blog.csdn.net/cnds123/article/details/137874107 那个不能拖动播放进度条上的滑块到新的位置播放。下面介绍的可以拖动播放进度条上的滑块到新的位置播放。 简单实用的音乐播放器 这个简单实用的…

[网安工具] 端口信息收集工具 —— 御剑高速 TCP 全端口扫描工具 · 使用手册

&#x1f31f;想了解其它网安工具&#xff1f;看看这个&#xff1a;[网安工具] 网络安全工具管理 —— 工具仓库 管理手册 https://github.com/NepoloHebo/Yujian-high-speed-TCP-full-port-scannerhttps://github.com/NepoloHebo/Yujian-high-speed-TCP-full-port-scanner 0…

数字孪生赋能智慧城市:从概念到落地的深度实践

在城市规模与复杂度持续攀升的当下&#xff0c;传统管理模式已难以满足现代城市精细化治理需求。数字孪生技术凭借构建虚拟城市镜像、实现实时数据交互与智能决策的特性&#xff0c;成为智慧城市建设的核心引擎。本文将通过多个典型案例&#xff0c;深度解析数字孪生技术如何重…

DeFi开发系统软件开发:技术架构与生态重构

DeFi开发系统软件开发&#xff1a;技术架构与生态重构 ——2025年去中心化金融开发的范式革新与实践指南 一、技术架构演进&#xff1a;从单一链到多链混合引擎 现代DeFi系统开发已从单一公链架构转向“跨链互操作混合模式”&#xff0c;结合中心化效率与去中心化安全双重优势…

相同IP和端口的服务器ssh连接时出现异常

起因 把服务器上的一个虚拟机搞坏了&#xff0c;所以删除重新创建了一个&#xff0c;端口号和IP与之前的虚拟机相同。 ssh usernameIP -p port 时报错 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone…

验证es启动成功

1. 查看命令行输出信息 在启动 Elasticsearch 时&#xff0c;命令行窗口会输出一系列日志信息。若启动成功&#xff0c;日志里通常会有类似下面的信息&#xff1a; plaintext [2025-05-06T13:20:00,000][INFO ][o.e.n.Node ] [node_name] started其中 [node_na…

CentOS网络之network和NetworkManager深度解析

文章目录 CentOS网络之network和NetworkManager深度解析1. CentOS网络服务发展历史1.1 传统network阶段&#xff08;CentOS 5-6&#xff09;1.2 过渡期&#xff08;CentOS 7&#xff09;1.3 新时代&#xff08;CentOS 8&#xff09; 2. network和NetworkManager的核心区别3. ne…