STM32定时器知识点及标准库使用指南

目录

一、STM32定时器分类

二、定时器核心概念

[1. 计数模式](#1. 计数模式)

[2. 时钟源](#2. 时钟源)

[3. 主要寄存器](#3. 主要寄存器)

三、定时器基本结构

四、定时器工作原理

[1. 基本定时功能](#1. 基本定时功能)

[2. 输入捕获功能](#2. 输入捕获功能)

五、定时器配置步骤

1.基本定时配置步骤:

2.输入捕获配置步骤:

六、标准库中使用定时器

[1. 基本定时器使用步骤](#1. 基本定时器使用步骤)

[2. 通用定时器实现PWM输出](#2. 通用定时器实现PWM输出)

[3. 输入捕获功能](#3. 输入捕获功能)

七、超声波测距中的定时器应用

[1. 基本定时中断方式](#1. 基本定时中断方式)

[2. 输入捕获方式](#2. 输入捕获方式)

3.使用通用定时器的计数值直接读取


STM32系列微控制器中的定时器是非常强大的外设,可用于多种功能,如定时计数、PWM生成、输入捕获、正交解码等。下面我将详细介绍STM32定时器的相关知识及标准库使用方法。

一、STM32定时器分类

STM32定时器根据功能和复杂度可分为以下几类:

二、定时器核心概念

1. 计数模式

向上计数 :从0计数到自动重载值(TIMx_ARR),然后重新从0开始

向下计数 :从自动重载值计数到0,然后重新从自动重载值开始

中央对齐模式 :先向上计数到ARR,再向下计数到0,重复此过程

2. 时钟源

内部时钟(CK_INT):来自APB总线

外部时钟模式1:通过TIx引脚输入

外部时钟模式2:通过ETR引脚输入

内部触发输入(ITRx):来自其他定时器

3. 主要寄存器

TIMx_CR1/CR2 :控制寄存器

TIMx_PSC :预分频器

TIMx_ARR :自动重载寄存器

TIMx_CNT :计数器

TIMx_CCR1-4 :捕获/比较寄存器

TIMx_DIER :中断使能寄存器

TIMx_SR :状态寄存器

三、定时器基本结构

定时器的核心是一个计数器,其工作原理基于时钟源的驱动:

  1. 时钟源 :通常来自 APB1 或 APB2 总线时钟

  2. 预分频器 :将时钟源分频,降低计数频率

  3. 计数器 :递增或递减计数

  4. 自动重装载寄存器 :存储计数器的最大值

  5. 捕获/比较通道 :用于输入捕获或输出比较

四、定时器工作原理

1. 基本定时功能

基本定时功能是定时器最基础的应用,通过以下步骤实现:

  1. 配置时钟源 :选择内部时钟或外部时钟

  2. 设置预分频器 :确定计数频率

  3. 设置自动重装载值 :确定定时周期

  4. 使能更新中断 :定时时间到达时触发中断

计算公式 :

计数频率 = 时钟源频率 / (预分频器值 + 1)
定时周期 = (自动重装载值 + 1) / 计数频率

2. 输入捕获功能

输入捕获用于测量外部信号的脉冲宽度或频率,工作原理:

  1. 配置通道 :选择捕获通道和触发方式

  2. 设置触发极性 :上升沿、下降沿或双边沿

  3. 使能捕获中断 :捕获到信号时触发中断

  4. 记录时间戳 :在中断中读取捕获寄存器的值

五、定时器配置步骤

1.基本定时配置步骤:

  1. 使能定时器时钟

  2. 配置时基结构(预分频器、自动重装载值等)

  3. 配置中断(NVIC)

  4. 使能更新中断

  5. 启动定时器

2.输入捕获配置步骤:

  1. 使能定时器和 GPIO 时钟

  2. 配置 GPIO 为输入模式

  3. 配置时基结构

  4. 配置捕获通道(极性、滤波等)

  5. 配置中断(NVIC)

  6. 使能捕获中断

  7. 启动定时器

六、标准库中使用定时器

1. 基本定时器使用步骤

以TIM6为例,实现基本定时功能:

cpp 复制代码
#include "stm32f10x.h"

void TIM6_Configuration(void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    
    // 1. 使能定时器时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
    
    // 2. 配置定时器基本参数
    // 计算方法:定时时间 = (PSC+1) * (ARR+1) / 时钟频率
    // 例如:72MHz时钟,PSC=7199,ARR=9999,定时时间=1s
    TIM_TimeBaseStructure.TIM_Period = 9999;           // 自动重载值
    TIM_TimeBaseStructure.TIM_Prescaler = 7199;        // 预分频器
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;        // 时钟分割
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  // 向上计数模式
    TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
    
    // 3. 配置中断
    TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);          // 使能更新中断
    
    NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    // 4. 启动定时器
    TIM_Cmd(TIM6, ENABLE);
}

// 5. 中断服务函数
void TIM6_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
    {
        // 在这里执行定时任务
        
        TIM_ClearITPendingBit(TIM6, TIM_IT_Update);  // 清除中断标志位
    }
}

2. 通用定时器实现PWM输出

以TIM3为例,实现PWM输出:

cpp 复制代码
void TIM3_PWM_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
    
    // 1. 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    
    // 2. 配置GPIO
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;  // TIM3_CH1
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 3. 配置定时器
    TIM_TimeBaseStructure.TIM_Period = 999;     // PWM周期 = (999+1) * (71+1)/72MHz = 1ms
    TIM_TimeBaseStructure.TIM_Prescaler = 71;    // 预分频器
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
    
    // 4. 配置PWM模式
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;  // PWM模式1
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 500;  // 占空比50%
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init(TIM3, &TIM_OCInitStructure);
    
    TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  // 使能预装载寄存器
    TIM_ARRPreloadConfig(TIM3, ENABLE);  // 使能自动重载寄存器
    
    // 5. 启动定时器
    TIM_Cmd(TIM3, ENABLE);
}

// 更改占空比
void SetPWM_DutyCycle(uint16_t duty)
{
    TIM_SetCompare1(TIM3, duty);  // 设置比较值,范围0-999
}

3. 输入捕获功能

以TIM2为例,实现输入捕获测量信号频率:

cpp 复制代码
volatile uint32_t CaptureValue = 0;
volatile uint32_t Frequency = 0;

void TIM2_InputCapture_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_ICInitTypeDef TIM_ICInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    
    // 1. 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    
    // 2. 配置GPIO
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;  // TIM2_CH1
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 3. 配置定时器
    TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
    TIM_TimeBaseStructure.TIM_Prescaler = 71;  // 1MHz计数频率
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    
    // 4. 配置输入捕获
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;  // 上升沿触发
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_ICInitStructure.TIM_ICFilter = 0x0;
    TIM_ICInit(TIM2, &TIM_ICInitStructure);
    
    // 5. 配置中断
    TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE);
    
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    // 6. 启动定时器
    TIM_Cmd(TIM2, ENABLE);
}

// 中断服务函数
void TIM2_IRQHandler(void)
{
    static uint32_t LastCaptureValue = 0;
    
    if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
    {
        CaptureValue = TIM_GetCapture1(TIM2);
        
        if (CaptureValue > LastCaptureValue)
        {
            Frequency = 1000000 / (CaptureValue - LastCaptureValue);  // 计算频率
        }
        else
        {
            Frequency = 1000000 / (0xFFFF - LastCaptureValue + CaptureValue);
        }
        
        LastCaptureValue = CaptureValue;
        TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
    }
}

七、超声波测距中的定时器应用

1. 基本定时中断方式

原理 :

使用 TIM3 定时器,每 10 微秒触发一次中断

在中断中递增计数器,累计 Echo 信号的高电平持续时间

根据累计时间计算距离

cpp 复制代码
uint16_t TimeCount;
//	Trig PB15  
//  Echo PC15
void HC_SR04Init()
{
	HC_SR04GPIO_Init();
	HC_SR04TIM_Init();
}

float Sonar()
{
	float Distance,Distance_mm;
	uint32_t Time_end;
	GPIO_WriteBit(GPIOB,GPIO_Pin_15,Bit_SET);  //Trig发送触发信号 >10us
	Delay_us(15);
	GPIO_WriteBit(GPIOB,GPIO_Pin_15,Bit_RESET); 
	while(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_15) == 0);
	TimeCount = 0;
	while(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_15) == 1);
	Time_end = TimeCount*10;
	if (Time_end < 24000)
	{
		Distance = Time_end * 0.000001f * 340 / 2;
		Distance_mm = Distance * 1000;
	}
	return Distance_mm;
}

void HC_SR04GPIO_Init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);
	
	//Trig GPIO设置
	GPIO_InitTypeDef	GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//HC-SR04 超声波模块的工作电压通常为 5V
	//开漏输出可通过外部上拉电阻(如 1kΩ)连接到 5V,使输出高电平为 5V,符合模块要求
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;//下拉输入模式避免了信号干扰,确保了回波信号的准确检测
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_15;
	GPIO_Init(GPIOC,&GPIO_InitStructure);
	GPIO_WriteBit(GPIOC,GPIO_Pin_15,Bit_RESET);
}

void HC_SR04TIM_Init()
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	
	TIM_InternalClockConfig(TIM3);
	
	TIM_TimeBaseInitTypeDef	TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period  = 10 - 1;
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
	
//	- 预分频器:71(72-1),72MHz / 72 = 1MHz,即 1 微秒计数一次
//  - 自动重装载值:9(10-1),每计数 10 次触发一次中断,即 10 微秒中断一次
	
	TIM_ClearFlag(TIM3,TIM_FLAG_Update);
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	
	TIM_Cmd(TIM3,ENABLE);
}

