STM32的定时器_蓝桥杯信号测量与PWM输出实验_输入捕获/输出比较功能

定时器的PWM生成功能

PWM生成的配置与原理

  • PWM生成与输出比较

    在STM8的教程中,我们通过配置TIM1_CCMR寄存器,启用TIM独立通道的PWM生成功能来生成PWM信号,需要注意的是,PWM生成模式属于输出比较功能的一个子集,两者有所不同

    根据官方的数据手册对TIM1_CCMR_OCiM功能位的介绍:PWM模式1下计数值CNT>CCRx时通道x为有效电平,反之为无效电平;而输出比较模式的原理是每当CCR=CNT时对输出电平反转

    简单来说,PWM生成模式下:通过ARR设定频率,通过CCR设置占空比,起始相位不可更改;输出比较模式则同样通过ARR设置频率,但通过CCR所修改的是起始相位,占空比不可更改(所谓不可更改指的是初始状态确定后,在初始化配置时是可以改变的)

    一般来说我们不必了解这些理论也能使用PWM生成功能,如果需要更具体的介绍参阅这篇文章:https://shequ.stmicroelectronics.cn/thread-633899-1-1.html

  • 启动PWM生成的各项配置
    1. 首先配置时钟,具体见前文,总之可得HCLK=80MHz
    2. 在左侧栏Timers中选择要使用的TIM,在右侧窗口上部分在Channe1菜单选项中选择功能为PWM Generation CH1(TIM15-17需要勾选Activated),让该计时器的通道1输出PWM
    3. 在右侧窗口的下部分Parameter Settings中配置Prescaler分频系数与Counter Period自动重装载系数(这两处数值对应PSC与ARR)
    4. 把引脚连接上刚刚启用的计数器独立通道,PA6引脚的功能选择TIM16_CH1,PA7通道选择TIM17_CH1
  • 计算输出频率

    前文已经写出定时器溢出频率公式(对PWM输出模式也即所生成输出信号的频率):F=CK_PSC/[(PSC+1)(ARR+1)]

    根据要求的输出频率,可得ARR和PSC的配置值:

    • ARR+1=CK_PSC/[f×(PSC+1)]

    比如说,要求输出频率是100Hz,那么ARR+1=80M/[100×(PSC+1)]

    可以得到PSC=8000-1,ARR=100-1

  • 计算输出占空比

    根据输出比较的原理,可知输出信号占空比=CCRx/(ARR+1)

    上文在配置时将ARR配置为100-1,为的是方便得到CCRx的配置值:接续上文的配置,当CCRx=10时,输出的便是占空比为10%的100Hz信号

PWM生成的使用

  • 使能PWM生成函数HAL_TIM_PWM_Start
    void HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim,uint32_t Channel);
    /*
    第一个参数是TIM的句柄,在使用的是TIM1时,此处写&htim1,以此类推
    第二个参数是所使用的通道,这部分有宏定义,按TIM_CHANNEL_x格式写即可
    */
    HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
    //使用例:使用TIM3的CH1输出PWM
    
  • 修改频率与占空比

    在程序中修改ARR、CCRx参数即可实现修改频率、占空比的效果,根据前文的介绍,实时调整ARR需要禁用auto-reload preload以降低延迟

    例如,修改CCR可以使用捕获/比较寄存器设置函数

    __HAL_TIM_SET_COMPARE(TIM_HandleTypeDef *htim,uint32_t Channel,__POLARITY__)
    

    由前一篇介绍的宏函数的原理可知,这样的修改可以通过直接读写寄存器实现,对于知晓寄存器原理的我们来说比使用前文使用宏函数更便捷

    TIM2->ARR=//修改TIM2的ARR,从而修改频率
    TIM2->CCR1=//修改TIM2的通道1的CCR,从而修改占空比
    
  • PWM生成的代码实现

    让PA6(TIM16_CH1)输出100Hz(ARR=99,PSC=7999),PA7(TIM17_CH1)输出200Hz(ARR=99,PSC=3999)的信号,两者占空比默认为10%,使用按钮控制占空比,每按下一次增加10%,将两者的频率与占空比都显示到LCD上

    uint16_t PA6_frq,PA7_frq;
    uint8_t PA6_duty=10,PA7_duty=10;//占空比默认为10
    /* USER CODE END PV */
    
    HAL_TIM_PWM_Start(&htim16,TIM_CHANNEL_1);   //PA6
    HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1);  //PA7
    PA6_frq=10000/(TIM16->ARR+1);//80 000 000/(PSC+1)/(ARR+1)
    PA7_frq=20000/(TIM17->ARR+1);//计算当前输出频率,单位Hz
    TIM16->CCR1=PA6_duty;
    TIM17->CCR1=PA7_duty;//设置当前占空比
    /* USER CODE END 2 */
    
    void Key_Proc(void)
    {
    	key_val=Key_Scan();
    	key_down = key_val & (key_val^key_old);
    	key_up = ~key_val & (key_val^key_old);
    	key_old = key_val;
    	if(key_down==1)
    	{
    		LCD_Clear(Black);
    		LCD_SetBackColor(Black);
    		LCD_SetTextColor(White);
    		if(++lcd_page==3) lcd_page=0;
    	}
    	if(key_down==2&&lcd_page==0)
    	{
    		PA6_duty+=10;
    		if(PA6_duty==100) PA6_duty=10;
    		TIM16->CCR1=PA6_duty;//更改输出占空比
    	}
    	if(key_down==3&&lcd_page==0)
    	{
    		PA7_duty+=10;
    		if(PA7_duty==100) PA7_duty=10;
    		TIM17->CCR1=PA7_duty;
    	}
    }
    void Lcd_Proc(void)
    {
    	if(lcd_page==0)
    	{
    		sprintf(buf,"PA6_frq:%dHz",PA6_frq);
    		LCD_DisplayStringLine(Line4,(uint8_t*)buf);
    		sprintf(buf,"PA7_frq:%dHz",PA7_frq);
    		LCD_DisplayStringLine(Line5,(uint8_t*)buf);
    
    		sprintf(buf,"PA6_duty:%d%%",PA6_duty);
    		LCD_DisplayStringLine(Line7,(uint8_t*)buf);
    		sprintf(buf,"PA7_duty:%d%%",PA7_duty);
    		LCD_DisplayStringLine(Line8,(uint8_t*)buf);
    	}
    }
    

