## PWM
介绍了 STM32 的通用定时器 TIM3，用该定时器的中断来控制 DS1 的闪烁

TIM3_REMAP[1:0]重映射控制表, 查对应的引脚

TIM3_CH2 部分重映射可以对应 PB5, PB5 对应红色LED

### 配置

TIM_HandleTypeDef htim6;
三个结构体
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};  // 时钟源
  TIM_MasterConfigTypeDef sMasterConfig = {0};  // 触发器模式
  TIM_OC_InitTypeDef sConfigOC = {0};   //输出比较


  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
  sConfigOC.OCFastMode = TIM_OCFAST_ENABLE;

HAL_TIM_MspPostInit  可以候补配置使用的GPIO
#### 定义引脚  MspPostInit
 
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)

```C
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(timHandle->Instance==TIM3)
  {
  /* USER CODE BEGIN TIM3_MspPostInit 0 */

  /* USER CODE END TIM3_MspPostInit 0 */

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**TIM3 GPIO Configuration
    PB5     ------> TIM3_CH2
    */
    GPIO_InitStruct.Pin = PWM_TIM3_CH2_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(PWM_TIM3_CH2_GPIO_Port, &GPIO_InitStruct);

    // __HAL_AFIO_REMAP_TIM3_PARTIAL();   // 部分重映射
//   __HAL_AFIO_REMAP_TIM3_ENABLE();		// 完全映射
  /* USER CODE BEGIN TIM3_MspPostInit 1 */

  /* USER CODE END TIM3_MspPostInit 1 */
  }

}
```

#### 时间计算
https://blog.csdn.net/weixin_44453694/article/details/122351580
设置分频系数为72，重装载值为500，  1us
自动重装载 

PWM
mode：  PWM1 : CNT << CCR 为有效电平
pulse:  0  占空比  这里设置为200，则占空比为200/500(重装载值为)
output compare preload:  输出比较装载使能
Fastmode   ：  Enable
CH Polarity:  Low  低电平有效

参数说明
Input capture direct mode	输入捕获直接模式
Input capture indirectmode	输入捕获间接模式
Input capture triggered by TRC	输入捕获触发器模式
Output compare no output	输出比较（冻结模式000 ）
Output compare CH3	输出比较(001)
PWM Generation No output	PWM产生无输出
PWM Generation CH3	PWM输出到CH3
CH3 Combined channels	联合通道
xor activation	定时器输入异或模式
 
 启动通道  PostMSP
  HAL_TIM_PWM_Start(timHandle,TIM_CHANNEL_3);//启动通道3
  HAL_TIM_PWM_Start(timHandle,TIM_CHANNEL_4);//启动通道4

TIM1 TIM8 可以在cubemx 看到互补引脚

 #### CCRx
 在向上计数 PWM模式，
且当 CNT<CCRx 时，输出 0，当 CNT>=CCRx 时输出 1

所以：
当 CNT 值小于 CCRx 的时候， 输出低电平，
当 CNT 值大于等于 CCRx 的时候， 输出高电平，
当 CNT 达到 ARR 值的时候，重新归零，然后重新向上计数。
改变 CCRx 的值，就可以改变 PWM 输出的占空比
改变 ARR 的值，就可以改变 PWM 输出的频率
 

#### 输出模式

输出模式 ： 不可以改变频率，改变  pulse
__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,pwm_num);  //设置pwm函数  Pulse 值
比较输出模式  : 可以改变频率， 改变 ARR 自动重装载值


###  捕获 CAP
输入捕获模式可以用来测量脉冲宽度或者测量频率。 捕获电平上升或下降沿中断
中间可能会有溢出， 需要配置溢出中断 

HAL_TIM_Base_Start_IT(&htim5);                // 开启中断
HAL_TIM_IC_Start_IT(&htim5, TIM_CHANNEL_1);   // 开启捕获中断
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)   //溢出回调

捕获回调
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);//点灯
}
***通过分频，设置为1us***


```C

// 参考例程
//可以写在主函数，也可以在time.c
/*
	sta当前的捕获状态： 
	[14]0x4000 =1表示捕获到了上升沿 现在待测量是高电平
	[15]0x8000 =1表示捕获到了下降沿 现在待测量是低电平,此时第6位依然是1
	[0~13] 用于记录计数器溢出次数

	val记录计数器的值
*/
uint16_t tim5_ch1_cap_sta = 0;
uint32_t tim5_ch1_cap_val;

//定时器捕获中断回调函数
//第1次捕获的是上升沿
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim == &htim5)
		{
			HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);//点亮或关闭DS1
			
			if((tim5_ch1_cap_sta&0x8000) == 0)
				{
					if((tim5_ch1_cap_sta&0x4000))//捕获到了下降沿
						{
							tim5_ch1_cap_sta |= 0x8000;
							//获取当前计数器的值
							tim5_ch1_cap_val=HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_1);
							//清除之前的沿触发捕获模式
							TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1);
							//设置为上升沿触发
							TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);
						}
					else//捕获到了上升沿
						{
							/*重启定时器5*/
							//关闭
							__HAL_TIM_DISABLE(&htim5);  
							//计数器清0
							__HAL_TIM_SET_COUNTER(&htim5,0);
							//清除之前的沿触发捕获模式
							TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1);
							//定时器5通道1设置为下降沿捕获
							TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);
							//使能定时器5
							__HAL_TIM_ENABLE(&htim5);
							
							tim5_ch1_cap_sta = 0;
							tim5_ch1_cap_val = 0;
							tim5_ch1_cap_sta |= 0x4000;
						}
				}
		}
}

//计数器溢出中断回调函数（TIM5_CNT 只有16位）
//计数器记录的是自己的执行了多少个周期 如：1Mhz 1us计数器+1
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim == &htim5)
		{
			if((tim5_ch1_cap_sta&0x8000) == 0)
				{
					if((tim5_ch1_cap_sta&0x4000))
						{
							if((tim5_ch1_cap_sta&0x3fff) == 0x3fff)//记录溢出次数过多（高电平太长）
								{
									tim5_ch1_cap_sta |= 0x8000;//强行表示高电平结束
									tim5_ch1_cap_val &= 0xffffffff;
								}
							else
								{
									tim5_ch1_cap_sta++;
								}
						}
				}
		}
}

/*主函数测试部分代码*/
long long temp;
while (1)
{
	if((tim5_ch1_cap_sta&0x8000))//确认当前为低电平
		{
			u1_printf("Capture ok\r\n");
			temp = tim5_ch1_cap_sta&0x3fff;
			temp = temp*65535;
			temp = temp + tim5_ch1_cap_val;
			
			u1_printf("%.2lf s\r\n",(double)temp/(1000*1000));
			HAL_Delay(1000);
			break;
		}
}
u1_printf("done\r\n");

```