void TIM3_IRQHandler()
{
	if (TIM_GetITStatus(TIM3,TIM_IT_Update) == SET)//触发中断
	{
		TimeCount++;
		TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
	}
}

2. 输入捕获方式

原理 :

使用 TIM2 定时器的输入捕获功能

捕获 Echo 信号的上升沿和下降沿

计算两个时间戳的差值,得到 Echo 信号的持续时间

根据时间差计算距离

cpp 复制代码
#include "stm32f10x.h"
#include "delay.h"

// 定义引脚
#define TRIG_PORT GPIOB
#define TRIG_PIN GPIO_Pin_15
#define ECHO_PORT GPIOC
#define ECHO_PIN GPIO_Pin_15

// 定义定时器
#define TIMx TIM2
#define RCC_APB1Periph_TIMx RCC_APB1Periph_TIM2

// 全局变量
volatile uint32_t capture_start = 0;
volatile uint32_t capture_end = 0;
volatile uint8_t capture_complete = 0;

/**
 * @brief  初始化GPIO和定时器
 */
void HC_SR04_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_ICInitTypeDef TIM_ICInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    // 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIMx, ENABLE);

    // 配置Trig引脚
    GPIO_InitStructure.GPIO_Pin = TRIG_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(TRIG_PORT, &GPIO_InitStructure);
    GPIO_ResetBits(TRIG_PORT, TRIG_PIN);

    // 配置Echo引脚
    GPIO_InitStructure.GPIO_Pin = ECHO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
    GPIO_Init(ECHO_PORT, &GPIO_InitStructure);

    // 配置定时器
    TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
    TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 1MHz计数频率
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure);

    // 配置输入捕获
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_BothEdge;
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_ICInitStructure.TIM_ICFilter = 0x0;
    TIM_ICInit(TIMx, &TIM_ICInitStructure);

    // 配置NVIC
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    // 使能捕获中断
    TIM_ITConfig(TIMx, TIM_IT_CC1, ENABLE);

    // 启动定时器
    TIM_Cmd(TIMx, ENABLE);
}