定时器的输入捕获功能

信号频率测量的硬件结构

  • 电路结构

    在蓝桥杯开发板上的四个电位器,左边的R37和R38用于ADC电压测量,而右边的R39和R40便用于频率测量

    这两个电位器连接在XL555的OUT与TRIG之间,通过调节电位器可以实现调整555信号发生器的频率,也即产生了待测信号

  • 引脚连接

    PB4经J9连接到R39的电刷与555的OUT,其可配置为TIM3_CH1的输入引脚

    PA15经J10连接到R40的电刷与555的OUT,其可配置为TIM2_CH1的输入引脚

输入捕获的配置与原理

  • 启动输入捕获的各项配置
    1. 在左侧栏Timers中选择TIM2与TIM3,并在Channel1菜单选项中选择功能为Input Capture direct mode(TIM15-17使用时要在右侧窗口勾选Activated)

    2. 在右侧窗口的下部分Parameter Settings中配置Prescaler分频系数,配置为79(运行频率80MHz减去1)

    3. 切换到各个TIM各自的NVIC Setting标签页,勾选NVIC Interrupt Table一栏下TIM2 global interrupt的Enabled选项,使能中断

    4. 把引脚连接上刚刚启用的计数器独立通道,配置PB4为TIM3_CH1,PA15为TIM2_CH1

    如果需要测量占空比,还需要进一步配置:

    1. 将TIM的Channel2选择为Input Capture indirect mode
    2. 在Channel2的Parameter Settings窗口Polarity Selection中选择Falling Edge
  • 计算输入频率

    捕获开始后,检测到第一个上升沿时,CNT的值复制到CCRx中并开始计数,当再次捕捉到上升沿变化时,根据CNT中的值与上一次捕获的差值,即信号一个周期的持续时间,再根据计数频率可计算信号的频率

    由此原理可知得到,一个周期持续时间t=CCRx/[CK_PSC/(PSC+1)]

    将此式变形得测量到的频率f=CK_PSC/[(PSC+1)×CCRx]

  • 计算输入占空比

    在测量频率的基础上,新增另一通道来捕获下降沿(输入捕获功能可按需设置为上升沿捕获、下降沿捕获与都捕获),自第一个上升沿开始,到捕获到下降沿为高电平持续时间,再到捕获到第二个上升沿时为一个周期

    因为计算占空比要使用两个通道,所以不能使用TIM15-17这些只有一个通道的定时器

    (周期总用时-捕获到下降沿的时间)/周期总用时=占空比

