文章目录
- 
- [SysTick 和 TIM](#SysTick 和 TIM)
- 
- 
- [1. SysTick 系统嘀嗒](#1. SysTick 系统嘀嗒)
- 
- [1.1 SysTick 系统嘀嗒概述](#1.1 SysTick 系统嘀嗒概述)
- [1.2 SysTick 核心作用](#1.2 SysTick 核心作用)
- [1.3 定时器的基本实现图例](#1.3 定时器的基本实现图例)
- [1.4 SysTick 相关寄存器内容](#1.4 SysTick 相关寄存器内容)
- 
- [1.4.1 SysTick重载值寄存器 (SYST_RVR)](#1.4.1 SysTick重载值寄存器 (SYST_RVR))
- [1.4.2 SysTick重载值寄存器 (SYST_RVR)](#1.4.2 SysTick重载值寄存器 (SYST_RVR))
- [1.4.3 SysTick当前值寄存器 (SYST_CVR)](#1.4.3 SysTick当前值寄存器 (SYST_CVR))
 
- [1.5 系统内核针对于 SysTick_Config 操作函数](#1.5 系统内核针对于 SysTick_Config 操作函数)
- [1.6 精准延时控制函数](#1.6 精准延时控制函数)
- 
- [1.6.1 SysTick_Handler 函数注释](#1.6.1 SysTick_Handler 函数注释)
- [1.6.2 代码实现](#1.6.2 代码实现)
 
 
- [2. 基础定时器 TIM6 和 TIM7](#2. 基础定时器 TIM6 和 TIM7)
- 
- [2.1 基础定时器概述](#2.1 基础定时器概述)
- [2.2 基础定时器开发流程](#2.2 基础定时器开发流程)
- 
- [2.2.1 开发流程概述](#2.2.1 开发流程概述)
- [2.2.2 TIMx_PSC 预分频倍数寄存器](#2.2.2 TIMx_PSC 预分频倍数寄存器)
- [2.2.3 TIMx_ARR 自动重装载寄存器](#2.2.3 TIMx_ARR 自动重装载寄存器)
- [2.2.4 TIMx_CR1控制寄存器](#2.2.4 TIMx_CR1控制寄存器)
- [2.2.5 TIMx_DIER DMA/中断使能寄存器](#2.2.5 TIMx_DIER DMA/中断使能寄存器)
- [2.2.6 TIMx_SR 定时器状态寄存器](#2.2.6 TIMx_SR 定时器状态寄存器)
- [2.2.7 TIM6 定时器案例](#2.2.7 TIM6 定时器案例)
 
 
- [3. 通用定时器](#3. 通用定时器)
- 
- [3.1 通用定时器概述](#3.1 通用定时器概述)
- [3.2 通用定时器框图分析](#3.2 通用定时器框图分析)
- [3.3 通用定时器基本功能](#3.3 通用定时器基本功能)
- [3.4 PWM 实现呼吸灯](#3.4 PWM 实现呼吸灯)
- 
- [3.4.1 PWM 概述](#3.4.1 PWM 概述)
- [3.4.2 AFIO 控制定时器对应输出通道重映射](#3.4.2 AFIO 控制定时器对应输出通道重映射)
- [3.4.3 TIM3CH2 寄存器分析](#3.4.3 TIM3CH2 寄存器分析)
- [3.4.5 核心代码实现](#3.4.5 核心代码实现)
 
 
- [4. 作业](#4. 作业)
- 
- [4.1 作业要求](#4.1 作业要求)
- [4.2 SG90 舵机相关参数](#4.2 SG90 舵机相关参数)
- [4.3 相关寄存器分析](#4.3 相关寄存器分析)
- [4.4 高阶实现](#4.4 高阶实现)
 
 
 
- 
 
SysTick 和 TIM
1. SysTick 系统嘀嗒
1.1 SysTick 系统嘀嗒概述
- SysTick 是 ARM Cortex-M 内核芯片内部设计的系统定时器。
- 内置一个 24 位递减计数器,最大数值多少 0x0 ~ 0xFF FFFF
- SysTick 响应速度很快,基本可以认为 SysTick 单次计数周期时间是 当前 MCU 的 1/频率,例如 72MHz STM32F103,单次时间为 13.8888ns,从而提供标准的定时器时间标准。
1.2 SysTick 核心作用
- 为操作系统 OS 提供心跳时钟,精准时间参考,操作系统有 RTT(RT_Thread) FreeRTOS, Hi3861 中 lite-OS
- 实现代码的精准延时,相较于 _nop() 操作,计时精度更高
- 作为同样 TIM 定时器的参考依据
1.3 定时器的基本实现图例

1.4 SysTick 相关寄存器内容
1.4.1 SysTick重载值寄存器 (SYST_RVR)

| 位域 | 名称 | 功能描述 | 
|---|---|---|
| 16 | COUNTFLAG | 计数标志位 (只读) • 当计数器从1递减到0时,该位会被硬件自动置1。 • 读取该寄存器后,该位会自动清零 。也可以通过向 SYST_CVR寄存器写入任何值来清零。 | 
| 2 | CLKSOURCE | 时钟源选择位 (读写) • 0: 使用外部参考时钟(AHB/8,具体取决于芯片设计)。 •1: 使用处理器内核时钟(AHB,即HCLK)。 注意 :通常为了获得更精确的定时,会选择内核时钟(CLKSOURCE=1)。 | 
| 1 | TICKINT | 中断使能位 (读写) • 0: 计数器减到0时不产生 SysTick异常(中断)请求。 •1: 计数器减到0时产生 SysTick异常(中断)请求(异常号15)。 注意:如果使用SysTick做操作系统心跳,此位必须置1。如果仅用于查询式延时,则可以置0。 | 
| 0 | ENABLE | 计数器使能位 (读写) • 0: 关闭SysTick计数器。 •1: 开启SysTick计数器。 注意 :开启后,计数器立即从SYST_RVR装载值并开始递减。 | 
1.4.2 SysTick重载值寄存器 (SYST_RVR)

| 位域 | 名称 | 功能描述 | 
|---|---|---|
| 23:0 | RELOAD | 重载值 (读写) • 当计数器减到0时,下一次时钟到来时, SYST_CVR的值会被自动重载为此值,并继续递减。 • 写入一个新值到该寄存器后,它不会立即影响当前的计数周期。当前周期结束后,新的值才会被加载 。 • 计算公式 :定时时间 = (RELOAD + 1) * (1 / CLK频率)• Example :HCLK=72MHz, 要产生1ms中断,则RELOAD = (72,000,000 / 1000) - 1 = 71999。 | 
1.4.3 SysTick当前值寄存器 (SYST_CVR)

| 位域 | 名称 | 功能描述 | 
|---|---|---|
| 23:0 | CURRENT | 当前计数值 (读写) • 读操作 :返回当前计数器的瞬时值。 • 写操作 :向该寄存器写入任何值,都会将计数器清0 ,同时清除 SYST_CSR中的COUNTFLAG状态位。写入操作不会触发重载。 注意:在调试时读取此寄存器可以知道还剩多少计数。在初始化时,通常通过写入此寄存器来将其清零,以确保第一个定时周期的准确性。 | 
1.5 系统内核针对于 SysTick_Config 操作函数
            
            
              c
              
              
            
          
          static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* Reload value impossible */
                                                               
  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  
    						/* set Priority for Cortex-M0 System Interrupts */
  SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
                   SysTick_CTRL_TICKINT_Msk   | 
                   SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */
  return (0);                                                  /* Function successful */
}
1.6 精准延时控制函数
1.6.1 SysTick_Handler 函数注释
请注意保存!!!

1.6.2 代码实现
            
            
              c
              
              
            
          
          #include "systick.h"
u16 SysTick_Count = 0;
void SysTick_Delay_us(u32 us) 
{
	u32 temp;
	/*
	1us 对应的 reload_value 为 72 - 1 ==> 71
	5us 对应 72 * 5 - 1 = 360 - 1 = 359
	*/
	u32 reload_value = SYS_CLK_FREQ / 1000 / 1000 - 1;
	
	/*
	判断是否超出最大重装载值
	*/
	if (reload_value > SysTick_LOAD_RELOAD_Msk)
	{
		reload_value = 	SysTick_LOAD_RELOAD_Msk;
	}
	
	// 修改 SysTick 对应的重装载值
	SysTick->LOAD = reload_value;
	
	// 这是当前计数器初始化为 0
	SysTick->VAL = 0;
	
	SysTick->CTRL |= 
		SysTick_CTRL_CLKSOURCE_Msk |
		SysTick_CTRL_TICKINT_Msk   |
		SysTick_CTRL_ENABLE_Msk;
	
	
	/*
	利用 SysTick->CTRL 寄存器中 COUNTFLAG 计数器标志位
		如果当前计数器到 0, COUNTFLAG 标志灯对应 1
		如果当前计数器未到 0, COUNTFLAG 标志灯对应 0	
	*/
	for (u32 i = 0; i < us; i++) 
	{
		while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
	}
	/*
	需要对应当前 SysTick->CTRL 寄存器中 COUNTFLAG 标志位进行清除 0 操作。
	*/
	temp = SysTick->CTRL;
	
	/*
	当前系统嘀嗒延时任务已经完成,关闭当前 SysTick
	*/
	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
	
}
void SysTick_Delay_ms(u32 ms) 
{
	u32 temp;
	/*
	1ms 对应的 reload_value 为 72000 - 1 ==> 71999
	5ms 对应 72000 * 5 - 1 = 360000 - 1 = 359999
	*/
	u32 reload_value = SYS_CLK_FREQ / 1000 - 1;
	
	/*
	判断是否超出最大重装载值
	*/
	if (reload_value > SysTick_LOAD_RELOAD_Msk)
	{
		reload_value = 	SysTick_LOAD_RELOAD_Msk;
	}
	
	// 修改 SysTick 对应的重装载值
	SysTick->LOAD = reload_value;
	
	// 这是当前计数器初始化为 0
	SysTick->VAL = 0;
	
	SysTick->CTRL |= 
		SysTick_CTRL_CLKSOURCE_Msk |
		SysTick_CTRL_TICKINT_Msk   |
		SysTick_CTRL_ENABLE_Msk;
	
	
	/*
	利用 SysTick->CTRL 寄存器中 COUNTFLAG 计数器标志位
		如果当前计数器到 0, COUNTFLAG 标志灯对应 1
		如果当前计数器未到 0, COUNTFLAG 标志灯对应 0
	
	for 循环控制当前的总延时时间,当前延时函数单位对应 1ms
	while 控制当前 SysTick 定时器重复工作
	*/
	
	for (u32 i = 0; i < ms; i++) 
	{
		while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
	}
	/*
	需要对应当前 SysTick->CTRL 寄存器中 COUNTFLAG 标志位进行清除 0 操作。
	*/
	temp = SysTick->CTRL;
	
	/*
	当前系统嘀嗒延时任务已经完成,关闭当前 SysTick
	*/
	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
/*
以上延时函数都有对应的中断触发,中断是当前 SysTick 完成定时任务之后
触发中断内容,在当前中断中,完成变量累加操作。
*/
void SysTick_Handler(void)
{
	/*
	系统嘀嗒之外完成一次任务,SysTick_Count += 1
	系统嘀嗒计数器到 0 SysTick_Count += 1
	*/
	SysTick_Count += 1;
}
void SysTick_Init(void) 
{
	// 1. 清理整了个 SysTick->CTRL 所有相关配置
	// 关闭当前 SysTick 系统嘀嗒
	SysTick->CTRL = 0;
	
	// 2. 设置当前 SysTick 对应的时钟源为内部使用
	SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk;
	
	// 3. 设置当前 SysTick 对应的 Tick Interrupt 开启
	SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;
	
	// 4. 对当前计数器和对应重载数据进行初始化赋值
	// 分别 VAL = 0 LOAD 为当前重加载最大值。
	SysTick->VAL = 0;
	// SysTick_LOAD_RELOAD_Msk ==> 0xFFFFFFul 
	SysTick->LOAD = SysTick_LOAD_RELOAD_Msk - 1;
	
	/*
	SysTick_IRQn 对应的优先级是最高优先级
	*/
	NVIC_SetPriority(SysTick_IRQn, 0);
	
	/*
	开启 SysTick
	*/
	SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
}2. 基础定时器 TIM6 和 TIM7
2.1 基础定时器概述
TIM6和TIM7定时器的主要功能包括:
- 16位自动重装载累加计数器
- 16位可编程(可实时修改)预分频器,用于对输入的时钟按系数为1~65536之间的任意数值分频
- 触发DAC的同步电路
- 在更新事件(计数器溢出)时产生中断/DMA请求


2.2 基础定时器开发流程
2.2.1 开发流程概述
- 定时器时钟使能,案例使用 TIM6
- 需要配置 PSC 预分频器数据
- 自动重装载寄存器数据
- 计数器累加或者递减规则
- 注册 IRQn 和实现对应的 TIM6_IRQHandler 中断函数
2.2.2 TIMx_PSC 预分频倍数寄存器

2.2.3 TIMx_ARR 自动重装载寄存器

2.2.4 TIMx_CR1控制寄存器

2.2.5 TIMx_DIER DMA/中断使能寄存器

2.2.6 TIMx_SR 定时器状态寄存器

2.2.7 TIM6 定时器案例
            
            
              c
              
              
            
          
          #include "tim6.h"
void TIM6_Init(u16 psc, u16 arr)
{
	// 1. 定时器 TIM6 时钟使能
	RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;
	
	/*
	2. 【定时器关闭】避免之前定时器任务的冲突
	因为定时器开启状态下,无法对 PSC 和 ARR 进行有效修改
	*/
	TIM6->CR1 &= ~(0x01);
	
	/*
	3. PSC 预分配寄存器和 ARR 自动重装载寄存器配置
		寄存器存储数据是真实期望数据 - 1
		假设 PSC 预分配寄存器配置倍数为 1,
			需要提供给 PSC 寄存器数据为 1 - 1 ==> 0
		假设 ARR 自动重装载寄存器位 7200
			需要提供给 PSC 寄存器数据为 7200 - 1 ==> 7199
	【注意】
		PSC 和 ARR 都是 16 位寄存器,对应数据有效范围是 1 ~ 65536
	*/
	TIM6->PSC = psc - 1;
	TIM6->ARR = arr - 1;
	
	/*
	4. 中断使能控制
		TIM6->DIER 对应 UIE [位0] 控制为 1
	*/
	TIM6->DIER |= 0x01;
	
	/*
	5. 定时器开启
	*/
	TIM6->CR1 |= 0x01;
	/*
	6. 利用 NVIC 注册中断内容
		6.1 利用 EnableIRQ 注册对应的中断请求编号
		6.2 给予当前 TIM6 定时器中断优先级配置
	*/
	NVIC_EnableIRQ(TIM6_IRQn);
	NVIC_SetPriority(TIM6_IRQn, 5);
}
u16 TIM6_IT_Count = 0;
void TIM6_IRQHandler(void)
{
	/*
	根据 TIM6 状态寄存器 SR 判断当前中断是否触发
	*/
	if (TIM6->SR & 0x01)
	{
		// 清除中断标志位
		TIM6->SR = 0;
		
		if (TIM6_IT_Count % 2)
		{
			Led0_Ctrl(1);
			Led1_Ctrl(0);
		}
		else
		{
			Led0_Ctrl(0);
			Led1_Ctrl(1);
		}
		
		TIM6_IT_Count += 1;
	}
}main.c
            
            
              c
              
              
            
          
          #include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "beep.h"
#include "usart1.h"
#include "adc.h"
#include "systick.h"
#include "tim6.h"
int main(void)
{
	Led_Init();
	Beep_Init();
	
	NVIC_SetPriorityGrouping(2);
	
	/*
	PSC 7200 ==> 100 us 
	ARR 10000
	根据当前 MCU 的时钟 72 MHz 情况下,对应的时间为 1 s
	【注意】
		PSC 和 ARR 数据都不可以超过 65536
	*/
	TIM6_Init(7200, 10000);
	
	while (1)
	{
		Beep_Alarm();
	}
	
}3. 通用定时器
3.1 通用定时器概述

3.2 通用定时器框图分析
通用定时器核心模块

通用定时器TIM比较捕获输出控制框图

通用定时器输入端口概述

通用定时器在目前的 STM32F103ZET6,对应 TIM2 ~ TIM5,每一个定时器对应 4 通道(Channel),可以进入输入捕获,也可以进行输出控制。
3.3 通用定时器基本功能
操作流程和基础定时器 TIM6 和 TIM7 一致
            
            
              c
              
              
            
          
          #include "tim6.h"
void TIM6_Init(u16 psc, u16 arr)
{
	// 1. 定时器 TIM6 时钟使能
	RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;
	
	/*
	2. 【定时器关闭】避免之前定时器任务的冲突
	因为定时器开启状态下,无法对 PSC 和 ARR 进行有效修改
	*/
	TIM6->CR1 &= ~(0x01);
	
	/*
	3. PSC 预分配寄存器和 ARR 自动重装载寄存器配置
		寄存器存储数据是真实期望数据 - 1
		假设 PSC 预分配寄存器配置倍数为 1,
			需要提供给 PSC 寄存器数据为 1 - 1 ==> 0
		假设 ARR 自动重装载寄存器位 7200
			需要提供给 PSC 寄存器数据为 7200 - 1 ==> 7199
	【注意】
		PSC 和 ARR 都是 16 位寄存器,对应数据有效范围是 1 ~ 65536
	*/
	TIM6->PSC = psc - 1;
	TIM6->ARR = arr - 1;
	
	/*
	4. 中断使能控制
		TIM6->DIER 对应 UIE [位0] 控制为 1
	*/
	TIM6->DIER |= 0x01;
	
	/*
	5. 定时器开启
	*/
	TIM6->CR1 |= 0x01;
	/*
	6. 利用 NVIC 注册中断内容
		6.1 利用 EnableIRQ 注册对应的中断请求编号
		6.2 给予当前 TIM6 定时器中断优先级配置
	*/
	NVIC_EnableIRQ(TIM6_IRQn);
	NVIC_SetPriority(TIM6_IRQn, 5);
}
u16 TIM6_IT_Count = 0;
void TIM6_IRQHandler(void)
{
	/*
	根据 TIM6 状态寄存器 SR 判断当前中断是否触发
	*/
	if (TIM6->SR & 0x01)
	{
		// 清除中断标志位
		TIM6->SR = 0;
		
		if (TIM6_IT_Count % 2)
		{
			Led0_Ctrl(1);
			Led1_Ctrl(0);
		}
		else
		{
			Led0_Ctrl(0);
			Led1_Ctrl(1);
		}
		
		TIM6_IT_Count += 1;
	}
}3.4 PWM 实现呼吸灯
3.4.1 PWM 概述
- PWM 核心参数
- 频率 : 1 秒钟,PWM 输出波形的次数。需要根据当前设备场景进行分析。
- 占空比:一个 PWM 周期内对应的高电平时间/整个 PWM 周期时间。
- 需要利用 TIM2 ~ 5 通用定时器完成 PWM 输出

3.4.2 AFIO 控制定时器对应输出通道重映射
- 主要针对不同的 TIM 定时对外输出通道引脚映射关系。

- TIM3 定时器通道对应的复用引脚关系

3.4.3 TIM3CH2 寄存器分析
TIM3 CCMR 输入输出模式控制寄存器

TIM3 CCER 控制捕获比较使能极性寄存器


TIMx_CCR2 捕获比较寄存器数据寄存器

3.4.5 核心代码实现
TIM3_CH2_PB5_PWM 和 TIM3_CH2_PB5_CCR
            
            
              c
              
              
            
          
          void TIM3_CH2_PB5_PWM(u16 psc, u16 arr)
{
	/*
	1. 时钟使能
		1.1 TIM3 使能
		1.2 AFIO 使能,因为需要控制 TIM3_CH2 通道的重映射操作
			需要 AFIO 进行配合
		1.3 PB5 使能
	*/
	RCC->APB2ENR |= (RCC_APB2ENR_AFIOEN | RCC_APB2ENR_IOPBEN);
	RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
	
	/*
	【关闭当前定时器】防止之前存在定时器任务
	无法修改 PSC 和 ARR 数据
	*/
	TIM3->CR1 = 0;
	
	/*
	2. PB5 设置工作模式为 GPIO 复用推挽输出模式,速度为 50 MHz
		【复用推挽输出模式】因为当前 GPIO 的输出模式收到外设影响
			同时需要满足可以提供高低电平的模式,选择复用推挽输出模式
	*/
	GPIOB->CRL &= ~(0x0F << 20);
	GPIOB->CRL |= (0x0B << 20);
	
	/*
	3. AFIO IO复用功能开启,同时设置 TIM3_CH2 重映像
		需要设置 TIM3 部分重映射,设置完成 CH2 ==> PB5 引脚
		对应 DS0 LED 灯
	*/
	AFIO->MAPR &= ~(0x03 << 10);
	AFIO->MAPR |= 0x02 << 10;
	
	/*
	4. 定时器基础功能控制
		PSC 预分配倍数
		ARR 自动重装载寄存器
	*/
	TIM3->PSC = psc - 1;
	TIM3->ARR = arr - 1;
	
	/*
	5. TIM3_CH2 配置为对外输出,要求
		1. 对应输出模式 PWM1
		2. 随时可以修改 CCR2 数据内容
	*/
	TIM3->CCMR1 &= ~(0xFF << 8);
	TIM3->CCMR1 |= (0x64 << 8);
	
	/*
	6. 当前通过 PWM 控制 DS0 LED
	对应计数器是向下规则
		CNT > CCR2 输出低电平,文档中对应 无效电平
		CNT < CCR2 输出高电平,文档中对应 有效电平
	设置有效电平为高电平
	*/
	TIM3->CCER &= ~(0x03 << 4);
	TIM3->CCER |= (0x01 << 4);
	
	/*
	7. 开启 TIM3 定时器
	*/
	TIM3->CR1 |= 0x01;
}
void TIM3_CH2_PB5_CCR(u16 ccr)
{
	TIM3->CCR2 = ccr;
}main.c
            
            
              c
              
              
            
          
          #include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "beep.h"
#include "usart1.h"
#include "adc.h"
#include "systick.h"
#include "tim6.h"
#include "tim3.h"
int main(void)
{
	Led_Init();
	Beep_Init();
	
	// Led1_Ctrl(1);
	
	/*
	PSC = 72
	ARR = 1000
		当前定时器执行任务周期为 1 ms
	*/
	TIM3_CH2_PB5_PWM(72, 1000);
	
	u16 duty_value = 0;
	u8 flag = 0;
	
	while (1)
	{
		TIM3_CH2_PB5_CCR(duty_value);
		
		if (flag)
		{
			duty_value -= 10;
			
			if (duty_value <= 0)
			{
				flag = 0;
			}
		}
		else
		{
			duty_value += 10;
			
			if (duty_value >= 1000)
			{
				flag = 1;
			}
				
		}
		
		SysTick_Delay_ms(10);
	}
	
}4. 作业
4.1 作业要求
- 实现定时器输出 PWM 控制舵机
- 利用按键 KEY_2 KEY_1 KEY_0
- KEY_2 实现顺时针旋转
- KEY_1 实现舵机停止
- KEY_0 实现逆时针旋转
- 定时器可以选择 TIM2 TIM3 TIM4 TIM5 注意接线和供电
4.2 SG90 舵机相关参数
- 要求 PWM 对应频率是 50 Hz
- 占空比 7.5 % 舵机通知状态
- 占空比 2.5 % ~ 7.5% 顺时针转动
- 占空比 7.5% ~ 12.5% 逆时针转动
4.3 相关寄存器分析
根据手册选择 TIM2_CH4 通道,在未重映像情况下对应已经是 PA3
PA3 工作模式要求为 GPIO 复用推挽模式
PWM 需求是 50 Hz ,对应的时间周期为 20 ms
- PSC => 7200 - 1 ==> 计数器累加/递减一次时间周期是 100 us
- ARR ==> 200 - 1 ==> 重装载计数器对应的 199 的情况下,每一个 TIM 定时器周期周期控制在 20 ms
TIM2_CH4 端口选择输出 ,对应的输出模式选择 PWM1 或者 PMW2 工作模式。
定时器中,控制占空比的比较寄存器数据可以随时修改,从而控制舵机工作。
根据 ARR 数据分析, ARR数据为 200 - 1
占空比 比较寄存器数据范围 CCR4 7.5% 15 2.5% ~ 7.5% 5 ~ 15 7.5% ~ 12.5% 15 ~ 25 
4.4 高阶实现
- 按键 KEY_UP 用于切换当前舵机的旋转方向 + 停止
- KEY_2 舵机旋转加速
- KEY_1 舵机旋转停止旋转
- KEY_0 舵机旋转减速