/**
 * @brief  发送触发信号
 */
void HC_SR04_Trigger(void)
{
    GPIO_SetBits(TRIG_PORT, TRIG_PIN);
    delay_us(15);
    GPIO_ResetBits(TRIG_PORT, TRIG_PIN);
}

/**
 * @brief  获取测量距离(毫米)
 */
float HC_SR04_GetDistance(void)
{
    float distance_mm = 0;
    uint32_t time_us = 0;

    // 发送触发信号
    HC_SR04_Trigger();

    // 等待捕获完成
    while(capture_complete == 0);
    capture_complete = 0;

    // 计算时间差(微秒)
    if(capture_end > capture_start)
    {
        time_us = capture_end - capture_start;
    }
    else
    {
        // 处理定时器溢出
        time_us = (0xFFFF - capture_start) + capture_end;
    }

    // 计算距离(毫米):距离 = 时间 * 声速 / 2
    // 声速约为340m/s = 0.34mm/us
    if(time_us < 24000) // 限制最大测量时间
    {
        distance_mm = time_us * 0.34f / 2.0f;
    }

    return distance_mm;
}

/**
 * @brief  TIM2中断处理函数
 */
void TIM2_IRQHandler(void)
{
    if (TIM_GetITStatus(TIMx, TIM_IT_CC1) != RESET)
    {
        TIM_ClearITPendingBit(TIMx, TIM_IT_CC1);

        // 检测Echo引脚状态
        if (GPIO_ReadInputDataBit(ECHO_PORT, ECHO_PIN))
        {
            // 上升沿,记录开始时间
            capture_start = TIM_GetCapture1(TIMx);
        }
        else
        {
            // 下降沿,记录结束时间
            capture_end = TIM_GetCapture1(TIMx);
            capture_complete = 1;
        }
    }
}