输入捕获的使用

  • 输入捕获中断方式启动函数HAL_TIM_IC_Start_IT

    在使用此函数后,就会开启输入捕获的中断(务必记得要在初始化配置时开启NVIC中断),在捕获到设定的边沿信号后将会产生一个中断,也就是开启定时器

    void HAL_TIM_IC_Start_IT(TIM_HandleTypeDef *htim,uint32_t Channel);
    /*
    第一个参数是TIM的句柄
    第二个参数是所使用的通道
    */
    HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);
    //使用例:启用TIM2的CH1进行输入捕获
    
  • 输入捕获中断方式停止函数HAL_TIM_IC_Stop_IT

    禁止输入捕获中断,关闭输入捕获通道的函数,也就是把原本启动函数的Start改成Stop(或者说将IC替换为其他功能也就是关闭其它功能的函数)

    但是启动与关闭是需要时间的,因此在实现按键或定时启停的功能时如无必要,最好不要直接执行此函数,而是设置一个启停标志位,在检测到标志位为关闭时停止获取到数值的显示

    void HAL_TIM_IC_Stop_IT(TIM_HandleTypeDef *htim,uint32_t Channel);
    
  • 输入捕获中断回调函数HAL_TIM_IC_CaptureCallback

    这个函数将会在捕获到设定边沿产生中断后执行,用于处理输入捕获时的数据

    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
    {
    	//捕获到设定的边沿后执行此函数中的语句
    	if(htim->Instance==TIM2)
    	{
    		//需要对句柄中的标志进行判断来找到中断来源
    		//例如此处语句表示TIM2捕获到对应边沿后需要执行对应操作
    		HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);
    		//重新开启中断以备下一次捕获
    	}
    }
    
  • 获取捕获值HAL_TIM_ReadCapturedValue
    uint32_t HAL_TIM_ReadCapturedValue(TIM_HandleTypeDef *htim,uint32_t Channel)
    //该函数将返回捕获到设定边沿时的CCRx值
    
  • 捕获边沿设置函数__HAL_TIM_SET_CAPTUREPOLARITY

    宏函数,设置输入信号的捕获边沿

    __HAL_TIM_SET_CAPTUREPOLARITY(__HANDLE__,__CHANNEL__,__POLARITY__)
    //__POLARITY__取值范围有:
    //TIM_INPUTCHANNELPOLARITY_RISING 上升沿捕获
    //TIM_INPUTCHANNELPOLARITY_FALLING 下降沿捕获
    //TIM_INPUTCHANNELPOLARITY_BOTHEDGE 双边沿捕获
    
  • 测量频率的代码实现
    uint32_t R40_frq,capture_val1=0;
    uint32_t R39_frq,capture_val2=0;
    /* USER CODE END PV */
    
    HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);
    HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);
    /* USER CODE END 2 */
    
    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
    {
    	if(htim->Instance==TIM2)//如果是TIM2产生了输入捕获中断
    	{
    		capture_val1=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1);//获取CCR捕获值
    		TIM2->CNT=0;//清除计数值,准备下一次计数
    		R40_frq=80000000/(80*capture_val1);//计算频率
    		HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);//重新开启中断
    	}
    	if(htim->Instance==TIM3)
    	{
    		capture_val2=HAL_TIM_ReadCapturedValue(&htim3,TIM_CHANNEL_1);
    		TIM3->CNT=0;
    		R39_frq=80000000/(80*capture_val2);
    		HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);
    	}
    }
    
  • 测量占空比的代码实现
    double duty1=0.0;
    
    void Lcd_Proc(void)
    {
    	if(lcd_page==0)
    	{
    		sprintf(buf,"duty1:%.2lf%%     ",duty1);
    		LCD_DisplayStringLine(Line5,(uint8_t*)buf);
    	}
    }
    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
    {
    	if(htim->Instance==TIM2)
    	{
    		if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1)
    		//由于使用了两个独立通道来测量占空比,因此要选择中断来源于输入通道
    		{
    			capture_val1=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1);//捕获上升沿
    			capture_val1_2=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_2);//捕获下降沿
    			TIM2->CNT=0;
    			R40_frq=80000000/(80*capture_val1);
    			//以上是计算频率的代码
    			duty1 = ((1.0*capture_val1_2)/capture_val1)*100;
    			//计算占空比,乘1.0以转换数据类型,乘100以便用%表示
    			HAL_TIM_IC_Start(&htim2,TIM_CHANNEL_1);
    			HAL_TIM_IC_Start(&htim2,TIM_CHANNEL_2);
    			//重新打开中断
    		}
    	}
    }
    
posted @ 2025-05-16 22:07  无术师  阅读(566)  评论(0)    收藏  举报