你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32Cube HAL 输入捕获——PWM测量

[复制链接]
STMCU小助手 发布时间:2022-4-7 11:17
对于PWM的捕获,我这里一共使用两种方法实现:第一种是PWM输入模式,采用一个定时器的两个通道(通道一和通道二),配置从模式为复位模式,没有进行溢出处理,所以需要考虑捕获的最低频率;第二种是普通的输入捕获模式,采用一个定时器一个通道,有进行溢出处理,所以没有最低频率的限制。

实验内容:使用高级定时器输入捕获测量PWM的周期和占空比。

一、原理图


${TYGVYQ_QQZHC]VL}]I0JU.png

—— —— —— —— —— —— —— ——PWM输入模式—— —— —— —— —— —— —— ——

二、 CubeMX配置

Step1.打开 STM32CubeMX,点击“New Project”,选择芯片型号,STM32F103VETx。

{`R__9]OPC@S~9YN`V}WM{R.png

Step2.选择时钟源,并配置时钟树。选择Crystal/Ceramic Resonator,并配置系统时钟为72M。

BP~6}2PK@A[$@C(BMEXKU.png

){8RU_68G[1(75AP]XK6B4P.png

Step3.配置SYS,我们这里选择的是Serial Wire。(正常情况配置不配置不影响,debug可以使用。但是你不可以把这两个引脚用于其他复用功能,如果用于其他复用功能,debug就不起作用了。)

2K(UN5~C8CH[4AF2$C_]~SM.png

Step4.串口配置(主要为了在串口调试助手显示测试的时间),因为没有用到中断和DMA所以我们就不过多讲解。

[_4]XKMS)RD~)AXHXRN)6BP.png

[6IW{FQLXA[Y@@G${_Y7LRU.png

Step5.接下来进行最主要的定时器配置。首先我们先配置PWM信号,用于等会捕获实验。我这边选用的定时器3通道4(PB1引脚),如原理图所示也就是我们蓝灯的控制IO口,我们更直观的观察到PWM信号的情况。

SA0()2R0XPWX5LAI)EAAQ.png

Y7{7)B45[)E@4`HBDZX~%DU.png

)DDF(AEYEHW911AR4@Q)HTV.png
Step6.定时器输入捕获参数的配置,因为我们使用的PWM捕获模式,我选用的定时器8通道1和通道2.(更改一下:直接模式和间接模式应该叫直连模式和非直连模式)

FE[)8U}FWV2LQ3T3[S3)[KU.png

}MGN)GPIFXG@4IYVA_CCG(D.png

7ZQJA6D[OB]12QT)_$AQ$H4.png

  到这里关于相关参数配置基本已经完成,只需要根据之前文章《STM32Cube HAL:GPIO输入/输出(一)》Step4-Step8,设置相关工程参数和生成代码。

三、添加功能代码

1、我们等会会向串口调试助手发送数据,进行实验结果的验证。 发送数据我们采用printf函数,所有需要重定向c库函数printf到串口。注意使用时需要在keil设置中勾选微库(use mircolib),同时需要添加头文件#include <stdio.h>。重定向代码如下(usart.c)

  1. //重定向c库函数printf到串口DEBUG_USART,重定向后可使用printf函数
  2. int fputc(int ch, FILE *f)
  3. {
  4.         /* 发送一个字节数据到串口DEBUG_USART */
  5.         HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);        
  6.         
  7.         return (ch);
  8. }

  9. //重定向c库函数scanf到串口DEBUG_USART,重写向后可使用scanf、getchar等函数
  10. int fgetc(FILE *f)
  11. {               
  12.         int ch;
  13.         HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, 1000);        
  14.         return (ch);
  15. }
复制代码

2、定义相关变量,以及使能相关定时器和计数处理代码(main.c)

  1. 宏定义、全局变量
  2. #define cnt_clk 72000000/(71+1)//计数器频率
  3. #define arr 65535//自定重装载值   
  4. uint32_t CCR1,CCR2,end_flag;//存捕获寄存器获取的值的变量
  5. float duty_cycle,frequency;//频率,占空比
复制代码
  1. //使能相关定时器以及计数结果处理代码
  2. HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);//开启PWM输出,不需要中断
  3. HAL_TIM_IC_Start_IT(&htim8,TIM_CHANNEL_1);
  4. HAL_TIM_IC_Start_IT(&htim8,TIM_CHANNEL_2);
复制代码

        
  1. //处理代码
  2. while (1)
  3.   {
  4.                 if(end_flag==1)
  5.                 {
  6.                         printf("\r\n频率=%.2fHZ,占空比=%.2f%%\r\n",frequency,duty_cycle);
  7.                         end_flag=0;
  8.                 }
  9.                 HAL_Delay(1000);
  10.   }
复制代码

  3、中断回调函数(捕获中断)(main.c)

  1. void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
  2. {
  3.           /*PWM 信号的第一个上升沿时,定时器产生中断,计数器,CCR寄存器被复位。
  4.     当下降沿到来时,IC2 会捕获,对应的是脉冲宽度测量,但不会产生中断。当第
  5.           二个上升沿时,IC1会捕获,对应的是周期宽度测量,而且会再次进入中断*/
  6.                                 //printf("0");
  7.                                 CCR1=HAL_TIM_ReadCapturedValue(&htim8, TIM_CHANNEL_1);
  8.                                 if(CCR1!=0)
  9.                                 {
  10.                                         CCR2=HAL_TIM_ReadCapturedValue(&htim8, TIM_CHANNEL_2);
  11.                                         frequency=(float)cnt_clk/(CCR1+1);
  12.                                         duty_cycle=(float)(CCR2+1)*100/(CCR1+1);        
  13.                                         end_flag=1;
  14.                                 }
  15.                                 else
  16.                                 {
  17.                                         frequency=0;
  18.                                         duty_cycle=0;
  19.                                 }
  20. }
复制代码


关于PWM模式主要需要的注意有两点:

一、PWM模式只能使用通道1和通道2,PWM输入模式工作原理如下图:

MR6HMUAO1MQGXF851ODL63J.png

_E_N@@YBXAA@6YNDNHWLH.png

二、用于捕获的定时器时基设置,需要考虑到能捕获的最低频率。

公式:能捕获的最低频率f=(时钟频率/(分频系数PSC+1))/(重装载值ARR+1)

结合这次配置的时基,可得:f=(72000000/(71+1))/(65535+1)=15.2HZ

—— —— —— —— —— —— —— 普通的输入捕获模式—— —— —— —— —— —— ——

二、 CubeMX配置

step1-step4与上面PWM输入模式一致

step5.同样是配置PWM信号,用于等会捕获实验。我这边选用的同样是定时器3通道4(PB1引脚)。唯一不同的是,为了验证溢出处理的功能,我做了一点小改动。配置为频率20HZ,占空比为60%的PWM。

T1E{HW%J_M`R26[6PGF7(6U.png

~O5_6JHF~JUHVO0JFPB9MH3.png



Step6.定时器输入捕获参数的配置,因为我们使用的普通的输入捕获模式,我选用的定时器8通道1。这里没有用到从模式,所以不进行配置。捕获频率最低频率设置为33.3HZ,增加使能更新中断,对溢出进行处理,解决了最小捕获频率的限制。因此我们可以捕获小于33.3HZ的频率。

)YNFO_6P(M53N(I_A~5F~TN.png



[HW1M9G~O{TXGB%N[8%Q[HG.png

DD~L`ZNE}0KP@IUBAXA8%N0.png

到这里关于串口参数配置基本已经完成,只需要根据之前文章《STM32Cube HAL:GPIO输入/输出(一)》Step4-Step8,设置相关工程参数和生成代码。

三、添加功能代码

1、我们等会会向串口调试助手发送数据,进行实验结果的验证。 发送数据我们采用printf函数,所有需要重定向c库函数printf到串口。注意使用时需要在keil设置中勾选微库(use mircolib),同时需要添加头文件#include <stdio.h>。(代码同上)

2、定义相关变量,以及使能相关定时器和处理代码(main.c)

  1. //宏定义,及全局变量
  2. #define cnt_clk 72000000/(71+1)//计时器时钟
  3. #define arr 30000  //重装载寄存器的值,根据实际情况设置。
  4. uint32_t ccr_cnt1,ccr_cnt2;//存捕获寄存器获取的值的变量
  5. uint32_t Period_cnt,Period_cnt1,Period_cnt2;//更新中断次数以及存放更新中断次数的变量
  6. uint32_t ic_flag,end_flag;//触发标志位,捕获完成标志
  7. float duty_cycle,frequency;//频率,占空比
复制代码
  1. //使能相关定时器功能
  2. HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);//开启PWM输出,不需要中断
  3.         
  4. __HAL_TIM_CLEAR_IT(&htim8,TIM_CHANNEL_1);//清除更新中断标志位,防止一使能就进入更新中断
  5. HAL_TIM_Base_Start_IT(&htim8);//使能定时器,更新中断
  6. HAL_TIM_IC_Start_IT(&htim8,TIM_CHANNEL_1);//使能定时器,使能捕获输入以及捕获中断
复制代码
  1. //捕获数据处理
  2. while (1)
  3.   {

  4.                 if(end_flag==1)//捕获完成标志位
  5.                 {
  6.              duty_cycle=(float)(Period_cnt1*(arr+1)+ccr_cnt1+1)*100/(Period_cnt2*(arr+1)+ccr_cnt2+1);
  7.                   frequency=(float)cnt_clk/(Period_cnt2*(arr+1)+ccr_cnt2+1);
  8.                         printf("\r\n频率=%.2fHZ,占空比=%.2f%%\r\n",frequency,duty_cycle);
  9.                         end_flag=0;//复位捕获完成标志
  10.                         
  11.                 }
  12.   }
复制代码

3、中断回调函数(更新中断、捕获中断)(main.c)

  1. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  2. {
  3.         Period_cnt++;
  4. }
复制代码
  1. void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
  2. {
  3.                                 switch(ic_flag)//触发标志位判断
  4.                                 {
  5.                                         case 0://第一个上升沿捕获
  6.                                         {
  7.                                                 __HAL_TIM_SET_COUNTER(&htim8,0);//清除计数器的计数
  8.                                                 ccr_cnt1=0;//初始化相关变量
  9.                                                 ccr_cnt2=0;
  10.                                                 Period_cnt=0;
  11.                                                 Period_cnt1=0;
  12.                                                 Period_cnt2=0;
  13.                                                 __HAL_TIM_SET_CAPTUREPOLARITY(&htim8,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_FALLING); //设置成下降沿触发        
  14.                                                 ic_flag=1;//更改捕获标志位,进入case1进行相关变量的处理。
  15.                                                 break;
  16.                                         }
  17.                                         case 1://第一个下降沿捕获
  18.                                         {
  19.                                                 ccr_cnt1=__HAL_TIM_GET_COMPARE(&htim8,TIM_CHANNEL_1);//获取存放在CCR寄存器的值(捕获值)
  20.                                                 Period_cnt1=Period_cnt;//获取第一个下降沿到来时的进入更新中断的次数
  21.                                                 __HAL_TIM_SET_CAPTUREPOLARITY(&htim8,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_RISING);        //设置成上升沿触发        
  22.                                                 ic_flag=2;        //更改捕获标志位,进入case2进行相关变量的处理
  23.                                                 break;
  24.                                         }
  25.                                         case 2://第二个上沿捕获        
  26.                                         {
  27.                                                 ccr_cnt2=__HAL_TIM_GET_COMPARE(&htim8,TIM_CHANNEL_1);//获取存放在CCR寄存器的值(捕获值)
  28.                                                 Period_cnt2=Period_cnt;//获取第二个上升沿到来时的进入更新中断的次数
  29.                                                 ic_flag=0;        //更改捕获标志位,进入case0进行相关变量的处理。
  30.                                                 end_flag=1;//捕获完成标志
  31.                                                 break;
  32.                                         }        
  33.                                         default:
  34.                                         break;
  35.                                 }
  36. }
复制代码


关于普通的输入模式捕获PWM。主要工作流程:

1、第一次捕获到上升沿,计数器清零,存捕获寄存器值的变量清零,存中断次数的变量清零。改变触发边沿为下降沿触发,并将触发标志位为设置为1,使下次下降沿触发时,能进行相对应的处理。

2、第一次捕获到下降沿,将捕获寄存器(CCR寄存器)的值,中断次数存入相应的变量。同时更改触发边沿为上升沿触发,并将触发标志位为设置为2,使下次上升沿触发时,能进行相对应的处理。

3、第二次捕获到上升沿,再次将捕获寄存器(CCR寄存器)的值,中断次数存入相应的变量。将触发标志设置为0,目的为能够循环捕获。同时捕获完成标志为置1。

4、在main函数中,使用在while循环,使用if语句进行捕获标志的轮询,从而在捕获完成后进行相应的数据处理。

虽然目前代码的配置以及实验结果是没有问题的。但是在调试时候,碰到一个比较疑惑的问题,ARR的设置影响了捕获的准确性,比如在设置为999和1000的时候,同样是存在溢出的,也进行了溢出处理。但是AAR=999时候频率不稳定,AAR=1000频率正常。暂时还是没法找到合理的解释。


THB5_2EM%0]V34WF8O@B0BP.png
WF4GIIJJ)@I0@ZJ(8)}KOTD.png
收藏 评论0 发布时间:2022-4-7 11:17

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版