/**
 * @brief  主函数
 */
int main(void)
{
    float distance_mm;
    uint16_t distance_cm, distance_cmf;

    // 系统初始化
    SystemInit();
    delay_init();
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    // 初始化超声波模块
    HC_SR04_Init();

    // 初始化OLED
    OLED_Init();
    OLED_ShowString(1, 1, "Length:");
    OLED_ShowChar(1, 11, '.');
    OLED_ShowString(1, 14, "cm");

    while(1)
    {
        // 获取距离
        distance_mm = HC_SR04_GetDistance();
        
        // 转换为厘米和小数部分
        distance_cm = (uint16_t)(distance_mm / 10);
        distance_cmf = (uint16_t)(distance_mm) % 10;
        
        // 显示距离
        OLED_ShowNum(1, 8, distance_cm, 3);
        OLED_ShowNum(1, 12, distance_cmf, 1);
        
        // 延时
        delay_ms(100);
    }
}

3.使用通用定时器的计数值直接读取

原理:

使用SysTick计数器,直接读取计数器值计算时间差

cpp 复制代码
#include "stm32f10x.h"
#include "delay.h"

// 定义引脚
#define TRIG_PORT GPIOB
#define TRIG_PIN GPIO_Pin_15
#define ECHO_PORT GPIOC
#define ECHO_PIN GPIO_Pin_15

/**
 * @brief  初始化GPIO
 */
void HC_SR04_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    // 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE);

    // 配置Trig引脚
    GPIO_InitStructure.GPIO_Pin = TRIG_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(TRIG_PORT, &GPIO_InitStructure);
    GPIO_ResetBits(TRIG_PORT, TRIG_PIN);

    // 配置Echo引脚
    GPIO_InitStructure.GPIO_Pin = ECHO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
    GPIO_Init(ECHO_PORT, &GPIO_InitStructure);
}

/**
 * @brief  获取测量距离(毫米)
 */
float HC_SR04_GetDistance(void)
{
    float distance_mm = 0;
    uint32_t start_time, end_time, time_us;

    // 发送触发信号
    GPIO_SetBits(TRIG_PORT, TRIG_PIN);
    delay_us(15);
    GPIO_ResetBits(TRIG_PORT, TRIG_PIN);

    // 等待Echo引脚变为高电平
    while(GPIO_ReadInputDataBit(ECHO_PORT, ECHO_PIN) == 0);
    
    // 记录开始时间(使用SysTick计数器)
    start_time = SysTick->VAL;

    // 等待Echo引脚变为低电平
    while(GPIO_ReadInputDataBit(ECHO_PORT, ECHO_PIN) == 1);
    
    // 记录结束时间
    end_time = SysTick->VAL;

    // 计算时间差(微秒)
    // 假设SystemCoreClock为72MHz,SysTick每计数一次为1/72us
    if(end_time < start_time)
    {
        time_us = (start_time - end_time) / 72;
    }
    else
    {
        // 处理SysTick溢出
        time_us = (0xFFFFFF - end_time + start_time) / 72;
    }

    // 计算距离(毫米)
    if(time_us < 24000) // 限制最大测量时间
    {
        distance_mm = time_us * 0.34f / 2.0f;
    }

    return distance_mm;
}

/**
 * @brief  主函数
 */
int main(void)
{
    float distance_mm;
    uint16_t distance_cm, distance_cmf;

    // 系统初始化
    SystemInit();
    delay_init();
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    // 初始化GPIO
    HC_SR04_GPIO_Init();

    // 初始化OLED
    OLED_Init();
    OLED_ShowString(1, 1, "Length:");
    OLED_ShowChar(1, 11, '.');
    OLED_ShowString(1, 14, "cm");

    while(1)
    {
        // 获取距离
        distance_mm = HC_SR04_GetDistance();
        
        // 转换为厘米和小数部分
        distance_cm = (uint16_t)(distance_mm / 10);
        distance_cmf = (uint16_t)(distance_mm) % 10;
        
        // 显示距离
        OLED_ShowNum(1, 8, distance_cm, 3);
        OLED_ShowNum(1, 12, distance_cmf, 1);
        
        // 延时
        delay_ms(100);
    }
}

定时器中断计数:实现简单,逻辑清晰但占用定时器中断资源,计时精度依赖中断处理速度

输入捕获:精度高,不占用主循环时间但配置稍复杂,需要额外定时器通道

SysTick直接计数:无需额外定时器,实现简单但精度可能受系统其他中断影响,SysTick可能被其他功能占用

相关推荐
不做无法实现的梦~15 小时前
ros2实现路径规划---nav2部分
linux·stm32·嵌入式硬件·机器人·自动驾驶
熊猫_豆豆19 小时前
同步整流 Buck 降压变换器
单片机·嵌入式硬件·matlab
chenchen000000001 天前
49元能否买到四核性能?HZ-RK3506G2_MiniEVM开发板评测:MCU+三核CPU带来的超高性价比
单片机·嵌入式硬件
孤芳剑影1 天前
反馈环路设计总结
嵌入式硬件·学习
dump linux1 天前
设备树子系统与驱动开发入门
linux·驱动开发·嵌入式硬件
专注VB编程开发20年1 天前
简易虚拟 PLC 服务器-流水线自动化,上位机程序维护升级,西门子PLC仿真
服务器·单片机·自动化·上位机·plc·流水线·工控
LeoZY_1 天前
CH347/339W开源项目:集SPI、I2C、JTAG、SWD、UART、GPIO多功能为一体(3)
stm32·单片机·嵌入式硬件·mcu·开源
chenchen000000001 天前
国产显示芯势力新篇章:内置DDR+四核A35!MY-SSD2351-MINI开发板深度评测
驱动开发·嵌入式硬件
BackCatK Chen1 天前
第13篇:TMC2240 StallGuard4失速检测|寄存器配置+状态读取(保姆级)
单片机·嵌入式硬件·tmc2240·stm32实战·stallguard4·失速检测·电机故障识别
Hello_Embed1 天前
libmodbus STM32 板载串口实验(双串口主从通信)
笔记·stm32·单片机·学习·modbus